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 }