# $Id: bro.init,v 1.142 2003/09/04 06:02:41 vern Exp $

global bro_signal: event(signal: count);

# Called (one day) if there's no handler for an internal event.
global no_handler: event(name: string, val: any);

# Type declarations
type string_array: table[count] of string;
type string_set: set[string];

type conn_id: record {
	orig_h: addr;
	orig_p: port;
	resp_h: addr;
	resp_p: port;
};

type icmp_conn: record {
	orig_h: addr;
	resp_h: addr;
	itype: count;
	icode: count;
};

type icmp_hdr: record {
	icmp_type: count;	# type of message
};

type icmp_context: record {
	id: conn_id;
	len: count;
	proto: count;
	frag_offset: count;
	bad_hdr_len: bool;
	bad_checksum: bool;
	MF: bool;
	DF: bool;
};

type addr_set: set[addr];

type dns_mapping: record {
	creation_time: time;

	req_host: string;
	req_addr: addr;

	valid: bool;
	hostname: string;
	addrs: addr_set;
};

type ftp_port: record {
	h: addr;
	p: port;
	valid: bool;	# true if format was right
};

type endpoint: record {
	size: count;
	state: count;
};

type endpoint_stats: record {
	num_pkts: count;
	num_rxmit: count;
	num_rxmit_bytes: count;
	num_in_order: count;
	num_OO: count;
	num_repl: count;
	endian_type: count;
};

type connection: record {
	id: conn_id;
	orig: endpoint;
	resp: endpoint;
	start_time: time;
	duration: interval;
	service: string;	# if empty, service hasn't been determined
	addl: string;
	hot: count;		# how hot; 0 = don't know or not hot
};

type SYN_packet: record {
	is_orig: bool;
	DF: bool;
	ttl: count;
	size: count;
	win_size: count;
	win_scale: int;
	MSS: count;
	SACK_OK: bool;
	NOP: count;
};

type bro_resources: record {
	real_time: interval;	# elapsed real time since Bro started running
	user_time: interval;	# user CPU seconds
	system_time: interval;	# system CPU seconds
	mem: count;		# maximum memory consumed, in KB
	minor_faults: count;	# page faults not requiring actual I/O
	major_faults: count;	# page faults requiring actual I/O
	num_swap: count;	# times swapped out
	blocking_input: count;	# blocking input operations
	blocking_output: count;	# blocking output operations
	num_context: count;	# number of involuntary context switches

	num_TCP_conns: count;	# current number of TCP connections
	num_UDP_conns: count;
	num_ICMP_conns: count;
	num_fragments: count;	# current number of fragments pending reassembly
	num_timers: count;	# current number of pending timers

	max_TCP_conns: count;	# maximum number of TCP connections, etc.
	max_UDP_conns: count;
	max_ICMP_conns: count;
	max_fragments: count;
	max_timers: count;
};


# Summary statistics of all DFA_State_Caches.
type matcher_stats: record {
	matchers: count;	# number of distinct RE matchers
	states: count;		# number of DFA states across all matchers
	computed: count;	# number of computed DFA state transitions
	mem: count;		# number of bytes used by DFA states
	hits: count;		# number of cache hits
	misses: count;		# number of cache misses
};

# This record should be read-only.
type packet: record {
	conn: connection;
	is_orig: bool;
	seq: count;	# seq=k => it is the kth *packet* of the connection
	timestamp: time;
};

type var_sizes: table[string] of count;	# indexed by var's name, returns size

# {precompile,install}_pcap_filter identify the filter by IDs
type PcapFilterID: enum { None };

type IPAddrAnonymization: enum {
	KEEP_ORIG_ADDR,
	SEQUENTIALLY_NUMBERED,
	RANDOM_MD5,
	PREFIX_PRESERVING_A50,
	PREFIX_PRESERVING_MD5,
};

# Prototypes of Bro built-in functions
@load bro.bif.bro

