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
36 static int
listensock(struct sockaddr_in * sin)37 listensock(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
58 static int
clientsock(struct sockaddr_in * sin)59 clientsock(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
69 static void
accfon(int l,struct accept_filter_arg * af)70 accfon(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 */
88 static ssize_t
usend(int s,const void * msg,size_t len)89 usend(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
98 ATF_TC_WITHOUT_HEAD(data);
ATF_TC_BODY(data,tc)99 ATF_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
116 ATF_TC_WITHOUT_HEAD(http);
ATF_TC_BODY(http,tc)117 ATF_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
155 ATF_TC_WITHOUT_HEAD(tls);
ATF_TC_BODY(tls,tc)156 ATF_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. */
213 ATF_TC_WITHOUT_HEAD(change);
ATF_TC_BODY(change,tc)214 ATF_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
ATF_TP_ADD_TCS(tp)243 ATF_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