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, dlen = MBLKL(mp);
 131 
 132         for (i = 0; i < dlen; i++) {
 133                 /* Collect the first four bytes for a protocol validation */
 134                 if (httpf->httpf_method == HTTPF_METHOD_UNSET &&
 135                     httpf->httpf_bytes_in < 4)
 136                         httpf->httpf_ff[httpf->httpf_bytes_in] = mp->b_rptr[i];
 137 
 138                 httpf->httpf_bytes_in++;
 139 
 140                 /*
 141                  * if we haven't yet determined out HTTP method, do it at
 142                  * exactly 4 bytes into the stream.
 143                  */
 144                 if (httpf->httpf_method == HTTPF_METHOD_UNSET &&
 145                     httpf->httpf_bytes_in == 4) {
 146                         /*
 147                          * if we find no good method, we can't defer this stream
 148                          */
 149                         httpf->httpf_method =
 150                             httpf_method_from_ff(httpf->httpf_ffw);
 151                         if (httpf->httpf_method == HTTPF_METHOD_INVALID)
 152                                 return (-1);
 153                 }
 154 
 155                 /*
 156                  * if the method is set, start looking for either
 157                  *  \r\n\r\n or \n\n
 158                  */
 159                 if (httpf->httpf_method > HTTPF_METHOD_UNSET)
 160                         if (httpf_progress_crlfcrlf_state(httpf, mp->b_rptr[i])
 161                             == 0) {
 162                                 return (1);
 163                         }
 164         }
 165         return (0);
 166 }
 167 
 168 /*
 169  * Allocate httpf state
 170  */
 171 sof_rval_t
 172 httpf_attach_passive_cb(sof_handle_t handle, sof_handle_t ph,
 173     void *parg, struct sockaddr *laddr, socklen_t laddrlen,
 174     struct sockaddr *faddr, socklen_t faddrlen, void **cookiep)
 175 {
 176         httpf_t *new;
 177 
 178         _NOTE(ARGUNUSED(handle, ph, parg, faddr, faddrlen, laddr, laddrlen));
 179 
 180         /* Allocate the SSL context for the new connection */
 181         new = kmem_zalloc(sizeof (httpf_t), KM_NOSLEEP);
 182         if (new == NULL)
 183                 return (SOF_RVAL_ENOMEM);
 184 
 185         new->httpf_bytes_in = 0;
 186         new->httpf_method = HTTPF_METHOD_UNSET;
 187 
 188         *cookiep = new;
 189         /*
 190          * We are awaiting a request, defer the notification of this
 191          * connection until it is completed.
 192          */
 193         return (SOF_RVAL_DEFER);
 194 }
 195 
 196 void
 197 httpf_detach_cb(sof_handle_t handle, void *cookie, cred_t *cr)
 198 {
 199         httpf_t *httpf = (httpf_t *)cookie;
 200 
 201         _NOTE(ARGUNUSED(handle, cr));
 202 
 203         if (httpf == NULL)
 204                 return;
 205 
 206         kmem_free(httpf, sizeof (httpf_t));
 207 }
 208 
 209 /*
 210  * Called for each incoming segment.
 211  */
 212 mblk_t *
 213 httpf_data_in_cb(sof_handle_t handle, void *cookie, mblk_t *mp, int flags,
 214     size_t *lenp)
 215 {
 216         httpf_t *httpf = cookie;
 217 
 218         _NOTE(ARGUNUSED(flags, lenp));
 219 
 220         if (httpf == NULL) {
 221                 sof_bypass(handle);
 222                 return (mp);
 223         }
 224 
 225         if (mp == NULL)
 226                 return (mp);
 227 
 228         if (httpf_process_input(httpf, mp))
 229                 sof_newconn_ready(handle);
 230 
 231         if (httpf->httpf_bytes_in > MAX_HTTP_FILTER_SIZE)
 232                 sof_newconn_ready(handle);
 233 
 234         return (mp);
 235 }
 236 
 237 sof_ops_t httpf_ops = {
 238         .sofop_attach_passive = httpf_attach_passive_cb,
 239         .sofop_detach = httpf_detach_cb,
 240         .sofop_data_in = httpf_data_in_cb,
 241 };
 242 
 243 int
 244 _init(void)
 245 {
 246         int error;
 247 
 248         if ((error = sof_register(SOF_VERSION, HTTPFILT_MODULE, &httpf_ops, 0))
 249             != 0) {
 250                 return (error);
 251         }
 252         if ((error = mod_install(&httpf_modlinkage)) != 0)
 253                 (void) sof_unregister(HTTPFILT_MODULE);
 254 
 255         return (error);
 256 }
 257 
 258 int
 259 _fini(void)
 260 {
 261         int error;
 262 
 263         if ((error = sof_unregister(HTTPFILT_MODULE)) != 0)
 264                 return (error);
 265 
 266         return (mod_remove(&httpf_modlinkage));
 267 }
 268 
 269 int
 270 _info(struct modinfo *modinfop)
 271 {
 272         return (mod_info(&httpf_modlinkage, modinfop));
 273 }