global bro_log_file: file &redef;
global log_hook: function(msg: string): bool &redef;
global log_file_name: function(tag: string): string &redef;
global open_log_file: function(tag: string): file &redef;

# Where to store the persistent state.
const state_file = "state.bst" &redef;


global done_with_network = F;
event net_done(t: time) { done_with_network = T; }

const SIGHUP = 1;
event bro_signal(signal: count)
	{
	if ( signal == SIGHUP )
		flush_all();
	}

function log_file_name(tag: string): string
	{
	local suffix = getenv("BRO_ID") == "" ? "log" : getenv("BRO_ID");
	return fmt("%s.%s", tag, suffix);
	}

function open_log_file(tag: string): file
	{
	return open(log_file_name(tag));
	}

function add_interface(iold: string, inew: string): string
	{
	if ( iold == "" )
		return inew;
	else
		return fmt("%s %s", iold, inew);
	}

global interfaces = "" &add_func = add_interface;

function add_signature_file(sold: string, snew: string): string
	{
	if ( sold == "" )
		return snew;
	else
		return cat(sold, " ", snew);
	}

global signature_files = "" &add_func = add_signature_file;

event no_handler_default(name: string, val: any)
	{
	print "no handler for event: ", name;
	print "value: ", val;
	}

const ftp = 21/tcp;
const ssh = 22/tcp;
const telnet = 23/tcp;
const smtp = 25/tcp;
const domain = 53/tcp;	# note, doesn't include UDP version
const gopher = 70/tcp;
const finger = 79/tcp;
const http = 80/tcp;
const ident = 113/tcp;
const bgp = 179/tcp;
const rlogin = 513/tcp;

const TCP_INACTIVE = 0;
const TCP_SYN_SENT = 1;
const TCP_SYN_ACK_SENT = 2;
const TCP_PARTIAL = 3;
const TCP_ESTABLISHED = 4;
const TCP_CLOSED = 5;
const TCP_RESET = 6;

# If true, don't verify checksums.  Useful for running on altered trace
# files, and for saving a few cycles, but of course dangerous, too ...
# Note that the -C command-line option overrides the setting of this
# variable.
const ignore_checksums = F &redef;

# If true, instantiate connection state when a partial connection
# (one missing its initial establishment negotiation) is seen.
const partial_connection_ok = T &redef;

# If true, instantiate connection state when a SYN ack is seen
# but not the initial SYN (even if partial_connection_ok is false).
const tcp_SYN_ack_ok = T &redef;

# If a connection state is removed there may still be some undelivered
# data waiting in the reassembler. If true, pass this to the signature
# engine before flushing the state.
const tcp_match_undelivered = T &redef;

# Check up on the result of an initial SYN after this much time.
const tcp_SYN_timeout = 120 secs &redef;

# After a connection has closed, wait this long for further activity
# before checking whether to time out its state.
const tcp_session_timer = 30 secs &redef;

# When checking a closed connection for further activity, consider it
# inactive if there hasn't been any for this long.  Complain if the
# connection is reused before this much time has elapsed.
const tcp_connection_linger = 30 secs &redef;

# Wait this long upon seeing an initial SYN before timing out the
# connection attempt.
const tcp_attempt_delay = 300 secs &redef;

# Upon seeing a normal connection close, flush state after this much time.
const tcp_close_delay = 15 secs &redef;

# Upon seeing a RST, flush state after this much time.
const tcp_reset_delay = 15 secs &redef;

# Generate a connection_partial_close event this much time after one half
# of a partial connection closes, assuming there has been no subsequent
# activity.
const tcp_partial_close_delay = 10 secs &redef;

# If a connection belongs to an application that we don't analyze,
# time it out after this interval.  If 0 secs, then don't time it out.
const non_analyzed_lifetime = 0 secs &redef;

# If a connection is inactive, time it out after this interval.
# If 0 secs, then don't time it out.
const inactivity_timeout = 0 secs &redef;

