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