153553Stanimura/*-
253553Stanimura * SPDX-License-Identifier: BSD-2-Clause
353553Stanimura *
453553Stanimura * Copyright (c) 2022-2024 Gleb Smirnoff <glebius@FreeBSD.org>
553553Stanimura *
653553Stanimura * Redistribution and use in source and binary forms, with or without
753553Stanimura * modification, are permitted provided that the following conditions
853553Stanimura * are met:
953553Stanimura * 1. Redistributions of source code must retain the above copyright
1053553Stanimura *    notice, this list of conditions and the following disclaimer.
1153553Stanimura * 2. Redistributions in binary form must reproduce the above copyright
1253553Stanimura *    notice, this list of conditions and the following disclaimer in the
1353553Stanimura *    documentation and/or other materials provided with the distribution.
1453553Stanimura *
1553553Stanimura * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1653553Stanimura * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1753553Stanimura * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1853553Stanimura * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1953553Stanimura * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2053553Stanimura * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2153553Stanimura * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2253553Stanimura * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2353553Stanimura * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2453553Stanimura * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2553553Stanimura * SUCH DAMAGE.
2653553Stanimura */
2753553Stanimura
2853553Stanimura#include <sys/socket.h>
2953553Stanimura#include <netinet/in.h>
3053553Stanimura#include <errno.h>
3177504Scg#include <fcntl.h>
3277504Scg#include <stdlib.h>
3377504Scg
3477504Scg#include <atf-c.h>
3577504Scg
3677504Scgstatic int
3778673Scglistensock(struct sockaddr_in *sin)
3877504Scg{
3953553Stanimura	int l;
4053553Stanimura
4153553Stanimura	ATF_REQUIRE((l = socket(PF_INET, SOCK_STREAM, 0)) > 0);
4253553Stanimura	ATF_REQUIRE(fcntl(l, F_SETFL, O_NONBLOCK) != -1);
4353553Stanimura	ATF_REQUIRE(setsockopt(l, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1},
4453553Stanimura	    sizeof(int)) == 0);
4553553Stanimura	*sin = (struct sockaddr_in){
4653553Stanimura		.sin_len = sizeof(sin),
4753553Stanimura		.sin_family = AF_INET,
4853553Stanimura		.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
4953553Stanimura	};
5053553Stanimura	ATF_REQUIRE(bind(l, (struct sockaddr *)sin, sizeof(*sin)) == 0);
5155320Stanimura	ATF_REQUIRE(getsockname(l, (struct sockaddr *)sin,
5255320Stanimura	    &(socklen_t){ sizeof(*sin) }) == 0);
5355320Stanimura	ATF_REQUIRE(listen(l, -1) == 0);
5477504Scg
5555320Stanimura	return (l);
5655320Stanimura}
5755320Stanimura
5855320Stanimurastatic int
5953553Stanimuraclientsock(struct sockaddr_in *sin)
6077504Scg{
6153553Stanimura	int s;
6253553Stanimura
6353553Stanimura	ATF_REQUIRE((s = socket(PF_INET, SOCK_STREAM, 0)) > 0);
6453553Stanimura	ATF_REQUIRE(connect(s, (struct sockaddr *)sin, sizeof(*sin)) == 0);
6553553Stanimura
6653553Stanimura	return (s);
6753553Stanimura}
6853553Stanimura
69147626Sglebiusstatic void
7053553Stanimuraaccfon(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