# This many FINs/RSTs in a row constitutes a "storm".
const tcp_storm_thresh = 1000 &redef;

# The FINs/RSTs must come with this much time or less between them.
const tcp_storm_interarrival_thresh = 1 sec &redef;

# For services without a handler, these sets define which
# side of a connection is to be reassembled
const tcp_reassembler_ports_orig: set[port] = {} &redef;
const tcp_reassembler_ports_resp: set[port] = {} &redef;

# Check for expired table entries after this amount of time
const table_expire_interval = 1 min &redef;

# Time to wait before timing out a DNS/NTP/RPC request.
const dns_session_timeout = 10 sec &redef;
const ntp_session_timeout = 300 sec &redef;
const rpc_timeout = 24 sec &redef;

# Time window for reordering packets (to deal with timestamp
# discrepency between multiple packet sources).
const packet_sort_window = 0 usecs &redef;


const UDP_INACTIVE = 0;
const UDP_ACTIVE = 1;	# means we've seen something from this endpoint

const ENDIAN_UNKNOWN = 0;
const ENDIAN_LITTLE = 1;
const ENDIAN_BIG = 2;
const ENDIAN_CONFUSED = 3;

function append_addl(c: connection, addl: string): bool
	{
	if ( c$addl == "" )
		c$addl= addl;

	else if ( addl !in c$addl )
		c$addl = fmt("%s %s", c$addl, addl);
	}

function append_addl_marker(c: connection, addl: string, marker: string): bool
	{
	if ( c$addl == "" )
		c$addl= addl;

	else if ( addl !in c$addl )
		c$addl = fmt("%s%s%s", c$addl, marker, addl);
	}


# Values for set_contents_file's "direction" argument.
const CONTENTS_NONE = 0;	# turn off recording of contents
const CONTENTS_ORIG = 1;	# record originator contents
const CONTENTS_RESP = 2;	# record responder contents
const CONTENTS_BOTH = 3;	# record both originator and responder contents

const ICMP_UNREACH_NET = 0;
const ICMP_UNREACH_HOST = 1;
const ICMP_UNREACH_PROTOCOL = 2;
const ICMP_UNREACH_PORT = 3;
const ICMP_UNREACH_NEEDFRAG = 4;
const ICMP_UNREACH_ADMIN_PROHIB = 13;
# The above list isn't exhaustive ...


# Definitions for access to packet headers.  Currently only used for
# discarders.
const IPPROTO_IP = 0;			# dummy for IP
const IPPROTO_ICMP = 1;			# control message protocol
const IPPROTO_IGMP = 2;			# group mgmt protocol
const IPPROTO_IPIP = 4;			# IP encapsulation in IP
const IPPROTO_TCP = 6;			# TCP
const IPPROTO_UDP = 17;			# user datagram protocol
const IPPROTO_RAW = 255;		# raw IP packet

type ip_hdr: record {
	hl: count;		# header length (in bytes)
	tos: count;		# type of service
	len: count;		# total length
	id: count;		# identification
	ttl: count;		# time to live
	p: count;		# protocol
	src: addr;		# source address
	dst: addr;		# dest address
};

# TCP flags.
const TH_FIN = 1;
const TH_SYN = 2;
const TH_RST = 4;
const TH_PUSH = 8;
const TH_ACK = 16;
const TH_URG = 32;
const TH_FLAGS = 63;		# (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG)

type tcp_hdr: record {
	sport: port;		# source port
	dport: port;		# destination port
	seq: count;		# sequence number
	ack: count;		# acknowledgement number
	hl: count;		# header length (in bytes)
	dl: count;		# data length (xxx: not in original tcphdr!)
	flags: count;		# flags
	win: count;		# window
};

type udp_hdr: record {
	sport: port;		# source port
	dport: port;		# destination port
	ulen: count;		# udp length
};

global discarder_maxlen = 128 &redef;	# maximum amount of data passed to fnc

