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