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