test_echo_tcp_sendmsg_recvmsg_fd: add test_tcp_sendmsg_recvmsg_fd_different() tests
[socket_wrapper.git] / tests / test_echo_tcp_sendmsg_recvmsg_fd.c
1 #include <stdarg.h>
2 #include <stddef.h>
3 #include <setjmp.h>
4 #include <cmocka.h>
5
6 #include "config.h"
7 #include "torture.h"
8
9 #include <sys/types.h>
10 #include <sys/socket.h>
11 #include <sys/wait.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14
15 #include <errno.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <stdio.h>
21
22 static int setup_echo_srv_tcp_ipv4(void **state)
23 {
24         torture_setup_echo_srv_tcp_ipv4(state);
25
26         return 0;
27 }
28
29 static int teardown(void **state)
30 {
31         torture_teardown_echo_srv(state);
32
33         return 0;
34 }
35
36 struct test_fd {
37         int fd;
38         struct torture_address sock_addr;
39         struct torture_address peer_addr;
40 };
41
42 static int test_fill_test_fd(struct test_fd *tfd, int fd)
43 {
44         struct torture_address saddr = {
45                 .sa_socklen = sizeof(struct sockaddr_storage),
46         };
47         struct torture_address paddr = {
48                 .sa_socklen = sizeof(struct sockaddr_storage),
49         };
50         int ret;
51
52         *tfd = (struct test_fd) { .fd = fd, };
53
54         ret = getsockname(fd, &saddr.sa.s, &saddr.sa_socklen);
55         if (ret == -1 && errno == ENOTSOCK) {
56                 return 0;
57         }
58         if (ret == -1) {
59                 return ret;
60         }
61
62         ret = getpeername(fd, &paddr.sa.s, &paddr.sa_socklen);
63         if (ret == -1) {
64                 return ret;
65         }
66
67         tfd->sock_addr = saddr;
68         tfd->peer_addr = paddr;
69         return 0;
70 }
71
72 static void _assert_torture_address_equal(const struct torture_address *ga,
73                                           const struct torture_address *ea,
74                                           const char * const file,
75                                           const int line)
76 {
77         _assert_int_equal(ga->sa_socklen, ea->sa_socklen, file, line);
78         if (ga->sa_socklen == 0) {
79                 return;
80         }
81         _assert_memory_equal(&ga->sa, &ea->sa, ga->sa_socklen, file, line);
82 }
83
84 #define assert_test_fd_equal(gfd, efd) \
85         _assert_test_fd_equal(gfd, efd, __FILE__, __LINE__)
86
87 static void _assert_test_fd_equal(const struct test_fd *gfd,
88                                   const struct test_fd *efd,
89                                   const char * const file,
90                                   const int line)
91 {
92         if (efd->fd == -1) {
93                 _assert_int_equal(gfd->fd, -1, file, line);
94                 return;
95         }
96
97         _assert_int_not_equal(gfd->fd, -1, file, line);
98
99         _assert_torture_address_equal(&gfd->sock_addr, &efd->sock_addr, file, line);
100         _assert_torture_address_equal(&gfd->peer_addr, &efd->peer_addr, file, line);
101 }
102
103 static void test_tcp_sendmsg_recvmsg_fd_array(const int *fds, size_t num_fds)
104 {
105         struct test_fd tfds[num_fds];
106         size_t idx;
107         int sv[2];
108         int child_fd, parent_fd;
109         pid_t pid;
110         int rc;
111
112         for (idx = 0; idx < num_fds; idx++) {
113                 rc = test_fill_test_fd(&tfds[idx], fds[idx]);
114                 assert_int_equal(rc, 0);
115         }
116
117         /* create unix domain socket stream */
118         rc = socketpair(AF_LOCAL, SOCK_STREAM, 0, sv);
119         assert_int_not_equal(rc, -1);
120
121         parent_fd = sv[0];
122         child_fd = sv[1];
123
124         pid = fork();
125         assert_int_not_equal(pid, -1);
126
127         if (pid == 0) {
128                 /* Child */
129                 struct msghdr child_msg;
130                 int recv_fd_array[num_fds];
131                 char cmsgbuf[CMSG_SPACE(sizeof(int)*num_fds)];
132                 struct cmsghdr *cmsg;
133                 ssize_t ret;
134                 char byte = { 0, };
135                 struct iovec iov;
136
137                 iov.iov_base = &byte;
138                 iov.iov_len = 1;
139
140                 memset(&child_msg, 0, sizeof(child_msg));
141                 child_msg.msg_iov = &iov;
142                 child_msg.msg_iovlen = 1;
143                 child_msg.msg_control = cmsgbuf;
144                 child_msg.msg_controllen = sizeof(cmsgbuf);
145
146                 rc = recvmsg(child_fd, &child_msg, 0);
147                 assert_int_equal(rc, iov.iov_len);
148                 assert_int_equal(byte, '!');
149
150                 cmsg = CMSG_FIRSTHDR(&child_msg);
151                 assert_non_null(cmsg);
152                 assert_int_equal(cmsg->cmsg_type, SCM_RIGHTS);
153
154                 memcpy(recv_fd_array, CMSG_DATA(cmsg), sizeof(int)*num_fds);
155                 for (idx = 0; idx < num_fds; idx++) {
156                         assert_int_not_equal(recv_fd_array[idx], -1);
157                 }
158
159                 for (idx = 0; idx < num_fds; idx++) {
160                         struct test_fd recv_tfd = { .fd = -1, };
161
162                         ret = test_fill_test_fd(&recv_tfd, recv_fd_array[idx]);
163                         assert_int_equal(ret, 0);
164
165                         assert_test_fd_equal(&recv_tfd, &tfds[idx]);
166                 }
167
168                 for (idx = 0; idx < num_fds; idx++) {
169                         int recv_fd = recv_fd_array[idx];
170                         char send_buf[64] = {0,};
171                         char recv_buf[64] = {0,};
172
173                         if (tfds[idx].sock_addr.sa_socklen == 0) {
174                                 /*
175                                  * skip fds not belonging to
176                                  * a socket.
177                                  */
178                                 continue;
179                         }
180
181                         snprintf(send_buf, sizeof(send_buf), "packet");
182
183                         ret = write(recv_fd,
184                                     send_buf,
185                                     sizeof(send_buf));
186                         assert_int_not_equal(ret, -1);
187
188                         ret = read(recv_fd,
189                                    recv_buf,
190                                    sizeof(recv_buf));
191                         assert_int_not_equal(ret, -1);
192
193                         assert_memory_equal(send_buf, recv_buf, sizeof(send_buf));
194                 }
195
196                 exit(0);
197         } else {
198                 /* Parent */
199                 struct msghdr parent_msg;
200                 struct cmsghdr *cmsg;
201                 char cmsgbuf[CMSG_SPACE(sizeof(int)*num_fds)];
202                 int pass_fd_array[num_fds];
203                 char byte = '!';
204                 struct iovec iov;
205                 int cs;
206
207                 for (idx = 0; idx < num_fds; idx++) {
208                         pass_fd_array[idx] = tfds[idx].fd;
209                 }
210
211                 iov.iov_base = &byte;
212                 iov.iov_len = 1;
213
214                 memset(&parent_msg, 0, sizeof(parent_msg));
215                 parent_msg.msg_iov = &iov;
216                 parent_msg.msg_iovlen = 1;
217                 parent_msg.msg_control = cmsgbuf;
218                 parent_msg.msg_controllen = sizeof(cmsgbuf);
219
220                 cmsg = CMSG_FIRSTHDR(&parent_msg);
221                 cmsg->cmsg_level = SOL_SOCKET;
222                 cmsg->cmsg_type = SCM_RIGHTS;
223                 cmsg->cmsg_len = CMSG_LEN(sizeof(int)*num_fds);
224
225                 /* place previously connected socket fd as ancillary data */
226                 memcpy(CMSG_DATA(cmsg), pass_fd_array, sizeof(int)*num_fds);
227                 parent_msg.msg_controllen = cmsg->cmsg_len;
228
229                 rc = sendmsg(parent_fd, &parent_msg, 0);
230                 assert_int_not_equal(rc, -1);
231
232                 alarm(5);           /* 5 seconds timeout for the child */
233                 waitpid(pid, &cs, 0);
234                 if (WIFEXITED(cs)) {
235                         assert_int_equal(WEXITSTATUS(cs), 0);
236                 }
237         }
238 }
239
240 static void test_tcp_sendmsg_recvmsg_fd_same(size_t num_fds)
241 {
242         struct torture_address addr = {
243                 .sa_socklen = sizeof(struct sockaddr_in),
244         };
245         int pass_sock_fd;
246         int fd_array[num_fds];
247         size_t idx;
248         int rc;
249
250         /* create socket file descriptor to be passed */
251         pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
252         assert_int_not_equal(pass_sock_fd, -1);
253
254         addr.sa.in.sin_family = AF_INET;
255         addr.sa.in.sin_port = htons(torture_server_port());
256
257         rc = inet_pton(addr.sa.in.sin_family,
258                        torture_server_address(AF_INET),
259                        &addr.sa.in.sin_addr);
260         assert_int_equal(rc, 1);
261
262         rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
263         assert_int_equal(rc, 0);
264
265         for (idx = 0; idx < num_fds; idx++) {
266                 fd_array[idx] = pass_sock_fd;
267         }
268
269         test_tcp_sendmsg_recvmsg_fd_array(fd_array, num_fds);
270
271         close(pass_sock_fd);
272 }
273
274 static void test_tcp_sendmsg_recvmsg_fd_1(void **state)
275 {
276         (void) state; /* unused */
277         test_tcp_sendmsg_recvmsg_fd_same(1);
278 }
279
280 static void test_tcp_sendmsg_recvmsg_fd_2s(void **state)
281 {
282         (void) state; /* unused */
283         test_tcp_sendmsg_recvmsg_fd_same(2);
284 }
285
286 static void test_tcp_sendmsg_recvmsg_fd_3s(void **state)
287 {
288         (void) state; /* unused */
289         test_tcp_sendmsg_recvmsg_fd_same(3);
290 }
291
292 static void test_tcp_sendmsg_recvmsg_fd_4s(void **state)
293 {
294         (void) state; /* unused */
295         test_tcp_sendmsg_recvmsg_fd_same(4);
296 }
297
298 static void test_tcp_sendmsg_recvmsg_fd_5s(void **state)
299 {
300         (void) state; /* unused */
301         test_tcp_sendmsg_recvmsg_fd_same(5);
302 }
303
304 static void test_tcp_sendmsg_recvmsg_fd_6s(void **state)
305 {
306         (void) state; /* unused */
307         test_tcp_sendmsg_recvmsg_fd_same(6);
308 }
309
310 static void test_tcp_sendmsg_recvmsg_fd_different(size_t num_fds)
311 {
312         int fd_array[num_fds];
313         size_t idx;
314
315         for (idx = 0; idx < num_fds; idx++) {
316                 struct torture_address addr = {
317                         .sa_socklen = sizeof(struct sockaddr_in),
318                 };
319                 int pass_sock_fd;
320                 int rc;
321
322                 /* create socket file descriptor to be passed */
323                 pass_sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
324                 assert_int_not_equal(pass_sock_fd, -1);
325
326                 addr.sa.in.sin_family = AF_INET;
327                 addr.sa.in.sin_port = htons(torture_server_port());
328
329                 rc = inet_pton(addr.sa.in.sin_family,
330                                torture_server_address(AF_INET),
331                                &addr.sa.in.sin_addr);
332                 assert_int_equal(rc, 1);
333
334                 rc = connect(pass_sock_fd, &addr.sa.s, addr.sa_socklen);
335                 assert_int_equal(rc, 0);
336
337                 fd_array[idx] = pass_sock_fd;
338         }
339
340         test_tcp_sendmsg_recvmsg_fd_array(fd_array, num_fds);
341
342         for (idx = 0; idx < num_fds; idx++) {
343                 close(fd_array[idx]);
344         }
345 }
346
347 static void test_tcp_sendmsg_recvmsg_fd_2d(void **state)
348 {
349         (void) state; /* unused */
350         test_tcp_sendmsg_recvmsg_fd_different(2);
351 }
352
353 static void test_tcp_sendmsg_recvmsg_fd_3d(void **state)
354 {
355         (void) state; /* unused */
356         test_tcp_sendmsg_recvmsg_fd_different(3);
357 }
358
359 static void test_tcp_sendmsg_recvmsg_fd_4d(void **state)
360 {
361         (void) state; /* unused */
362         test_tcp_sendmsg_recvmsg_fd_different(4);
363 }
364
365 static void test_tcp_sendmsg_recvmsg_fd_5d(void **state)
366 {
367         (void) state; /* unused */
368         test_tcp_sendmsg_recvmsg_fd_different(5);
369 }
370
371 static void test_tcp_sendmsg_recvmsg_fd_6d(void **state)
372 {
373         (void) state; /* unused */
374         test_tcp_sendmsg_recvmsg_fd_different(6);
375 }
376
377 int main(void) {
378         int rc;
379
380         const struct CMUnitTest tests[] = {
381                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_1,
382                                  setup_echo_srv_tcp_ipv4,
383                                  teardown),
384                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_2s,
385                                  setup_echo_srv_tcp_ipv4,
386                                  teardown),
387                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_3s,
388                                  setup_echo_srv_tcp_ipv4,
389                                  teardown),
390                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_4s,
391                                  setup_echo_srv_tcp_ipv4,
392                                  teardown),
393                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_5s,
394                                  setup_echo_srv_tcp_ipv4,
395                                  teardown),
396                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_6s,
397                                  setup_echo_srv_tcp_ipv4,
398                                  teardown),
399                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_2d,
400                                  setup_echo_srv_tcp_ipv4,
401                                  teardown),
402                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_3d,
403                                  setup_echo_srv_tcp_ipv4,
404                                  teardown),
405                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_4d,
406                                  setup_echo_srv_tcp_ipv4,
407                                  teardown),
408                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_5d,
409                                  setup_echo_srv_tcp_ipv4,
410                                  teardown),
411                 cmocka_unit_test_setup_teardown(test_tcp_sendmsg_recvmsg_fd_6d,
412                                  setup_echo_srv_tcp_ipv4,
413                                  teardown),
414         };
415
416         rc = cmocka_run_group_tests(tests, NULL, NULL);
417
418         return rc;
419 }