1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #include <assert.h>
5 #include <fcntl.h>
6 #include <sys/socket.h>
7 #include <unistd.h>
8
9 #include <lib/fdio/util.h>
10 #include <unittest/unittest.h>
11 #include <zircon/processargs.h>
12 #include <zircon/syscalls.h>
13 #include <zircon/types.h>
14
create_socket_fdio_pair(zx_handle_t * socket_out,int * fd_out)15 static bool create_socket_fdio_pair(zx_handle_t* socket_out, int* fd_out) {
16 // Create new socket pair.
17 zx_handle_t s1, s2;
18 ASSERT_EQ(ZX_OK, zx_socket_create(ZX_SOCKET_STREAM | ZX_SOCKET_HAS_CONTROL, &s1, &s2), "Socket create failed");
19
20 // Convert one socket to FDIO
21 uint32_t type = PA_FDIO_SOCKET;
22 int fd;
23 ASSERT_EQ(ZX_OK, fdio_create_fd(&s2, &type, 1, &fd), "Socket from handle failed");
24
25 *fd_out = fd;
26 *socket_out = s1;
27
28 return true;
29 }
30
set_nonblocking_io(int fd)31 static bool set_nonblocking_io(int fd) {
32 int flags = fcntl(fd, F_GETFL);
33 EXPECT_NE(-1, flags, "fcntl failed");
34 EXPECT_NE(-1, fcntl(fd, F_SETFL, flags | O_NONBLOCK), "Set NONBLOCK failed");
35 return true;
36 }
37
38 // Verify scenario, where multi-segment recvmsg is requested, but the socket has
39 // just enough data to *completely* fill one segment.
40 // In this scenario, an attempt to read data for the next segment immediately
41 // fails with ZX_ERR_SHOULD_WAIT, and this may lead to bogus EAGAIN even if some
42 // data has actually been read.
socket_recvmsg_nonblock_boundary_test(void)43 bool socket_recvmsg_nonblock_boundary_test(void) {
44 BEGIN_TEST;
45
46 zx_handle_t s;
47 int fd;
48
49 if (!create_socket_fdio_pair(&s, &fd) || !set_nonblocking_io(fd)) {
50 return false;
51 }
52
53 // Write 4 bytes of data to socket.
54 size_t actual;
55 const uint32_t data_out = 0x12345678;
56 EXPECT_EQ(ZX_OK, zx_socket_write(s, 0, &data_out, sizeof(data_out), &actual), "Socket write failed");
57 EXPECT_EQ(sizeof(data_out), actual, "Socket write length mismatch");
58
59 uint32_t data_in1, data_in2;
60 // Fail at compilation stage if anyone changes types.
61 // This is mandatory here: we need the first chunk to be exactly the same
62 // length as total size of data we just wrote.
63 assert(sizeof(data_in1) == sizeof(data_out));
64
65 struct iovec iov[2];
66 iov[0].iov_base = &data_in1;
67 iov[0].iov_len = sizeof(data_in1);
68 iov[1].iov_base = &data_in2;
69 iov[1].iov_len = sizeof(data_in2);
70
71 struct msghdr msg;
72 msg.msg_name = NULL;
73 msg.msg_namelen = 0;
74 msg.msg_iov = iov;
75 msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
76 msg.msg_control = NULL;
77 msg.msg_controllen = 0;
78 msg.msg_flags = 0;
79
80 actual = recvmsg(fd, &msg, 0);
81 EXPECT_EQ(4u, actual, "Socket read failed");
82
83 zx_handle_close(s);
84 close(fd);
85 END_TEST;
86 }
87
88 // Verify scenario, where multi-segment sendmsg is requested, but the socket has
89 // just enough spare buffer to *completely* read one segment.
90 // In this scenario, an attempt to send second segment should immediately fail
91 // with ZX_ERR_SHOULD_WAIT, but the sendmsg should report first segment length
92 // rather than failing with EAGAIN.
socket_sendmsg_nonblock_boundary_test(void)93 bool socket_sendmsg_nonblock_boundary_test(void) {
94 BEGIN_TEST;
95
96 const size_t memlength = 65536;
97 void* memchunk = malloc(memlength);
98
99 struct iovec iov[2];
100 iov[0].iov_base = memchunk;
101 iov[0].iov_len = memlength;
102 iov[1].iov_base = memchunk;
103 iov[1].iov_len = memlength;
104
105 zx_handle_t s;
106 int fd;
107
108 if (!create_socket_fdio_pair(&s, &fd) || !set_nonblocking_io(fd)) {
109 return false;
110 }
111
112 struct msghdr msg;
113 msg.msg_name = NULL;
114 msg.msg_namelen = 0;
115 msg.msg_iov = iov;
116 msg.msg_iovlen = sizeof(iov) / sizeof(*iov);
117 msg.msg_control = NULL;
118 msg.msg_controllen = 0;
119 msg.msg_flags = 0;
120
121 // 1. Keep sending data until socket can take no more.
122 while (sendmsg(fd, &msg, 0) > 0)
123 ;
124
125 // 2. Consume one segment of the data
126 size_t actual = 0;
127 zx_socket_read(s, 0, memchunk, memlength, &actual);
128 EXPECT_EQ(memlength, actual, "Failed to read from a full socket");
129
130 // 3. Push again 2 packets of <memlength> bytes, observe only one sent.
131 EXPECT_EQ((ssize_t)memlength, sendmsg(fd, &msg, 0),
132 "Partial sendmsg failed; is the socket buffer varying?");
133
134 zx_handle_close(s);
135 close(fd);
136 free(memchunk);
137 END_TEST;
138 }
139
140 BEGIN_TEST_CASE(newsocket_tests)
141 RUN_TEST(socket_recvmsg_nonblock_boundary_test)
142 RUN_TEST(socket_sendmsg_nonblock_boundary_test)
143 END_TEST_CASE(newsocket_tests)
144