global discarder_check_ip: function(i: ip_hdr): bool;
global discarder_check_tcp: function(i: ip_hdr, t: tcp_hdr, d: string): bool;
global discarder_check_udp: function(i: ip_hdr, u: udp_hdr, d: string): bool;
global discarder_check_icmp: function(i: ip_hdr, ih: icmp_hdr): bool;
# End of definition of access to packet headers, discarders.


type net_stats: record {
	# All counts are cumulative.
	pkts_recvd: count;
	pkts_dropped: count;
	interface_drops: count;
};


const watchdog_interval = 10 sec &redef;
const heartbeat_interval = 10 sec &redef;

# The maximum number of timers to expire after processing each new
# packet.  The value trades off spreading out the timer expiration load
# with possibly having to hold state longer.  A value of 0 means
# "process all expired timers with each new packet".
const max_timer_expires = 32 &redef;


# These need to match the definitions in Login.h.
const LOGIN_STATE_AUTHENTICATE = 0;	# trying to authenticate
const LOGIN_STATE_LOGGED_IN = 1;	# successful authentication
const LOGIN_STATE_SKIP = 2;	# skip any further processing
const LOGIN_STATE_CONFUSED = 3;	# we're confused

# It would be nice to replace these function definitions with some
# form of parameterized types.
function min_double(a: double, b: double): double { return a < b ? a : b; }
function max_double(a: double, b: double): double { return a > b ? a : b; }
function min_interval(a: interval, b: interval): interval { return a < b ? a : b; }
function max_interval(a: interval, b: interval): interval { return a > b ? a : b; }
function min_count(a: count, b: count): count { return a < b ? a : b; }
function max_count(a: count, b: count): count { return a > b ? a : b; }

global skip_authentication: set[string] &redef;
global direct_login_prompts: set[string] &redef;
global login_prompts: set[string] &redef;
global login_non_failure_msgs: set[string] &redef;
global login_failure_msgs: set[string] &redef;
global login_success_msgs: set[string] &redef;
global login_timeouts: set[string] &redef;

type mime_header_rec: record {
	name: string;
	value: string;
};
type mime_header_list: table[count] of mime_header_rec;
global mime_segment_length = 1024 &redef;
global mime_segment_overlap_length = 32 &redef;

type pm_mapping: record {
	program: count;
	version: count;
	p: port;
};

type pm_mappings: table[count] of pm_mapping;

type pm_port_request: record {
	program: count;
	version: count;
	is_tcp: bool;
};

type pm_callit_request: record {
	program: count;
	version: count;
	proc: count;
	arg_size: count;
};

const RPC_SUCCESS = 0;
const RPC_PROG_UNAVAIL = 1;
const RPC_PROG_MISMATCH = 2;
const RPC_PROC_UNAVAIL = 3;
const RPC_GARBAGE_ARGS = 4;
const RPC_SYSTEM_ERR = 5;
const RPC_TIMEOUT = 6;
const RPC_AUTH_ERROR = 7;
const RPC_UNKNOWN_ERROR = 8;

const RPC_status = {
	[RPC_SUCCESS] = "ok",
	[RPC_PROG_UNAVAIL] = "prog unavail",
	[RPC_PROG_MISMATCH] = "mismatch",
	[RPC_PROC_UNAVAIL] = "proc unavail",
	[RPC_GARBAGE_ARGS] = "garbage args",
	[RPC_SYSTEM_ERR] = "system err",
	[RPC_TIMEOUT] = "timeout",
	[RPC_AUTH_ERROR] = "auth error",
	[RPC_UNKNOWN_ERROR] = "unknown"
};

type ntp_msg: record {
	id: count;
	code: count;
	stratum: count;
	poll: count;
	precision: int;
	distance: interval;
	dispersion: interval;
	ref_t: time;
	originate_t: time;
	receive_t: time;
	xmit_t: time;
};

# Maps Samba command numbers to descriptive names.
global samba_cmds: table[count] of string &redef
	&default = function(c: count): string
		{ return fmt("samba-unknown-%d", c); };


