2 * Netgrind -- HTTP Analyser
4 * (c) 2003 Martin Mares <mj@ucw.cz>
6 * This software may be freely distributed and used according to the terms
7 * of the GNU General Public License.
13 #include "lib/pools.h"
14 #include "netgrind/pkt.h"
15 #include "netgrind/netgrind.h"
20 #include <netinet/in.h>
32 HTTP_INIT, /* initialized, waiting for request */
33 HTTP_ERROR, /* protocol error, ignoring everything else */
34 HTTP_CUT, /* unexpected EOF in one direction, ignoring everything else */
35 HTTP_REQUEST, /* parsing request */
36 HTTP_BODY_CHUNKED, /* receiving body: chunked encoding */
37 HTTP_BODY_LENGTH, /* receiving body: length given */
38 HTTP_BODY_INF, /* receiving body: till EOF */
39 HTTP_RESPONSE, /* parsing response */
40 HTTP_DONE, /* transaction finished, logging it */
41 HTTP_CONNECT, /* inside CONNECT transaction */
47 list tx_queue, rx_queue;
48 byte *req_line, *resp_line;
49 list req_headers, resp_headers;
58 static void http_open(struct flow *f, u64 when)
60 static int http_counter;
61 struct http_state *s = xmalloc_zero(sizeof(*s));
64 s->id = http_counter++;
65 DBG("HTTP: %d NEW %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", s->id,
66 IPQUAD(f->saddr), ntohs(f->sport), IPQUAD(f->daddr), ntohs(f->dport));
67 list_init(&s->tx_queue);
68 list_init(&s->rx_queue);
71 static void http_close(struct flow *f, int cause, u64 when)
73 struct http_state *s = f->appl_data;
74 DBG("HTTP: %d CLOSE in state %d (cause %d)\n", s->id, s->state, cause);
75 pkt_flush_queue(&s->rx_queue);
76 pkt_flush_queue(&s->tx_queue);
82 static struct http_header *http_get_line(struct http_state *s, list *l)
86 struct pkt *p = list_head(l);
89 while (p->data < p->stop)
96 struct http_header *h = mp_alloc(s->pool, sizeof(*h) + s->line_len);
97 memcpy(h->buf, s->line, s->line_len);
98 h->buf[s->line_len] = 0;
99 h->name = h->value = NULL;
103 else if (s->line_len >= MAXLINE-1)
105 DBG("HTTP: Line too long!\n");
106 s->state = HTTP_ERROR;
110 s->line[s->line_len++] = c;
117 static int http_skip_bytes(list *l, uns *pcnt)
121 struct pkt *p = list_head(l);
124 uns avail = pkt_len(p);
126 uns go = MIN(avail, want);
139 static int http_have_input(list *l)
143 struct pkt *p = list_head(l);
153 static void http_init_xact(struct http_state *s)
155 list_init(&s->req_headers);
156 list_init(&s->resp_headers);
160 s->pool = mp_new(4096);
161 s->req_line = s->resp_line = NULL;
165 static void http_parse_hdr(list *l, struct http_header *h)
169 while (*x && *x != ' ' && *x != '\t')
171 while (*x == ' ' || *x == '\t')
174 list_add_tail(l, &h->n);
177 static byte *http_lookup_hdr(list *l, byte *name)
179 struct http_header *h;
181 if (!strcasecmp(h->name, name))
186 static int http_ask_body(struct http_state *s, list *hdr)
189 if (x = http_lookup_hdr(hdr, "Transfer-Encoding:"))
191 DBG("\tBody encoding: %s\n", x);
192 if (!strcasecmp(x, "chunked"))
194 s->state = HTTP_BODY_CHUNKED;
199 s->state = HTTP_ERROR;
201 else if (x = http_lookup_hdr(hdr, "Content-Length:"))
203 s->body_len = atol(x);
204 DBG("\tBody length: %d\n", s->body_len);
205 s->state = HTTP_BODY_LENGTH;
212 static void http_parse_req(struct http_state *s)
214 if (!strstr(s->req_line, " HTTP/1"))
216 DBG("\tNot a HTTP/1.x request!\n");
217 s->state = HTTP_ERROR;
219 else if (http_ask_body(s, &s->req_headers))
221 else if (!strncasecmp(s->req_line, "POST ", 4))
223 DBG("\tPOST with no request body, that smells!\n");
224 s->state = HTTP_BODY_INF;
228 DBG("\tNo request body, awaiting reply\n");
229 s->state = HTTP_RESPONSE;
231 s->body_queue = &s->tx_queue;
232 s->body_end_state = HTTP_RESPONSE;
235 static void http_parse_resp(struct http_state *s)
237 if (http_ask_body(s, &s->resp_headers))
239 else if (!strncasecmp(s->req_line, "GET ", 4) && strstr(s->resp_line, " 200 "))
241 DBG("\tGET with no response body, that smells!\n");
242 s->state = HTTP_BODY_INF;
246 DBG("\tNo response body\n");
247 s->state = HTTP_DONE;
249 s->body_queue = &s->rx_queue;
250 s->body_end_state = HTTP_DONE;
253 static void http_input(struct flow *f, int dir, struct pkt *p)
255 struct http_state *s = f->appl_data;
256 struct http_header *h;
257 int fin_tx = (f->pipe[0].state == FLOW_FINISHED);
258 int fin_rx = (f->pipe[1].state == FLOW_FINISHED);
260 // DBG("dir=%d txf=%d rxf=%d len=%d\n", dir, fin_tx, fin_rx, pkt_len(p));
261 if (s->state == HTTP_ERROR || s->state == HTTP_CUT)
263 DBG("HTTP: %d DROPPING INPUT\n", s->id);
268 list_add_tail((dir ? &s->tx_queue : &s->rx_queue), &p->n);
271 DBG("HTTP: %d STATE %d\n", s->id, s->state);
275 if (fin_tx || !http_have_input(&s->tx_queue))
277 s->state = HTTP_REQUEST;
281 if (fin_tx || fin_rx)
283 if (!(h = http_get_line(s, &s->tx_queue)))
285 DBG("\t>> %s\n", h->buf);
290 s->req_line = h->buf;
293 http_parse_hdr(&s->req_headers, h);
297 case HTTP_BODY_LENGTH:
300 if (!http_skip_bytes(s->body_queue, &s->body_len))
302 DBG("\tEnd of body\n");
303 s->state = s->body_end_state;
305 case HTTP_BODY_CHUNKED:
310 if (!http_skip_bytes(s->body_queue, &s->body_len))
313 else if (s->body_trailer)
315 if (!(h = http_get_line(s, s->body_queue)))
319 DBG("\tEnd of chunk-encoded body\n");
320 s->state = s->body_end_state;
325 if (!(h = http_get_line(s, s->body_queue)))
327 if (sscanf(h->buf, "%x", &s->body_len) != 1)
330 s->body_len += 2; /* extra CRLF */
331 else /* last chunk */
337 http_skip_bytes(s->body_queue, &s->body_len);
340 DBG("\tEnd of FIN-delimited body\n");
341 s->state = s->body_end_state;
349 if (!(h = http_get_line(s, &s->rx_queue)))
351 DBG("\t<< %s\n", h->buf);
356 s->resp_line = h->buf;
359 http_parse_hdr(&s->resp_headers, h);
364 DBG("\tTransaction finished.\n");
365 if (!strncasecmp(s->req_line, "CONNECT ", 8))
367 s->state = HTTP_CONNECT;
370 s->state = HTTP_INIT;
374 http_skip_bytes(&s->rx_queue, &s->body_len);
376 http_skip_bytes(&s->tx_queue, &s->body_len);
387 DBG("HTTP: %d ERROR: PROTOCOL VIOLATION\n", s->id);
388 s->state = HTTP_ERROR;
392 DBG("HTTP: %d ERROR: UNEXPECTED EOF\n", s->id);
396 struct appl_hooks appl_http = {