accf_http.c revision 130480
165534Salfred/*
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 *	$FreeBSD: head/sys/netinet/accf_http.c 130480 2004-06-14 18:16:22Z rwatson $
2861837Salfred */
2961837Salfred
3061837Salfred#define ACCEPT_FILTER_MOD
3161837Salfred
3261837Salfred#include <sys/param.h>
3361837Salfred#include <sys/kernel.h>
3495759Stanimura#include <sys/mbuf.h>
35129880Sphk#include <sys/module.h>
3695759Stanimura#include <sys/signalvar.h>
3765534Salfred#include <sys/sysctl.h>
3861837Salfred#include <sys/socketvar.h>
3961837Salfred
4065534Salfred/* check for GET/HEAD */
4161837Salfredstatic void sohashttpget(struct socket *so, void *arg, int waitflag);
4265534Salfred/* check for HTTP/1.0 or HTTP/1.1 */
4365534Salfredstatic void soparsehttpvers(struct socket *so, void *arg, int waitflag);
4465534Salfred/* check for end of HTTP/1.x request */
4561837Salfredstatic void soishttpconnected(struct socket *so, void *arg, int waitflag);
4665534Salfred/* strcmp on an mbuf chain */
4765534Salfredstatic int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
4865534Salfred/* strncmp on an mbuf chain */
4965534Salfredstatic int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
5065534Salfred	int max, char *cmp);
5165534Salfred/* socketbuffer is full */
5265534Salfredstatic int sbfull(struct sockbuf *sb);
5361837Salfred
5461837Salfredstatic struct accept_filter accf_http_filter = {
5561837Salfred	"httpready",
5661837Salfred	sohashttpget,
5761837Salfred	NULL,
5861837Salfred	NULL
5961837Salfred};
6061837Salfred
6161837Salfredstatic moduledata_t accf_http_mod = {
6261837Salfred	"accf_http",
6361837Salfred	accept_filt_generic_mod_event,
6461837Salfred	&accf_http_filter
6561837Salfred};
6661837Salfred
6761837SalfredDECLARE_MODULE(accf_http, accf_http_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
6861837Salfred
6965534Salfredstatic int parse_http_version = 1;
7061837Salfred
7165534SalfredSYSCTL_NODE(_net_inet_accf, OID_AUTO, http, CTLFLAG_RW, 0,
7265534Salfred"HTTP accept filter");
7365534SalfredSYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
7465534Salfred&parse_http_version, 1,
7565534Salfred"Parse http version so that non 1.x requests work");
7665534Salfred
7765534Salfred#ifdef ACCF_HTTP_DEBUG
7895865Salfred#define DPRINT(fmt, args...)						\
7995865Salfred	do {								\
8095865Salfred		printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args);	\
8165534Salfred	} while (0)
8265534Salfred#else
8365534Salfred#define DPRINT(fmt, args...)
8465534Salfred#endif
8565534Salfred
8665534Salfredstatic int
8765534Salfredsbfull(struct sockbuf *sb)
8861837Salfred{
8961837Salfred
9095865Salfred	DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
9195865Salfred	    "mbcnt(%ld) >= mbmax(%ld): %d",
9295865Salfred	    sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
9395865Salfred	    sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
9495865Salfred	return (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
9565534Salfred}
9665534Salfred
9765534Salfred/*
9865534Salfred * start at mbuf m, (must provide npkt if exists)
9965534Salfred * starting at offset in m compare characters in mbuf chain for 'cmp'
10065534Salfred */
10165534Salfredstatic int
10265534Salfredmbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
10365534Salfred{
10465534Salfred	struct mbuf *n;
10565534Salfred
10695865Salfred	for (; m != NULL; m = n) {
10765534Salfred		n = npkt;
10865534Salfred		if (npkt)
10965534Salfred			npkt = npkt->m_nextpkt;
11065534Salfred		for (; m; m = m->m_next) {
11165534Salfred			for (; offset < m->m_len; offset++, cmp++) {
11295865Salfred				if (*cmp == '\0')
11365534Salfred					return (1);
11495865Salfred				else if (*cmp != *(mtod(m, char *) + offset))
11565534Salfred					return (0);
11665534Salfred			}
11795867Salfred			if (*cmp == '\0')
11895867Salfred				return (1);
11965534Salfred			offset = 0;
12061837Salfred		}
12161837Salfred	}
12265534Salfred	return (0);
12361837Salfred}
12461837Salfred
12565534Salfred/*
12665534Salfred * start at mbuf m, (must provide npkt if exists)
12765534Salfred * starting at offset in m compare characters in mbuf chain for 'cmp'
12865534Salfred * stop at 'max' characters
12965534Salfred */
13065534Salfredstatic int
13165534Salfredmbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
13265534Salfred{
13365534Salfred	struct mbuf *n;
13465534Salfred
13595865Salfred	for (; m != NULL; m = n) {
13665534Salfred		n = npkt;
13765534Salfred		if (npkt)
13865534Salfred			npkt = npkt->m_nextpkt;
13965534Salfred		for (; m; m = m->m_next) {
14065534Salfred			for (; offset < m->m_len; offset++, cmp++, max--) {
14195865Salfred				if (max == 0 || *cmp == '\0')
14265534Salfred					return (1);
14395865Salfred				else if (*cmp != *(mtod(m, char *) + offset))
14465534Salfred					return (0);
14565534Salfred			}
14695867Salfred			if (max == 0 || *cmp == '\0')
14795867Salfred				return (1);
14865534Salfred			offset = 0;
14965534Salfred		}
15065534Salfred	}
15165534Salfred	return (0);
15265534Salfred}
15365534Salfred
15495865Salfred#define STRSETUP(sptr, slen, str)					\
15595865Salfred	do {								\
15695865Salfred		sptr = str;						\
15795865Salfred		slen = sizeof(str) - 1;					\
15865534Salfred	} while(0)
15965534Salfred
16061837Salfredstatic void
16161837Salfredsohashttpget(struct socket *so, void *arg, int waitflag)
16261837Salfred{
16361837Salfred
164130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
16561837Salfred		struct mbuf *m;
16665534Salfred		char *cmp;
16765534Salfred		int	cmplen, cc;
16861837Salfred
16961837Salfred		m = so->so_rcv.sb_mb;
17065534Salfred		cc = so->so_rcv.sb_cc - 1;
17165534Salfred		if (cc < 1)
17261837Salfred			return;
17365534Salfred		switch (*mtod(m, char *)) {
17465534Salfred		case 'G':
17565534Salfred			STRSETUP(cmp, cmplen, "ET ");
17665534Salfred			break;
17765534Salfred		case 'H':
17865534Salfred			STRSETUP(cmp, cmplen, "EAD ");
17965534Salfred			break;
18065534Salfred		default:
18165534Salfred			goto fallout;
18261837Salfred		}
18365534Salfred		if (cc < cmplen) {
18465534Salfred			if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
18565534Salfred				DPRINT("short cc (%d) but mbufstrncmp ok", cc);
18665534Salfred				return;
18765534Salfred			} else {
18865534Salfred				DPRINT("short cc (%d) mbufstrncmp failed", cc);
18965534Salfred				goto fallout;
19065534Salfred			}
19165534Salfred		}
19265534Salfred		if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
19365534Salfred			DPRINT("mbufstrcmp ok");
19465534Salfred			if (parse_http_version == 0)
19565534Salfred				soishttpconnected(so, arg, waitflag);
19665534Salfred			else
19765534Salfred				soparsehttpvers(so, arg, waitflag);
19865534Salfred			return;
19965534Salfred		}
20065534Salfred		DPRINT("mbufstrcmp bad");
20197658Stanimura	}
20261837Salfred
20365534Salfredfallout:
20465534Salfred	DPRINT("fallout");
20561837Salfred	so->so_upcall = NULL;
20661837Salfred	so->so_rcv.sb_flags &= ~SB_UPCALL;
20798385Stanimura	soisconnected(so);
20861837Salfred	return;
20961837Salfred}
21061837Salfred
21161837Salfredstatic void
21265534Salfredsoparsehttpvers(struct socket *so, void *arg, int waitflag)
21365534Salfred{
21465534Salfred	struct mbuf *m, *n;
21565534Salfred	int	i, cc, spaces, inspaces;
21665534Salfred
217130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
21865534Salfred		goto fallout;
21965534Salfred
22065534Salfred	m = so->so_rcv.sb_mb;
22165534Salfred	cc = so->so_rcv.sb_cc;
22265534Salfred	inspaces = spaces = 0;
22365534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
22465534Salfred		n = m->m_nextpkt;
22565534Salfred		for (; m; m = m->m_next) {
22665534Salfred			for (i = 0; i < m->m_len; i++, cc--) {
22765534Salfred				switch (*(mtod(m, char *) + i)) {
22865534Salfred				case ' ':
22995865Salfred					/* tabs? '\t' */
23065534Salfred					if (!inspaces) {
23165534Salfred						spaces++;
23265534Salfred						inspaces = 1;
23365534Salfred					}
23465534Salfred					break;
23565534Salfred				case '\r':
23665534Salfred				case '\n':
23765534Salfred					DPRINT("newline");
23865534Salfred					goto fallout;
23965534Salfred				default:
24095865Salfred					if (spaces != 2) {
24195865Salfred						inspaces = 0;
24295865Salfred						break;
24395865Salfred					}
24495865Salfred
24595865Salfred					/*
24695865Salfred					 * if we don't have enough characters
24795865Salfred					 * left (cc < sizeof("HTTP/1.0") - 1)
24895865Salfred					 * then see if the remaining ones
24995865Salfred					 * are a request we can parse.
25095865Salfred					 */
25195865Salfred					if (cc < sizeof("HTTP/1.0") - 1) {
25295865Salfred						if (mbufstrncmp(m, n, i, cc,
25395865Salfred							"HTTP/1.") == 1) {
25495865Salfred							DPRINT("ok");
25595865Salfred							goto readmore;
25665534Salfred						} else {
25795865Salfred							DPRINT("bad");
25865534Salfred							goto fallout;
25965534Salfred						}
26095865Salfred					} else if (
26195865Salfred					    mbufstrcmp(m, n, i, "HTTP/1.0") ||
26295865Salfred					    mbufstrcmp(m, n, i, "HTTP/1.1")) {
26395865Salfred							DPRINT("ok");
26495865Salfred							soishttpconnected(so,
26595865Salfred							    arg, waitflag);
26695865Salfred							return;
26795865Salfred					} else {
26895865Salfred						DPRINT("bad");
26995865Salfred						goto fallout;
27065534Salfred					}
27165534Salfred				}
27265534Salfred			}
27365534Salfred		}
27465534Salfred	}
27565534Salfredreadmore:
27665534Salfred	DPRINT("readmore");
27765534Salfred	/*
27865534Salfred	 * if we hit here we haven't hit something
27965534Salfred	 * we don't understand or a newline, so try again
28065534Salfred	 */
28165534Salfred	so->so_upcall = soparsehttpvers;
28265534Salfred	so->so_rcv.sb_flags |= SB_UPCALL;
28365534Salfred	return;
28465534Salfred
28565534Salfredfallout:
28665534Salfred	DPRINT("fallout");
28765534Salfred	so->so_upcall = NULL;
28865534Salfred	so->so_rcv.sb_flags &= ~SB_UPCALL;
28998385Stanimura	soisconnected(so);
29065534Salfred	return;
29165534Salfred}
29265534Salfred
29365534Salfred
29465534Salfred#define NCHRS 3
29565534Salfred
29665534Salfredstatic void
29761837Salfredsoishttpconnected(struct socket *so, void *arg, int waitflag)
29861837Salfred{
29961837Salfred	char a, b, c;
30065534Salfred	struct mbuf *m, *n;
30165534Salfred	int ccleft, copied;
30261837Salfred
30365534Salfred	DPRINT("start");
304130480Srwatson	if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
30565534Salfred		goto gotit;
30661837Salfred
30765534Salfred	/*
30865534Salfred	 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
30965534Salfred	 * copied - how much we've copied so far
31065534Salfred	 * ccleft - how many bytes remaining in the socketbuffer
31165534Salfred	 * just loop over the mbufs subtracting from 'ccleft' until we only
31265534Salfred	 * have NCHRS left
31365534Salfred	 */
31465534Salfred	copied = 0;
31565534Salfred	ccleft = so->so_rcv.sb_cc;
31665534Salfred	if (ccleft < NCHRS)
31765534Salfred		goto readmore;
31865534Salfred	a = b = c = '\0';
31965534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
32065534Salfred		n = m->m_nextpkt;
32165534Salfred		for (; m; m = m->m_next) {
32265534Salfred			ccleft -= m->m_len;
32365534Salfred			if (ccleft <= NCHRS) {
32465534Salfred				char *src;
32565534Salfred				int tocopy;
32661837Salfred
32765534Salfred				tocopy = (NCHRS - ccleft) - copied;
32865534Salfred				src = mtod(m, char *) + (m->m_len - tocopy);
32961837Salfred
33065534Salfred				while (tocopy--) {
33165534Salfred					switch (copied++) {
33265534Salfred					case 0:
33365534Salfred						a = *src++;
33465534Salfred						break;
33565534Salfred					case 1:
33665534Salfred						b = *src++;
33765534Salfred						break;
33865534Salfred					case 2:
33965534Salfred						c = *src++;
34065534Salfred						break;
34165534Salfred					}
34265534Salfred				}
34365534Salfred			}
34461837Salfred		}
34561837Salfred	}
34665534Salfred	if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
34765534Salfred		/* we have all request headers */
34865534Salfred		goto gotit;
34965534Salfred	}
35061837Salfred
35165534Salfredreadmore:
35265534Salfred	so->so_upcall = soishttpconnected;
35365534Salfred	so->so_rcv.sb_flags |= SB_UPCALL;
35465534Salfred	return;
35565534Salfred
35665534Salfredgotit:
35761837Salfred	so->so_upcall = NULL;
35861837Salfred	so->so_rcv.sb_flags &= ~SB_UPCALL;
35998385Stanimura	soisconnected(so);
36061837Salfred	return;
36161837Salfred}
362