type dns_msg: record {
	id: count;

	opcode: count;
	rcode: count;

	QR: bool;
	AA: bool;
	TC: bool;
	RD: bool;
	RA: bool;
	Z: count;

	num_queries: count;
	num_answers: count;
	num_auth: count;
	num_addl: count;
};

type dns_soa: record {
	mname: string;	# primary source of data for zone
	rname: string;	# mailbox for responsible person
	serial: count;	# version number of zone
	refresh: interval;	# seconds before refreshing
	retry: interval;	# how long before retrying failed refresh
	expire: interval;	# when zone no longer authoritative
	minimum: interval;	# minimum TTL to use when exporting
};

# Different values for "answer_type" in the following.  DNS_QUESTION
# shouldn't occur, it's just for completeness.
const DNS_QUERY = 0;
const DNS_ANS = 1;
const DNS_AUTH = 2;
const DNS_ADDL = 3;

type dns_answer: record {
	answer_type: count;
	query: string;
	qtype: count;
	qclass: count;
	TTL: interval;
};

global dns_skip_auth: set[addr] &redef;
global dns_skip_addl: set[addr] &redef;

type http_stats_rec: record {
	num_requests: count;
	num_replies: count;
	request_version: double;
	reply_version: double;
};

type http_message_stat: record {
	interrupted: bool;	# whether the message is interrupted
	finish_msg: string;	# reason phrase if interrupted
	body_length: count;	# len. body processed (before finished/interrupted)
	content_gap_length: count;	# total len of gaps within body_length
};

global http_entity_data_delivery_size = 1500 &redef;

const stp_delta: interval &redef;
const stp_idle_min: interval &redef;

const interconn_min_interarrival: interval &redef;
const interconn_max_interarrival: interval &redef;
const interconn_max_keystroke_pkt_size: count &redef;
const interconn_default_pkt_size: count &redef;
const interconn_stat_period: interval &redef;
const interconn_stat_backoff: double &redef;

type interconn_endp_stats: record {
	num_pkts: count;
	num_keystrokes_two_in_row: count;
	num_normal_interarrivals: count;
	num_8k0_pkts: count;
	num_8k4_pkts: count;
	is_partial: bool;
	num_bytes: count;
	num_7bit_ascii: count;
	num_lines: count;
	num_normal_lines: count;
};

const backdoor_stat_period: interval &redef;
const backdoor_stat_backoff: double &redef;

type backdoor_endp_stats: record {
	is_partial: bool;
	num_pkts: count;
	num_8k0_pkts: count;
	num_8k4_pkts: count;
	num_lines: count;
	num_normal_lines: count;
	num_bytes: count;
	num_7bit_ascii: count;
};

type signature_state: record {
	id: string;          # ID of the signature
	conn: connection;    # Current connection
	is_orig: bool;       # True if current endpoint is originator
	payload_size: count; # Payload size of the first pkt of curr. endpoint

};

type software_version: record {
	major: int;	# Major version number
	minor: int;	# Minor version number
	minor2: int;	# Minor subversion number
	addl: string;	# Additional version string (e.g. "beta42")
};

type software : record {
	name: string;	# Unique name of a software
	version: software_version;
};

@load const.bif.bro
@load event.bif.bro

@load common-rw.bif.bro
@load finger-rw.bif.bro
@load ftp-rw.bif.bro
@load ident-rw.bif.bro
@load smtp-rw.bif.bro
@load http-rw.bif.bro

function subst(s: string, from: pattern, to: string): string
	{
	local p = split_all(s, from);

	for ( p_i in p )
		if ( p_i % 2 == 0 )
			p[p_i] = to;

	return cat_string_array(p);
	}

function cut_tail(s: string, tail_len: count): string
	{
	local len = byte_len(s);

	if ( tail_len > len )
		tail_len = len;

	return sub_bytes(s, 1, int_to_count(len - tail_len));
	}

@load pcap.bro
