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 }