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