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);
95274421Sglebius	return (sbused(sb) >= 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
165274421Sglebius	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0 &&
166274421Sglebius	    !sbfull(&so->so_rcv)) {
16761837Salfred		struct mbuf *m;
16865534Salfred		char *cmp;
16965534Salfred		int	cmplen, cc;
17061837Salfred
17161837Salfred		m = so->so_rcv.sb_mb;
172274421Sglebius		cc = sbavail(&so->so_rcv) - 1;
17365534Salfred		if (cc < 1)
174193272Sjhb			return (SU_OK);
17565534Salfred		switch (*mtod(m, char *)) {
17665534Salfred		case 'G':
17765534Salfred			STRSETUP(cmp, cmplen, "ET ");
17865534Salfred			break;
17965534Salfred		case 'H':
18065534Salfred			STRSETUP(cmp, cmplen, "EAD ");
18165534Salfred			break;
18265534Salfred		default:
18365534Salfred			goto fallout;
18461837Salfred		}
18565534Salfred		if (cc < cmplen) {
18665534Salfred			if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
18765534Salfred				DPRINT("short cc (%d) but mbufstrncmp ok", cc);
188193272Sjhb				return (SU_OK);
18965534Salfred			} else {
19065534Salfred				DPRINT("short cc (%d) mbufstrncmp failed", cc);
19165534Salfred				goto fallout;
19265534Salfred			}
19365534Salfred		}
19465534Salfred		if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
19565534Salfred			DPRINT("mbufstrcmp ok");
19665534Salfred			if (parse_http_version == 0)
197193272Sjhb				return (soishttpconnected(so, arg, waitflag));
19865534Salfred			else
199193272Sjhb				return (soparsehttpvers(so, arg, waitflag));
20065534Salfred		}
20165534Salfred		DPRINT("mbufstrcmp bad");
20297658Stanimura	}
20361837Salfred
20465534Salfredfallout:
20565534Salfred	DPRINT("fallout");
206193272Sjhb	return (SU_ISCONNECTED);
20761837Salfred}
20861837Salfred
209193272Sjhbstatic int
21065534Salfredsoparsehttpvers(struct socket *so, void *arg, int waitflag)
21165534Salfred{
21265534Salfred	struct mbuf *m, *n;
21365534Salfred	int	i, cc, spaces, inspaces;
21465534Salfred
215130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
21665534Salfred		goto fallout;
21765534Salfred
21865534Salfred	m = so->so_rcv.sb_mb;
219274421Sglebius	cc = sbavail(&so->so_rcv);
22065534Salfred	inspaces = spaces = 0;
22165534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
22265534Salfred		n = m->m_nextpkt;
22365534Salfred		for (; m; m = m->m_next) {
22465534Salfred			for (i = 0; i < m->m_len; i++, cc--) {
22565534Salfred				switch (*(mtod(m, char *) + i)) {
22665534Salfred				case ' ':
22795865Salfred					/* tabs? '\t' */
22865534Salfred					if (!inspaces) {
22965534Salfred						spaces++;
23065534Salfred						inspaces = 1;
23165534Salfred					}
23265534Salfred					break;
23365534Salfred				case '\r':
23465534Salfred				case '\n':
23565534Salfred					DPRINT("newline");
23665534Salfred					goto fallout;
23765534Salfred				default:
23895865Salfred					if (spaces != 2) {
23995865Salfred						inspaces = 0;
24095865Salfred						break;
24195865Salfred					}
24295865Salfred
24395865Salfred					/*
24495865Salfred					 * if we don't have enough characters
24595865Salfred					 * left (cc < sizeof("HTTP/1.0") - 1)
24695865Salfred					 * then see if the remaining ones
24795865Salfred					 * are a request we can parse.
24895865Salfred					 */
24995865Salfred					if (cc < sizeof("HTTP/1.0") - 1) {
25095865Salfred						if (mbufstrncmp(m, n, i, cc,
25195865Salfred							"HTTP/1.") == 1) {
25295865Salfred							DPRINT("ok");
25395865Salfred							goto readmore;
25465534Salfred						} else {
25595865Salfred							DPRINT("bad");
25665534Salfred							goto fallout;
25765534Salfred						}
25895865Salfred					} else if (
25995865Salfred					    mbufstrcmp(m, n, i, "HTTP/1.0") ||
26095865Salfred					    mbufstrcmp(m, n, i, "HTTP/1.1")) {
261193272Sjhb						DPRINT("ok");
262193272Sjhb						return (soishttpconnected(so,
263193272Sjhb						    arg, waitflag));
26495865Salfred					} else {
26595865Salfred						DPRINT("bad");
26695865Salfred						goto fallout;
26765534Salfred					}
26865534Salfred				}
26965534Salfred			}
27065534Salfred		}
27165534Salfred	}
27265534Salfredreadmore:
27365534Salfred	DPRINT("readmore");
27465534Salfred	/*
27565534Salfred	 * if we hit here we haven't hit something
27665534Salfred	 * we don't understand or a newline, so try again
27765534Salfred	 */
278193272Sjhb	soupcall_set(so, SO_RCV, soparsehttpvers, arg);
279193272Sjhb	return (SU_OK);
28065534Salfred
28165534Salfredfallout:
28265534Salfred	DPRINT("fallout");
283193272Sjhb	return (SU_ISCONNECTED);
28465534Salfred}
28565534Salfred
28665534Salfred
28765534Salfred#define NCHRS 3
28865534Salfred
289193272Sjhbstatic int
29061837Salfredsoishttpconnected(struct socket *so, void *arg, int waitflag)
29161837Salfred{
29261837Salfred	char a, b, c;
29365534Salfred	struct mbuf *m, *n;
29465534Salfred	int ccleft, copied;
29561837Salfred
29665534Salfred	DPRINT("start");
297130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
29865534Salfred		goto gotit;
29961837Salfred
30065534Salfred	/*
30165534Salfred	 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
30265534Salfred	 * copied - how much we've copied so far
30365534Salfred	 * ccleft - how many bytes remaining in the socketbuffer
30465534Salfred	 * just loop over the mbufs subtracting from 'ccleft' until we only
30565534Salfred	 * have NCHRS left
30665534Salfred	 */
30765534Salfred	copied = 0;
308274421Sglebius	ccleft = sbavail(&so->so_rcv);
30965534Salfred	if (ccleft < NCHRS)
31065534Salfred		goto readmore;
31165534Salfred	a = b = c = '\0';
31265534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
31365534Salfred		n = m->m_nextpkt;
31465534Salfred		for (; m; m = m->m_next) {
31565534Salfred			ccleft -= m->m_len;
31665534Salfred			if (ccleft <= NCHRS) {
31765534Salfred				char *src;
31865534Salfred				int tocopy;
31961837Salfred
32065534Salfred				tocopy = (NCHRS - ccleft) - copied;
32165534Salfred				src = mtod(m, char *) + (m->m_len - tocopy);
32261837Salfred
32365534Salfred				while (tocopy--) {
32465534Salfred					switch (copied++) {
32565534Salfred					case 0:
32665534Salfred						a = *src++;
32765534Salfred						break;
32865534Salfred					case 1:
32965534Salfred						b = *src++;
33065534Salfred						break;
33165534Salfred					case 2:
33265534Salfred						c = *src++;
33365534Salfred						break;
33465534Salfred					}
33565534Salfred				}
33665534Salfred			}
33761837Salfred		}
33861837Salfred	}
33965534Salfred	if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
34065534Salfred		/* we have all request headers */
34165534Salfred		goto gotit;
34265534Salfred	}
34361837Salfred
34465534Salfredreadmore:
345193272Sjhb	soupcall_set(so, SO_RCV, soishttpconnected, arg);
346193272Sjhb	return (SU_OK);
34765534Salfred
34865534Salfredgotit:
349193272Sjhb	return (SU_ISCONNECTED);
35061837Salfred}
351