accf_http.c revision 65534
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 65534 2000-09-06 18:49:13Z alfred $
2861837Salfred */
2961837Salfred
3061837Salfred#define ACCEPT_FILTER_MOD
3161837Salfred
3261837Salfred#include <sys/param.h>
3361837Salfred#include <sys/systm.h>
3461837Salfred#include <sys/sysproto.h>
3561837Salfred#include <sys/kernel.h>
3661837Salfred#include <sys/proc.h>
3761837Salfred#include <sys/malloc.h>
3861837Salfred#include <sys/unistd.h>
3961837Salfred#include <sys/file.h>
4061837Salfred#include <sys/fcntl.h>
4161837Salfred#include <sys/protosw.h>
4265534Salfred#include <sys/sysctl.h>
4361837Salfred#include <sys/socket.h>
4461837Salfred#include <sys/socketvar.h>
4561837Salfred#include <sys/stat.h>
4661837Salfred#include <sys/mbuf.h>
4761837Salfred#include <sys/resource.h>
4861837Salfred#include <sys/sysent.h>
4961837Salfred#include <sys/resourcevar.h>
5061837Salfred
5165534Salfred/* check for GET/HEAD */
5261837Salfredstatic void sohashttpget(struct socket *so, void *arg, int waitflag);
5365534Salfred/* check for HTTP/1.0 or HTTP/1.1 */
5465534Salfredstatic void soparsehttpvers(struct socket *so, void *arg, int waitflag);
5565534Salfred/* check for end of HTTP/1.x request */
5661837Salfredstatic void soishttpconnected(struct socket *so, void *arg, int waitflag);
5765534Salfred/* strcmp on an mbuf chain */
5865534Salfredstatic int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
5965534Salfred/* strncmp on an mbuf chain */
6065534Salfredstatic int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
6165534Salfred	int max, char *cmp);
6265534Salfred/* socketbuffer is full */
6365534Salfredstatic int sbfull(struct sockbuf *sb);
6461837Salfred
6561837Salfredstatic struct accept_filter accf_http_filter = {
6661837Salfred	"httpready",
6761837Salfred	sohashttpget,
6861837Salfred	NULL,
6961837Salfred	NULL
7061837Salfred};
7161837Salfred
7261837Salfredstatic moduledata_t accf_http_mod = {
7361837Salfred	"accf_http",
7461837Salfred	accept_filt_generic_mod_event,
7561837Salfred	&accf_http_filter
7661837Salfred};
7761837Salfred
7861837SalfredDECLARE_MODULE(accf_http, accf_http_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
7961837Salfred
8065534Salfredstatic int parse_http_version = 1;
8161837Salfred
8265534SalfredSYSCTL_NODE(_net_inet_accf, OID_AUTO, http, CTLFLAG_RW, 0,
8365534Salfred"HTTP accept filter");
8465534SalfredSYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
8565534Salfred&parse_http_version, 1,
8665534Salfred"Parse http version so that non 1.x requests work");
8765534Salfred
8865534Salfred#ifdef ACCF_HTTP_DEBUG
8965534Salfred#define DPRINT(fmt, args...) \
9065534Salfred	do {	\
9165534Salfred		printf("%s:%d: " fmt "\n", __func__, __LINE__ , ##args);	\
9265534Salfred	} while (0)
9365534Salfred#else
9465534Salfred#define DPRINT(fmt, args...)
9565534Salfred#endif
9665534Salfred
9765534Salfredstatic int
9865534Salfredsbfull(struct sockbuf *sb)
9961837Salfred{
10061837Salfred
10165534Salfred	DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, mbcnt(%ld) >= mbmax(%ld): %d",
10265534Salfred		sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
10365534Salfred		sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
10465534Salfred	return(sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
10565534Salfred}
10665534Salfred
10765534Salfred/*
10865534Salfred * start at mbuf m, (must provide npkt if exists)
10965534Salfred * starting at offset in m compare characters in mbuf chain for 'cmp'
11065534Salfred */
11165534Salfredstatic int
11265534Salfredmbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
11365534Salfred{
11465534Salfred	struct mbuf *n;
11565534Salfred
11665534Salfred	for (;m != NULL; m = n) {
11765534Salfred		n = npkt;
11865534Salfred		if (npkt)
11965534Salfred			npkt = npkt->m_nextpkt;
12065534Salfred		for (; m; m = m->m_next) {
12165534Salfred			for (; offset < m->m_len; offset++, cmp++) {
12265534Salfred				if (*cmp == '\0') {
12365534Salfred					return (1);
12465534Salfred				} else if (*cmp != *(mtod(m, char *) + offset)) {
12565534Salfred					return (0);
12665534Salfred				}
12765534Salfred			}
12865534Salfred			offset = 0;
12961837Salfred		}
13061837Salfred	}
13165534Salfred	return (0);
13261837Salfred}
13361837Salfred
13465534Salfred/*
13565534Salfred * start at mbuf m, (must provide npkt if exists)
13665534Salfred * starting at offset in m compare characters in mbuf chain for 'cmp'
13765534Salfred * stop at 'max' characters
13865534Salfred */
13965534Salfredstatic int
14065534Salfredmbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
14165534Salfred{
14265534Salfred	struct mbuf *n;
14365534Salfred
14465534Salfred	for (;m != NULL; m = n) {
14565534Salfred		n = npkt;
14665534Salfred		if (npkt)
14765534Salfred			npkt = npkt->m_nextpkt;
14865534Salfred		for (; m; m = m->m_next) {
14965534Salfred			for (; offset < m->m_len; offset++, cmp++, max--) {
15065534Salfred				if (max == 0 || *cmp == '\0') {
15165534Salfred					return (1);
15265534Salfred				} else if (*cmp != *(mtod(m, char *) + offset)) {
15365534Salfred					return (0);
15465534Salfred				}
15565534Salfred			}
15665534Salfred			offset = 0;
15765534Salfred		}
15865534Salfred	}
15965534Salfred	return (0);
16065534Salfred}
16165534Salfred
16265534Salfred#define STRSETUP(sptr, slen, str) \
16365534Salfred	do {	\
16465534Salfred		sptr = str;	\
16565534Salfred		slen = sizeof(str) - 1;	\
16665534Salfred	} while(0)
16765534Salfred
16861837Salfredstatic void
16961837Salfredsohashttpget(struct socket *so, void *arg, int waitflag)
17061837Salfred{
17161837Salfred
17265534Salfred	if ((so->so_state & SS_CANTRCVMORE) == 0 || !sbfull(&so->so_rcv)) {
17361837Salfred		struct mbuf *m;
17465534Salfred		char *cmp;
17565534Salfred		int	cmplen, cc;
17661837Salfred
17761837Salfred		m = so->so_rcv.sb_mb;
17865534Salfred		cc = so->so_rcv.sb_cc - 1;
17965534Salfred		if (cc < 1)
18061837Salfred			return;
18165534Salfred		switch (*mtod(m, char *)) {
18265534Salfred		case 'G':
18365534Salfred			STRSETUP(cmp, cmplen, "ET ");
18465534Salfred			break;
18565534Salfred		case 'H':
18665534Salfred			STRSETUP(cmp, cmplen, "EAD ");
18765534Salfred			break;
18865534Salfred		default:
18965534Salfred			goto fallout;
19061837Salfred		}
19165534Salfred		if (cc < cmplen) {
19265534Salfred			if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
19365534Salfred				DPRINT("short cc (%d) but mbufstrncmp ok", cc);
19465534Salfred				return;
19565534Salfred			} else {
19665534Salfred				DPRINT("short cc (%d) mbufstrncmp failed", cc);
19765534Salfred				goto fallout;
19865534Salfred			}
19965534Salfred		}
20065534Salfred		if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
20165534Salfred			DPRINT("mbufstrcmp ok");
20265534Salfred			if (parse_http_version == 0)
20365534Salfred				soishttpconnected(so, arg, waitflag);
20465534Salfred			else
20565534Salfred				soparsehttpvers(so, arg, waitflag);
20665534Salfred			return;
20765534Salfred		}
20865534Salfred		DPRINT("mbufstrcmp bad");
20961837Salfred	}
21061837Salfred
21165534Salfredfallout:
21265534Salfred	DPRINT("fallout");
21361837Salfred	so->so_upcall = NULL;
21461837Salfred	so->so_rcv.sb_flags &= ~SB_UPCALL;
21561837Salfred	soisconnected(so);
21661837Salfred	return;
21761837Salfred}
21861837Salfred
21961837Salfredstatic void
22065534Salfredsoparsehttpvers(struct socket *so, void *arg, int waitflag)
22165534Salfred{
22265534Salfred	struct mbuf *m, *n;
22365534Salfred	int	i, cc, spaces, inspaces;
22465534Salfred
22565534Salfred	if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
22665534Salfred		goto fallout;
22765534Salfred
22865534Salfred	m = so->so_rcv.sb_mb;
22965534Salfred	cc = so->so_rcv.sb_cc;
23065534Salfred	inspaces = spaces = 0;
23165534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
23265534Salfred		n = m->m_nextpkt;
23365534Salfred		for (; m; m = m->m_next) {
23465534Salfred			for (i = 0; i < m->m_len; i++, cc--) {
23565534Salfred				switch (*(mtod(m, char *) + i)) {
23665534Salfred				case ' ':
23765534Salfred					if (!inspaces) {
23865534Salfred						spaces++;
23965534Salfred						inspaces = 1;
24065534Salfred					}
24165534Salfred					break;
24265534Salfred				case '\r':
24365534Salfred				case '\n':
24465534Salfred					DPRINT("newline");
24565534Salfred					goto fallout;
24665534Salfred				default:
24765534Salfred					if (spaces == 2) {
24865534Salfred						/* make sure we have enough data left */
24965534Salfred						if (cc < sizeof("HTTP/1.0") - 1) {
25065534Salfred							if (mbufstrncmp(m, n, i, cc, "HTTP/1.") == 1) {
25165534Salfred								DPRINT("mbufstrncmp ok");
25265534Salfred								goto readmore;
25365534Salfred							} else {
25465534Salfred								DPRINT("mbufstrncmp bad");
25565534Salfred								goto fallout;
25665534Salfred							}
25765534Salfred						} else if (mbufstrcmp(m, n, i, "HTTP/1.0") == 1 ||
25865534Salfred									mbufstrcmp(m, n, i, "HTTP/1.1") == 1) {
25965534Salfred								DPRINT("mbufstrcmp ok");
26065534Salfred								soishttpconnected(so, arg, waitflag);
26165534Salfred								return;
26265534Salfred						} else {
26365534Salfred							DPRINT("mbufstrcmp bad");
26465534Salfred							goto fallout;
26565534Salfred						}
26665534Salfred					}
26765534Salfred					inspaces = 0;
26865534Salfred					break;
26965534Salfred				}
27065534Salfred			}
27165534Salfred		}
27265534Salfred	}
27365534Salfredreadmore:
27465534Salfred	DPRINT("readmore");
27565534Salfred	/*
27665534Salfred	 * if we hit here we haven't hit something
27765534Salfred	 * we don't understand or a newline, so try again
27865534Salfred	 */
27965534Salfred	so->so_upcall = soparsehttpvers;
28065534Salfred	so->so_rcv.sb_flags |= SB_UPCALL;
28165534Salfred	return;
28265534Salfred
28365534Salfredfallout:
28465534Salfred	DPRINT("fallout");
28565534Salfred	so->so_upcall = NULL;
28665534Salfred	so->so_rcv.sb_flags &= ~SB_UPCALL;
28765534Salfred	soisconnected(so);
28865534Salfred	return;
28965534Salfred}
29065534Salfred
29165534Salfred
29265534Salfred#define NCHRS 3
29365534Salfred
29465534Salfredstatic void
29561837Salfredsoishttpconnected(struct socket *so, void *arg, int waitflag)
29661837Salfred{
29761837Salfred	char a, b, c;
29865534Salfred	struct mbuf *m, *n;
29965534Salfred	int ccleft, copied;
30061837Salfred
30165534Salfred	DPRINT("start");
30265534Salfred	if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
30365534Salfred		goto gotit;
30461837Salfred
30565534Salfred	/*
30665534Salfred	 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
30765534Salfred	 * copied - how much we've copied so far
30865534Salfred	 * ccleft - how many bytes remaining in the socketbuffer
30965534Salfred	 * just loop over the mbufs subtracting from 'ccleft' until we only
31065534Salfred	 * have NCHRS left
31165534Salfred	 */
31265534Salfred	copied = 0;
31365534Salfred	ccleft = so->so_rcv.sb_cc;
31465534Salfred	if (ccleft < NCHRS)
31565534Salfred		goto readmore;
31665534Salfred	a = b = c = '\0';
31765534Salfred	for (m = so->so_rcv.sb_mb; m; m = n) {
31865534Salfred		n = m->m_nextpkt;
31965534Salfred		for (; m; m = m->m_next) {
32065534Salfred			ccleft -= m->m_len;
32165534Salfred			if (ccleft <= NCHRS) {
32265534Salfred				char *src;
32365534Salfred				int tocopy;
32461837Salfred
32565534Salfred				tocopy = (NCHRS - ccleft) - copied;
32665534Salfred				src = mtod(m, char *) + (m->m_len - tocopy);
32761837Salfred
32865534Salfred				while (tocopy--) {
32965534Salfred					switch (copied++) {
33065534Salfred					case 0:
33165534Salfred						a = *src++;
33265534Salfred						break;
33365534Salfred					case 1:
33465534Salfred						b = *src++;
33565534Salfred						break;
33665534Salfred					case 2:
33765534Salfred						c = *src++;
33865534Salfred						break;
33965534Salfred					}
34065534Salfred				}
34165534Salfred			}
34261837Salfred		}
34361837Salfred	}
34465534Salfred	if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
34565534Salfred		/* we have all request headers */
34665534Salfred		goto gotit;
34765534Salfred	}
34861837Salfred
34965534Salfredreadmore:
35065534Salfred	so->so_upcall = soishttpconnected;
35165534Salfred	so->so_rcv.sb_flags |= SB_UPCALL;
35265534Salfred	return;
35365534Salfred
35465534Salfredgotit:
35561837Salfred	so->so_upcall = NULL;
35661837Salfred	so->so_rcv.sb_flags &= ~SB_UPCALL;
35761837Salfred	soisconnected(so);
35861837Salfred	return;
35961837Salfred}
360