The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/netinet/accf_http.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*      $NetBSD: accf_http.c,v 1.10 2020/03/16 21:20:11 pgoyette Exp $  */
    2 
    3 /*-
    4  * Copyright (c) 2000 Paycounter, Inc.
    5  * Author: Alfred Perlstein <alfred@paycounter.com>, <alfred@FreeBSD.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __KERNEL_RCSID(0, "$NetBSD: accf_http.c,v 1.10 2020/03/16 21:20:11 pgoyette Exp $");
   32 
   33 #define ACCEPT_FILTER_MOD
   34 
   35 #include <sys/param.h>
   36 #include <sys/kernel.h>
   37 #include <sys/mbuf.h>
   38 #include <sys/module.h>
   39 #include <sys/signalvar.h>
   40 #include <sys/sysctl.h>
   41 #include <sys/socket.h>
   42 #include <sys/socketvar.h>
   43 
   44 #include <netinet/accept_filter.h>
   45 
   46 #include "ioconf.h"
   47 
   48 MODULE(MODULE_CLASS_MISC, accf_httpready, NULL);
   49 
   50 /* check for GET/HEAD */
   51 static void sohashttpget(struct socket *so, void *arg, int events, int waitflag);
   52 /* check for HTTP/1.0 or HTTP/1.1 */
   53 static void soparsehttpvers(struct socket *so, void *arg, int events, int waitflag);
   54 /* check for end of HTTP/1.x request */
   55 static void soishttpconnected(struct socket *so, void *arg, int events, int waitflag);
   56 /* strcmp on an mbuf chain */
   57 static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp);
   58 /* strncmp on an mbuf chain */
   59 static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
   60         int len, const char *cmp);
   61 /* socketbuffer is full */
   62 static int sbfull(struct sockbuf *sb);
   63 
   64 static struct accept_filter accf_http_filter = {
   65         .accf_name = "httpready",
   66         .accf_callback = sohashttpget,
   67 };
   68 
   69 /*
   70  * Names of HTTP Accept filter sysctl objects
   71  */
   72 
   73 #define ACCFCTL_PARSEVER        1       /* Parse HTTP version */
   74 
   75 static int parse_http_version = 1;
   76 
   77 void
   78 accf_httpattach(int junk)
   79 {
   80 
   81 }
   82 
   83 SYSCTL_SETUP(accf_sysctl_setup, "accf sysctl")
   84 {
   85 
   86         sysctl_createv(clog, 0, NULL, NULL,
   87                CTLFLAG_PERMANENT,
   88                CTLTYPE_NODE, "inet", NULL,
   89                NULL, 0, NULL, 0,
   90                CTL_NET, PF_INET, CTL_EOL);
   91         sysctl_createv(clog, 0, NULL, NULL,
   92                CTLFLAG_PERMANENT,
   93                CTLTYPE_NODE, "accf", NULL,
   94                NULL, 0, NULL, 0,
   95                CTL_NET, PF_INET, SO_ACCEPTFILTER, CTL_EOL);
   96         sysctl_createv(clog, 0, NULL, NULL,
   97                CTLFLAG_PERMANENT,
   98                CTLTYPE_NODE, "http",
   99                SYSCTL_DESCR("HTTP accept filter"),
  100                NULL, 0, NULL, 0,
  101                CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP, CTL_EOL);
  102         sysctl_createv(clog, 0, NULL, NULL,
  103                CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
  104                CTLTYPE_INT, "parsehttpversion",
  105                SYSCTL_DESCR("Parse http version so that non "
  106                             "1.x requests work"),
  107                NULL, 0, &parse_http_version, 0,
  108                CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP,
  109                ACCFCTL_PARSEVER, CTL_EOL);
  110 }
  111 
  112 static int
  113 accf_httpready_modcmd(modcmd_t cmd, void *arg)
  114 {
  115         int error;
  116 
  117         switch (cmd) {
  118         case MODULE_CMD_INIT:
  119                 error = accept_filt_add(&accf_http_filter);
  120                 return 0;
  121 
  122         case MODULE_CMD_FINI:
  123                 error = accept_filt_del(&accf_http_filter);
  124                 return error;
  125 
  126         default:
  127                 return ENOTTY;
  128         }
  129 }
  130 
  131 #ifdef ACCF_HTTP_DEBUG
  132 #define DPRINT(fmt, args...)                                            \
  133         do {                                                            \
  134                 printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
  135         } while (0)
  136 #else
  137 #define DPRINT(fmt, args...)
  138 #endif
  139 
  140 static int
  141 sbfull(struct sockbuf *sb)
  142 {
  143 
  144         DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
  145             "mbcnt(%ld) >= mbmax(%ld): %d",
  146             sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
  147             sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
  148         return (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
  149 }
  150 
  151 /*
  152  * start at mbuf m, (must provide npkt if exists)
  153  * starting at offset in m compare characters in mbuf chain for 'cmp'
  154  */
  155 static int
  156 mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp)
  157 {
  158         struct mbuf *n;
  159 
  160         for (; m != NULL; m = n) {
  161                 n = npkt;
  162                 if (npkt)
  163                         npkt = npkt->m_nextpkt;
  164                 for (; m; m = m->m_next) {
  165                         for (; offset < m->m_len; offset++, cmp++) {
  166                                 if (*cmp == '\0')
  167                                         return (1);
  168                                 else if (*cmp != *(mtod(m, char *) + offset))
  169                                         return (0);
  170                         }
  171                         if (*cmp == '\0')
  172                                 return (1);
  173                         offset = 0;
  174                 }
  175         }
  176         return (0);
  177 }
  178 
  179 /*
  180  * start at mbuf m, (must provide npkt if exists)
  181  * starting at offset in m compare characters in mbuf chain for 'cmp'
  182  * stop at 'max' characters
  183  */
  184 static int
  185 mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int len, const char *cmp)
  186 {
  187         struct mbuf *n;
  188 
  189         for (; m != NULL; m = n) {
  190                 n = npkt;
  191                 if (npkt)
  192                         npkt = npkt->m_nextpkt;
  193                 for (; m; m = m->m_next) {
  194                         for (; offset < m->m_len; offset++, cmp++, len--) {
  195                                 if (len == 0 || *cmp == '\0')
  196                                         return (1);
  197                                 else if (*cmp != *(mtod(m, char *) + offset))
  198                                         return (0);
  199                         }
  200                         if (len == 0 || *cmp == '\0')
  201                                 return (1);
  202                         offset = 0;
  203                 }
  204         }
  205         return (0);
  206 }
  207 
  208 #define STRSETUP(sptr, slen, str)                                       \
  209         do {                                                            \
  210                 sptr = str;                                             \
  211                 slen = sizeof(str) - 1;                                 \
  212         } while(0)
  213 
  214 static void
  215 sohashttpget(struct socket *so, void *arg, int events, int waitflag)
  216 {
  217 
  218         if ((so->so_state & SS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
  219                 struct mbuf *m;
  220                 const char *cmp;
  221                 int     cmplen, cc;
  222 
  223                 m = so->so_rcv.sb_mb;
  224                 cc = so->so_rcv.sb_cc - 1;
  225                 if (cc < 1)
  226                         return;
  227                 switch (*mtod(m, char *)) {
  228                 case 'G':
  229                         STRSETUP(cmp, cmplen, "ET ");
  230                         break;
  231                 case 'H':
  232                         STRSETUP(cmp, cmplen, "EAD ");
  233                         break;
  234                 default:
  235                         goto fallout;
  236                 }
  237                 if (cc < cmplen) {
  238                         if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
  239                                 DPRINT("short cc (%d) but mbufstrncmp ok", cc);
  240                                 return;
  241                         } else {
  242                                 DPRINT("short cc (%d) mbufstrncmp failed", cc);
  243                                 goto fallout;
  244                         }
  245                 }
  246                 if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
  247                         DPRINT("mbufstrcmp ok");
  248                         if (parse_http_version == 0)
  249                                 soishttpconnected(so, arg, events, waitflag);
  250                         else
  251                                 soparsehttpvers(so, arg, events, waitflag);
  252                         return;
  253                 }
  254                 DPRINT("mbufstrcmp bad");
  255         }
  256 
  257 fallout:
  258         DPRINT("fallout");
  259         so->so_upcall = NULL;
  260         so->so_rcv.sb_flags &= ~SB_UPCALL;
  261         soisconnected(so);
  262         return;
  263 }
  264 
  265 static void
  266 soparsehttpvers(struct socket *so, void *arg, int events, int waitflag)
  267 {
  268         struct mbuf *m, *n;
  269         int     i, cc, spaces, inspaces;
  270 
  271         if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
  272                 goto fallout;
  273 
  274         m = so->so_rcv.sb_mb;
  275         cc = so->so_rcv.sb_cc;
  276         inspaces = spaces = 0;
  277         for (m = so->so_rcv.sb_mb; m; m = n) {
  278                 n = m->m_nextpkt;
  279                 for (; m; m = m->m_next) {
  280                         for (i = 0; i < m->m_len; i++, cc--) {
  281                                 switch (*(mtod(m, char *) + i)) {
  282                                 case ' ':
  283                                         /* tabs? '\t' */
  284                                         if (!inspaces) {
  285                                                 spaces++;
  286                                                 inspaces = 1;
  287                                         }
  288                                         break;
  289                                 case '\r':
  290                                 case '\n':
  291                                         DPRINT("newline");
  292                                         goto fallout;
  293                                 default:
  294                                         if (spaces != 2) {
  295                                                 inspaces = 0;
  296                                                 break;
  297                                         }
  298 
  299                                         /*
  300                                          * if we don't have enough characters
  301                                          * left (cc < sizeof("HTTP/1.0") - 1)
  302                                          * then see if the remaining ones
  303                                          * are a request we can parse.
  304                                          */
  305                                         if (cc < sizeof("HTTP/1.0") - 1) {
  306                                                 if (mbufstrncmp(m, n, i, cc,
  307                                                         "HTTP/1.") == 1) {
  308                                                         DPRINT("ok");
  309                                                         goto readmore;
  310                                                 } else {
  311                                                         DPRINT("bad");
  312                                                         goto fallout;
  313                                                 }
  314                                         } else if (
  315                                             mbufstrcmp(m, n, i, "HTTP/1.0") ||
  316                                             mbufstrcmp(m, n, i, "HTTP/1.1")) {
  317                                                 DPRINT("ok");
  318                                                 soishttpconnected(so,
  319                                                     arg, events, waitflag);
  320                                                 return;
  321                                         } else {
  322                                                 DPRINT("bad");
  323                                                 goto fallout;
  324                                         }
  325                                 }
  326                         }
  327                 }
  328         }
  329 readmore:
  330         DPRINT("readmore");
  331         /*
  332          * if we hit here we haven't hit something
  333          * we don't understand or a newline, so try again
  334          */
  335         so->so_upcall = soparsehttpvers;
  336         so->so_rcv.sb_flags |= SB_UPCALL;
  337         return;
  338 
  339 fallout:
  340         DPRINT("fallout");
  341         so->so_upcall = NULL;
  342         so->so_rcv.sb_flags &= ~SB_UPCALL;
  343         soisconnected(so);
  344         return;
  345 }
  346 
  347 
  348 #define NCHRS 3
  349 
  350 static void
  351 soishttpconnected(struct socket *so, void *arg, int events, int waitflag)
  352 {
  353         char a, b, c;
  354         struct mbuf *m, *n;
  355         int ccleft, copied;
  356 
  357         DPRINT("start");
  358         if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
  359                 goto gotit;
  360 
  361         /*
  362          * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
  363          * copied - how much we've copied so far
  364          * ccleft - how many bytes remaining in the socketbuffer
  365          * just loop over the mbufs subtracting from 'ccleft' until we only
  366          * have NCHRS left
  367          */
  368         copied = 0;
  369         ccleft = so->so_rcv.sb_cc;
  370         if (ccleft < NCHRS)
  371                 goto readmore;
  372         a = b = c = '\0';
  373         for (m = so->so_rcv.sb_mb; m; m = n) {
  374                 n = m->m_nextpkt;
  375                 for (; m; m = m->m_next) {
  376                         ccleft -= m->m_len;
  377                         if (ccleft <= NCHRS) {
  378                                 char *src;
  379                                 int tocopy;
  380 
  381                                 tocopy = (NCHRS - ccleft) - copied;
  382                                 src = mtod(m, char *) + (m->m_len - tocopy);
  383 
  384                                 while (tocopy--) {
  385                                         switch (copied++) {
  386                                         case 0:
  387                                                 a = *src++;
  388                                                 break;
  389                                         case 1:
  390                                                 b = *src++;
  391                                                 break;
  392                                         case 2:
  393                                                 c = *src++;
  394                                                 break;
  395                                         }
  396                                 }
  397                         }
  398                 }
  399         }
  400         if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
  401                 /* we have all request headers */
  402                 goto gotit;
  403         }
  404 
  405 readmore:
  406         so->so_upcall = soishttpconnected;
  407         so->so_rcv.sb_flags |= SB_UPCALL;
  408         return;
  409 
  410 gotit:
  411         so->so_upcall = NULL;
  412         so->so_rcv.sb_flags &= ~SB_UPCALL;
  413         soisconnected(so);
  414         return;
  415 }

Cache object: b9719b0fa979089d2ab3907b364784a9


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.