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