accf_http.c revision 1.9.18.1 1 1.9.18.1 martin /* $NetBSD: accf_http.c,v 1.9.18.1 2020/04/08 14:08:58 martin Exp $ */
2 1.2 ad
3 1.1 tls /*-
4 1.1 tls * Copyright (c) 2000 Paycounter, Inc.
5 1.1 tls * Author: Alfred Perlstein <alfred (at) paycounter.com>, <alfred (at) FreeBSD.org>
6 1.1 tls * All rights reserved.
7 1.1 tls *
8 1.1 tls * Redistribution and use in source and binary forms, with or without
9 1.1 tls * modification, are permitted provided that the following conditions
10 1.1 tls * are met:
11 1.1 tls * 1. Redistributions of source code must retain the above copyright
12 1.1 tls * notice, this list of conditions and the following disclaimer.
13 1.1 tls * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 tls * notice, this list of conditions and the following disclaimer in the
15 1.1 tls * documentation and/or other materials provided with the distribution.
16 1.1 tls *
17 1.1 tls * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 1.1 tls * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 1.1 tls * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 1.1 tls * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 1.1 tls * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.1 tls * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 1.1 tls * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 1.1 tls * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1.1 tls * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 1.1 tls * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 1.1 tls * SUCH DAMAGE.
28 1.1 tls */
29 1.1 tls
30 1.1 tls #include <sys/cdefs.h>
31 1.9.18.1 martin __KERNEL_RCSID(0, "$NetBSD: accf_http.c,v 1.9.18.1 2020/04/08 14:08:58 martin Exp $");
32 1.1 tls
33 1.1 tls #define ACCEPT_FILTER_MOD
34 1.1 tls
35 1.1 tls #include <sys/param.h>
36 1.1 tls #include <sys/kernel.h>
37 1.1 tls #include <sys/mbuf.h>
38 1.3 ad #include <sys/module.h>
39 1.1 tls #include <sys/signalvar.h>
40 1.1 tls #include <sys/sysctl.h>
41 1.1 tls #include <sys/socket.h>
42 1.1 tls #include <sys/socketvar.h>
43 1.2 ad
44 1.1 tls #include <netinet/accept_filter.h>
45 1.1 tls
46 1.9 christos #include "ioconf.h"
47 1.9 christos
48 1.4 ad MODULE(MODULE_CLASS_MISC, accf_httpready, NULL);
49 1.3 ad
50 1.1 tls /* check for GET/HEAD */
51 1.7 tls static void sohashttpget(struct socket *so, void *arg, int events, int waitflag);
52 1.1 tls /* check for HTTP/1.0 or HTTP/1.1 */
53 1.7 tls static void soparsehttpvers(struct socket *so, void *arg, int events, int waitflag);
54 1.1 tls /* check for end of HTTP/1.x request */
55 1.7 tls static void soishttpconnected(struct socket *so, void *arg, int events, int waitflag);
56 1.1 tls /* strcmp on an mbuf chain */
57 1.1 tls static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp);
58 1.1 tls /* strncmp on an mbuf chain */
59 1.1 tls static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
60 1.1 tls int len, const char *cmp);
61 1.1 tls /* socketbuffer is full */
62 1.1 tls static int sbfull(struct sockbuf *sb);
63 1.1 tls
64 1.1 tls static struct accept_filter accf_http_filter = {
65 1.2 ad .accf_name = "httpready",
66 1.2 ad .accf_callback = sohashttpget,
67 1.1 tls };
68 1.1 tls
69 1.1 tls /*
70 1.1 tls * Names of HTTP Accept filter sysctl objects
71 1.1 tls */
72 1.1 tls
73 1.1 tls #define ACCFCTL_PARSEVER 1 /* Parse HTTP version */
74 1.1 tls
75 1.1 tls static int parse_http_version = 1;
76 1.1 tls
77 1.3 ad void
78 1.3 ad accf_httpattach(int junk)
79 1.3 ad {
80 1.3 ad
81 1.3 ad }
82 1.3 ad
83 1.9.18.1 martin SYSCTL_SETUP(accf_sysctl_setup, "accf sysctl")
84 1.9.18.1 martin {
85 1.9.18.1 martin
86 1.9.18.1 martin sysctl_createv(clog, 0, NULL, NULL,
87 1.9.18.1 martin CTLFLAG_PERMANENT,
88 1.9.18.1 martin CTLTYPE_NODE, "inet", NULL,
89 1.9.18.1 martin NULL, 0, NULL, 0,
90 1.9.18.1 martin CTL_NET, PF_INET, CTL_EOL);
91 1.9.18.1 martin sysctl_createv(clog, 0, NULL, NULL,
92 1.9.18.1 martin CTLFLAG_PERMANENT,
93 1.9.18.1 martin CTLTYPE_NODE, "accf", NULL,
94 1.9.18.1 martin NULL, 0, NULL, 0,
95 1.9.18.1 martin CTL_NET, PF_INET, SO_ACCEPTFILTER, CTL_EOL);
96 1.9.18.1 martin sysctl_createv(clog, 0, NULL, NULL,
97 1.9.18.1 martin CTLFLAG_PERMANENT,
98 1.9.18.1 martin CTLTYPE_NODE, "http",
99 1.9.18.1 martin SYSCTL_DESCR("HTTP accept filter"),
100 1.9.18.1 martin NULL, 0, NULL, 0,
101 1.9.18.1 martin CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP, CTL_EOL);
102 1.9.18.1 martin sysctl_createv(clog, 0, NULL, NULL,
103 1.9.18.1 martin CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
104 1.9.18.1 martin CTLTYPE_INT, "parsehttpversion",
105 1.9.18.1 martin SYSCTL_DESCR("Parse http version so that non "
106 1.9.18.1 martin "1.x requests work"),
107 1.9.18.1 martin NULL, 0, &parse_http_version, 0,
108 1.9.18.1 martin CTL_NET, PF_INET, SO_ACCEPTFILTER, ACCF_HTTP,
109 1.9.18.1 martin ACCFCTL_PARSEVER, CTL_EOL);
110 1.9.18.1 martin }
111 1.9.18.1 martin
112 1.3 ad static int
113 1.5 ad accf_httpready_modcmd(modcmd_t cmd, void *arg)
114 1.1 tls {
115 1.3 ad int error;
116 1.3 ad
117 1.3 ad switch (cmd) {
118 1.3 ad case MODULE_CMD_INIT:
119 1.3 ad error = accept_filt_add(&accf_http_filter);
120 1.3 ad return 0;
121 1.1 tls
122 1.3 ad case MODULE_CMD_FINI:
123 1.3 ad error = accept_filt_del(&accf_http_filter);
124 1.9.18.1 martin return error;
125 1.1 tls
126 1.3 ad default:
127 1.3 ad return ENOTTY;
128 1.3 ad }
129 1.1 tls }
130 1.1 tls
131 1.1 tls #ifdef ACCF_HTTP_DEBUG
132 1.1 tls #define DPRINT(fmt, args...) \
133 1.1 tls do { \
134 1.1 tls printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
135 1.1 tls } while (0)
136 1.1 tls #else
137 1.1 tls #define DPRINT(fmt, args...)
138 1.1 tls #endif
139 1.1 tls
140 1.1 tls static int
141 1.1 tls sbfull(struct sockbuf *sb)
142 1.1 tls {
143 1.1 tls
144 1.1 tls DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
145 1.1 tls "mbcnt(%ld) >= mbmax(%ld): %d",
146 1.1 tls sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
147 1.1 tls sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
148 1.1 tls return (sb->sb_cc >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
149 1.1 tls }
150 1.1 tls
151 1.1 tls /*
152 1.1 tls * start at mbuf m, (must provide npkt if exists)
153 1.1 tls * starting at offset in m compare characters in mbuf chain for 'cmp'
154 1.1 tls */
155 1.1 tls static int
156 1.1 tls mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, const char *cmp)
157 1.1 tls {
158 1.1 tls struct mbuf *n;
159 1.1 tls
160 1.1 tls for (; m != NULL; m = n) {
161 1.1 tls n = npkt;
162 1.1 tls if (npkt)
163 1.1 tls npkt = npkt->m_nextpkt;
164 1.1 tls for (; m; m = m->m_next) {
165 1.1 tls for (; offset < m->m_len; offset++, cmp++) {
166 1.1 tls if (*cmp == '\0')
167 1.1 tls return (1);
168 1.1 tls else if (*cmp != *(mtod(m, char *) + offset))
169 1.1 tls return (0);
170 1.1 tls }
171 1.1 tls if (*cmp == '\0')
172 1.1 tls return (1);
173 1.1 tls offset = 0;
174 1.1 tls }
175 1.1 tls }
176 1.1 tls return (0);
177 1.1 tls }
178 1.1 tls
179 1.1 tls /*
180 1.1 tls * start at mbuf m, (must provide npkt if exists)
181 1.1 tls * starting at offset in m compare characters in mbuf chain for 'cmp'
182 1.1 tls * stop at 'max' characters
183 1.1 tls */
184 1.1 tls static int
185 1.1 tls mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int len, const char *cmp)
186 1.1 tls {
187 1.1 tls struct mbuf *n;
188 1.1 tls
189 1.1 tls for (; m != NULL; m = n) {
190 1.1 tls n = npkt;
191 1.1 tls if (npkt)
192 1.1 tls npkt = npkt->m_nextpkt;
193 1.1 tls for (; m; m = m->m_next) {
194 1.1 tls for (; offset < m->m_len; offset++, cmp++, len--) {
195 1.1 tls if (len == 0 || *cmp == '\0')
196 1.1 tls return (1);
197 1.1 tls else if (*cmp != *(mtod(m, char *) + offset))
198 1.1 tls return (0);
199 1.1 tls }
200 1.1 tls if (len == 0 || *cmp == '\0')
201 1.1 tls return (1);
202 1.1 tls offset = 0;
203 1.1 tls }
204 1.1 tls }
205 1.1 tls return (0);
206 1.1 tls }
207 1.1 tls
208 1.1 tls #define STRSETUP(sptr, slen, str) \
209 1.1 tls do { \
210 1.1 tls sptr = str; \
211 1.1 tls slen = sizeof(str) - 1; \
212 1.1 tls } while(0)
213 1.1 tls
214 1.1 tls static void
215 1.7 tls sohashttpget(struct socket *so, void *arg, int events, int waitflag)
216 1.1 tls {
217 1.1 tls
218 1.1 tls if ((so->so_state & SS_CANTRCVMORE) == 0 && !sbfull(&so->so_rcv)) {
219 1.1 tls struct mbuf *m;
220 1.1 tls const char *cmp;
221 1.1 tls int cmplen, cc;
222 1.1 tls
223 1.1 tls m = so->so_rcv.sb_mb;
224 1.1 tls cc = so->so_rcv.sb_cc - 1;
225 1.1 tls if (cc < 1)
226 1.1 tls return;
227 1.1 tls switch (*mtod(m, char *)) {
228 1.1 tls case 'G':
229 1.1 tls STRSETUP(cmp, cmplen, "ET ");
230 1.1 tls break;
231 1.1 tls case 'H':
232 1.1 tls STRSETUP(cmp, cmplen, "EAD ");
233 1.1 tls break;
234 1.1 tls default:
235 1.1 tls goto fallout;
236 1.1 tls }
237 1.1 tls if (cc < cmplen) {
238 1.1 tls if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
239 1.1 tls DPRINT("short cc (%d) but mbufstrncmp ok", cc);
240 1.1 tls return;
241 1.1 tls } else {
242 1.1 tls DPRINT("short cc (%d) mbufstrncmp failed", cc);
243 1.1 tls goto fallout;
244 1.1 tls }
245 1.1 tls }
246 1.1 tls if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
247 1.1 tls DPRINT("mbufstrcmp ok");
248 1.1 tls if (parse_http_version == 0)
249 1.7 tls soishttpconnected(so, arg, events, waitflag);
250 1.1 tls else
251 1.7 tls soparsehttpvers(so, arg, events, waitflag);
252 1.1 tls return;
253 1.1 tls }
254 1.1 tls DPRINT("mbufstrcmp bad");
255 1.1 tls }
256 1.1 tls
257 1.1 tls fallout:
258 1.1 tls DPRINT("fallout");
259 1.1 tls so->so_upcall = NULL;
260 1.1 tls so->so_rcv.sb_flags &= ~SB_UPCALL;
261 1.1 tls soisconnected(so);
262 1.1 tls return;
263 1.1 tls }
264 1.1 tls
265 1.1 tls static void
266 1.7 tls soparsehttpvers(struct socket *so, void *arg, int events, int waitflag)
267 1.1 tls {
268 1.1 tls struct mbuf *m, *n;
269 1.1 tls int i, cc, spaces, inspaces;
270 1.1 tls
271 1.1 tls if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
272 1.1 tls goto fallout;
273 1.1 tls
274 1.1 tls m = so->so_rcv.sb_mb;
275 1.1 tls cc = so->so_rcv.sb_cc;
276 1.1 tls inspaces = spaces = 0;
277 1.1 tls for (m = so->so_rcv.sb_mb; m; m = n) {
278 1.1 tls n = m->m_nextpkt;
279 1.1 tls for (; m; m = m->m_next) {
280 1.1 tls for (i = 0; i < m->m_len; i++, cc--) {
281 1.1 tls switch (*(mtod(m, char *) + i)) {
282 1.1 tls case ' ':
283 1.1 tls /* tabs? '\t' */
284 1.1 tls if (!inspaces) {
285 1.1 tls spaces++;
286 1.1 tls inspaces = 1;
287 1.1 tls }
288 1.1 tls break;
289 1.1 tls case '\r':
290 1.1 tls case '\n':
291 1.1 tls DPRINT("newline");
292 1.1 tls goto fallout;
293 1.1 tls default:
294 1.1 tls if (spaces != 2) {
295 1.1 tls inspaces = 0;
296 1.1 tls break;
297 1.1 tls }
298 1.1 tls
299 1.1 tls /*
300 1.1 tls * if we don't have enough characters
301 1.1 tls * left (cc < sizeof("HTTP/1.0") - 1)
302 1.1 tls * then see if the remaining ones
303 1.1 tls * are a request we can parse.
304 1.1 tls */
305 1.1 tls if (cc < sizeof("HTTP/1.0") - 1) {
306 1.1 tls if (mbufstrncmp(m, n, i, cc,
307 1.1 tls "HTTP/1.") == 1) {
308 1.1 tls DPRINT("ok");
309 1.1 tls goto readmore;
310 1.1 tls } else {
311 1.1 tls DPRINT("bad");
312 1.1 tls goto fallout;
313 1.1 tls }
314 1.1 tls } else if (
315 1.1 tls mbufstrcmp(m, n, i, "HTTP/1.0") ||
316 1.1 tls mbufstrcmp(m, n, i, "HTTP/1.1")) {
317 1.6 joerg DPRINT("ok");
318 1.6 joerg soishttpconnected(so,
319 1.7 tls arg, events, waitflag);
320 1.6 joerg return;
321 1.1 tls } else {
322 1.1 tls DPRINT("bad");
323 1.1 tls goto fallout;
324 1.1 tls }
325 1.1 tls }
326 1.1 tls }
327 1.1 tls }
328 1.1 tls }
329 1.1 tls readmore:
330 1.1 tls DPRINT("readmore");
331 1.1 tls /*
332 1.1 tls * if we hit here we haven't hit something
333 1.1 tls * we don't understand or a newline, so try again
334 1.1 tls */
335 1.1 tls so->so_upcall = soparsehttpvers;
336 1.1 tls so->so_rcv.sb_flags |= SB_UPCALL;
337 1.1 tls return;
338 1.1 tls
339 1.1 tls fallout:
340 1.1 tls DPRINT("fallout");
341 1.1 tls so->so_upcall = NULL;
342 1.1 tls so->so_rcv.sb_flags &= ~SB_UPCALL;
343 1.1 tls soisconnected(so);
344 1.1 tls return;
345 1.1 tls }
346 1.1 tls
347 1.1 tls
348 1.1 tls #define NCHRS 3
349 1.1 tls
350 1.1 tls static void
351 1.7 tls soishttpconnected(struct socket *so, void *arg, int events, int waitflag)
352 1.1 tls {
353 1.1 tls char a, b, c;
354 1.1 tls struct mbuf *m, *n;
355 1.1 tls int ccleft, copied;
356 1.1 tls
357 1.1 tls DPRINT("start");
358 1.1 tls if ((so->so_state & SS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
359 1.1 tls goto gotit;
360 1.1 tls
361 1.1 tls /*
362 1.1 tls * Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
363 1.1 tls * copied - how much we've copied so far
364 1.1 tls * ccleft - how many bytes remaining in the socketbuffer
365 1.1 tls * just loop over the mbufs subtracting from 'ccleft' until we only
366 1.1 tls * have NCHRS left
367 1.1 tls */
368 1.1 tls copied = 0;
369 1.1 tls ccleft = so->so_rcv.sb_cc;
370 1.1 tls if (ccleft < NCHRS)
371 1.1 tls goto readmore;
372 1.1 tls a = b = c = '\0';
373 1.1 tls for (m = so->so_rcv.sb_mb; m; m = n) {
374 1.1 tls n = m->m_nextpkt;
375 1.1 tls for (; m; m = m->m_next) {
376 1.1 tls ccleft -= m->m_len;
377 1.1 tls if (ccleft <= NCHRS) {
378 1.1 tls char *src;
379 1.1 tls int tocopy;
380 1.1 tls
381 1.1 tls tocopy = (NCHRS - ccleft) - copied;
382 1.1 tls src = mtod(m, char *) + (m->m_len - tocopy);
383 1.1 tls
384 1.1 tls while (tocopy--) {
385 1.1 tls switch (copied++) {
386 1.1 tls case 0:
387 1.1 tls a = *src++;
388 1.1 tls break;
389 1.1 tls case 1:
390 1.1 tls b = *src++;
391 1.1 tls break;
392 1.1 tls case 2:
393 1.1 tls c = *src++;
394 1.1 tls break;
395 1.1 tls }
396 1.1 tls }
397 1.1 tls }
398 1.1 tls }
399 1.1 tls }
400 1.1 tls if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
401 1.1 tls /* we have all request headers */
402 1.1 tls goto gotit;
403 1.1 tls }
404 1.1 tls
405 1.1 tls readmore:
406 1.1 tls so->so_upcall = soishttpconnected;
407 1.1 tls so->so_rcv.sb_flags |= SB_UPCALL;
408 1.1 tls return;
409 1.1 tls
410 1.1 tls gotit:
411 1.1 tls so->so_upcall = NULL;
412 1.1 tls so->so_rcv.sb_flags &= ~SB_UPCALL;
413 1.1 tls soisconnected(so);
414 1.1 tls return;
415 1.1 tls }
416