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