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