xref: /netbsd-src/tests/usr.sbin/inetd/t_inetd.c (revision 4724848cf0da353df257f730694b7882798e5daf)
1 /*	$NetBSD: t_inetd.c,v 1.2 2021/09/01 06:12:50 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2021 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by James Browning, Gabe Coffland, Alex Gavin, and Solomon Ritzow.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __RCSID("$NetBSD: t_inetd.c,v 1.2 2021/09/01 06:12:50 christos Exp $");
34 
35 #include <atf-c.h>
36 #include <spawn.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <signal.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <err.h>
43 #include <sys/wait.h>
44 #include <sys/time.h>
45 #include <sys/socket.h>
46 #include <netdb.h>
47 
48 #define CHECK_ERROR(expr) ATF_REQUIRE_MSG((expr) != -1,\
49     "%s", strerror(errno))
50 
51 #define TCP 6
52 #define UDP 17
53 
54 static pid_t	run(const char *, char *const *);
55 static char	*concat(const char *restrict, const char *restrict);
56 static void	waitfor(pid_t, const char *);
57 static bool	run_udp_client(const char *);
58 static int	create_socket(const char *, const char *, int, int, time_t, struct sockaddr_storage *);
59 static bool	run_tcp_client(const char *);
60 
61 /* This test should take around 5 to 7 seconds to complete. */
62 ATF_TC(test_ratelimit);
63 
64 ATF_TC_HEAD(test_ratelimit, tc)
65 {
66 	atf_tc_set_md_var(tc, "descr", "Test inetd rate limiting values, "
67 	"uses UDP/TCP ports 5432-5439 with localhost.");
68 	/* Need to run as root so inetd can set uid */
69 	atf_tc_set_md_var(tc, "require.user", "root");
70 	atf_tc_set_md_var(tc, "require.progs", "inetd");
71 	/* Time out after 10 seconds, just in case */
72 	atf_tc_set_md_var(tc, "timeout", "10");
73 }
74 
75 ATF_TC_BODY(test_ratelimit, tc)
76 {
77 	pid_t proc;
78 
79 	/* Copy test server to relative path used in inetd_ratelimit.conf */
80 	atf_utils_copy_file(
81 	    concat(atf_tc_get_config_var(tc, "srcdir"), "/test_server"),
82 	    "test_server"
83 	);
84 
85 	/* Run inetd in debug mode using specified config file */
86 	proc = run("inetd", (char* const []) {
87 	    __UNCONST("inetd"), __UNCONST("-d"),
88 	    concat(atf_tc_get_config_var(tc, "srcdir"),
89 	    "/inetd_ratelimit.conf"),
90 	    NULL
91 	});
92 
93 	/* Wait for inetd to load services */
94 	sleep(1);
95 
96 	/*
97 	 * TODO test dgram/nowait? Specified in manpage but doesn't seem to
98 	 * work
99 	 */
100 
101 	/* dgram/wait ip_max of 3, should receive these 3 responses */
102 	for (int i = 0; i < 3; i++) {
103 		ATF_REQUIRE(run_udp_client("5432"));
104 	}
105 
106 	/* Rate limiting should prevent a response to this request */
107 	ATF_REQUIRE(!run_udp_client("5432"));
108 
109 	/* dgram/wait ip_max of 0 */
110 	ATF_REQUIRE(!run_udp_client("5433"));
111 
112 	/* dgram/wait service_max of 2 */
113 	ATF_REQUIRE(run_udp_client("5434"));
114 	ATF_REQUIRE(run_udp_client("5434"));
115 	ATF_REQUIRE(!run_udp_client("5434"));
116 
117 	/* dgram/wait service_max of 0 */
118 	ATF_REQUIRE(!run_udp_client("5435"));
119 
120 	/* stream/wait service_max of 2 */
121 	ATF_REQUIRE(run_tcp_client("5434"));
122 	ATF_REQUIRE(run_tcp_client("5434"));
123 	ATF_REQUIRE(!run_tcp_client("5434"));
124 
125 	/* stream/wait service_max of 0 */
126 	ATF_REQUIRE(!run_tcp_client("5435"));
127 
128 	/* stream/nowait ip_max of 3 */
129 	for (int i = 0; i < 3; i++) {
130 		ATF_REQUIRE(run_tcp_client("5436"));
131 	}
132 	ATF_REQUIRE(!run_tcp_client("5436"));
133 
134 	/* stream/nowait ip_max of 0 */
135 	ATF_REQUIRE(!run_tcp_client("5437"));
136 
137 	/* dgram/wait service_max of 2 */
138 	ATF_REQUIRE(run_tcp_client("5438"));
139 	ATF_REQUIRE(run_tcp_client("5438"));
140 	ATF_REQUIRE(!run_tcp_client("5438"));
141 
142 	/* dgram/wait service_max of 0 */
143 	ATF_REQUIRE(!run_tcp_client("5439"));
144 
145 	/* Exit inetd */
146 	CHECK_ERROR(kill(proc, SIGTERM));
147 
148 	waitfor(proc, "inetd");
149 }
150 
151 ATF_TP_ADD_TCS(tp)
152 {
153 	ATF_TP_ADD_TC(tp, test_ratelimit);
154 
155 	return atf_no_error();
156 }
157 
158 /* Return true if successfully received message, false if timeout */
159 static bool
160 run_udp_client(const char *port)
161 {
162 	char buffer[] = "test";
163 	struct sockaddr_storage addr;
164 
165 	int udp = create_socket("127.0.0.1", port, SOCK_DGRAM, UDP, 1, &addr);
166 
167 	CHECK_ERROR(sendto(udp, buffer, sizeof(buffer), 0,
168 	    (struct sockaddr *)&addr, addr.ss_len));
169 
170 	struct iovec iov = {
171 		.iov_base = buffer,
172 		.iov_len = sizeof(buffer)
173 	};
174 
175 	struct msghdr msg = {
176 		.msg_name = &addr,
177 		.msg_namelen = addr.ss_len, /* is this correct? */
178 		.msg_iov = &iov,
179 		.msg_iovlen = 1
180 	};
181 
182 	ssize_t count = recvmsg(udp, &msg, 0);
183 	if (count == -1) {
184 		if (errno == EAGAIN) {
185 			/* Timed out, return false */
186 			CHECK_ERROR(close(udp));
187 			return false;
188 		} else {
189 			/* All other errors fatal */
190 			CHECK_ERROR(-1);
191 		}
192 	}
193 	CHECK_ERROR(close(udp));
194 	return true;
195 }
196 
197 /* Run localhost tcp echo, return true if successful, false if timeout/disconnect */
198 static bool
199 run_tcp_client(const char *port)
200 {
201 	struct sockaddr_storage remote;
202 	ssize_t count;
203 	int tcp;
204 	char buffer[] = "test";
205 
206 	tcp = create_socket("127.0.0.1", port, SOCK_STREAM, TCP, 1, &remote);
207 	CHECK_ERROR(connect(tcp, (const struct sockaddr *)&remote,
208 	    remote.ss_len));
209 	CHECK_ERROR(send(tcp, buffer, sizeof(buffer), 0));
210 	count = recv(tcp, buffer, sizeof(buffer), 0);
211 	if (count == -1) {
212 		/*
213 		 * Connection reset by peer indicates the connection was
214 		 * dropped. EAGAIN indicates the timeout expired. Any other
215 		 * error is unexpected for this client program test.
216 		 */
217 		if(errno == ECONNRESET || errno == EAGAIN) {
218 			return false;
219 		} else {
220 			CHECK_ERROR(-1);
221 			return false;
222 		}
223 	}
224 
225 	if (count == 0) {
226 		/* socket was shutdown by inetd, no more data available */
227 		return false;
228 	}
229 	return true;
230 }
231 
232 /*
233  * Create a socket with the characteristics inferred by the args, return parsed
234  * socket address in dst.
235  */
236 static int
237 create_socket(const char *address, const char *port,
238     int socktype, int proto, time_t timeout_sec, struct sockaddr_storage *dst)
239 {
240         struct addrinfo hints = {
241 		.ai_flags = AI_NUMERICHOST,
242 		.ai_socktype = socktype,
243 		.ai_protocol = proto
244 	};
245 	struct addrinfo * res;
246 	int error, fd;
247 
248 	ATF_REQUIRE_EQ_MSG(error = getaddrinfo(address, port, &hints, &res), 0,
249 	    "%s", gai_strerror(error));
250 
251 	/* Make sure there is only one possible bind address */
252 	ATF_REQUIRE_MSG(res->ai_next == NULL, "Ambiguous create_socket args");
253 	CHECK_ERROR(fd = socket(res->ai_family,
254 	    res->ai_socktype, res->ai_protocol));
255 	struct timeval timeout = { timeout_sec, 0 };
256 	CHECK_ERROR(setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout,
257 	    sizeof(timeout)));
258 	memcpy(dst, res->ai_addr, res->ai_addrlen);
259 	freeaddrinfo(res);
260 	return fd;
261 }
262 
263 /* Run program with args */
264 static pid_t
265 run(const char *prog, char *const *args)
266 {
267 	pid_t proc;
268 	extern char **environ;
269 	ATF_REQUIRE_EQ(posix_spawnp(&proc, prog,
270 	    NULL, NULL, args, environ), 0);
271 	return proc;
272 }
273 
274 /* Wait for a process to exit, check return value */
275 static void
276 waitfor(pid_t pid, const char *taskname)
277 {
278 	int status;
279 	int rpid = waitpid(pid, &status, WALLSIG);
280 	ATF_REQUIRE_MSG(rpid == pid, "wait %d != %d %s",
281 	    rpid, pid, strerror(errno));
282 
283 	ATF_REQUIRE_EQ_MSG(WEXITSTATUS(status), EXIT_SUCCESS,
284 	    "%s failed with "
285 	    "exit status %d", taskname, WEXITSTATUS(status));
286 }
287 
288 /* Concatenate two const strings, do not free arguments */
289 static char *
290 concat(const char *restrict left, const char *restrict right)
291 {
292 	char *res;
293 	if (asprintf(&res, "%s%s", left, right) == -1)
294 		return NULL;
295 	return res;
296 }
297