]> git.proxmox.com Git - mirror_qemu.git/blame - tests/test-io-channel-socket.c
io: bind to loopback IP addrs in test suite
[mirror_qemu.git] / tests / test-io-channel-socket.c
CommitLineData
559607ea
DB
1/*
2 * QEMU I/O channel sockets test
3 *
4 * Copyright (c) 2015 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 "io/channel-socket.h"
22#include "io-channel-helpers.h"
23#ifdef HAVE_IFADDRS_H
24#include <ifaddrs.h>
25#endif
26
27static int check_protocol_support(bool *has_ipv4, bool *has_ipv6)
28{
29#ifdef HAVE_IFADDRS_H
30 struct ifaddrs *ifaddr = NULL, *ifa;
31 struct addrinfo hints = { 0 };
32 struct addrinfo *ai = NULL;
33 int gaierr;
34
35 *has_ipv4 = *has_ipv6 = false;
36
37 if (getifaddrs(&ifaddr) < 0) {
38 g_printerr("Failed to lookup interface addresses: %s\n",
39 strerror(errno));
40 return -1;
41 }
42
43 for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
44 if (!ifa->ifa_addr) {
45 continue;
46 }
47
48 if (ifa->ifa_addr->sa_family == AF_INET) {
49 *has_ipv4 = true;
50 }
51 if (ifa->ifa_addr->sa_family == AF_INET6) {
52 *has_ipv6 = true;
53 }
54 }
55
56 freeifaddrs(ifaddr);
57
58 hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
59 hints.ai_family = AF_INET6;
60 hints.ai_socktype = SOCK_STREAM;
61
62 gaierr = getaddrinfo("::1", NULL, &hints, &ai);
63 if (gaierr != 0) {
64 if (gaierr == EAI_ADDRFAMILY ||
65 gaierr == EAI_FAMILY ||
66 gaierr == EAI_NONAME) {
67 *has_ipv6 = false;
68 } else {
69 g_printerr("Failed to resolve ::1 address: %s\n",
70 gai_strerror(gaierr));
71 return -1;
72 }
73 }
74
75 freeaddrinfo(ai);
76
77 return 0;
78#else
79 *has_ipv4 = *has_ipv6 = false;
80
81 return -1;
82#endif
83}
84
85
86static void test_io_channel_set_socket_bufs(QIOChannel *src,
87 QIOChannel *dst)
88{
89 int buflen = 64 * 1024;
90
91 /*
92 * Make the socket buffers small so that we see
93 * the effects of partial reads/writes
94 */
95 setsockopt(((QIOChannelSocket *)src)->fd,
96 SOL_SOCKET, SO_SNDBUF,
97 (char *)&buflen,
98 sizeof(buflen));
99
100 setsockopt(((QIOChannelSocket *)dst)->fd,
101 SOL_SOCKET, SO_SNDBUF,
102 (char *)&buflen,
103 sizeof(buflen));
104}
105
106
107static void test_io_channel_setup_sync(SocketAddress *listen_addr,
108 SocketAddress *connect_addr,
109 QIOChannel **src,
110 QIOChannel **dst)
111{
112 QIOChannelSocket *lioc;
113
114 lioc = qio_channel_socket_new();
115 qio_channel_socket_listen_sync(lioc, listen_addr, &error_abort);
116
117 if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
118 SocketAddress *laddr = qio_channel_socket_get_local_address(
119 lioc, &error_abort);
120
121 g_free(connect_addr->u.inet->port);
122 connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
123
124 qapi_free_SocketAddress(laddr);
125 }
126
127 *src = QIO_CHANNEL(qio_channel_socket_new());
128 qio_channel_socket_connect_sync(
129 QIO_CHANNEL_SOCKET(*src), connect_addr, &error_abort);
130 qio_channel_set_delay(*src, false);
131
132 *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
133 g_assert(*dst);
134
135 test_io_channel_set_socket_bufs(*src, *dst);
136
137 object_unref(OBJECT(lioc));
138}
139
140
141struct TestIOChannelData {
142 bool err;
143 GMainLoop *loop;
144};
145
146
147static void test_io_channel_complete(Object *src,
148 Error *err,
149 gpointer opaque)
150{
151 struct TestIOChannelData *data = opaque;
152 data->err = err != NULL;
153 g_main_loop_quit(data->loop);
154}
155
156
157static void test_io_channel_setup_async(SocketAddress *listen_addr,
158 SocketAddress *connect_addr,
159 QIOChannel **src,
160 QIOChannel **dst)
161{
162 QIOChannelSocket *lioc;
163 struct TestIOChannelData data;
164
165 data.loop = g_main_loop_new(g_main_context_default(),
166 TRUE);
167
168 lioc = qio_channel_socket_new();
169 qio_channel_socket_listen_async(
170 lioc, listen_addr,
171 test_io_channel_complete, &data, NULL);
172
173 g_main_loop_run(data.loop);
174 g_main_context_iteration(g_main_context_default(), FALSE);
175
176 g_assert(!data.err);
177
178 if (listen_addr->type == SOCKET_ADDRESS_KIND_INET) {
179 SocketAddress *laddr = qio_channel_socket_get_local_address(
180 lioc, &error_abort);
181
182 g_free(connect_addr->u.inet->port);
183 connect_addr->u.inet->port = g_strdup(laddr->u.inet->port);
184
185 qapi_free_SocketAddress(laddr);
186 }
187
188 *src = QIO_CHANNEL(qio_channel_socket_new());
189
190 qio_channel_socket_connect_async(
191 QIO_CHANNEL_SOCKET(*src), connect_addr,
192 test_io_channel_complete, &data, NULL);
193
194 g_main_loop_run(data.loop);
195 g_main_context_iteration(g_main_context_default(), FALSE);
196
197 g_assert(!data.err);
198
199 *dst = QIO_CHANNEL(qio_channel_socket_accept(lioc, &error_abort));
200 g_assert(*dst);
201
202 qio_channel_set_delay(*src, false);
203 test_io_channel_set_socket_bufs(*src, *dst);
204
205 object_unref(OBJECT(lioc));
206
207 g_main_loop_unref(data.loop);
208}
209
210
211static void test_io_channel(bool async,
212 SocketAddress *listen_addr,
213 SocketAddress *connect_addr)
214{
215 QIOChannel *src, *dst;
216 QIOChannelTest *test;
217 if (async) {
218 test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
219
220 test = qio_channel_test_new();
221 qio_channel_test_run_threads(test, true, src, dst);
222 qio_channel_test_validate(test);
223
224 object_unref(OBJECT(src));
225 object_unref(OBJECT(dst));
226
227 test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst);
228
229 test = qio_channel_test_new();
230 qio_channel_test_run_threads(test, false, src, dst);
231 qio_channel_test_validate(test);
232
233 object_unref(OBJECT(src));
234 object_unref(OBJECT(dst));
235 } else {
236 test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
237
238 test = qio_channel_test_new();
239 qio_channel_test_run_threads(test, true, src, dst);
240 qio_channel_test_validate(test);
241
242 object_unref(OBJECT(src));
243 object_unref(OBJECT(dst));
244
245 test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst);
246
247 test = qio_channel_test_new();
248 qio_channel_test_run_threads(test, false, src, dst);
249 qio_channel_test_validate(test);
250
251 object_unref(OBJECT(src));
252 object_unref(OBJECT(dst));
253 }
254}
255
256
257static void test_io_channel_ipv4(bool async)
258{
259 SocketAddress *listen_addr = g_new0(SocketAddress, 1);
260 SocketAddress *connect_addr = g_new0(SocketAddress, 1);
261
262 listen_addr->type = SOCKET_ADDRESS_KIND_INET;
263 listen_addr->u.inet = g_new0(InetSocketAddress, 1);
e4d2edc9 264 listen_addr->u.inet->host = g_strdup("127.0.0.1");
559607ea
DB
265 listen_addr->u.inet->port = NULL; /* Auto-select */
266
267 connect_addr->type = SOCKET_ADDRESS_KIND_INET;
268 connect_addr->u.inet = g_new0(InetSocketAddress, 1);
269 connect_addr->u.inet->host = g_strdup("127.0.0.1");
270 connect_addr->u.inet->port = NULL; /* Filled in later */
271
272 test_io_channel(async, listen_addr, connect_addr);
273
274 qapi_free_SocketAddress(listen_addr);
275 qapi_free_SocketAddress(connect_addr);
276}
277
278
279static void test_io_channel_ipv4_sync(void)
280{
281 return test_io_channel_ipv4(false);
282}
283
284
285static void test_io_channel_ipv4_async(void)
286{
287 return test_io_channel_ipv4(true);
288}
289
290
291static void test_io_channel_ipv6(bool async)
292{
293 SocketAddress *listen_addr = g_new0(SocketAddress, 1);
294 SocketAddress *connect_addr = g_new0(SocketAddress, 1);
295
296 listen_addr->type = SOCKET_ADDRESS_KIND_INET;
297 listen_addr->u.inet = g_new0(InetSocketAddress, 1);
e4d2edc9 298 listen_addr->u.inet->host = g_strdup("::1");
559607ea
DB
299 listen_addr->u.inet->port = NULL; /* Auto-select */
300
301 connect_addr->type = SOCKET_ADDRESS_KIND_INET;
302 connect_addr->u.inet = g_new0(InetSocketAddress, 1);
303 connect_addr->u.inet->host = g_strdup("::1");
304 connect_addr->u.inet->port = NULL; /* Filled in later */
305
306 test_io_channel(async, listen_addr, connect_addr);
307
308 qapi_free_SocketAddress(listen_addr);
309 qapi_free_SocketAddress(connect_addr);
310}
311
312
313static void test_io_channel_ipv6_sync(void)
314{
315 return test_io_channel_ipv6(false);
316}
317
318
319static void test_io_channel_ipv6_async(void)
320{
321 return test_io_channel_ipv6(true);
322}
323
324
325#ifndef _WIN32
326static void test_io_channel_unix(bool async)
327{
328 SocketAddress *listen_addr = g_new0(SocketAddress, 1);
329 SocketAddress *connect_addr = g_new0(SocketAddress, 1);
330
331#define TEST_SOCKET "test-io-channel-socket.sock"
332 listen_addr->type = SOCKET_ADDRESS_KIND_UNIX;
333 listen_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
334 listen_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
335
336 connect_addr->type = SOCKET_ADDRESS_KIND_UNIX;
337 connect_addr->u.q_unix = g_new0(UnixSocketAddress, 1);
338 connect_addr->u.q_unix->path = g_strdup(TEST_SOCKET);
339
340 test_io_channel(async, listen_addr, connect_addr);
341
342 qapi_free_SocketAddress(listen_addr);
343 qapi_free_SocketAddress(connect_addr);
344 unlink(TEST_SOCKET);
345}
346
347
348static void test_io_channel_unix_sync(void)
349{
350 return test_io_channel_unix(false);
351}
352
353
354static void test_io_channel_unix_async(void)
355{
356 return test_io_channel_unix(true);
357}
358#endif /* _WIN32 */
359
360
361int main(int argc, char **argv)
362{
363 bool has_ipv4, has_ipv6;
364
365 module_call_init(MODULE_INIT_QOM);
366
367 g_test_init(&argc, &argv, NULL);
368
369 /* We're creating actual IPv4/6 sockets, so we should
370 * check if the host running tests actually supports
371 * each protocol to avoid breaking tests on machines
372 * with either IPv4 or IPv6 disabled.
373 */
374 if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) {
375 return 1;
376 }
377
378 if (has_ipv4) {
379 g_test_add_func("/io/channel/socket/ipv4-sync",
380 test_io_channel_ipv4_sync);
381 g_test_add_func("/io/channel/socket/ipv4-async",
382 test_io_channel_ipv4_async);
383 }
384 if (has_ipv6) {
385 g_test_add_func("/io/channel/socket/ipv6-sync",
386 test_io_channel_ipv6_sync);
387 g_test_add_func("/io/channel/socket/ipv6-async",
388 test_io_channel_ipv6_async);
389 }
390
391#ifndef _WIN32
392 g_test_add_func("/io/channel/socket/unix-sync",
393 test_io_channel_unix_sync);
394 g_test_add_func("/io/channel/socket/unix-async",
395 test_io_channel_unix_async);
396#endif /* _WIN32 */
397
398 return g_test_run();
399}