1a9643ea8Slogwang /*-
2*22ce4affSfengbojiang * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*22ce4affSfengbojiang *
4a9643ea8Slogwang * Copyright (c) 2000 Paycounter, Inc.
5a9643ea8Slogwang * Author: Alfred Perlstein <[email protected]>, <[email protected]>
6a9643ea8Slogwang * All rights reserved.
7a9643ea8Slogwang *
8a9643ea8Slogwang * Redistribution and use in source and binary forms, with or without
9a9643ea8Slogwang * modification, are permitted provided that the following conditions
10a9643ea8Slogwang * are met:
11a9643ea8Slogwang * 1. Redistributions of source code must retain the above copyright
12a9643ea8Slogwang * notice, this list of conditions and the following disclaimer.
13a9643ea8Slogwang * 2. Redistributions in binary form must reproduce the above copyright
14a9643ea8Slogwang * notice, this list of conditions and the following disclaimer in the
15a9643ea8Slogwang * documentation and/or other materials provided with the distribution.
16a9643ea8Slogwang *
17a9643ea8Slogwang * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a9643ea8Slogwang * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a9643ea8Slogwang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a9643ea8Slogwang * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a9643ea8Slogwang * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a9643ea8Slogwang * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a9643ea8Slogwang * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a9643ea8Slogwang * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a9643ea8Slogwang * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a9643ea8Slogwang * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a9643ea8Slogwang * SUCH DAMAGE.
28a9643ea8Slogwang */
29a9643ea8Slogwang
30a9643ea8Slogwang #include <sys/cdefs.h>
31a9643ea8Slogwang __FBSDID("$FreeBSD$");
32a9643ea8Slogwang
33a9643ea8Slogwang #define ACCEPT_FILTER_MOD
34a9643ea8Slogwang
35a9643ea8Slogwang #include <sys/param.h>
36a9643ea8Slogwang #include <sys/kernel.h>
37a9643ea8Slogwang #include <sys/mbuf.h>
38a9643ea8Slogwang #include <sys/module.h>
39a9643ea8Slogwang #include <sys/signalvar.h>
40a9643ea8Slogwang #include <sys/sysctl.h>
41a9643ea8Slogwang #include <sys/socketvar.h>
42a9643ea8Slogwang
43a9643ea8Slogwang /* check for GET/HEAD */
44a9643ea8Slogwang static int sohashttpget(struct socket *so, void *arg, int waitflag);
45a9643ea8Slogwang /* check for HTTP/1.0 or HTTP/1.1 */
46a9643ea8Slogwang static int soparsehttpvers(struct socket *so, void *arg, int waitflag);
47a9643ea8Slogwang /* check for end of HTTP/1.x request */
48a9643ea8Slogwang static int soishttpconnected(struct socket *so, void *arg, int waitflag);
49a9643ea8Slogwang /* strcmp on an mbuf chain */
50a9643ea8Slogwang static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
51a9643ea8Slogwang /* strncmp on an mbuf chain */
52a9643ea8Slogwang static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
53a9643ea8Slogwang int max, char *cmp);
54a9643ea8Slogwang /* socketbuffer is full */
55a9643ea8Slogwang static int sbfull(struct sockbuf *sb);
56a9643ea8Slogwang
57*22ce4affSfengbojiang ACCEPT_FILTER_DEFINE(accf_http, "httpready", sohashttpget, NULL, NULL, 1);
58a9643ea8Slogwang
59a9643ea8Slogwang static int parse_http_version = 1;
60a9643ea8Slogwang
61*22ce4affSfengbojiang static SYSCTL_NODE(_net_inet_accf, OID_AUTO, http,
62*22ce4affSfengbojiang CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
63a9643ea8Slogwang "HTTP accept filter");
64a9643ea8Slogwang SYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
65a9643ea8Slogwang &parse_http_version, 1,
66a9643ea8Slogwang "Parse http version so that non 1.x requests work");
67a9643ea8Slogwang
68a9643ea8Slogwang #ifdef ACCF_HTTP_DEBUG
69a9643ea8Slogwang #define DPRINT(fmt, args...) \
70a9643ea8Slogwang do { \
71a9643ea8Slogwang printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
72a9643ea8Slogwang } while (0)
73a9643ea8Slogwang #else
74a9643ea8Slogwang #define DPRINT(fmt, args...)
75a9643ea8Slogwang #endif
76a9643ea8Slogwang
77a9643ea8Slogwang static int
sbfull(struct sockbuf * sb)78a9643ea8Slogwang sbfull(struct sockbuf *sb)
79a9643ea8Slogwang {
80a9643ea8Slogwang
81a9643ea8Slogwang DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
82a9643ea8Slogwang "mbcnt(%ld) >= mbmax(%ld): %d",
83a9643ea8Slogwang sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
84a9643ea8Slogwang sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
85a9643ea8Slogwang return (sbused(sb) >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
86a9643ea8Slogwang }
87a9643ea8Slogwang
88a9643ea8Slogwang /*
89a9643ea8Slogwang * start at mbuf m, (must provide npkt if exists)
90a9643ea8Slogwang * starting at offset in m compare characters in mbuf chain for 'cmp'
91a9643ea8Slogwang */
92a9643ea8Slogwang static int
mbufstrcmp(struct mbuf * m,struct mbuf * npkt,int offset,char * cmp)93a9643ea8Slogwang mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
94a9643ea8Slogwang {
95a9643ea8Slogwang struct mbuf *n;
96a9643ea8Slogwang
97a9643ea8Slogwang for (; m != NULL; m = n) {
98a9643ea8Slogwang n = npkt;
99a9643ea8Slogwang if (npkt)
100a9643ea8Slogwang npkt = npkt->m_nextpkt;
101a9643ea8Slogwang for (; m; m = m->m_next) {
102a9643ea8Slogwang for (; offset < m->m_len; offset++, cmp++) {
103a9643ea8Slogwang if (*cmp == '\0')
104a9643ea8Slogwang return (1);
105a9643ea8Slogwang else if (*cmp != *(mtod(m, char *) + offset))
106a9643ea8Slogwang return (0);
107a9643ea8Slogwang }
108a9643ea8Slogwang if (*cmp == '\0')
109a9643ea8Slogwang return (1);
110a9643ea8Slogwang offset = 0;
111a9643ea8Slogwang }
112a9643ea8Slogwang }
113a9643ea8Slogwang return (0);
114a9643ea8Slogwang }
115a9643ea8Slogwang
116a9643ea8Slogwang /*
117a9643ea8Slogwang * start at mbuf m, (must provide npkt if exists)
118a9643ea8Slogwang * starting at offset in m compare characters in mbuf chain for 'cmp'
119a9643ea8Slogwang * stop at 'max' characters
120a9643ea8Slogwang */
121a9643ea8Slogwang static int
mbufstrncmp(struct mbuf * m,struct mbuf * npkt,int offset,int max,char * cmp)122a9643ea8Slogwang mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
123a9643ea8Slogwang {
124a9643ea8Slogwang struct mbuf *n;
125a9643ea8Slogwang
126a9643ea8Slogwang for (; m != NULL; m = n) {
127a9643ea8Slogwang n = npkt;
128a9643ea8Slogwang if (npkt)
129a9643ea8Slogwang npkt = npkt->m_nextpkt;
130a9643ea8Slogwang for (; m; m = m->m_next) {
131a9643ea8Slogwang for (; offset < m->m_len; offset++, cmp++, max--) {
132a9643ea8Slogwang if (max == 0 || *cmp == '\0')
133a9643ea8Slogwang return (1);
134a9643ea8Slogwang else if (*cmp != *(mtod(m, char *) + offset))
135a9643ea8Slogwang return (0);
136a9643ea8Slogwang }
137a9643ea8Slogwang if (max == 0 || *cmp == '\0')
138a9643ea8Slogwang return (1);
139a9643ea8Slogwang offset = 0;
140a9643ea8Slogwang }
141a9643ea8Slogwang }
142a9643ea8Slogwang return (0);
143a9643ea8Slogwang }
144a9643ea8Slogwang
145a9643ea8Slogwang #define STRSETUP(sptr, slen, str) \
146a9643ea8Slogwang do { \
147a9643ea8Slogwang sptr = str; \
148a9643ea8Slogwang slen = sizeof(str) - 1; \
149a9643ea8Slogwang } while(0)
150a9643ea8Slogwang
151a9643ea8Slogwang static int
sohashttpget(struct socket * so,void * arg,int waitflag)152a9643ea8Slogwang sohashttpget(struct socket *so, void *arg, int waitflag)
153a9643ea8Slogwang {
154a9643ea8Slogwang
155a9643ea8Slogwang if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0 &&
156a9643ea8Slogwang !sbfull(&so->so_rcv)) {
157a9643ea8Slogwang struct mbuf *m;
158a9643ea8Slogwang char *cmp;
159a9643ea8Slogwang int cmplen, cc;
160a9643ea8Slogwang
161a9643ea8Slogwang m = so->so_rcv.sb_mb;
162a9643ea8Slogwang cc = sbavail(&so->so_rcv) - 1;
163a9643ea8Slogwang if (cc < 1)
164a9643ea8Slogwang return (SU_OK);
165a9643ea8Slogwang switch (*mtod(m, char *)) {
166a9643ea8Slogwang case 'G':
167a9643ea8Slogwang STRSETUP(cmp, cmplen, "ET ");
168a9643ea8Slogwang break;
169a9643ea8Slogwang case 'H':
170a9643ea8Slogwang STRSETUP(cmp, cmplen, "EAD ");
171a9643ea8Slogwang break;
172a9643ea8Slogwang default:
173a9643ea8Slogwang goto fallout;
174a9643ea8Slogwang }
175a9643ea8Slogwang if (cc < cmplen) {
176a9643ea8Slogwang if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
177a9643ea8Slogwang DPRINT("short cc (%d) but mbufstrncmp ok", cc);
178a9643ea8Slogwang return (SU_OK);
179a9643ea8Slogwang } else {
180a9643ea8Slogwang DPRINT("short cc (%d) mbufstrncmp failed", cc);
181a9643ea8Slogwang goto fallout;
182a9643ea8Slogwang }
183a9643ea8Slogwang }
184a9643ea8Slogwang if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
185a9643ea8Slogwang DPRINT("mbufstrcmp ok");
186a9643ea8Slogwang if (parse_http_version == 0)
187a9643ea8Slogwang return (soishttpconnected(so, arg, waitflag));
188a9643ea8Slogwang else
189a9643ea8Slogwang return (soparsehttpvers(so, arg, waitflag));
190a9643ea8Slogwang }
191a9643ea8Slogwang DPRINT("mbufstrcmp bad");
192a9643ea8Slogwang }
193a9643ea8Slogwang
194a9643ea8Slogwang fallout:
195a9643ea8Slogwang DPRINT("fallout");
196a9643ea8Slogwang return (SU_ISCONNECTED);
197a9643ea8Slogwang }
198a9643ea8Slogwang
199a9643ea8Slogwang static int
soparsehttpvers(struct socket * so,void * arg,int waitflag)200a9643ea8Slogwang soparsehttpvers(struct socket *so, void *arg, int waitflag)
201a9643ea8Slogwang {
202a9643ea8Slogwang struct mbuf *m, *n;
203a9643ea8Slogwang int i, cc, spaces, inspaces;
204a9643ea8Slogwang
205a9643ea8Slogwang if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
206a9643ea8Slogwang goto fallout;
207a9643ea8Slogwang
208a9643ea8Slogwang m = so->so_rcv.sb_mb;
209a9643ea8Slogwang cc = sbavail(&so->so_rcv);
210a9643ea8Slogwang inspaces = spaces = 0;
211a9643ea8Slogwang for (m = so->so_rcv.sb_mb; m; m = n) {
212a9643ea8Slogwang n = m->m_nextpkt;
213a9643ea8Slogwang for (; m; m = m->m_next) {
214a9643ea8Slogwang for (i = 0; i < m->m_len; i++, cc--) {
215a9643ea8Slogwang switch (*(mtod(m, char *) + i)) {
216a9643ea8Slogwang case ' ':
217a9643ea8Slogwang /* tabs? '\t' */
218a9643ea8Slogwang if (!inspaces) {
219a9643ea8Slogwang spaces++;
220a9643ea8Slogwang inspaces = 1;
221a9643ea8Slogwang }
222a9643ea8Slogwang break;
223a9643ea8Slogwang case '\r':
224a9643ea8Slogwang case '\n':
225a9643ea8Slogwang DPRINT("newline");
226a9643ea8Slogwang goto fallout;
227a9643ea8Slogwang default:
228a9643ea8Slogwang if (spaces != 2) {
229a9643ea8Slogwang inspaces = 0;
230a9643ea8Slogwang break;
231a9643ea8Slogwang }
232a9643ea8Slogwang
233a9643ea8Slogwang /*
234a9643ea8Slogwang * if we don't have enough characters
235a9643ea8Slogwang * left (cc < sizeof("HTTP/1.0") - 1)
236a9643ea8Slogwang * then see if the remaining ones
237a9643ea8Slogwang * are a request we can parse.
238a9643ea8Slogwang */
239a9643ea8Slogwang if (cc < sizeof("HTTP/1.0") - 1) {
240a9643ea8Slogwang if (mbufstrncmp(m, n, i, cc,
241a9643ea8Slogwang "HTTP/1.") == 1) {
242a9643ea8Slogwang DPRINT("ok");
243a9643ea8Slogwang goto readmore;
244a9643ea8Slogwang } else {
245a9643ea8Slogwang DPRINT("bad");
246a9643ea8Slogwang goto fallout;
247a9643ea8Slogwang }
248a9643ea8Slogwang } else if (
249a9643ea8Slogwang mbufstrcmp(m, n, i, "HTTP/1.0") ||
250a9643ea8Slogwang mbufstrcmp(m, n, i, "HTTP/1.1")) {
251a9643ea8Slogwang DPRINT("ok");
252a9643ea8Slogwang return (soishttpconnected(so,
253a9643ea8Slogwang arg, waitflag));
254a9643ea8Slogwang } else {
255a9643ea8Slogwang DPRINT("bad");
256a9643ea8Slogwang goto fallout;
257a9643ea8Slogwang }
258a9643ea8Slogwang }
259a9643ea8Slogwang }
260a9643ea8Slogwang }
261a9643ea8Slogwang }
262a9643ea8Slogwang readmore:
263a9643ea8Slogwang DPRINT("readmore");
264a9643ea8Slogwang /*
265a9643ea8Slogwang * if we hit here we haven't hit something
266a9643ea8Slogwang * we don't understand or a newline, so try again
267a9643ea8Slogwang */
268a9643ea8Slogwang soupcall_set(so, SO_RCV, soparsehttpvers, arg);
269a9643ea8Slogwang return (SU_OK);
270a9643ea8Slogwang
271a9643ea8Slogwang fallout:
272a9643ea8Slogwang DPRINT("fallout");
273a9643ea8Slogwang return (SU_ISCONNECTED);
274a9643ea8Slogwang }
275a9643ea8Slogwang
276a9643ea8Slogwang #define NCHRS 3
277a9643ea8Slogwang
278a9643ea8Slogwang static int
soishttpconnected(struct socket * so,void * arg,int waitflag)279a9643ea8Slogwang soishttpconnected(struct socket *so, void *arg, int waitflag)
280a9643ea8Slogwang {
281a9643ea8Slogwang char a, b, c;
282a9643ea8Slogwang struct mbuf *m, *n;
283a9643ea8Slogwang int ccleft, copied;
284a9643ea8Slogwang
285a9643ea8Slogwang DPRINT("start");
286a9643ea8Slogwang if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
287a9643ea8Slogwang goto gotit;
288a9643ea8Slogwang
289a9643ea8Slogwang /*
290a9643ea8Slogwang * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
291a9643ea8Slogwang * copied - how much we've copied so far
292a9643ea8Slogwang * ccleft - how many bytes remaining in the socketbuffer
293a9643ea8Slogwang * just loop over the mbufs subtracting from 'ccleft' until we only
294a9643ea8Slogwang * have NCHRS left
295a9643ea8Slogwang */
296a9643ea8Slogwang copied = 0;
297a9643ea8Slogwang ccleft = sbavail(&so->so_rcv);
298a9643ea8Slogwang if (ccleft < NCHRS)
299a9643ea8Slogwang goto readmore;
300a9643ea8Slogwang a = b = c = '\0';
301a9643ea8Slogwang for (m = so->so_rcv.sb_mb; m; m = n) {
302a9643ea8Slogwang n = m->m_nextpkt;
303a9643ea8Slogwang for (; m; m = m->m_next) {
304a9643ea8Slogwang ccleft -= m->m_len;
305a9643ea8Slogwang if (ccleft <= NCHRS) {
306a9643ea8Slogwang char *src;
307a9643ea8Slogwang int tocopy;
308a9643ea8Slogwang
309a9643ea8Slogwang tocopy = (NCHRS - ccleft) - copied;
310a9643ea8Slogwang src = mtod(m, char *) + (m->m_len - tocopy);
311a9643ea8Slogwang
312a9643ea8Slogwang while (tocopy--) {
313a9643ea8Slogwang switch (copied++) {
314a9643ea8Slogwang case 0:
315a9643ea8Slogwang a = *src++;
316a9643ea8Slogwang break;
317a9643ea8Slogwang case 1:
318a9643ea8Slogwang b = *src++;
319a9643ea8Slogwang break;
320a9643ea8Slogwang case 2:
321a9643ea8Slogwang c = *src++;
322a9643ea8Slogwang break;
323a9643ea8Slogwang }
324a9643ea8Slogwang }
325a9643ea8Slogwang }
326a9643ea8Slogwang }
327a9643ea8Slogwang }
328a9643ea8Slogwang if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
329a9643ea8Slogwang /* we have all request headers */
330a9643ea8Slogwang goto gotit;
331a9643ea8Slogwang }
332a9643ea8Slogwang
333a9643ea8Slogwang readmore:
334a9643ea8Slogwang soupcall_set(so, SO_RCV, soishttpconnected, arg);
335a9643ea8Slogwang return (SU_OK);
336a9643ea8Slogwang
337a9643ea8Slogwang gotit:
338a9643ea8Slogwang return (SU_ISCONNECTED);
339a9643ea8Slogwang }
340