]> Git Repo - qemu.git/blob - tests/test-io-channel-socket.c
qapi: Adjust names of implicit types
[qemu.git] / tests / test-io-channel-socket.c
1 /*
2  * QEMU I/O channel sockets test
3  *
4  * Copyright (c) 2015-2016 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "qemu/osdep.h"
22 #include "io/channel-socket.h"
23 #include "io/channel-util.h"
24 #include "io-channel-helpers.h"
25
26 #ifndef AI_ADDRCONFIG
27 # define AI_ADDRCONFIG 0
28 #endif
29 #ifndef AI_V4MAPPED
30 # define AI_V4MAPPED 0
31 #endif
32 #ifndef EAI_ADDRFAMILY
33 # define EAI_ADDRFAMILY 0
34 #endif
35
36 static int check_bind(const char *hostname, bool *has_proto)
37 {
38     int fd = -1;
39     struct addrinfo ai, *res = NULL;
40     int rc;
41     int ret = -1;
42
43     memset(&ai, 0, sizeof(ai));
44     ai.ai_flags = AI_CANONNAME | AI_V4MAPPED | AI_ADDRCONFIG;
45     ai.ai_family = AF_UNSPEC;
46     ai.ai_socktype = SOCK_STREAM;
47
48     /* lookup */
49     rc = getaddrinfo(hostname, NULL, &ai, &res);
50     if (rc != 0) {
51         if (rc == EAI_ADDRFAMILY ||
52             rc == EAI_FAMILY) {
53             *has_proto = false;
54             goto done;
55         }
56         goto cleanup;
57     }
58
59     fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol);
60     if (fd < 0) {
61         goto cleanup;
62     }
63
64     if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
65         if (errno == EADDRNOTAVAIL) {
66             *has_proto = false;
67             goto done;
68         }
69         goto cleanup;
70     }
71
72     *has_proto = true;
73  done:
74     ret = 0;
75
76  cleanup:
77     if (fd != -1) {
78         close(fd);
79     }
80     if (res) {
81         freeaddrinfo(res);
82     }
83     return ret;
84 }
85
86 static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
87 {
88     if (check_bind("127.0.0.1", has_ipv4) < 0) {
89         return -1;
90     }
91     if (check_bind("::1", has_ipv6) < 0) {
92         return -1;
93     }
94
95     return 0;
96 }
97
98
99 static void test_io_channel_set_socket_bufs(QIOChannel *src,
100                                             QIOChannel *dst)
101 {
102     int buflen = 64 * 1024;
103
104     /*
105      * Make the socket buffers small so that we see
106      * the effects of partial reads/writes
107      */
108     setsockopt(((QIOChannelSocket *)src)->fd,
109                SOL_SOCKET, SO_SNDBUF,
110                (char *)&buflen,
111                sizeof(buflen));
112
113     setsockopt(((QIOChannelSocket *)dst)->fd,
114                SOL_SOCKET, SO_SNDBUF,
115                (char *)&buflen,
116                sizeof(buflen));
117 }
118
119
120 static void test_io_channel_setup_sync(SocketAddress *listen_addr,
121                                        SocketAddress *connect_addr,
122                                        QIOChannel **src,
123                                        QIOChannel **dst)
124 {
125     QIOChannelSocket *lioc;
126
127     lioc = qio_channel_socket_new();
128     qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
129
130     if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
131         SocketAddress *laddr = qio_channel_socket_get_local_address(
132             lioc, &error_abort);
133
134         g_free(connect_addr->u.inet->port);
135         connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
136
137         qapi_free_SocketAddress(laddr);
138     }
139
140     *src = QIO_CHANNEL(qio_channel_socket_new());
141     qio_channel_socket_connect_sync(
142         QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort);
143     qio_channel_set_delay(*src, false);
144
145     qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
146     *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
147     g_assert(*dst);
148
149     test_io_channel_set_socket_bufs(*src, *dst);
150
151     object_unref(OBJECT(lioc));
152 }
153
154
155 struct TestIOChannelData {
156     bool err;
157     GMainLoop *loop;
158 };
159
160
161 static void test_io_channel_complete(Object *src,
162                                      Error *err,
163                                      gpointer opaque)
164 {
165     struct TestIOChannelData *data = opaque;
166     data->err = err != NULL;
167     g_main_loop_quit(data->loop);
168 }
169
170
171 static void test_io_channel_setup_async(SocketAddress *listen_addr,
172                                         SocketAddress *connect_addr,
173                                         QIOChannel **src,
174                                         QIOChannel **dst)
175 {
176     QIOChannelSocket *lioc;
177     struct TestIOChannelData data;
178
179     data.loop = g_main_loop_new(g_main_context_default(),
180                                 TRUE);
181
182     lioc = qio_channel_socket_new();
183     qio_channel_socket_listen_async(
184         lioc, listen_addr,
185         test_io_channel_complete, &data, NULL);
186
187     g_main_loop_run(data.loop);
188     g_main_context_iteration(g_main_context_default(), FALSE);
189
190     g_assert(!data.err);
191
192     if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
193         SocketAddress *laddr = qio_channel_socket_get_local_address(
194             lioc, &error_abort);
195
196         g_free(connect_addr->u.inet->port);
197         connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
198
199         qapi_free_SocketAddress(laddr);
200     }
201
202     *src = QIO_CHANNEL(qio_channel_socket_new());
203
204     qio_channel_socket_connect_async(
205         QIO_CHANNEL_SOCKET(*src), connect_addr,
206         test_io_channel_complete, &data, NULL);
207
208     g_main_loop_run(data.loop);
209     g_main_context_iteration(g_main_context_default(), FALSE);
210
211     g_assert(!data.err);
212
213     qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN);
214     *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
215     g_assert(*dst);
216
217     qio_channel_set_delay(*src, false);
218     test_io_channel_set_socket_bufs(*src, *dst);
219
220     object_unref(OBJECT(lioc));
221
222     g_main_loop_unref(data.loop);
223 }
224
225
226 static void test_io_channel(bool async,
227                             SocketAddress *listen_addr,
228                             SocketAddress *connect_addr,
229                             bool passFD)
230 {
231     QIOChannel *src, *dst;
232     QIOChannelTest *test;
233     if (async) {
234         test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
235
236         g_assert(!passFD ||
237                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
238         g_assert(!passFD ||
239                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
240
241         test = qio_channel_test_new();
242         qio_channel_test_run_threads(test, true, src, dst);
243         qio_channel_test_validate(test);
244
245         object_unref(OBJECT(src));
246         object_unref(OBJECT(dst));
247
248         test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
249
250         g_assert(!passFD ||
251                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
252         g_assert(!passFD ||
253                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
254
255         test = qio_channel_test_new();
256         qio_channel_test_run_threads(test, false, src, dst);
257         qio_channel_test_validate(test);
258
259         object_unref(OBJECT(src));
260         object_unref(OBJECT(dst));
261     } else {
262         test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
263
264         g_assert(!passFD ||
265                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
266         g_assert(!passFD ||
267                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
268
269         test = qio_channel_test_new();
270         qio_channel_test_run_threads(test, true, src, dst);
271         qio_channel_test_validate(test);
272
273         object_unref(OBJECT(src));
274         object_unref(OBJECT(dst));
275
276         test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
277
278         g_assert(!passFD ||
279                  qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
280         g_assert(!passFD ||
281                  qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
282
283         test = qio_channel_test_new();
284         qio_channel_test_run_threads(test, false, src, dst);
285         qio_channel_test_validate(test);
286
287         object_unref(OBJECT(src));
288         object_unref(OBJECT(dst));
289     }
290 }
291
292
293 static void test_io_channel_ipv4(bool async)
294 {
295     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
296     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
297
298     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
299     listen_addr->u.inet = g_new(InetSocketAddress, 1);
300     *listen_addr->u.inet = (InetSocketAddress) {
301         .host = g_strdup("127.0.0.1"),
302         .port = NULL, /* Auto-select */
303     };
304
305     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
306     connect_addr->u.inet = g_new(InetSocketAddress, 1);
307     *connect_addr->u.inet = (InetSocketAddress) {
308         .host = g_strdup("127.0.0.1"),
309         .port = NULL, /* Filled in later */
310     };
311
312     test_io_channel(async, listen_addr, connect_addr, false);
313
314     qapi_free_SocketAddress(listen_addr);
315     qapi_free_SocketAddress(connect_addr);
316 }
317
318
319 static void test_io_channel_ipv4_sync(void)
320 {
321     return test_io_channel_ipv4(false);
322 }
323
324
325 static void test_io_channel_ipv4_async(void)
326 {
327     return test_io_channel_ipv4(true);
328 }
329
330
331 static void test_io_channel_ipv6(bool async)
332 {
333     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
334     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
335
336     listen_addr->type = SOCKET_ADDRESS_KIND_INET;
337     listen_addr->u.inet = g_new(InetSocketAddress, 1);
338     *listen_addr->u.inet = (InetSocketAddress) {
339         .host = g_strdup("::1"),
340         .port = NULL, /* Auto-select */
341     };
342
343     connect_addr->type = SOCKET_ADDRESS_KIND_INET;
344     connect_addr->u.inet = g_new(InetSocketAddress, 1);
345     *connect_addr->u.inet = (InetSocketAddress) {
346         .host = g_strdup("::1"),
347         .port = NULL, /* Filled in later */
348     };
349
350     test_io_channel(async, listen_addr, connect_addr, false);
351
352     qapi_free_SocketAddress(listen_addr);
353     qapi_free_SocketAddress(connect_addr);
354 }
355
356
357 static void test_io_channel_ipv6_sync(void)
358 {
359     return test_io_channel_ipv6(false);
360 }
361
362
363 static void test_io_channel_ipv6_async(void)
364 {
365     return test_io_channel_ipv6(true);
366 }
367
368
369 #ifndef _WIN32
370 static void test_io_channel_unix(bool async)
371 {
372     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
373     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
374
375 #define TEST_SOCKET "test-io-channel-socket.sock"
376     listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
377     listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
378     listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
379
380     connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
381     connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
382     connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
383
384     test_io_channel(async, listen_addr, connect_addr, true);
385
386     qapi_free_SocketAddress(listen_addr);
387     qapi_free_SocketAddress(connect_addr);
388     unlink(TEST_SOCKET);
389 }
390
391
392 static void test_io_channel_unix_sync(void)
393 {
394     return test_io_channel_unix(false);
395 }
396
397
398 static void test_io_channel_unix_async(void)
399 {
400     return test_io_channel_unix(true);
401 }
402
403 static void test_io_channel_unix_fd_pass(void)
404 {
405     SocketAddress *listen_addr = g_new0(SocketAddress, 1);
406     SocketAddress *connect_addr = g_new0(SocketAddress, 1);
407     QIOChannel *src, *dst;
408     int testfd;
409     int fdsend[3];
410     int *fdrecv = NULL;
411     size_t nfdrecv = 0;
412     size_t i;
413     char bufsend[12], bufrecv[12];
414     struct iovec iosend[1], iorecv[1];
415
416 #define TEST_SOCKET "test-io-channel-socket.sock"
417 #define TEST_FILE "test-io-channel-socket.txt"
418
419     testfd = open(TEST_FILE, O_RDWR|O_TRUNC|O_CREAT, 0700);
420     g_assert(testfd != -1);
421     fdsend[0] = testfd;
422     fdsend[1] = testfd;
423     fdsend[2] = testfd;
424
425     listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
426     listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
427     listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
428
429     connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
430     connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
431     connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
432
433     test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
434
435     memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend));
436
437     iosend[0].iov_base = bufsend;
438     iosend[0].iov_len = G_N_ELEMENTS(bufsend);
439
440     iorecv[0].iov_base = bufrecv;
441     iorecv[0].iov_len = G_N_ELEMENTS(bufrecv);
442
443     g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS));
444     g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_FD_PASS));
445
446     qio_channel_writev_full(src,
447                             iosend,
448                             G_N_ELEMENTS(iosend),
449                             fdsend,
450                             G_N_ELEMENTS(fdsend),
451                             &error_abort);
452
453     qio_channel_readv_full(dst,
454                            iorecv,
455                            G_N_ELEMENTS(iorecv),
456                            &fdrecv,
457                            &nfdrecv,
458                            &error_abort);
459
460     g_assert(nfdrecv == G_N_ELEMENTS(fdsend));
461     /* Each recvd FD should be different from sent FD */
462     for (i = 0; i < nfdrecv; i++) {
463         g_assert_cmpint(fdrecv[i], !=, testfd);
464     }
465     /* Each recvd FD should be different from each other */
466     g_assert_cmpint(fdrecv[0], !=, fdrecv[1]);
467     g_assert_cmpint(fdrecv[0], !=, fdrecv[2]);
468     g_assert_cmpint(fdrecv[1], !=, fdrecv[2]);
469
470     /* Check the I/O buf we sent at the same time matches */
471     g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0);
472
473     /* Write some data into the FD we received */
474     g_assert(write(fdrecv[0], bufsend, G_N_ELEMENTS(bufsend)) ==
475              G_N_ELEMENTS(bufsend));
476
477     /* Read data from the original FD and make sure it matches */
478     memset(bufrecv, 0, G_N_ELEMENTS(bufrecv));
479     g_assert(lseek(testfd, 0, SEEK_SET) == 0);
480     g_assert(read(testfd, bufrecv, G_N_ELEMENTS(bufrecv)) ==
481              G_N_ELEMENTS(bufrecv));
482     g_assert(memcmp(bufsend, bufrecv, G_N_ELEMENTS(bufsend)) == 0);
483
484     object_unref(OBJECT(src));
485     object_unref(OBJECT(dst));
486     qapi_free_SocketAddress(listen_addr);
487     qapi_free_SocketAddress(connect_addr);
488     unlink(TEST_SOCKET);
489     unlink(TEST_FILE);
490     close(testfd);
491     for (i = 0; i < nfdrecv; i++) {
492         close(fdrecv[i]);
493     }
494     g_free(fdrecv);
495 }
496 #endif /* _WIN32 */
497
498
499 static void test_io_channel_ipv4_fd(void)
500 {
501     QIOChannel *ioc;
502     int fd = -1;
503     struct sockaddr_in sa = {
504         .sin_family = AF_INET,
505         .sin_addr = {
506             .s_addr =  htonl(INADDR_LOOPBACK),
507         }
508         /* Leave port unset for auto-assign */
509     };
510     socklen_t salen = sizeof(sa);
511
512     fd = socket(AF_INET, SOCK_STREAM, 0);
513     g_assert_cmpint(fd, >, -1);
514
515     g_assert_cmpint(bind(fd, (struct sockaddr *)&sa, salen), ==, 0);
516
517     ioc = qio_channel_new_fd(fd, &error_abort);
518
519     g_assert_cmpstr(object_get_typename(OBJECT(ioc)),
520                     ==,
521                     TYPE_QIO_CHANNEL_SOCKET);
522
523     object_unref(OBJECT(ioc));
524 }
525
526
527 int main(int argc, char **argv)
528 {
529     bool has_ipv4, has_ipv6;
530
531     module_call_init(MODULE_INIT_QOM);
532     socket_init();
533
534     g_test_init(&argc, &argv, NULL);
535
536     /* We're creating actual IPv4/6 sockets, so we should
537      * check if the host running tests actually supports
538      * each protocol to avoid breaking tests on machines
539      * with either IPv4 or IPv6 disabled.
540      */
541     if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
542         return 1;
543     }
544
545     if (has_ipv4) {
546         g_test_add_func("/io/channel/socket/ipv4-sync",
547                         test_io_channel_ipv4_sync);
548         g_test_add_func("/io/channel/socket/ipv4-async",
549                         test_io_channel_ipv4_async);
550         g_test_add_func("/io/channel/socket/ipv4-fd",
551                         test_io_channel_ipv4_fd);
552     }
553     if (has_ipv6) {
554         g_test_add_func("/io/channel/socket/ipv6-sync",
555                         test_io_channel_ipv6_sync);
556         g_test_add_func("/io/channel/socket/ipv6-async",
557                         test_io_channel_ipv6_async);
558     }
559
560 #ifndef _WIN32
561     g_test_add_func("/io/channel/socket/unix-sync",
562                     test_io_channel_unix_sync);
563     g_test_add_func("/io/channel/socket/unix-async",
564                     test_io_channel_unix_async);
565     g_test_add_func("/io/channel/socket/unix-fd-pass",
566                     test_io_channel_unix_fd_pass);
567 #endif /* _WIN32 */
568
569     return g_test_run();
570 }
This page took 0.05417 seconds and 4 git commands to generate.