1c398230bSWarner Losh /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3fe267a55SPedro F. Giffuni *
434b94e8bSAlfred Perlstein * Copyright (c) 2000 Paycounter, Inc.
534b94e8bSAlfred Perlstein * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
6a79b7128SAlfred Perlstein * All rights reserved.
7a79b7128SAlfred Perlstein *
8a79b7128SAlfred Perlstein * Redistribution and use in source and binary forms, with or without
9a79b7128SAlfred Perlstein * modification, are permitted provided that the following conditions
10a79b7128SAlfred Perlstein * are met:
11a79b7128SAlfred Perlstein * 1. Redistributions of source code must retain the above copyright
12a79b7128SAlfred Perlstein * notice, this list of conditions and the following disclaimer.
13a79b7128SAlfred Perlstein * 2. Redistributions in binary form must reproduce the above copyright
14a79b7128SAlfred Perlstein * notice, this list of conditions and the following disclaimer in the
15a79b7128SAlfred Perlstein * documentation and/or other materials provided with the distribution.
16a79b7128SAlfred Perlstein *
17a79b7128SAlfred Perlstein * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18a79b7128SAlfred Perlstein * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a79b7128SAlfred Perlstein * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a79b7128SAlfred Perlstein * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21a79b7128SAlfred Perlstein * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a79b7128SAlfred Perlstein * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a79b7128SAlfred Perlstein * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a79b7128SAlfred Perlstein * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a79b7128SAlfred Perlstein * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a79b7128SAlfred Perlstein * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a79b7128SAlfred Perlstein * SUCH DAMAGE.
28a79b7128SAlfred Perlstein */
29a79b7128SAlfred Perlstein
304b421e2dSMike Silbersack #include <sys/cdefs.h>
31a79b7128SAlfred Perlstein #define ACCEPT_FILTER_MOD
32a79b7128SAlfred Perlstein
33a79b7128SAlfred Perlstein #include <sys/param.h>
34a79b7128SAlfred Perlstein #include <sys/kernel.h>
35960ed29cSSeigo Tanimura #include <sys/mbuf.h>
365dba30f1SPoul-Henning Kamp #include <sys/module.h>
37960ed29cSSeigo Tanimura #include <sys/signalvar.h>
3834b94e8bSAlfred Perlstein #include <sys/sysctl.h>
39a79b7128SAlfred Perlstein #include <sys/socketvar.h>
40a79b7128SAlfred Perlstein
4134b94e8bSAlfred Perlstein /* check for GET/HEAD */
4274fb0ba7SJohn Baldwin static int sohashttpget(struct socket *so, void *arg, int waitflag);
4334b94e8bSAlfred Perlstein /* check for HTTP/1.0 or HTTP/1.1 */
4474fb0ba7SJohn Baldwin static int soparsehttpvers(struct socket *so, void *arg, int waitflag);
4534b94e8bSAlfred Perlstein /* check for end of HTTP/1.x request */
4674fb0ba7SJohn Baldwin static int soishttpconnected(struct socket *so, void *arg, int waitflag);
4734b94e8bSAlfred Perlstein /* strcmp on an mbuf chain */
4834b94e8bSAlfred Perlstein static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
4934b94e8bSAlfred Perlstein /* strncmp on an mbuf chain */
5034b94e8bSAlfred Perlstein static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
5134b94e8bSAlfred Perlstein int max, char *cmp);
5234b94e8bSAlfred Perlstein /* socketbuffer is full */
5334b94e8bSAlfred Perlstein static int sbfull(struct sockbuf *sb);
54a79b7128SAlfred Perlstein
55591b09b4SMark Johnston ACCEPT_FILTER_DEFINE(accf_http, "httpready", sohashttpget, NULL, NULL, 1);
56a79b7128SAlfred Perlstein
5734b94e8bSAlfred Perlstein static int parse_http_version = 1;
58a79b7128SAlfred Perlstein
597029da5cSPawel Biernacki static SYSCTL_NODE(_net_inet_accf, OID_AUTO, http,
607029da5cSPawel Biernacki CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
6134b94e8bSAlfred Perlstein "HTTP accept filter");
6234b94e8bSAlfred Perlstein SYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
6334b94e8bSAlfred Perlstein &parse_http_version, 1,
6434b94e8bSAlfred Perlstein "Parse http version so that non 1.x requests work");
6534b94e8bSAlfred Perlstein
6634b94e8bSAlfred Perlstein #ifdef ACCF_HTTP_DEBUG
6734b94e8bSAlfred Perlstein #define DPRINT(fmt, args...) \
6834b94e8bSAlfred Perlstein do { \
6934b94e8bSAlfred Perlstein printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
7034b94e8bSAlfred Perlstein } while (0)
7134b94e8bSAlfred Perlstein #else
7234b94e8bSAlfred Perlstein #define DPRINT(fmt, args...)
7334b94e8bSAlfred Perlstein #endif
7434b94e8bSAlfred Perlstein
7534b94e8bSAlfred Perlstein static int
sbfull(struct sockbuf * sb)7634b94e8bSAlfred Perlstein sbfull(struct sockbuf *sb)
77a79b7128SAlfred Perlstein {
78a79b7128SAlfred Perlstein
79ef104730SAlfred Perlstein DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
80ef104730SAlfred Perlstein "mbcnt(%ld) >= mbmax(%ld): %d",
8134b94e8bSAlfred Perlstein sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
8234b94e8bSAlfred Perlstein sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
83cfa6009eSGleb Smirnoff return (sbused(sb) >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
8434b94e8bSAlfred Perlstein }
8534b94e8bSAlfred Perlstein
8634b94e8bSAlfred Perlstein /*
8734b94e8bSAlfred Perlstein * start at mbuf m, (must provide npkt if exists)
8834b94e8bSAlfred Perlstein * starting at offset in m compare characters in mbuf chain for 'cmp'
8934b94e8bSAlfred Perlstein */
9034b94e8bSAlfred Perlstein static int
mbufstrcmp(struct mbuf * m,struct mbuf * npkt,int offset,char * cmp)9134b94e8bSAlfred Perlstein mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
9234b94e8bSAlfred Perlstein {
9334b94e8bSAlfred Perlstein struct mbuf *n;
9434b94e8bSAlfred Perlstein
9534b94e8bSAlfred Perlstein for (; m != NULL; m = n) {
9634b94e8bSAlfred Perlstein n = npkt;
9734b94e8bSAlfred Perlstein if (npkt)
9834b94e8bSAlfred Perlstein npkt = npkt->m_nextpkt;
9934b94e8bSAlfred Perlstein for (; m; m = m->m_next) {
10034b94e8bSAlfred Perlstein for (; offset < m->m_len; offset++, cmp++) {
101ef104730SAlfred Perlstein if (*cmp == '\0')
10234b94e8bSAlfred Perlstein return (1);
103ef104730SAlfred Perlstein else if (*cmp != *(mtod(m, char *) + offset))
10434b94e8bSAlfred Perlstein return (0);
105a79b7128SAlfred Perlstein }
10659017610SAlfred Perlstein if (*cmp == '\0')
10759017610SAlfred Perlstein return (1);
10834b94e8bSAlfred Perlstein offset = 0;
109a79b7128SAlfred Perlstein }
11034b94e8bSAlfred Perlstein }
11134b94e8bSAlfred Perlstein return (0);
11234b94e8bSAlfred Perlstein }
11334b94e8bSAlfred Perlstein
11434b94e8bSAlfred Perlstein /*
11534b94e8bSAlfred Perlstein * start at mbuf m, (must provide npkt if exists)
11634b94e8bSAlfred Perlstein * starting at offset in m compare characters in mbuf chain for 'cmp'
11734b94e8bSAlfred Perlstein * stop at 'max' characters
11834b94e8bSAlfred Perlstein */
11934b94e8bSAlfred Perlstein static int
mbufstrncmp(struct mbuf * m,struct mbuf * npkt,int offset,int max,char * cmp)12034b94e8bSAlfred Perlstein mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
12134b94e8bSAlfred Perlstein {
12234b94e8bSAlfred Perlstein struct mbuf *n;
12334b94e8bSAlfred Perlstein
12434b94e8bSAlfred Perlstein for (; m != NULL; m = n) {
12534b94e8bSAlfred Perlstein n = npkt;
12634b94e8bSAlfred Perlstein if (npkt)
12734b94e8bSAlfred Perlstein npkt = npkt->m_nextpkt;
12834b94e8bSAlfred Perlstein for (; m; m = m->m_next) {
12934b94e8bSAlfred Perlstein for (; offset < m->m_len; offset++, cmp++, max--) {
130ef104730SAlfred Perlstein if (max == 0 || *cmp == '\0')
13134b94e8bSAlfred Perlstein return (1);
132ef104730SAlfred Perlstein else if (*cmp != *(mtod(m, char *) + offset))
13334b94e8bSAlfred Perlstein return (0);
13434b94e8bSAlfred Perlstein }
13559017610SAlfred Perlstein if (max == 0 || *cmp == '\0')
13659017610SAlfred Perlstein return (1);
13734b94e8bSAlfred Perlstein offset = 0;
13834b94e8bSAlfred Perlstein }
13934b94e8bSAlfred Perlstein }
14034b94e8bSAlfred Perlstein return (0);
14134b94e8bSAlfred Perlstein }
14234b94e8bSAlfred Perlstein
14334b94e8bSAlfred Perlstein #define STRSETUP(sptr, slen, str) \
14434b94e8bSAlfred Perlstein do { \
14534b94e8bSAlfred Perlstein sptr = str; \
14634b94e8bSAlfred Perlstein slen = sizeof(str) - 1; \
14734b94e8bSAlfred Perlstein } while(0)
148a79b7128SAlfred Perlstein
14974fb0ba7SJohn Baldwin static int
sohashttpget(struct socket * so,void * arg,int waitflag)150a79b7128SAlfred Perlstein sohashttpget(struct socket *so, void *arg, int waitflag)
151a79b7128SAlfred Perlstein {
152a79b7128SAlfred Perlstein
153cfa6009eSGleb Smirnoff if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0 &&
154cfa6009eSGleb Smirnoff !sbfull(&so->so_rcv)) {
155a79b7128SAlfred Perlstein struct mbuf *m;
15634b94e8bSAlfred Perlstein char *cmp;
15734b94e8bSAlfred Perlstein int cmplen, cc;
158a79b7128SAlfred Perlstein
159a79b7128SAlfred Perlstein m = so->so_rcv.sb_mb;
160cfa6009eSGleb Smirnoff cc = sbavail(&so->so_rcv) - 1;
16134b94e8bSAlfred Perlstein if (cc < 1)
16274fb0ba7SJohn Baldwin return (SU_OK);
16334b94e8bSAlfred Perlstein switch (*mtod(m, char *)) {
16434b94e8bSAlfred Perlstein case 'G':
16534b94e8bSAlfred Perlstein STRSETUP(cmp, cmplen, "ET ");
16634b94e8bSAlfred Perlstein break;
16734b94e8bSAlfred Perlstein case 'H':
16834b94e8bSAlfred Perlstein STRSETUP(cmp, cmplen, "EAD ");
16934b94e8bSAlfred Perlstein break;
17034b94e8bSAlfred Perlstein default:
17134b94e8bSAlfred Perlstein goto fallout;
17234b94e8bSAlfred Perlstein }
17334b94e8bSAlfred Perlstein if (cc < cmplen) {
17434b94e8bSAlfred Perlstein if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
17534b94e8bSAlfred Perlstein DPRINT("short cc (%d) but mbufstrncmp ok", cc);
17674fb0ba7SJohn Baldwin return (SU_OK);
17734b94e8bSAlfred Perlstein } else {
17834b94e8bSAlfred Perlstein DPRINT("short cc (%d) mbufstrncmp failed", cc);
17934b94e8bSAlfred Perlstein goto fallout;
18034b94e8bSAlfred Perlstein }
18134b94e8bSAlfred Perlstein }
18234b94e8bSAlfred Perlstein if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
18334b94e8bSAlfred Perlstein DPRINT("mbufstrcmp ok");
18434b94e8bSAlfred Perlstein if (parse_http_version == 0)
18574fb0ba7SJohn Baldwin return (soishttpconnected(so, arg, waitflag));
18634b94e8bSAlfred Perlstein else
18774fb0ba7SJohn Baldwin return (soparsehttpvers(so, arg, waitflag));
188a79b7128SAlfred Perlstein }
18934b94e8bSAlfred Perlstein DPRINT("mbufstrcmp bad");
1904cc20ab1SSeigo Tanimura }
191a79b7128SAlfred Perlstein
19234b94e8bSAlfred Perlstein fallout:
19334b94e8bSAlfred Perlstein DPRINT("fallout");
19474fb0ba7SJohn Baldwin return (SU_ISCONNECTED);
195a79b7128SAlfred Perlstein }
196a79b7128SAlfred Perlstein
19774fb0ba7SJohn Baldwin static int
soparsehttpvers(struct socket * so,void * arg,int waitflag)19834b94e8bSAlfred Perlstein soparsehttpvers(struct socket *so, void *arg, int waitflag)
19934b94e8bSAlfred Perlstein {
20034b94e8bSAlfred Perlstein struct mbuf *m, *n;
20134b94e8bSAlfred Perlstein int i, cc, spaces, inspaces;
20234b94e8bSAlfred Perlstein
203c0b99ffaSRobert Watson if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
20434b94e8bSAlfred Perlstein goto fallout;
20534b94e8bSAlfred Perlstein
20634b94e8bSAlfred Perlstein m = so->so_rcv.sb_mb;
207cfa6009eSGleb Smirnoff cc = sbavail(&so->so_rcv);
20834b94e8bSAlfred Perlstein inspaces = spaces = 0;
20934b94e8bSAlfred Perlstein for (m = so->so_rcv.sb_mb; m; m = n) {
21034b94e8bSAlfred Perlstein n = m->m_nextpkt;
21134b94e8bSAlfred Perlstein for (; m; m = m->m_next) {
21234b94e8bSAlfred Perlstein for (i = 0; i < m->m_len; i++, cc--) {
21334b94e8bSAlfred Perlstein switch (*(mtod(m, char *) + i)) {
21434b94e8bSAlfred Perlstein case ' ':
215ef104730SAlfred Perlstein /* tabs? '\t' */
21634b94e8bSAlfred Perlstein if (!inspaces) {
21734b94e8bSAlfred Perlstein spaces++;
21834b94e8bSAlfred Perlstein inspaces = 1;
21934b94e8bSAlfred Perlstein }
22034b94e8bSAlfred Perlstein break;
22134b94e8bSAlfred Perlstein case '\r':
22234b94e8bSAlfred Perlstein case '\n':
22334b94e8bSAlfred Perlstein DPRINT("newline");
22434b94e8bSAlfred Perlstein goto fallout;
22534b94e8bSAlfred Perlstein default:
226ef104730SAlfred Perlstein if (spaces != 2) {
22734b94e8bSAlfred Perlstein inspaces = 0;
22834b94e8bSAlfred Perlstein break;
22934b94e8bSAlfred Perlstein }
230ef104730SAlfred Perlstein
231ef104730SAlfred Perlstein /*
232ef104730SAlfred Perlstein * if we don't have enough characters
233ef104730SAlfred Perlstein * left (cc < sizeof("HTTP/1.0") - 1)
234ef104730SAlfred Perlstein * then see if the remaining ones
235ef104730SAlfred Perlstein * are a request we can parse.
236ef104730SAlfred Perlstein */
237ef104730SAlfred Perlstein if (cc < sizeof("HTTP/1.0") - 1) {
238ef104730SAlfred Perlstein if (mbufstrncmp(m, n, i, cc,
239ef104730SAlfred Perlstein "HTTP/1.") == 1) {
240ef104730SAlfred Perlstein DPRINT("ok");
241ef104730SAlfred Perlstein goto readmore;
242ef104730SAlfred Perlstein } else {
243ef104730SAlfred Perlstein DPRINT("bad");
244ef104730SAlfred Perlstein goto fallout;
245ef104730SAlfred Perlstein }
246ef104730SAlfred Perlstein } else if (
247ef104730SAlfred Perlstein mbufstrcmp(m, n, i, "HTTP/1.0") ||
248ef104730SAlfred Perlstein mbufstrcmp(m, n, i, "HTTP/1.1")) {
249ef104730SAlfred Perlstein DPRINT("ok");
25074fb0ba7SJohn Baldwin return (soishttpconnected(so,
25174fb0ba7SJohn Baldwin arg, waitflag));
252ef104730SAlfred Perlstein } else {
253ef104730SAlfred Perlstein DPRINT("bad");
254ef104730SAlfred Perlstein goto fallout;
255ef104730SAlfred Perlstein }
256ef104730SAlfred Perlstein }
25734b94e8bSAlfred Perlstein }
25834b94e8bSAlfred Perlstein }
25934b94e8bSAlfred Perlstein }
26034b94e8bSAlfred Perlstein readmore:
26134b94e8bSAlfred Perlstein DPRINT("readmore");
26234b94e8bSAlfred Perlstein /*
26334b94e8bSAlfred Perlstein * if we hit here we haven't hit something
26434b94e8bSAlfred Perlstein * we don't understand or a newline, so try again
26534b94e8bSAlfred Perlstein */
26674fb0ba7SJohn Baldwin soupcall_set(so, SO_RCV, soparsehttpvers, arg);
26774fb0ba7SJohn Baldwin return (SU_OK);
26834b94e8bSAlfred Perlstein
26934b94e8bSAlfred Perlstein fallout:
27034b94e8bSAlfred Perlstein DPRINT("fallout");
27174fb0ba7SJohn Baldwin return (SU_ISCONNECTED);
27234b94e8bSAlfred Perlstein }
27334b94e8bSAlfred Perlstein
27434b94e8bSAlfred Perlstein #define NCHRS 3
27534b94e8bSAlfred Perlstein
27674fb0ba7SJohn Baldwin static int
soishttpconnected(struct socket * so,void * arg,int waitflag)277a79b7128SAlfred Perlstein soishttpconnected(struct socket *so, void *arg, int waitflag)
278a79b7128SAlfred Perlstein {
279a79b7128SAlfred Perlstein char a, b, c;
28034b94e8bSAlfred Perlstein struct mbuf *m, *n;
28134b94e8bSAlfred Perlstein int ccleft, copied;
282a79b7128SAlfred Perlstein
28334b94e8bSAlfred Perlstein DPRINT("start");
284c0b99ffaSRobert Watson if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
28534b94e8bSAlfred Perlstein goto gotit;
286a79b7128SAlfred Perlstein
28734b94e8bSAlfred Perlstein /*
28834b94e8bSAlfred Perlstein * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
28934b94e8bSAlfred Perlstein * copied - how much we've copied so far
29034b94e8bSAlfred Perlstein * ccleft - how many bytes remaining in the socketbuffer
29134b94e8bSAlfred Perlstein * just loop over the mbufs subtracting from 'ccleft' until we only
29234b94e8bSAlfred Perlstein * have NCHRS left
29334b94e8bSAlfred Perlstein */
29434b94e8bSAlfred Perlstein copied = 0;
295cfa6009eSGleb Smirnoff ccleft = sbavail(&so->so_rcv);
29634b94e8bSAlfred Perlstein if (ccleft < NCHRS)
29734b94e8bSAlfred Perlstein goto readmore;
29834b94e8bSAlfred Perlstein a = b = c = '\0';
29934b94e8bSAlfred Perlstein for (m = so->so_rcv.sb_mb; m; m = n) {
30034b94e8bSAlfred Perlstein n = m->m_nextpkt;
30134b94e8bSAlfred Perlstein for (; m; m = m->m_next) {
30234b94e8bSAlfred Perlstein ccleft -= m->m_len;
30334b94e8bSAlfred Perlstein if (ccleft <= NCHRS) {
30434b94e8bSAlfred Perlstein char *src;
30534b94e8bSAlfred Perlstein int tocopy;
306a79b7128SAlfred Perlstein
30734b94e8bSAlfred Perlstein tocopy = (NCHRS - ccleft) - copied;
30834b94e8bSAlfred Perlstein src = mtod(m, char *) + (m->m_len - tocopy);
30934b94e8bSAlfred Perlstein
31034b94e8bSAlfred Perlstein while (tocopy--) {
31134b94e8bSAlfred Perlstein switch (copied++) {
312a79b7128SAlfred Perlstein case 0:
31334b94e8bSAlfred Perlstein a = *src++;
314a79b7128SAlfred Perlstein break;
31534b94e8bSAlfred Perlstein case 1:
31634b94e8bSAlfred Perlstein b = *src++;
31734b94e8bSAlfred Perlstein break;
31834b94e8bSAlfred Perlstein case 2:
31934b94e8bSAlfred Perlstein c = *src++;
320a79b7128SAlfred Perlstein break;
321a79b7128SAlfred Perlstein }
322a79b7128SAlfred Perlstein }
32334b94e8bSAlfred Perlstein }
32434b94e8bSAlfred Perlstein }
32534b94e8bSAlfred Perlstein }
326a79b7128SAlfred Perlstein if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
327a79b7128SAlfred Perlstein /* we have all request headers */
32834b94e8bSAlfred Perlstein goto gotit;
32934b94e8bSAlfred Perlstein }
33034b94e8bSAlfred Perlstein
33134b94e8bSAlfred Perlstein readmore:
33274fb0ba7SJohn Baldwin soupcall_set(so, SO_RCV, soishttpconnected, arg);
33374fb0ba7SJohn Baldwin return (SU_OK);
334a79b7128SAlfred Perlstein
33534b94e8bSAlfred Perlstein gotit:
33674fb0ba7SJohn Baldwin return (SU_ISCONNECTED);
337a79b7128SAlfred Perlstein }
338