Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/netinet/accf_http.c
103829 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2000 Paycounter, Inc.
5
* Author: Alfred Perlstein <[email protected]>, <[email protected]>
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
#define ACCEPT_FILTER_MOD
31
32
#include <sys/param.h>
33
#include <sys/kernel.h>
34
#include <sys/mbuf.h>
35
#include <sys/module.h>
36
#include <sys/signalvar.h>
37
#include <sys/sysctl.h>
38
#include <sys/socketvar.h>
39
40
/* check for GET/HEAD */
41
static int sohashttpget(struct socket *so, void *arg, int waitflag);
42
/* check for HTTP/1.0 or HTTP/1.1 */
43
static int soparsehttpvers(struct socket *so, void *arg, int waitflag);
44
/* check for end of HTTP/1.x request */
45
static int soishttpconnected(struct socket *so, void *arg, int waitflag);
46
/* strcmp on an mbuf chain */
47
static int mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp);
48
/* strncmp on an mbuf chain */
49
static int mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset,
50
int max, char *cmp);
51
/* socketbuffer is full */
52
static int sbfull(struct sockbuf *sb);
53
54
ACCEPT_FILTER_DEFINE(accf_http, "httpready", sohashttpget, NULL, NULL, 1);
55
56
static int parse_http_version = 1;
57
58
static SYSCTL_NODE(_net_inet_accf, OID_AUTO, http,
59
CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
60
"HTTP accept filter");
61
SYSCTL_INT(_net_inet_accf_http, OID_AUTO, parsehttpversion, CTLFLAG_RW,
62
&parse_http_version, 1,
63
"Parse http version so that non 1.x requests work");
64
65
#ifdef ACCF_HTTP_DEBUG
66
#define DPRINT(fmt, args...) \
67
do { \
68
printf("%s:%d: " fmt "\n", __func__, __LINE__, ##args); \
69
} while (0)
70
#else
71
#define DPRINT(fmt, args...)
72
#endif
73
74
static int
75
sbfull(struct sockbuf *sb)
76
{
77
78
DPRINT("sbfull, cc(%ld) >= hiwat(%ld): %d, "
79
"mbcnt(%ld) >= mbmax(%ld): %d",
80
sb->sb_cc, sb->sb_hiwat, sb->sb_cc >= sb->sb_hiwat,
81
sb->sb_mbcnt, sb->sb_mbmax, sb->sb_mbcnt >= sb->sb_mbmax);
82
return (sbused(sb) >= sb->sb_hiwat || sb->sb_mbcnt >= sb->sb_mbmax);
83
}
84
85
/*
86
* start at mbuf m, (must provide npkt if exists)
87
* starting at offset in m compare characters in mbuf chain for 'cmp'
88
*/
89
static int
90
mbufstrcmp(struct mbuf *m, struct mbuf *npkt, int offset, char *cmp)
91
{
92
struct mbuf *n;
93
94
for (; m != NULL; m = n) {
95
n = npkt;
96
if (npkt)
97
npkt = npkt->m_nextpkt;
98
for (; m; m = m->m_next) {
99
for (; offset < m->m_len; offset++, cmp++) {
100
if (*cmp == '\0')
101
return (1);
102
else if (*cmp != *(mtod(m, char *) + offset))
103
return (0);
104
}
105
if (*cmp == '\0')
106
return (1);
107
offset = 0;
108
}
109
}
110
return (0);
111
}
112
113
/*
114
* start at mbuf m, (must provide npkt if exists)
115
* starting at offset in m compare characters in mbuf chain for 'cmp'
116
* stop at 'max' characters
117
*/
118
static int
119
mbufstrncmp(struct mbuf *m, struct mbuf *npkt, int offset, int max, char *cmp)
120
{
121
struct mbuf *n;
122
123
for (; m != NULL; m = n) {
124
n = npkt;
125
if (npkt)
126
npkt = npkt->m_nextpkt;
127
for (; m; m = m->m_next) {
128
for (; offset < m->m_len; offset++, cmp++, max--) {
129
if (max == 0 || *cmp == '\0')
130
return (1);
131
else if (*cmp != *(mtod(m, char *) + offset))
132
return (0);
133
}
134
if (max == 0 || *cmp == '\0')
135
return (1);
136
offset = 0;
137
}
138
}
139
return (0);
140
}
141
142
#define STRSETUP(sptr, slen, str) \
143
do { \
144
sptr = str; \
145
slen = sizeof(str) - 1; \
146
} while(0)
147
148
static int
149
sohashttpget(struct socket *so, void *arg, int waitflag)
150
{
151
152
if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) == 0 &&
153
!sbfull(&so->so_rcv)) {
154
struct mbuf *m;
155
char *cmp;
156
int cmplen, cc;
157
158
m = so->so_rcv.sb_mb;
159
cc = sbavail(&so->so_rcv) - 1;
160
if (cc < 1)
161
return (SU_OK);
162
switch (*mtod(m, char *)) {
163
case 'G':
164
STRSETUP(cmp, cmplen, "ET ");
165
break;
166
case 'H':
167
STRSETUP(cmp, cmplen, "EAD ");
168
break;
169
default:
170
goto fallout;
171
}
172
if (cc < cmplen) {
173
if (mbufstrncmp(m, m->m_nextpkt, 1, cc, cmp) == 1) {
174
DPRINT("short cc (%d) but mbufstrncmp ok", cc);
175
return (SU_OK);
176
} else {
177
DPRINT("short cc (%d) mbufstrncmp failed", cc);
178
goto fallout;
179
}
180
}
181
if (mbufstrcmp(m, m->m_nextpkt, 1, cmp) == 1) {
182
DPRINT("mbufstrcmp ok");
183
if (parse_http_version == 0)
184
return (soishttpconnected(so, arg, waitflag));
185
else
186
return (soparsehttpvers(so, arg, waitflag));
187
}
188
DPRINT("mbufstrcmp bad");
189
}
190
191
fallout:
192
DPRINT("fallout");
193
return (SU_ISCONNECTED);
194
}
195
196
static int
197
soparsehttpvers(struct socket *so, void *arg, int waitflag)
198
{
199
struct mbuf *m, *n;
200
int i, cc, spaces, inspaces;
201
202
if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
203
goto fallout;
204
205
m = so->so_rcv.sb_mb;
206
cc = sbavail(&so->so_rcv);
207
inspaces = spaces = 0;
208
for (m = so->so_rcv.sb_mb; m; m = n) {
209
n = m->m_nextpkt;
210
for (; m; m = m->m_next) {
211
for (i = 0; i < m->m_len; i++, cc--) {
212
switch (*(mtod(m, char *) + i)) {
213
case ' ':
214
/* tabs? '\t' */
215
if (!inspaces) {
216
spaces++;
217
inspaces = 1;
218
}
219
break;
220
case '\r':
221
case '\n':
222
DPRINT("newline");
223
goto fallout;
224
default:
225
if (spaces != 2) {
226
inspaces = 0;
227
break;
228
}
229
230
/*
231
* if we don't have enough characters
232
* left (cc < sizeof("HTTP/1.0") - 1)
233
* then see if the remaining ones
234
* are a request we can parse.
235
*/
236
if (cc < sizeof("HTTP/1.0") - 1) {
237
if (mbufstrncmp(m, n, i, cc,
238
"HTTP/1.") == 1) {
239
DPRINT("ok");
240
goto readmore;
241
} else {
242
DPRINT("bad");
243
goto fallout;
244
}
245
} else if (
246
mbufstrcmp(m, n, i, "HTTP/1.0") ||
247
mbufstrcmp(m, n, i, "HTTP/1.1")) {
248
DPRINT("ok");
249
return (soishttpconnected(so,
250
arg, waitflag));
251
} else {
252
DPRINT("bad");
253
goto fallout;
254
}
255
}
256
}
257
}
258
}
259
readmore:
260
DPRINT("readmore");
261
/*
262
* if we hit here we haven't hit something
263
* we don't understand or a newline, so try again
264
*/
265
soupcall_set(so, SO_RCV, soparsehttpvers, arg);
266
return (SU_OK);
267
268
fallout:
269
DPRINT("fallout");
270
return (SU_ISCONNECTED);
271
}
272
273
#define NCHRS 3
274
275
static int
276
soishttpconnected(struct socket *so, void *arg, int waitflag)
277
{
278
char a, b, c;
279
struct mbuf *m, *n;
280
int ccleft, copied;
281
282
DPRINT("start");
283
if ((so->so_rcv.sb_state & SBS_CANTRCVMORE) != 0 || sbfull(&so->so_rcv))
284
goto gotit;
285
286
/*
287
* Walk the socketbuffer and copy the last NCHRS (3) into a, b, and c
288
* copied - how much we've copied so far
289
* ccleft - how many bytes remaining in the socketbuffer
290
* just loop over the mbufs subtracting from 'ccleft' until we only
291
* have NCHRS left
292
*/
293
copied = 0;
294
ccleft = sbavail(&so->so_rcv);
295
if (ccleft < NCHRS)
296
goto readmore;
297
a = b = c = '\0';
298
for (m = so->so_rcv.sb_mb; m; m = n) {
299
n = m->m_nextpkt;
300
for (; m; m = m->m_next) {
301
ccleft -= m->m_len;
302
if (ccleft <= NCHRS) {
303
char *src;
304
int tocopy;
305
306
tocopy = (NCHRS - ccleft) - copied;
307
src = mtod(m, char *) + (m->m_len - tocopy);
308
309
while (tocopy--) {
310
switch (copied++) {
311
case 0:
312
a = *src++;
313
break;
314
case 1:
315
b = *src++;
316
break;
317
case 2:
318
c = *src++;
319
break;
320
}
321
}
322
}
323
}
324
}
325
if (c == '\n' && (b == '\n' || (b == '\r' && a == '\n'))) {
326
/* we have all request headers */
327
goto gotit;
328
}
329
330
readmore:
331
soupcall_set(so, SO_RCV, soishttpconnected, arg);
332
return (SU_OK);
333
334
gotit:
335
return (SU_ISCONNECTED);
336
}
337
338