# $Id: conn.bro 6782 2009-06-28 02:19:03Z vern $ @load notice @load hot @load port-name @load netstats @load conn-id redef enum Notice += { SensitiveConnection, # connection marked "hot" }; const conn_closed = { TCP_CLOSED, TCP_RESET }; global have_FTP = F; # if true, we've loaded ftp.bro global have_SMTP = F; # if true, we've loaded smtp.bro # TODO: Do we have a nicer way of doing this? export { global FTP::is_ftp_data_conn: function(c: connection): bool; } # Whether to include connection state history in the logs generated # by record_connection. const record_state_history = F &redef; # Whether to translate the local address in SensitiveConnection notices # to a hostname. Meant as a demonstration of the "when" construct. const xlate_hot_local_addr = F &redef; # Whether to use DPD for generating the service field in the summaries. # Default off, because it changes the format of conn.log in a way # potentially incompatible with existing scripts. const dpd_conn_logs = F &redef; # Maps a given port on a given server's address to an RPC service. # If we haven't loaded portmapper.bro, then it will be empty # (and, ideally, queries to it would be optimized away ...). global RPC_server_map: table[addr, port] of string; const conn_file = open_log_file("conn") &redef; function conn_state(c: connection, trans: transport_proto): string { local os = c$orig$state; local rs = c$resp$state; local o_inactive = os == TCP_INACTIVE || os == TCP_PARTIAL; local r_inactive = rs == TCP_INACTIVE || rs == TCP_PARTIAL; if ( trans == tcp ) { if ( rs == TCP_RESET ) { if ( os == TCP_SYN_SENT || os == TCP_SYN_ACK_SENT || (os == TCP_RESET && c$orig$size == 0 && c$resp$size == 0) ) return "REJ"; else if ( o_inactive ) return "RSTRH"; else return "RSTR"; } else if ( os == TCP_RESET ) return r_inactive ? "RSTOS0" : "RSTO"; else if ( rs == TCP_CLOSED && os == TCP_CLOSED ) return "SF"; else if ( os == TCP_CLOSED ) return r_inactive ? "SH" : "S2"; else if ( rs == TCP_CLOSED ) return o_inactive ? "SHR" : "S3"; else if ( os == TCP_SYN_SENT && rs == TCP_INACTIVE ) return "S0"; else if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED ) return "S1"; else return "OTH"; } else if ( trans == udp ) { if ( os == UDP_ACTIVE ) return rs == UDP_ACTIVE ? "SF" : "S0"; else return rs == UDP_ACTIVE ? "SHR" : "OTH"; } else return "OTH"; } function conn_size(e: endpoint, trans: transport_proto): string { if ( e$size > 0 || (trans == tcp && e$state == TCP_CLOSED) ) return fmt("%d", e$size); else ### should return 0 for TCP_RESET that went through TCP_CLOSED return "?"; } function service_name(c: connection): string { local p = c$id$resp_p; if ( p in port_names ) return port_names[p]; else return "other"; } const state_graphic = { ["OTH"] = "?>?", ["REJ"] = "[", ["RSTO"] = ">]", ["RSTOS0"] = "}]", ["RSTR"] = ">[", ["RSTRH"] = "<[", ["S0"] = "}", ["S1"] = ">", ["S2"] = "}2", ["S3"] = "}3", ["SF"] = ">", ["SH"] = ">h", ["SHR"] = " 0 ) log_hot_conn(c); if ( trans == tcp ) { if ( c$orig$state in conn_closed || c$resp$state in conn_closed ) duration = fmt("%.06f", c$duration); else duration = "?"; } else duration = fmt("%.06f", c$duration); local addl = c$addl; @ifdef ( estimate_flow_size_and_remove ) # Annotate connection with separately-estimated size, if present. local orig_est = estimate_flow_size_and_remove(id, T); local resp_est = estimate_flow_size_and_remove(id, F); if ( orig_est$have_est ) addl = fmt("%s olower=%.0fMB oupper=%.0fMB oincon=%s", addl, orig_est$lower / 1e6, orig_est$upper / 1e6, orig_est$num_inconsistent); if ( resp_est$have_est ) addl = fmt("%s rlower=%.0fMB rupper=%.0fMB rincon=%s", addl, resp_est$lower / 1e6, resp_est$upper / 1e6, resp_est$num_inconsistent); @endif local service = determine_service(c); local log_msg = fmt("%.6f %s %s %s %s %d %d %s %s %s %s %s", c$start_time, duration, id$orig_h, id$resp_h, service, id$orig_p, id$resp_p, trans, conn_size(c$orig, trans), conn_size(c$resp, trans), conn_state(c, trans), flags); if ( record_state_history ) log_msg = fmt("%s %s", log_msg, c$history == "" ? "X" : c$history); if ( addl != "" ) log_msg = fmt("%s %s", log_msg, addl); print f, log_msg; } event connection_established(c: connection) { Hot::check_hot(c, Hot::CONN_ESTABLISHED); if ( c$hot > 0 ) log_hot_conn(c); } event partial_connection(c: connection) { if ( c$orig$state == TCP_PARTIAL && c$resp$state == TCP_INACTIVE ) # This appears to be a stealth scan. Don't do hot-checking # as there wasn't an established connection. ; else { Hot::check_hot(c, Hot::CONN_ESTABLISHED); Hot::check_hot(c, Hot::APPL_ESTABLISHED); # assume it's been established } if ( c$hot > 0 ) log_hot_conn(c); } event connection_attempt(c: connection) { Hot::check_spoof(c); Hot::check_hot(c, Hot::CONN_ATTEMPTED); } event connection_finished(c: connection) { if ( c$orig$size == 0 || c$resp$size == 0 ) # Hard to get excited about this - not worth logging again. c$hot = 0; else Hot::check_hot(c, Hot::CONN_FINISHED); } event connection_partial_close(c: connection) { if ( c$orig$size == 0 || c$resp$size == 0 ) # Hard to get excited about this - not worth logging again. c$hot = 0; else Hot::check_hot(c, Hot::CONN_FINISHED); } event connection_half_finished(c: connection) { Hot::check_hot(c, Hot::CONN_ATTEMPTED); } event connection_rejected(c: connection) { Hot::check_hot(c, Hot::CONN_REJECTED); } event connection_reset(c: connection) { Hot::check_hot(c, Hot::CONN_FINISHED); } event connection_pending(c: connection) { if ( c$orig$state in conn_closed && (c$resp$state == TCP_INACTIVE || c$resp$state == TCP_PARTIAL) ) # This is a stray FIN or RST - don't bother reporting. return; if ( c$orig$state == TCP_RESET || c$resp$state == TCP_RESET ) # We already reported this connection when the RST # occurred. return; Hot::check_hot(c, Hot::CONN_FINISHED); } function connection_gone(c: connection, gone_type: string) { if ( c$orig$size == 0 || c$resp$size == 0 ) { if ( c$orig$state == TCP_RESET && c$resp$state == TCP_INACTIVE) # A bare RST, no other context. Ignore it. return; # Hard to get excited about this - not worth logging again, # per connection_finished(). c$hot = 0; } else Hot::check_hot(c, Hot::CONN_TIMEOUT); } event connection_state_remove(c: connection) &priority = -10 { local os = c$orig$state; local rs = c$resp$state; if ( os == TCP_ESTABLISHED && rs == TCP_ESTABLISHED ) # It was still active, no summary generated. connection_gone(c, "remove"); else if ( (os == TCP_CLOSED || rs == TCP_CLOSED) && (os == TCP_ESTABLISHED || rs == TCP_ESTABLISHED) ) # One side has closed, the other hasn't - it's in state S2 # or S3, hasn't been reported yet. connection_gone(c, "remove"); record_connection(conn_file, c); delete hot_conns_reported[c$id]; }