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