1 /*
2 * Test suite for network client and read/write functions.
3 *
4 * The canonical version of this file is maintained in the rra-c-util package,
5 * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
6 *
7 * Written by Russ Allbery <eagle@eyrie.org>
8 * Copyright 2005, 2013-2014, 2016-2020 Russ Allbery <eagle@eyrie.org>
9 * Copyright 2009-2013
10 * The Board of Trustees of the Leland Stanford Junior University
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
18 *
19 * The above copyright notice and this permission notice shall be included in
20 * all copies or substantial portions of the Software.
21 *
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 *
30 * SPDX-License-Identifier: MIT
31 */
32
33 #define LIBTEST_NEW_FORMAT 1
34
35 #include "config.h"
36 #include "portable/socket.h"
37 #include "portable/system.h"
38
39 #include <errno.h>
40 #include <signal.h>
41 #include <sys/wait.h>
42
43 #include "inn/macros.h"
44 #include "inn/messages.h"
45 #include "inn/network.h"
46 #include "tap/basic.h"
47
48
49 /*
50 * A client writer to test network_client_create. Connects to IPv4 localhost,
51 * and expects to always succeed on the connection, taking the source address
52 * to pass into network_client_create.
53 */
54 __attribute__((__noreturn__)) static void
client_create_writer(const char * source)55 client_create_writer(const char *source)
56 {
57 socket_type fd;
58 struct sockaddr_in sin;
59 FILE *out;
60
61 fd = network_client_create(PF_INET, SOCK_STREAM, source);
62 if (fd == INVALID_SOCKET)
63 _exit(1);
64 memset(&sin, 0, sizeof(sin));
65 sin.sin_family = AF_INET;
66 sin.sin_port = htons(11119);
67 sin.sin_addr.s_addr = htonl(0x7f000001UL);
68 if (connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0)
69 _exit(1);
70 out = fdopen(fd, "w");
71 if (out == NULL)
72 _exit(1);
73 fputs("socket test\r\n", out);
74 fclose(out);
75 _exit(0);
76 }
77
78
79 /*
80 * Used to test network_read. Connects, sends a couple of strings, then
81 * sleeps for 10 seconds before sending another string so that timeouts can be
82 * tested. Meant to be run in a child process.
83 */
84 __attribute__((__noreturn__)) static void
client_delay_writer(const char * host)85 client_delay_writer(const char *host)
86 {
87 socket_type fd;
88
89 fd = network_connect_host(host, 11119, NULL, 0);
90 if (fd == INVALID_SOCKET)
91 _exit(1);
92 if (socket_write(fd, "one\n", 4) != 4)
93 _exit(1);
94 if (socket_write(fd, "two\n", 4) != 4)
95 _exit(1);
96 sleep(10);
97 if (socket_write(fd, "three\n", 6) != 6)
98 _exit(1);
99 _exit(0);
100 }
101
102
103 /*
104 * Used to test network_write. Connects, reads 64KB from the network, then
105 * sleeps before reading another 64KB. Meant to be run in a child process.
106 */
107 __attribute__((__noreturn__)) static void
client_delay_reader(const char * host)108 client_delay_reader(const char *host)
109 {
110 char *buffer;
111 socket_type fd;
112
113 fd = network_connect_host(host, 11119, NULL, 0);
114 if (fd == INVALID_SOCKET)
115 _exit(1);
116 buffer = malloc(64 * 1024);
117 if (buffer == NULL)
118 _exit(1);
119 if (!network_read(fd, buffer, 64 * 1024, 0))
120 _exit(1);
121 sleep(10);
122 if (!network_read(fd, buffer, 64 * 1024, 0))
123 _exit(1);
124 free(buffer);
125 _exit(0);
126 }
127
128
129 /*
130 * When testing the bind (server) functions, we create listening sockets, fork
131 * a child process to connect to it, and accept the connection and read the
132 * data in the server. The test reporting is therefore done by the listener.
133 * There are two listeners, depending on whether we're listening to a single
134 * socket or an array of sockets, both of which invoke this handler when the
135 * connection is accepted.
136 *
137 * Check that the result of accept, read data from the client, and ensure we
138 * got the expected data, reporting all results through the normal test
139 * reporting mechanism.
140 */
141 static void
test_server_connection(socket_type client)142 test_server_connection(socket_type client)
143 {
144 FILE *out;
145 char buffer[512];
146
147 /* Verify that the result of accept is good. */
148 if (client == INVALID_SOCKET) {
149 sysdiag("cannot accept connection from socket");
150 ok_block(2, 0, "...socket read test");
151 return;
152 }
153 ok(1, "...socket accept");
154
155 /* Read data from the client and ensure it matches our expectations. */
156 out = fdopen(client, "r");
157 if (fgets(buffer, sizeof(buffer), out) == NULL) {
158 sysdiag("cannot read from socket");
159 ok(0, "...socket read");
160 }
161 is_string("socket test\r\n", buffer, "...socket read");
162 fclose(out);
163 }
164
165
166 /*
167 * Test a single listening socket. Accepts one connection and invokes
168 * test_server_connection. For skipping purposes, this produces two tests.
169 */
170 static void
test_server_accept(socket_type fd)171 test_server_accept(socket_type fd)
172 {
173 socket_type client;
174
175 client = accept(fd, NULL, NULL);
176 test_server_connection(client);
177 socket_close(fd);
178 }
179
180
181 /*
182 * Bring up a server on port 11119 on the loopback address and test connecting
183 * to it via IPv4 using network_client_create. Takes an optional source
184 * address to use for client connections.
185 */
186 static void
test_create_ipv4(const char * source)187 test_create_ipv4(const char *source)
188 {
189 socket_type fd;
190 pid_t child;
191 int status;
192
193 /* Create the socket and listen to it. */
194 fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
195 if (fd == INVALID_SOCKET)
196 sysbail("cannot create or bind socket");
197 ok(fd != INVALID_SOCKET, "IPv4 network client");
198 if (listen(fd, 1) < 0)
199 sysbail("cannot listen to socket");
200
201 /* Fork off a child that uses network_client_create. */
202 child = fork();
203 if (child < 0)
204 sysbail("cannot fork");
205 else if (child == 0)
206 client_create_writer(source);
207 else {
208 test_server_accept(fd);
209 waitpid(child, &status, 0);
210 is_int(0, status, "client made correct connections");
211 }
212 }
213
214
215 /*
216 * Test connect timeouts using IPv4. Bring up a server on port 11119 on the
217 * loopback address and test connections to it. The server only accepts one
218 * connection at a time, so a subsequent connection will time out.
219 */
220 static void
test_timeout_ipv4(void)221 test_timeout_ipv4(void)
222 {
223 socket_type fd, c;
224 pid_t child;
225 socket_type block[20];
226 unsigned int conn, i;
227 int err;
228
229 /*
230 * Create the listening socket. We set the listening queue size to 1,
231 * but some operating systems, including Linux, will allow more
232 * connection attempts to succeed than the backlog size. We'll therefore
233 * have to hammer this server with connections to try to get it to fail.
234 */
235 fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
236 if (fd == INVALID_SOCKET)
237 sysbail("cannot create or bind socket");
238 if (listen(fd, 1) < 0)
239 sysbail("cannot listen to socket");
240
241 /* Fork off a child that just runs accept once and then sleeps. */
242 child = fork();
243 if (child < 0)
244 sysbail("cannot fork");
245 else if (child == 0) {
246 alarm(10);
247 c = accept(fd, NULL, NULL);
248 if (c == INVALID_SOCKET)
249 _exit(1);
250 sleep(9);
251 _exit(0);
252 }
253
254 /* In the parent. Open that first connection. */
255 socket_close(fd);
256 c = network_connect_host("127.0.0.1", 11119, NULL, 1);
257 ok(c != INVALID_SOCKET, "Timeout: first connection worked");
258
259 /*
260 * It can take up to fifteen connections on Linux before connections start
261 * actually timing out, and sometimes they never do.
262 */
263 alarm(20);
264 for (conn = 0; conn < ARRAY_SIZE(block); conn++) {
265 block[conn] = network_connect_host("127.0.0.1", 11119, NULL, 1);
266 if (block[conn] == INVALID_SOCKET)
267 break;
268 }
269 err = socket_errno;
270
271 /*
272 * If we reached the end of the array, we can't force a connection
273 * timeout, so just skip this test. It's also possible that the
274 * connection will fail with ECONNRESET or ECONNREFUSED if the nine second
275 * sleep in the child passed, so skip in that case as well. Otherwise,
276 * expect a failure due to timeout in a reasonable amount of time (less
277 * than our 20-second alarm).
278 */
279 if (conn == ARRAY_SIZE(block))
280 skip_block(2, "short listen queue does not prevent connections");
281 else {
282 diag("Finally timed out on socket %u", conn);
283 ok(block[conn] == INVALID_SOCKET, "Later connection timed out");
284 if (err == ECONNRESET || err == ECONNREFUSED)
285 skip("connections rejected without timeout");
286 else
287 is_int(ETIMEDOUT, err, "...with correct error code");
288 }
289 alarm(0);
290
291 /* Shut down the client and clean up resources. */
292 kill(child, SIGTERM);
293 waitpid(child, NULL, 0);
294 socket_close(c);
295 for (i = 0; i < conn; i++)
296 if (block[i] != INVALID_SOCKET)
297 socket_close(block[i]);
298 socket_close(fd);
299 }
300
301
302 /*
303 * Test the network read function with a timeout. We fork off a child process
304 * that runs delay_writer, and then we read from the network twice, once with
305 * a timeout and once without, and then try a third time when we should time
306 * out.
307 */
308 static void
test_network_read(void)309 test_network_read(void)
310 {
311 socket_type fd, c;
312 pid_t child;
313 char buffer[4];
314
315 /* Create the listening socket. */
316 fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
317 if (fd == INVALID_SOCKET)
318 sysbail("cannot create or bind socket");
319 if (listen(fd, 1) < 0)
320 sysbail("cannot listen to socket");
321
322 /* Fork off a child process that writes some data with delays. */
323 child = fork();
324 if (child < 0)
325 sysbail("cannot fork");
326 else if (child == 0) {
327 socket_close(fd);
328 client_delay_writer("127.0.0.1");
329 }
330
331 /* Set an alarm just in case our timeouts don't work. */
332 alarm(10);
333
334 /* Accept the client connection. */
335 c = accept(fd, NULL, NULL);
336 if (c == INVALID_SOCKET)
337 sysbail("cannot accept on socket");
338
339 /* Now test a couple of simple reads, with and without timeout. */
340 socket_set_errno(0);
341 ok(network_read(c, buffer, sizeof(buffer), 0), "network_read");
342 ok(memcmp("one\n", buffer, sizeof(buffer)) == 0, "...with good data");
343 ok(network_read(c, buffer, sizeof(buffer), 1),
344 "network_read with timeout");
345 ok(memcmp("two\n", buffer, sizeof(buffer)) == 0, "...with good data");
346
347 /*
348 * The third read should abort with a timeout, since the writer is writing
349 * with a ten second delay.
350 */
351 ok(!network_read(c, buffer, sizeof(buffer), 1),
352 "network_read aborted with timeout");
353 is_int(ETIMEDOUT, socket_errno, "...with correct error");
354 ok(memcmp("two\n", buffer, sizeof(buffer)) == 0, "...and data unchanged");
355 alarm(0);
356
357 /* Clean up. */
358 socket_close(c);
359 kill(child, SIGTERM);
360 waitpid(child, NULL, 0);
361 socket_close(fd);
362 }
363
364
365 /*
366 * Test the network write function with a timeout. We fork off a child
367 * process that runs delay_reader, and then we write 64KB to the network in
368 * two chunks, once with a timeout and once without, and then try a third time
369 * when we should time out.
370 */
371 static void
test_network_write(void)372 test_network_write(void)
373 {
374 socket_type fd, c;
375 pid_t child;
376 char *buffer;
377
378 /*
379 * 15MB chosen because it's larger than the default TCP buffer size of
380 * 12MB used by Debian cloud images (see https://bugs.debian.org/830353).
381 */
382 const size_t bufsize = 15 * 1024 * 1024;
383
384 /* Create the listening socket. */
385 fd = network_bind_ipv4(SOCK_STREAM, "127.0.0.1", 11119);
386 if (fd == INVALID_SOCKET)
387 sysbail("cannot create or bind socket");
388 if (listen(fd, 1) < 0)
389 sysbail("cannot listen to socket");
390
391 /* Create the child, which will connect and then read data with delay. */
392 child = fork();
393 if (child < 0)
394 sysbail("cannot fork");
395 else if (child == 0) {
396 socket_close(fd);
397 client_delay_reader("127.0.0.1");
398 }
399
400 /* Create the data that we're going to send. */
401 buffer = bmalloc(bufsize);
402 memset(buffer, 'a', bufsize);
403
404 /* Set an alarm just in case our timeouts don't work. */
405 alarm(10);
406
407 /* Accept the client connection. */
408 c = accept(fd, NULL, NULL);
409 if (c == INVALID_SOCKET)
410 sysbail("cannot accept on socket");
411
412 /*
413 * Test some successful writes with and without a timeout. Don't send
414 * the whole giant buffer here to make the test run a bit faster.
415 */
416 socket_set_errno(0);
417 ok(network_write(c, buffer, 32 * 1024, 0), "network_write");
418 ok(network_write(c, buffer, 32 * 1024, 1), "network_write with timeout");
419
420 /*
421 * A longer write cannot be completely absorbed before the client sleep,
422 * so should fail with a timeout.
423 */
424 ok(!network_write(c, buffer, bufsize, 1),
425 "network_write aborted with timeout");
426 is_int(ETIMEDOUT, socket_errno, "...with correct error");
427 alarm(0);
428
429 /* Clean up. */
430 socket_close(c);
431 kill(child, SIGTERM);
432 waitpid(child, NULL, 0);
433 socket_close(fd);
434 free(buffer);
435 }
436
437
438 int
main(void)439 main(void)
440 {
441 /* Set up the plan. */
442 plan(22);
443
444 /* Test network_client_create. */
445 test_create_ipv4(NULL);
446 test_create_ipv4("127.0.0.1");
447
448 /* Test network_connect with a timeout. */
449 test_timeout_ipv4();
450
451 /* Test network_read and network_write. */
452 test_network_read();
453 test_network_write();
454 return 0;
455 }
456