1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2012, OmniTI Computer Consulting, Inc. All rights reserved.
  23  */
  24 
  25 #include <sys/kmem.h>
  26 #include <sys/systm.h>
  27 #include <sys/stropts.h>
  28 #include <sys/strsun.h>
  29 #include <sys/socketvar.h>
  30 #include <sys/sockfilter.h>
  31 #include <sys/note.h>
  32 #include <sys/taskq.h>
  33 
  34 static struct modlmisc httpf_modlmisc = {
  35         &mod_miscops,
  36         "Kernel HTTP socket filter"
  37 };
  38 
  39 static struct modlinkage httpf_modlinkage = {
  40         MODREV_1,
  41         &httpf_modlmisc,
  42         NULL
  43 };
  44 /*
  45  * Name of the HTTP filter
  46  */
  47 #define HTTPFILT_MODULE "httpfilt"
  48 #define MAX_HTTP_FILTER_SIZE 8192
  49 
  50 /*
  51  * httpf filter cookie
  52  */
  53 typedef struct httpf {
  54         size_t  httpf_bytes_in; /* bytes read */
  55         int     httpf_method;   /* HTTP method */
  56         int     httpf_sm;       /* \r\n\r\n | \n\n state machine */
  57         char    httpf_ff[4];    /* the first 4 bytes of the data stream */
  58         union {
  59                 char httpfu_ff[4]; /* the first 4 bytes of the data stream */
  60                 uint32_t httpfu_w; /* in an easy-to-compare 32-bit word */
  61         } httpf_u;
  62 #define httpf_ff httpf_u.httpfu_ff
  63 #define httpf_ffw httpf_u.httpfu_w
  64 } httpf_t;
  65 
  66 #ifdef _BIG_ENDIAN
  67 #define CHAR_TO_32(a, b, c, d) ((d) + ((c) << 8) + ((b) << 16) + ((a) << 24))
  68 #else
  69 #define CHAR_TO_32(a, b, c, d) ((a) + ((b) << 8) + ((c) << 16) + ((d) << 24))
  70 #endif
  71 
  72 #define WORD_GET        CHAR_TO_32('G', 'E', 'T', ' ')
  73 #define WORD_PUT        CHAR_TO_32('P', 'U', 'T', ' ')
  74 #define WORD_POST       CHAR_TO_32('P', 'O', 'S', 'T')
  75 #define WORD_HEAD       CHAR_TO_32('H', 'E', 'A', 'D')
  76 
  77 #define HTTPF_METHOD_INVALID -1
  78 #define HTTPF_METHOD_UNSET 0
  79 #define HTTPF_METHOD_GET   1
  80 #define HTTPF_METHOD_HEAD  2
  81 #define HTTPF_METHOD_PUT   3
  82 #define HTTPF_METHOD_POST  4
  83 
  84 static int
  85 httpf_method_from_ff(uint32_t ffw)
  86 {
  87         switch (ffw) {
  88         case WORD_GET:
  89                 return (HTTPF_METHOD_GET);
  90         case WORD_PUT:
  91                 return (HTTPF_METHOD_PUT);
  92         case WORD_POST:
  93                 return (HTTPF_METHOD_POST);
  94         case WORD_HEAD:
  95                 return (HTTPF_METHOD_HEAD);
  96         }
  97 
  98         return (HTTPF_METHOD_INVALID);
  99 }
 100 
 101 static int
 102 httpf_progress_crlfcrlf_state(httpf_t *httpf, unsigned char ch) {
 103 #define HTTPF_STATE(a) httpf->httpf_sm = (a)
 104 #define IF_HTTPF_TOKEN(a) if ((ch) == (a))
 105         switch (httpf->httpf_sm) {
 106         case 0:
 107                 IF_HTTPF_TOKEN('\r') HTTPF_STATE(1);
 108                 IF_HTTPF_TOKEN('\n') HTTPF_STATE(3);
 109                 break;
 110         case 1:
 111                 IF_HTTPF_TOKEN('\n') HTTPF_STATE(2);
 112                 else HTTPF_STATE(0);
 113                 break;
 114         case 2:
 115                 IF_HTTPF_TOKEN('\n') return (0);
 116                 IF_HTTPF_TOKEN('\r') HTTPF_STATE(3);
 117                 else HTTPF_STATE(0);
 118                 break;
 119         case 3:
 120                 IF_HTTPF_TOKEN('\n') return (0);
 121                 IF_HTTPF_TOKEN('\r') HTTPF_STATE(1);
 122                 else HTTPF_STATE(0);
 123                 break;
 124         }
 125         return (-1);
 126 }
 127 
 128 static int
 129 httpf_process_input(httpf_t *httpf, mblk_t *mp) {
 130         int i, blen = MBLKL(mp), dlen = msgdsize(mp);
 131 
 132         for (i = 0; i < dlen; i++) {
 133                 while (blen-- == 0) {
 134                         DTRACE_PROBE(http__zero__len__mblk);
 135                         mp = mp->b_cont;
 136                         /*
 137                          * If we are here we must have some of "dlen" bytes
 138                          * left, which means we MUST have a non-zero-length
 139                          * mblk in the chain.  We will never fall off the
 140                          * edge after a zero-length mblk UNLESS there's no
 141                          * dlen left, which means we'll never get here!
 142                          */
 143                         ASSERT(mp != NULL);
 144                         blen = MBLKL(mp);
 145                 }
 146 
 147                 /* Collect the first four bytes for a protocol validation */
 148                 if (httpf->httpf_method == HTTPF_METHOD_UNSET &&
 149                     httpf->httpf_bytes_in < 4)
 150                         httpf->httpf_ff[httpf->httpf_bytes_in] = mp->b_rptr[i];
 151 
 152                 httpf->httpf_bytes_in++;
 153 
 154                 /*
 155                  * if we haven't yet determined out HTTP method, do it at
 156                  * exactly 4 bytes into the stream.
 157                  */
 158                 if (httpf->httpf_method == HTTPF_METHOD_UNSET &&
 159                     httpf->httpf_bytes_in == 4) {
 160                         /*
 161                          * if we find no good method, we can't defer this stream
 162                          */
 163                         httpf->httpf_method =
 164                             httpf_method_from_ff(httpf->httpf_ffw);
 165                         if (httpf->httpf_method == HTTPF_METHOD_INVALID)
 166                                 return (-1);
 167                 }
 168 
 169                 /*
 170                  * if the method is set, start looking for either
 171                  *  \r\n\r\n or \n\n
 172                  */
 173                 if (httpf->httpf_method > HTTPF_METHOD_UNSET)
 174                         if (httpf_progress_crlfcrlf_state(httpf, mp->b_rptr[i])
 175                             == 0) {
 176                                 return (1);
 177                         }
 178         }
 179         return (0);
 180 }
 181 
 182 /*
 183  * Allocate httpf state
 184  */
 185 sof_rval_t
 186 httpf_attach_passive_cb(sof_handle_t handle, sof_handle_t ph,
 187     void *parg, struct sockaddr *laddr, socklen_t laddrlen,
 188     struct sockaddr *faddr, socklen_t faddrlen, void **cookiep)
 189 {
 190         httpf_t *new;
 191 
 192         _NOTE(ARGUNUSED(handle, ph, parg, faddr, faddrlen, laddr, laddrlen));
 193 
 194         /* Allocate the SSL context for the new connection */
 195         new = kmem_zalloc(sizeof (httpf_t), KM_NOSLEEP);
 196         if (new == NULL)
 197                 return (SOF_RVAL_ENOMEM);
 198 
 199         new->httpf_bytes_in = 0;
 200         new->httpf_method = HTTPF_METHOD_UNSET;
 201 
 202         *cookiep = new;
 203         /*
 204          * We are awaiting a request, defer the notification of this
 205          * connection until it is completed.
 206          */
 207         return (SOF_RVAL_DEFER);
 208 }
 209 
 210 void
 211 httpf_detach_cb(sof_handle_t handle, void *cookie, cred_t *cr)
 212 {
 213         httpf_t *httpf = (httpf_t *)cookie;
 214 
 215         _NOTE(ARGUNUSED(handle, cr));
 216 
 217         if (httpf == NULL)
 218                 return;
 219 
 220         kmem_free(httpf, sizeof (httpf_t));
 221 }
 222 
 223 /*
 224  * Called for each incoming segment.
 225  */
 226 mblk_t *
 227 httpf_data_in_cb(sof_handle_t handle, void *cookie, mblk_t *mp, int flags,
 228     size_t *lenp)
 229 {
 230         httpf_t *httpf = cookie;
 231 
 232         _NOTE(ARGUNUSED(flags, lenp));
 233 
 234         if (httpf == NULL) {
 235                 sof_bypass(handle);
 236                 return (mp);
 237         }
 238 
 239         if (mp == NULL)
 240                 return (mp);
 241 
 242         if (httpf_process_input(httpf, mp))
 243                 sof_newconn_ready(handle);
 244 
 245         if (httpf->httpf_bytes_in > MAX_HTTP_FILTER_SIZE)
 246                 sof_newconn_ready(handle);
 247 
 248         return (mp);
 249 }
 250 
 251 sof_ops_t httpf_ops = {
 252         .sofop_attach_passive = httpf_attach_passive_cb,
 253         .sofop_detach = httpf_detach_cb,
 254         .sofop_data_in = httpf_data_in_cb,
 255 };
 256 
 257 int
 258 _init(void)
 259 {
 260         int error;
 261 
 262         if ((error = sof_register(SOF_VERSION, HTTPFILT_MODULE, &httpf_ops, 0))
 263             != 0) {
 264                 return (error);
 265         }
 266         if ((error = mod_install(&httpf_modlinkage)) != 0)
 267                 (void) sof_unregister(HTTPFILT_MODULE);
 268 
 269         return (error);
 270 }
 271 
 272 int
 273 _fini(void)
 274 {
 275         int error;
 276 
 277         if ((error = sof_unregister(HTTPFILT_MODULE)) != 0)
 278                 return (error);
 279 
 280         return (mod_remove(&httpf_modlinkage));
 281 }
 282 
 283 int
 284 _info(struct modinfo *modinfop)
 285 {
 286         return (mod_info(&httpf_modlinkage, modinfop));
 287 }