1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022-2024 Gleb Smirnoff <glebius@FreeBSD.org>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28#include <sys/socket.h>
29#include <netinet/in.h>
30#include <errno.h>
31#include <fcntl.h>
32#include <stdlib.h>
33
34#include <atf-c.h>
35
36static int
37listensock(struct sockaddr_in *sin)
38{
39	int l;
40
41	ATF_REQUIRE((l = socket(PF_INET, SOCK_STREAM, 0)) > 0);
42	ATF_REQUIRE(fcntl(l, F_SETFL, O_NONBLOCK) != -1);
43	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
44	    sizeof(int)) == 0);
45	*sin = (struct sockaddr_in){
46		.sin_len = sizeof(sin),
47		.sin_family = AF_INET,
48		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
49	};
50	ATF_REQUIRE(bind(l, (struct sockaddr *)sin, sizeof(*sin)) == 0);
51	ATF_REQUIRE(getsockname(l, (struct sockaddr *)sin,
52	    &(socklen_t){ sizeof(*sin) }) == 0);
53	ATF_REQUIRE(listen(l, -1) == 0);
54
55	return (l);
56}
57
58static int
59clientsock(struct sockaddr_in *sin)
60{
61	int s;
62
63	ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, 0)) > 0);
64	ATF_REQUIRE(connect(s, (struct sockaddr *)sin, sizeof(*sin)) == 0);
65
66	return (s);
67}
68
69static void
70accfon(int l, struct accept_filter_arg *af)
71{
72
73	if (setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, af, sizeof(*af)) != 0) {
74		if (errno == ENOENT)
75			atf_tc_skip("Accept filter %s not loaded in kernel",
76			    af->af_name);
77		else
78			atf_tc_fail("setsockopt(SO_ACCEPTFILTER): %s",
79			    strerror(errno));
80	}
81}
82
83/*
84 * XXX: return from send(2) on a localhost connection doesn't guarantee that
85 * netisr has fully processed and delivered the data to the remote local
86 * socket.  Sleep a fraction of second to "guarantee" that it did.
87 */
88static ssize_t
89usend(int s, const void *msg, size_t len)
90{
91	ssize_t rv;
92
93	rv = send(s, msg, len, 0);
94	usleep(100000);
95	return (rv);
96}
97
98ATF_TC_WITHOUT_HEAD(data);
99ATF_TC_BODY(data, tc)
100{
101	struct accept_filter_arg afa = {
102		.af_name = "dataready"
103	};
104	struct sockaddr_in sin;
105	int l, s, a;
106
107	l = listensock(&sin);
108	accfon(l, &afa);
109	s = clientsock(&sin);
110	ATF_REQUIRE(accept(l, NULL, 0) == -1);
111	ATF_REQUIRE(errno == EAGAIN);
112	ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
113	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
114}
115
116ATF_TC_WITHOUT_HEAD(http);
117ATF_TC_BODY(http, tc)
118{
119	struct accept_filter_arg afa = {
120		.af_name = "httpready"
121	};
122	struct sockaddr_in sin;
123	int l, s, a;
124
125	l = listensock(&sin);
126	accfon(l, &afa);
127	s = clientsock(&sin);
128
129	/* 1) No data. */
130	ATF_REQUIRE(accept(l, NULL, 0) == -1);
131	ATF_REQUIRE(errno == EAGAIN);
132
133	/* 2) Data, that doesn't look like HTTP. */
134	ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
135	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
136
137	close(s);
138	close(a);
139
140#define	CHUNK1	"GET / "
141#define	CHUNK2	"HTTP/1.0\r\n\n"
142#define	LEN(c)	(sizeof(c) - 1)
143
144	/* 3) Partial HTTP. */
145	s = clientsock(&sin);
146	ATF_REQUIRE(usend(s, CHUNK1, LEN(CHUNK1)) == LEN(CHUNK1));
147	ATF_REQUIRE(accept(l, NULL, 0) == -1);
148	ATF_REQUIRE(errno == EAGAIN);
149
150	/* 4) Complete HTTP. */
151	ATF_REQUIRE(usend(s, CHUNK2, LEN(CHUNK2)) == LEN(CHUNK2));
152	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
153}
154
155ATF_TC_WITHOUT_HEAD(tls);
156ATF_TC_BODY(tls, tc)
157{
158	struct accept_filter_arg afa = {
159		.af_name = "tlsready"
160	};
161	struct sockaddr_in sin;
162	int l, s, a;
163
164	l = listensock(&sin);
165	accfon(l, &afa);
166	s = clientsock(&sin);
167
168	/* 1) No data. */
169	ATF_REQUIRE(accept(l, NULL, 0) == -1);
170	ATF_REQUIRE(errno == EAGAIN);
171
172	/* 2) Less than 5 bytes. */
173	ATF_REQUIRE(usend(s, "foo", sizeof("foo")) == sizeof("foo"));
174	ATF_REQUIRE(errno == EAGAIN);
175
176	/* 3) Something that doesn't look like TLS handshake. */
177	ATF_REQUIRE(usend(s, "bar", sizeof("bar")) == sizeof("bar"));
178	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
179
180	close(s);
181	close(a);
182
183	/* 4) Partial TLS record. */
184	s = clientsock(&sin);
185	struct {
186		uint8_t  type;
187		uint16_t version;
188		uint16_t length;
189	} __attribute__((__packed__)) header = {
190		.type = 0x16,
191		.length = htons((uint16_t)(arc4random() % 16384)),
192	};
193	_Static_assert(sizeof(header) == 5, "");
194	ATF_REQUIRE(usend(s, &header, sizeof(header)) == sizeof(header));
195	ssize_t sent = 0;
196	do {
197		size_t len;
198		char *buf;
199
200		ATF_REQUIRE(accept(l, NULL, 0) == -1);
201		ATF_REQUIRE(errno == EAGAIN);
202
203		len = arc4random() % 1024;
204		buf = alloca(len);
205		ATF_REQUIRE(usend(s, buf, len) == (ssize_t)len);
206		sent += len;
207	} while (sent < ntohs(header.length));
208	/* TLS header with bytes >= declared length. */
209	ATF_REQUIRE((a = accept(l, NULL, 0)) > 0);
210}
211
212/* Check changing to a different filter. */
213ATF_TC_WITHOUT_HEAD(change);
214ATF_TC_BODY(change, tc)
215{
216	struct accept_filter_arg dfa = {
217		.af_name = "dataready"
218	};
219	struct accept_filter_arg hfa = {
220		.af_name = "httpready"
221	};
222	struct sockaddr_in sin;
223	int n, l;
224
225	l = listensock(&sin);
226	accfon(l, &dfa);
227
228	/* Refuse to change filter without explicit removal of the old one. */
229	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa,
230	    sizeof(hfa)) != 0 && errno == EBUSY);
231
232	/* But allow after clearing. */
233	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0) == 0);
234	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_ACCEPTFILTER, &hfa,
235	    sizeof(hfa)) == 0);
236
237	/* Must be listening socket. */
238	ATF_REQUIRE((n = socket(PF_INET, SOCK_STREAM, 0)) > 0);
239	ATF_REQUIRE(setsockopt(n, SOL_SOCKET, SO_ACCEPTFILTER, &dfa,
240	    sizeof(dfa)) != 0 && errno == EINVAL);
241}
242
243ATF_TP_ADD_TCS(tp)
244{
245	ATF_TP_ADD_TC(tp, data);
246	ATF_TP_ADD_TC(tp, http);
247	ATF_TP_ADD_TC(tp, tls);
248	ATF_TP_ADD_TC(tp, change);
249
250	return (atf_no_error());
251}
252