* of the GNU General Public License.
*/
-#define LOCAL_DEBUG
+#undef LOCAL_DEBUG
#include "lib/lib.h"
#include "lib/pools.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <alloca.h>
#include <netinet/in.h>
#define MAXLINE 4096
struct http_state {
enum {
- HTTP_INIT, /* initialized, waiting for request */
+ HTTP_IDLE, /* initialized, waiting for request */
HTTP_ERROR, /* protocol error, ignoring everything else */
HTTP_CUT, /* unexpected EOF in one direction, ignoring everything else */
HTTP_REQUEST, /* parsing request */
HTTP_CONNECT, /* inside CONNECT transaction */
} state;
byte *error;
- u64 init_time;
+ u64 req_start_time, resp_start_time;
uns id;
struct mempool *pool;
list tx_queue, rx_queue;
uns body_trailer;
list *body_queue;
uns body_end_state;
+ uns body_total_size;
+ uns req_counter;
};
static void http_open(struct flow *f, u64 when)
static int http_counter;
struct http_state *s = xmalloc_zero(sizeof(*s));
f->appl_data = s;
- s->init_time = when;
s->id = http_counter++;
DBG("HTTP: %d NEW %d.%d.%d.%d:%d -> %d.%d.%d.%d:%d\n", s->id,
IPQUAD(f->saddr), ntohs(f->sport), IPQUAD(f->daddr), ntohs(f->dport));
list_init(&s->tx_queue);
list_init(&s->rx_queue);
+ s->req_start_time = when;
+}
+
+static byte *http_lookup_hdr(list *l, byte *name)
+{
+ struct http_header *h;
+ WALK_LIST(h, *l)
+ if (!strcasecmp(h->name, name))
+ return h->value;
+ return NULL;
+}
+
+static void http_report(struct flow *f, struct http_state *s, u64 when, byte *reason)
+{
+ byte *method, *url, *x, *y, *stat;
+ static uns http_counter;
+
+ if (!(method = s->req_line))
+ return;
+
+ /* Analyse request line */
+ url = method;
+ while (*url && *url != ' ')
+ url++;
+ while (*url == ' ')
+ *url++ = 0;
+ x = url;
+ while (*x != ' ')
+ x++;
+ *x = 0;
+
+ /* Analyse response line */
+ if (stat = s->resp_line)
+ {
+ while (*stat && *stat != ' ')
+ stat++;
+ while (*stat == ' ')
+ stat++;
+ x = stat;
+ while (*x && *x != ' ')
+ x++;
+ *x = 0;
+ }
+ else
+ stat = "";
+ if (!reason)
+ reason = stat[0] ? stat : (byte*)"???";
+
+ /* Reconstruct full URL */
+ if (!strstr(url, "://") && strcasecmp(method, "CONNECT"))
+ {
+ if (!(x = http_lookup_hdr(&s->req_headers, "Host:")))
+ x = "???";
+ y = url;
+ url = alloca(7 + strlen(x) + strlen(y) + 1);
+ sprintf(url, "http://%s%s", x, y);
+ }
+
+ byte stamp[TIMESTAMP_LEN], src[22], dst[22];
+ sprintf(src, "%d.%d.%d.%d:%d", IPQUAD(f->saddr), ntohs(f->sport));
+ sprintf(dst, "%d.%d.%d.%d:%d", IPQUAD(f->daddr), ntohs(f->dport));
+ format_timestamp(stamp, s->req_start_time);
+ u64 ttotal = when - s->req_start_time;
+ u64 tresp = (s->resp_line ? (s->resp_start_time - s->req_start_time) : 0);
+ if (!http_counter++)
+ printf("# timestamp source destination result que length total time wait time method URL\n");
+ /* 2003-06-06 22:53:38.642 81.27.194.19:1175 205.217.153.53:80 200 0 14030 0.957 0.444 GET http://... */
+ printf("%s %-21s %-21s %-3s %3d %8d %6d.%03d %6d.%03d %s %s\n",
+ stamp, src, dst, reason,
+ s->req_counter,
+ s->body_total_size,
+ (uns)(ttotal/1000000), (uns)(ttotal%1000000)/1000,
+ (uns)(tresp/1000000), (uns)(tresp%1000000)/1000,
+ method, url);
+
+ s->req_counter++;
}
static void http_close(struct flow *f, int cause, u64 when)
{
struct http_state *s = f->appl_data;
DBG("HTTP: %d CLOSE in state %d (cause %d)\n", s->id, s->state, cause);
+ if (cause != CAUSE_CLOSE)
+ {
+ if (s->state != HTTP_IDLE)
+ {
+ byte buf[16];
+ sprintf(buf, "T%s", flow_cause_names_short[cause]);
+ http_report(f, s, when, buf);
+ }
+ }
+ else
+ switch (s->state)
+ {
+ case HTTP_ERROR:
+ http_report(f, s, when, "ERR");
+ break;
+ case HTTP_CUT:
+ http_report(f, s, when, "CUT");
+ break;
+ case HTTP_CONNECT:
+ http_report(f, s, when, "FIN");
+ break;
+ }
pkt_flush_queue(&s->rx_queue);
pkt_flush_queue(&s->tx_queue);
if (s->pool)
}
}
-static int http_skip_bytes(list *l, uns *pcnt)
+static int http_skip_body_bytes(struct http_state *s)
{
for(;;)
{
- struct pkt *p = list_head(l);
+ struct pkt *p = list_head(s->body_queue);
if (!p)
return 0;
uns avail = pkt_len(p);
- uns want = *pcnt;
+ uns want = s->body_len;
uns go = MIN(avail, want);
p->data += go;
- *pcnt -= go;
- if (!*pcnt)
+ s->body_len -= go;
+ s->body_total_size += go;
+ if (!s->body_len)
return 1;
if (!pkt_len(p))
{
s->pool = mp_new(4096);
s->req_line = s->resp_line = NULL;
s->line_len = 0;
+ s->body_total_size = 0;
}
static void http_parse_hdr(list *l, struct http_header *h)
list_add_tail(l, &h->n);
}
-static byte *http_lookup_hdr(list *l, byte *name)
-{
- struct http_header *h;
- WALK_LIST(h, *l)
- if (!strcasecmp(h->name, name))
- return h->value;
- return NULL;
-}
-
static int http_ask_body(struct http_state *s, list *hdr)
{
byte *x;
static void http_parse_resp(struct http_state *s)
{
- if (http_ask_body(s, &s->resp_headers))
+ if (!strncasecmp(s->req_line, "HEAD ", 5))
+ {
+ DBG("\tHEAD has no body :)\n");
+ s->state = HTTP_DONE;
+ }
+ else if (http_ask_body(s, &s->resp_headers))
;
else if (!strncasecmp(s->req_line, "GET ", 4) && strstr(s->resp_line, " 200 "))
{
DBG("HTTP: %d STATE %d\n", s->id, s->state);
switch (s->state)
{
- case HTTP_INIT:
+ case HTTP_IDLE:
if (fin_tx || !http_have_input(&s->tx_queue))
return;
s->state = HTTP_REQUEST;
http_init_xact(s);
+ if (!s->req_start_time)
+ s->req_start_time = p->timestamp;
break;
case HTTP_REQUEST:
if (fin_tx || fin_rx)
case HTTP_BODY_LENGTH:
if (fin_rx)
goto cut;
- if (!http_skip_bytes(s->body_queue, &s->body_len))
+ if (!http_skip_body_bytes(s))
return;
DBG("\tEnd of body\n");
s->state = s->body_end_state;
goto cut;
if (s->body_len)
{
- if (!http_skip_bytes(s->body_queue, &s->body_len))
+ if (!http_skip_body_bytes(s))
return;
}
else if (s->body_trailer)
break;
case HTTP_BODY_INF:
s->body_len = ~0U;
- http_skip_bytes(s->body_queue, &s->body_len);
+ http_skip_body_bytes(s);
if (fin_rx)
{
DBG("\tEnd of FIN-delimited body\n");
if (!h->buf[0])
goto err;
s->resp_line = h->buf;
+ s->resp_start_time = p->timestamp;
}
else if (h->buf[0])
http_parse_hdr(&s->resp_headers, h);
s->state = HTTP_CONNECT;
return;
}
- s->state = HTTP_INIT;
+ http_report(f, s, p->timestamp, NULL);
+ s->state = HTTP_IDLE;
+ s->req_start_time = 0;
break;
case HTTP_CONNECT:
s->body_len = ~0U;
- http_skip_bytes(&s->rx_queue, &s->body_len);
+ s->body_queue = &s->rx_queue;
+ http_skip_body_bytes(s);
s->body_len = ~0U;
- http_skip_bytes(&s->tx_queue, &s->body_len);
+ s->body_queue = &s->tx_queue;
+ http_skip_body_bytes(s);
return;
case HTTP_ERROR:
case HTTP_CUT:
static void asave_close(struct flow *f, int cause, u64 when)
{
struct asave_state *s = f->appl_data;
- static byte *close_reasons[] = { "Close", "Connection reset", "Timeout", "Doomsday", "Corrupted" };
- asave_event(s, when, "Terminated: %s\n", close_reasons[cause]);
+ asave_event(s, when, "Terminated: %s\n", flow_cause_names[cause]);
asave_event(s, when, "TX %Ld bytes in %Ld packets, RX %Ld bytes in %Ld packets\n",
f->pipe[1].stat.bytes, f->pipe[1].stat.packets,
f->pipe[0].stat.bytes, f->pipe[0].stat.packets);
u64 duration = when - s->estab_time;
byte src[22], dst[22];
byte stamp[TIMESTAMP_LEN];
- static byte *sum_causes[] = { "OK", "RE", "TO", "DD", "CO" };
format_timestamp(stamp, s->estab_time);
sprintf(src, "%d.%d.%d.%d:%d", IPQUAD(f->saddr), ntohs(f->sport));
sprintf(dst, "%d.%d.%d.%d:%d", IPQUAD(f->daddr), ntohs(f->dport));
double avs = duration ? ((beff / ((double)duration/1000000)) / 1024) : 1e30;
printf("%s %-21s %-21s %6d.%03d %s %7Ld %7Ld %3d%% ",
stamp, src, dst,
- (uns)(duration/1000000), (uns)(duration%1000000)/1000, sum_causes[cause],
+ (uns)(duration/1000000), (uns)(duration%1000000)/1000, flow_cause_names_short[cause],
f->pipe[1].stat.bytes, f->pipe[0].stat.bytes,
CLAMP((int)over,0,100));
printf((avs < 100) ? "%6.3f" : "%6.0f", MIN(avs, 999999));