1139823Simp/*-
265534Salfred * Copyright (c) 2000 Paycounter, Inc.
365534Salfred * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
461837Salfred * All rights reserved.
561837Salfred *
661837Salfred * Redistribution and use in source and binary forms, with or without
761837Salfred * modification, are permitted provided that the following conditions
861837Salfred * are met:
961837Salfred * 1. Redistributions of source code must retain the above copyright
1061837Salfred *    notice, this list of conditions and the following disclaimer.
1161837Salfred * 2. Redistributions in binary form must reproduce the above copyright
1261837Salfred *    notice, this list of conditions and the following disclaimer in the
1361837Salfred *    documentation and/or other materials provided with the distribution.
1461837Salfred *
1561837Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1661837Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1761837Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1861837Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1961837Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2061837Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2161837Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2261837Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2361837Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2461837Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2561837Salfred * SUCH DAMAGE.
2661837Salfred */
2761837Salfred
28172467Ssilby#include <sys/cdefs.h>
29172467Ssilby__FBSDID("$FreeBSD$");
30172467Ssilby
3161837Salfred#define ACCEPT_FILTER_MOD
3261837Salfred
3361837Salfred#include <sys/param.h>
3461837Salfred#include <sys/kernel.h>
3595759Stanimura#include <sys/mbuf.h>
36129880Sphk#include <sys/module.h>
3795759Stanimura#include <sys/signalvar.h>
3865534Salfred#include <sys/sysctl.h>
3961837Salfred#include <sys/socketvar.h>
4061837Salfred
4165534Salfred/* check for GET/HEAD */
42193272Sjhbstatic int sohashttpget(struct socket *so, void *arg, int waitflag);
4365534Salfred/* check for HTTP/1.0 or HTTP/1.1 */
44193272Sjhbstatic int soparsehttpvers(struct socket *so, void *arg, int waitflag);
4565534Salfred/* check for end of HTTP/1.x request */
46193272Sjhbstatic int soishttpconnected(struct socket *so, void *arg, int waitflag);
4765534Salfred/* strcmp on an mbuf chain */
4865534Salfredstatic int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
4965534Salfred/* strncmp on an mbuf chain */
5065534Salfredstatic int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
5165534Salfred	int max, char *cmp);
5265534Salfred/* socketbuffer is full */
5365534Salfredstatic int sbfull(struct sockbuf *sb);
5461837Salfred
5561837Salfredstatic struct accept_filter accf_http_filter = {
5661837Salfred	"httpready",
5761837Salfred	sohashttpget,
5861837Salfred	NULL,
5961837Salfred	NULL
6061837Salfred};
6161837Salfred
6261837Salfredstatic moduledata_t accf_http_mod = {
6361837Salfred	"accf_http",
6461837Salfred	accept_filt_generic_mod_event,
6561837Salfred	&accf_http_filter
6661837Salfred};
6761837Salfred
6861837SalfredDECLARE_MODULE(accf_http, accf_http_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
6961837Salfred
7065534Salfredstatic int parse_http_version = 1;
7161837Salfred
72227309Sedstatic SYSCTL_NODE(_net_inet_accf, OID_AUTO, http, CTLFLAG_RW, 0,
7365534Salfred"HTTP accept filter");
7465534SalfredSYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
7565534Salfred&parse_http_version, 1,
7665534Salfred"Parse http version so that non 1.x requests work");
7765534Salfred
7865534Salfred#ifdef ACCF_HTTP_DEBUG
7995865Salfred#define DPRINT(fmt, args...)						\
8095865Salfred	do {								\
8195865Salfred		printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args);	\
8265534Salfred	} while (0)
8365534Salfred#else
8465534Salfred#define DPRINT(fmt, args...)
8565534Salfred#endif
8665534Salfred
8765534Salfredstatic int
8865534Salfredsbfull(struct sockbuf *sb)
8961837Salfred{
9061837Salfred
9195865Salfred	DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
9295865Salfred	    "mbcnt(%ld) >= mbmax(%ld): %d",
9395865Salfred	    sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
9495865Salfred	    sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
9595865Salfred	return (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
9665534Salfred}
9765534Salfred
9865534Salfred/*
9965534Salfred * start at mbuf m, (must provide npkt if exists)
10065534Salfred * starting at offset in m compare characters in mbuf chain for 'cmp'
10165534Salfred */
10265534Salfredstatic int
10365534Salfredmbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
10465534Salfred{
10565534Salfred	struct mbuf *n;
10665534Salfred
10795865Salfred	for (; m != NULL; m = n) {
10865534Salfred		n = npkt;
10965534Salfred		if (npkt)
11065534Salfred			npkt = npkt->m_nextpkt;
11165534Salfred		for (; m; m = m->m_next) {
11265534Salfred			for (; offset < m->m_len; offset++, cmp++) {
11395865Salfred				if (*cmp == '\0')
11465534Salfred					return (1);
11595865Salfred				else if (*cmp != *(mtod(m, char *) + offset))
11665534Salfred					return (0);
11765534Salfred			}
11895867Salfred			if (*cmp == '\0')
11995867Salfred				return (1);
12065534Salfred			offset = 0;
12161837Salfred		}
12261837Salfred	}
12365534Salfred	return (0);
12461837Salfred}
12561837Salfred
12665534Salfred/*
12765534Salfred * start at mbuf m, (must provide npkt if exists)
12865534Salfred * starting at offset in m compare characters in mbuf chain for 'cmp'
12965534Salfred * stop at 'max' characters
13065534Salfred */
13165534Salfredstatic int
13265534Salfredmbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
13365534Salfred{
13465534Salfred	struct mbuf *n;
13565534Salfred
13695865Salfred	for (; m != NULL; m = n) {
13765534Salfred		n = npkt;
13865534Salfred		if (npkt)
13965534Salfred			npkt = npkt->m_nextpkt;
14065534Salfred		for (; m; m = m->m_next) {
14165534Salfred			for (; offset < m->m_len; offset++, cmp++, max--) {
14295865Salfred				if (max == 0 || *cmp == '\0')
14365534Salfred					return (1);
14495865Salfred				else if (*cmp != *(mtod(m, char *) + offset))
14565534Salfred					return (0);
14665534Salfred			}
14795867Salfred			if (max == 0 || *cmp == '\0')
14895867Salfred				return (1);
14965534Salfred			offset = 0;
15065534Salfred		}
15165534Salfred	}
15265534Salfred	return (0);
15365534Salfred}
15465534Salfred
15595865Salfred#define STRSETUP(sptr, slen, str)					\
15695865Salfred	do {								\
15795865Salfred		sptr = str;						\
15895865Salfred		slen = sizeof(str) - 1;					\
15965534Salfred	} while(0)
16065534Salfred
161193272Sjhbstatic int
16261837Salfredsohashttpget(struct socket *so, void *arg, int waitflag)
16361837Salfred{
16461837Salfred
165130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
16661837Salfred		struct mbuf *m;
16765534Salfred		char *cmp;
16865534Salfred		int	cmplen, cc;
16961837Salfred
17061837Salfred		m = so->so_rcv.sb_mb;
17165534Salfred		cc = so->so_rcv.sb_cc - 1;
17265534Salfred		if (cc < 1)
173193272Sjhb			return (SU_OK);
17465534Salfred		switch (*mtod(m, char *)) {
17565534Salfred		case 'G':
17665534Salfred			STRSETUP(cmp, cmplen, "ET ");
17765534Salfred			break;
17865534Salfred		case 'H':
17965534Salfred			STRSETUP(cmp, cmplen, "EAD ");
18065534Salfred			break;
18165534Salfred		default:
18265534Salfred			goto fallout;
18361837Salfred		}
18465534Salfred		if (cc < cmplen) {
18565534Salfred			if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
18665534Salfred				DPRINT("short cc (%d) but mbufstrncmp ok", cc);
187193272Sjhb				return (SU_OK);
18865534Salfred			} else {
18965534Salfred				DPRINT("short cc (%d) mbufstrncmp failed", cc);
19065534Salfred				goto fallout;
19165534Salfred			}
19265534Salfred		}
19365534Salfred		if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
19465534Salfred			DPRINT("mbufstrcmp ok");
19565534Salfred			if (parse_http_version == 0)
196193272Sjhb				return (soishttpconnected(so, arg, waitflag));
19765534Salfred			else
198193272Sjhb				return (soparsehttpvers(so, arg, waitflag));
19965534Salfred		}
20065534Salfred		DPRINT("mbufstrcmp bad");
20197658Stanimura	}
20261837Salfred
20365534Salfredfallout:
20465534Salfred	DPRINT("fallout");
205193272Sjhb	return (SU_ISCONNECTED);
20661837Salfred}
20761837Salfred
208193272Sjhbstatic int
20965534Salfredsoparsehttpvers(struct socket *so, void *arg, int waitflag)
21065534Salfred{
21165534Salfred	struct mbuf *m, *n;
21265534Salfred	int	i, cc, spaces, inspaces;
21365534Salfred
214130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
21565534Salfred		goto fallout;
21665534Salfred
21765534Salfred	m = so->so_rcv.sb_mb;
21865534Salfred	cc = so->so_rcv.sb_cc;
21965534Salfred	inspaces = spaces = 0;
22065534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
22165534Salfred		n = m->m_nextpkt;
22265534Salfred		for (; m; m = m->m_next) {
22365534Salfred			for (i = 0; i < m->m_len; i++, cc--) {
22465534Salfred				switch (*(mtod(m, char *) + i)) {
22565534Salfred				case ' ':
22695865Salfred					/* tabs? '\t' */
22765534Salfred					if (!inspaces) {
22865534Salfred						spaces++;
22965534Salfred						inspaces = 1;
23065534Salfred					}
23165534Salfred					break;
23265534Salfred				case '\r':
23365534Salfred				case '\n':
23465534Salfred					DPRINT("newline");
23565534Salfred					goto fallout;
23665534Salfred				default:
23795865Salfred					if (spaces != 2) {
23895865Salfred						inspaces = 0;
23995865Salfred						break;
24095865Salfred					}
24195865Salfred
24295865Salfred					/*
24395865Salfred					 * if we don't have enough characters
24495865Salfred					 * left (cc < sizeof("HTTP/1.0") - 1)
24595865Salfred					 * then see if the remaining ones
24695865Salfred					 * are a request we can parse.
24795865Salfred					 */
24895865Salfred					if (cc < sizeof("HTTP/1.0") - 1) {
24995865Salfred						if (mbufstrncmp(m, n, i, cc,
25095865Salfred							"HTTP/1.") == 1) {
25195865Salfred							DPRINT("ok");
25295865Salfred							goto readmore;
25365534Salfred						} else {
25495865Salfred							DPRINT("bad");
25565534Salfred							goto fallout;
25665534Salfred						}
25795865Salfred					} else if (
25895865Salfred					    mbufstrcmp(m, n, i, "HTTP/1.0") ||
25995865Salfred					    mbufstrcmp(m, n, i, "HTTP/1.1")) {
260193272Sjhb						DPRINT("ok");
261193272Sjhb						return (soishttpconnected(so,
262193272Sjhb						    arg, waitflag));
26395865Salfred					} else {
26495865Salfred						DPRINT("bad");
26595865Salfred						goto fallout;
26665534Salfred					}
26765534Salfred				}
26865534Salfred			}
26965534Salfred		}
27065534Salfred	}
27165534Salfredreadmore:
27265534Salfred	DPRINT("readmore");
27365534Salfred	/*
27465534Salfred	 * if we hit here we haven't hit something
27565534Salfred	 * we don't understand or a newline, so try again
27665534Salfred	 */
277193272Sjhb	soupcall_set(so, SO_RCV, soparsehttpvers, arg);
278193272Sjhb	return (SU_OK);
27965534Salfred
28065534Salfredfallout:
28165534Salfred	DPRINT("fallout");
282193272Sjhb	return (SU_ISCONNECTED);
28365534Salfred}
28465534Salfred
28565534Salfred
28665534Salfred#define NCHRS 3
28765534Salfred
288193272Sjhbstatic int
28961837Salfredsoishttpconnected(struct socket *so, void *arg, int waitflag)
29061837Salfred{
29161837Salfred	char a, b, c;
29265534Salfred	struct mbuf *m, *n;
29365534Salfred	int ccleft, copied;
29461837Salfred
29565534Salfred	DPRINT("start");
296130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
29765534Salfred		goto gotit;
29861837Salfred
29965534Salfred	/*
30065534Salfred	 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
30165534Salfred	 * copied - how much we've copied so far
30265534Salfred	 * ccleft - how many bytes remaining in the socketbuffer
30365534Salfred	 * just loop over the mbufs subtracting from 'ccleft' until we only
30465534Salfred	 * have NCHRS left
30565534Salfred	 */
30665534Salfred	copied = 0;
30765534Salfred	ccleft = so->so_rcv.sb_cc;
30865534Salfred	if (ccleft < NCHRS)
30965534Salfred		goto readmore;
31065534Salfred	a = b = c = '\0';
31165534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
31265534Salfred		n = m->m_nextpkt;
31365534Salfred		for (; m; m = m->m_next) {
31465534Salfred			ccleft -= m->m_len;
31565534Salfred			if (ccleft <= NCHRS) {
31665534Salfred				char *src;
31765534Salfred				int tocopy;
31861837Salfred
31965534Salfred				tocopy = (NCHRS - ccleft) - copied;
32065534Salfred				src = mtod(m, char *) + (m->m_len - tocopy);
32161837Salfred
32265534Salfred				while (tocopy--) {
32365534Salfred					switch (copied++) {
32465534Salfred					case 0:
32565534Salfred						a = *src++;
32665534Salfred						break;
32765534Salfred					case 1:
32865534Salfred						b = *src++;
32965534Salfred						break;
33065534Salfred					case 2:
33165534Salfred						c = *src++;
33265534Salfred						break;
33365534Salfred					}
33465534Salfred				}
33565534Salfred			}
33661837Salfred		}
33761837Salfred	}
33865534Salfred	if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
33965534Salfred		/* we have all request headers */
34065534Salfred		goto gotit;
34165534Salfred	}
34261837Salfred
34365534Salfredreadmore:
344193272Sjhb	soupcall_set(so, SO_RCV, soishttpconnected, arg);
345193272Sjhb	return (SU_OK);
34665534Salfred
34765534Salfredgotit:
348193272Sjhb	return (SU_ISCONNECTED);
34961837Salfred}
350