mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00

The expire timeout for the http_sessions table is unnecessary and it actually breaks http session semantics for long-lived sessions. The connection_state_remove() event can take care of cleaning up unanswered sessions. If a HTTP transfer exceeds the expire timer, then once the expire timer fires we get an "unanswered" HTTP request in http.log and once the reply is done (http_reply_done event), it fails to locate the associated request (because it expired) and thus results in an "unsolicited" HTTP reply being logged (althoug they should be one http session). There was a comment in the expire_function mentioning that without the expire timer some requests don't show up with the test-suite. However, after checking back with Robin, I could not reproduce this behavior. (Actually there's one fewer request in the output without the expire-timer, but this can be explained by the above observation, so this is not an error but the way it should be). This patch results in changes to test-suite output: * Timestamps for unanswered HTTP replies differ for unanswered request in the "short" test. * Medium testcase (note: lines are sorted, they are not in the order):: -902189670.828700 <unknown request> (0 "" [40880 (interrupted)]) -902189670.828700 GET /1998/b142.ps <no reply> -902189670.828700 start <<IP>>:<<port>> <<IP>>:80 +902189670.828700 GET /1998/b142.ps (200 "OK" [40880 (interrupted)] <<a.host.name>>)
202 lines
4.8 KiB
Text
202 lines
4.8 KiB
Text
# $Id: http.bro 6726 2009-06-07 22:09:55Z vern $
|
|
|
|
@load notice
|
|
@load site
|
|
@load conn-id
|
|
|
|
module HTTP;
|
|
|
|
export {
|
|
redef enum Notice += {
|
|
HTTP_SensitiveURI, # sensitive URI in GET/POST/HEAD
|
|
};
|
|
}
|
|
|
|
# DPM configuration.
|
|
global http_ports = {
|
|
80/tcp, 81/tcp, 631/tcp, 1080/tcp, 3138/tcp,
|
|
8000/tcp, 8080/tcp, 8888/tcp,
|
|
};
|
|
redef dpd_config += { [ANALYZER_HTTP] = [$ports = http_ports] };
|
|
redef dpd_config += { [ANALYZER_HTTP_BINPAC] = [$ports = http_ports] };
|
|
|
|
# HTTP processing options.
|
|
export {
|
|
const process_HTTP_replies = F &redef;
|
|
const process_HTTP_data = F &redef;
|
|
const include_HTTP_abstract = F &redef;
|
|
const log_HTTP_data = F &redef;
|
|
}
|
|
|
|
type http_pending_request: record {
|
|
method: string;
|
|
URI: string;
|
|
log_it: bool;
|
|
|
|
# Whether we determined it's an attempted passwd file fetch.
|
|
passwd_req: bool;
|
|
};
|
|
|
|
# Eventually we will combine http_pending_request and http_message.
|
|
|
|
type http_message: record {
|
|
initiated: bool;
|
|
code: count; # for HTTP reply message
|
|
reason: string; # for HTTP reply message
|
|
entity_level: count; # depth of enclosing MIME entities
|
|
data_length: count; # actual length of data delivered
|
|
content_length: string; # length specified in CONTENT-LENGTH header
|
|
header_slot: count; # rewrite slot at the end of headers
|
|
abstract: string; # data abstract
|
|
skip_abstract: bool; # to skip abstract for certain content types
|
|
host: string; # host indicated in Host header
|
|
};
|
|
|
|
type http_pending_request_stream: record {
|
|
# Number of first pending request.
|
|
first_pending_request: count &default = 0;
|
|
|
|
# Total number of pending requests.
|
|
num_pending_requests: count &default = 0;
|
|
|
|
# Indexed from [first_pending_request ..
|
|
# (first_pending_request + num_pending_requests - 1)]
|
|
requests: table[count] of http_pending_request;
|
|
|
|
next_request: http_message; # the on-going request
|
|
next_reply: http_message; # the on-going reply
|
|
|
|
# len_next_reply: count; # 0 means unspecified
|
|
# len_next_request: count;
|
|
|
|
id: string; # repeated from http_session_info, for convenience
|
|
};
|
|
|
|
type http_session_info: record {
|
|
id: string;
|
|
request_stream: http_pending_request_stream;
|
|
};
|
|
|
|
const http_log = open_log_file("http") &redef;
|
|
|
|
# Indexed by conn_id.
|
|
global http_sessions: table[conn_id] of http_session_info;
|
|
|
|
global http_session_id = 0;
|
|
|
|
function init_http_message(msg: http_message)
|
|
{
|
|
msg$initiated = F;
|
|
msg$code = 0;
|
|
msg$reason = "";
|
|
msg$entity_level = 0;
|
|
msg$data_length = 0;
|
|
msg$content_length = "";
|
|
msg$header_slot = 0;
|
|
msg$abstract = "";
|
|
msg$skip_abstract = F;
|
|
msg$host = "";
|
|
}
|
|
|
|
function new_http_message(): http_message
|
|
{
|
|
local msg: http_message;
|
|
init_http_message(msg);
|
|
return msg;
|
|
}
|
|
|
|
function new_http_session(c: connection): http_session_info
|
|
{
|
|
local session = c$id;
|
|
local new_id = ++http_session_id;
|
|
|
|
local info: http_session_info;
|
|
info$id = fmt("%%%s", prefixed_id(new_id));
|
|
|
|
local rs: http_pending_request_stream;
|
|
|
|
rs$first_pending_request = 1;
|
|
rs$num_pending_requests = 0;
|
|
rs$id = info$id;
|
|
|
|
rs$next_request = new_http_message();
|
|
rs$next_reply = new_http_message();
|
|
rs$requests = table();
|
|
|
|
info$request_stream = rs;
|
|
|
|
http_sessions[session] = info;
|
|
|
|
print http_log, fmt("%.6f %s start %s:%d > %s:%d", network_time(),
|
|
info$id, c$id$orig_h,
|
|
c$id$orig_p, c$id$resp_h, c$id$resp_p);
|
|
|
|
return info;
|
|
}
|
|
|
|
function lookup_http_session(c: connection): http_session_info
|
|
{
|
|
local s: http_session_info;
|
|
local id = c$id;
|
|
|
|
s = id in http_sessions ? http_sessions[id] : new_http_session(c);
|
|
|
|
append_addl(c, s$id);
|
|
|
|
return s;
|
|
}
|
|
|
|
function lookup_http_request_stream(c: connection): http_pending_request_stream
|
|
{
|
|
local s = lookup_http_session(c);
|
|
|
|
return s$request_stream;
|
|
}
|
|
|
|
function get_http_message(s: http_pending_request_stream, is_orig: bool): http_message
|
|
{
|
|
return is_orig ? s$next_request : s$next_reply;
|
|
}
|
|
|
|
function finish_stream(session: conn_id, id: string,
|
|
rs: http_pending_request_stream)
|
|
{
|
|
### We really want to do this in sequential order, not table order.
|
|
for ( i in rs$requests )
|
|
{
|
|
local req = rs$requests[i];
|
|
|
|
if ( req$log_it )
|
|
NOTICE([$note=HTTP_SensitiveURI,
|
|
$src=session$orig_h, $dst=session$resp_h,
|
|
$URL=req$URI,
|
|
$method=req$method,
|
|
$msg=fmt("%s:%d -> %s:%d %s: <no reply>",
|
|
session$orig_h, session$orig_p,
|
|
session$resp_h, session$resp_p, id)]);
|
|
|
|
local msg = fmt("%s %s <no reply>", req$method, req$URI);
|
|
print http_log, fmt("%.6f %s %s", network_time(), rs$id, msg);
|
|
}
|
|
}
|
|
|
|
event connection_state_remove(c: connection)
|
|
{
|
|
local id = c$id;
|
|
|
|
if ( id !in http_sessions )
|
|
return;
|
|
|
|
local s = http_sessions[id];
|
|
finish_stream(id, s$id, s$request_stream);
|
|
delete http_sessions[c$id];
|
|
}
|
|
|
|
|
|
# event http_stats(c: connection, stats: http_stats_rec)
|
|
# {
|
|
# if ( stats$num_requests == 0 && stats$num_replies == 0 )
|
|
# return;
|
|
#
|
|
# c$addl = fmt("%s (%d v%.1f v%.1f)", c$addl, stats$num_requests, stats$request_version, stats$reply_version);
|
|
# }
|