1 /* $NetBSD: accf_http.c,v 1.2 2008/10/14 13:05:44 ad 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.2 2008/10/14 13:05:44 ad 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/lkm.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 /* check for GET/HEAD */
47 static void sohashttpget(struct socket *so, void *arg, int waitflag);
48 /* check for HTTP/1.0 or HTTP/1.1 */
49 static void soparsehttpvers(struct socket *so, void *arg, int waitflag);
50 /* check for end of HTTP/1.x request */
51 static void soishttpconnected(struct socket *so, void *arg, int waitflag);
52 /* strcmp on an mbuf chain */
53 static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp);
54 /* strncmp on an mbuf chain */
55 static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
56 int len, const char *cmp);
57 /* socketbuffer is full */
58 static int sbfull(struct sockbuf *sb);
59
60 static struct accept_filter accf_http_filter = {
61 .accf_name = "httpready",
62 .accf_callback = sohashttpget,
63 };
64
65 /*
66 * Names of HTTP Accept filter sysctl objects
67 */
68
69 #define ACCFCTL_PARSEVER 1 /* Parse HTTP version */
70
71 static int parse_http_version = 1;
72
73 SYSCTL_SETUP(sysctl_net_inet_accf__http_setup, "sysctl net.inet.accf.http subtree setup")
74 {
75 sysctl_createv(clog, 0, NULL, NULL,
76 CTLFLAG_PERMANENT,
77 CTLTYPE_NODE, "net", NULL,
78 NULL, 0, NULL, 0,
79 CTL_NET, CTL_EOL);
80 sysctl_createv(clog, 0, NULL, NULL,
81 CTLFLAG_PERMANENT,
82 CTLTYPE_NODE, "inet", NULL,
83 NULL, 0, NULL, 0,
84 CTL_NET, PF_INET, CTL_EOL);
85 sysctl_createv(clog, 0, NULL, NULL,
86 CTLFLAG_PERMANENT,
87 CTLTYPE_NODE, "accf", NULL,
88 NULL, 0, NULL, 0,
89 CTL_NET, PF_INET, SO_ACCEPTFILTER, CTL_EOL);
90 sysctl_createv(clog, 0, NULL, NULL,
91 CTLFLAG_PERMANENT,
92 CTLTYPE_NODE, "http",
93 SYSCTL_DESCR("HTTP accept filter"),
94 NULL, 0, NULL, 0,
95 CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP, CTL_EOL);
96 sysctl_createv(clog, 0, NULL, NULL,
97 CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
98 CTLTYPE_INT, "parsehttpversion",
99 SYSCTL_DESCR("Parse http version so that non "
100 "1.x requests work"),
101 NULL, 0, &parse_http_version, 0,
102 CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP,
103 ACCFCTL_PARSEVER, CTL_EOL);
104 }
105
106 void accf_httpattach(int);
107 void accf_httpattach(int num)
108 {
109 accept_filt_generic_mod_event(NULL, LKM_E_LOAD, &accf_http_filter);
110 }
111
112 /*
113 * This code is to make HTTP ready accept filer as LKM.
114 * To compile as LKM we need to move this set of code into
115 * another file and include this file for compilation when
116 * making LKM
117 */
118
119 #ifdef _LKM
120 static int accf_http_handle(struct lkm_table * lkmtp, int cmd);
121 int accf_http_lkmentry(struct lkm_table * lkmtp, int cmd, int ver);
122
123 MOD_MISC("accf_http");
124
125 static int accf_http_handle(struct lkm_table * lkmtp, int cmd)
126 {
127
128 return accept_filt_generic_mod_event(lkmtp, cmd, &accf_http_filter);
129 }
130
131 /*
132 * the module entry point.
133 */
134 int
135 accf_http_lkmentry(struct lkm_table *lkmtp, int cmd, int ver)
136 {
137 DISPATCH(lkmtp, cmd, ver, accf_http_handle, accf_http_handle,
138 accf_http_handle)
139 }
140 #endif
141
142 #ifdef ACCF_HTTP_DEBUG
143 #define DPRINT(fmt, args...) \
144 do { \
145 printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
146 } while (0)
147 #else
148 #define DPRINT(fmt, args...)
149 #endif
150
151 static int
152 sbfull(struct sockbuf *sb)
153 {
154
155 DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
156 "mbcnt(%ld) >= mbmax(%ld): %d",
157 sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
158 sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
159 return (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
160 }
161
162 /*
163 * start at mbuf m, (must provide npkt if exists)
164 * starting at offset in m compare characters in mbuf chain for 'cmp'
165 */
166 static int
167 mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp)
168 {
169 struct mbuf *n;
170
171 for (; m != NULL; m = n) {
172 n = npkt;
173 if (npkt)
174 npkt = npkt->m_nextpkt;
175 for (; m; m = m->m_next) {
176 for (; offset < m->m_len; offset++, cmp++) {
177 if (*cmp == '\0')
178 return (1);
179 else if (*cmp != *(mtod(m, char *) + offset))
180 return (0);
181 }
182 if (*cmp == '\0')
183 return (1);
184 offset = 0;
185 }
186 }
187 return (0);
188 }
189
190 /*
191 * start at mbuf m, (must provide npkt if exists)
192 * starting at offset in m compare characters in mbuf chain for 'cmp'
193 * stop at 'max' characters
194 */
195 static int
196 mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int len, const char *cmp)
197 {
198 struct mbuf *n;
199
200 for (; m != NULL; m = n) {
201 n = npkt;
202 if (npkt)
203 npkt = npkt->m_nextpkt;
204 for (; m; m = m->m_next) {
205 for (; offset < m->m_len; offset++, cmp++, len--) {
206 if (len == 0 || *cmp == '\0')
207 return (1);
208 else if (*cmp != *(mtod(m, char *) + offset))
209 return (0);
210 }
211 if (len == 0 || *cmp == '\0')
212 return (1);
213 offset = 0;
214 }
215 }
216 return (0);
217 }
218
219 #define STRSETUP(sptr, slen, str) \
220 do { \
221 sptr = str; \
222 slen = sizeof(str) - 1; \
223 } while(0)
224
225 static void
226 sohashttpget(struct socket *so, void *arg, int waitflag)
227 {
228
229 if ((so->so_state & SS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
230 struct mbuf *m;
231 const char *cmp;
232 int cmplen, cc;
233
234 m = so->so_rcv.sb_mb;
235 cc = so->so_rcv.sb_cc - 1;
236 if (cc < 1)
237 return;
238 switch (*mtod(m, char *)) {
239 case 'G':
240 STRSETUP(cmp, cmplen, "ET ");
241 break;
242 case 'H':
243 STRSETUP(cmp, cmplen, "EAD ");
244 break;
245 default:
246 goto fallout;
247 }
248 if (cc < cmplen) {
249 if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
250 DPRINT("short cc (%d) but mbufstrncmp ok", cc);
251 return;
252 } else {
253 DPRINT("short cc (%d) mbufstrncmp failed", cc);
254 goto fallout;
255 }
256 }
257 if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
258 DPRINT("mbufstrcmp ok");
259 if (parse_http_version == 0)
260 soishttpconnected(so, arg, waitflag);
261 else
262 soparsehttpvers(so, arg, waitflag);
263 return;
264 }
265 DPRINT("mbufstrcmp bad");
266 }
267
268 fallout:
269 DPRINT("fallout");
270 so->so_upcall = NULL;
271 so->so_rcv.sb_flags &= ~SB_UPCALL;
272 soisconnected(so);
273 return;
274 }
275
276 static void
277 soparsehttpvers(struct socket *so, void *arg, int waitflag)
278 {
279 struct mbuf *m, *n;
280 int i, cc, spaces, inspaces;
281
282 if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
283 goto fallout;
284
285 m = so->so_rcv.sb_mb;
286 cc = so->so_rcv.sb_cc;
287 inspaces = spaces = 0;
288 for (m = so->so_rcv.sb_mb; m; m = n) {
289 n = m->m_nextpkt;
290 for (; m; m = m->m_next) {
291 for (i = 0; i < m->m_len; i++, cc--) {
292 switch (*(mtod(m, char *) + i)) {
293 case ' ':
294 /* tabs? '\t' */
295 if (!inspaces) {
296 spaces++;
297 inspaces = 1;
298 }
299 break;
300 case '\r':
301 case '\n':
302 DPRINT("newline");
303 goto fallout;
304 default:
305 if (spaces != 2) {
306 inspaces = 0;
307 break;
308 }
309
310 /*
311 * if we don't have enough characters
312 * left (cc < sizeof("HTTP/1.0") - 1)
313 * then see if the remaining ones
314 * are a request we can parse.
315 */
316 if (cc < sizeof("HTTP/1.0") - 1) {
317 if (mbufstrncmp(m, n, i, cc,
318 "HTTP/1.") == 1) {
319 DPRINT("ok");
320 goto readmore;
321 } else {
322 DPRINT("bad");
323 goto fallout;
324 }
325 } else if (
326 mbufstrcmp(m, n, i, "HTTP/1.0") ||
327 mbufstrcmp(m, n, i, "HTTP/1.1")) {
328 DPRINT("ok");
329 soishttpconnected(so,
330 arg, waitflag);
331 return;
332 } else {
333 DPRINT("bad");
334 goto fallout;
335 }
336 }
337 }
338 }
339 }
340 readmore:
341 DPRINT("readmore");
342 /*
343 * if we hit here we haven't hit something
344 * we don't understand or a newline, so try again
345 */
346 so->so_upcall = soparsehttpvers;
347 so->so_rcv.sb_flags |= SB_UPCALL;
348 return;
349
350 fallout:
351 DPRINT("fallout");
352 so->so_upcall = NULL;
353 so->so_rcv.sb_flags &= ~SB_UPCALL;
354 soisconnected(so);
355 return;
356 }
357
358
359 #define NCHRS 3
360
361 static void
362 soishttpconnected(struct socket *so, void *arg, int waitflag)
363 {
364 char a, b, c;
365 struct mbuf *m, *n;
366 int ccleft, copied;
367
368 DPRINT("start");
369 if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
370 goto gotit;
371
372 /*
373 * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
374 * copied - how much we've copied so far
375 * ccleft - how many bytes remaining in the socketbuffer
376 * just loop over the mbufs subtracting from 'ccleft' until we only
377 * have NCHRS left
378 */
379 copied = 0;
380 ccleft = so->so_rcv.sb_cc;
381 if (ccleft < NCHRS)
382 goto readmore;
383 a = b = c = '\0';
384 for (m = so->so_rcv.sb_mb; m; m = n) {
385 n = m->m_nextpkt;
386 for (; m; m = m->m_next) {
387 ccleft -= m->m_len;
388 if (ccleft <= NCHRS) {
389 char *src;
390 int tocopy;
391
392 tocopy = (NCHRS - ccleft) - copied;
393 src = mtod(m, char *) + (m->m_len - tocopy);
394
395 while (tocopy--) {
396 switch (copied++) {
397 case 0:
398 a = *src++;
399 break;
400 case 1:
401 b = *src++;
402 break;
403 case 2:
404 c = *src++;
405 break;
406 }
407 }
408 }
409 }
410 }
411 if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
412 /* we have all request headers */
413 goto gotit;
414 }
415
416 readmore:
417 so->so_upcall = soishttpconnected;
418 so->so_rcv.sb_flags |= SB_UPCALL;
419 return;
420
421 gotit:
422 so->so_upcall = NULL;
423 so->so_rcv.sb_flags &= ~SB_UPCALL;
424 soisconnected(so);
425 return;
426 }
Cache object: 56dfeb7a9d6b7d8fbeb9496c5d699f91
|