// $Id: Sessions.cc,v 1.84 2003/08/31 00:38:11 vern Exp $
//
// Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
//      The Regents of the University of California.  All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that: (1) source code distributions
// retain the above copyright notice and this paragraph in its entirety, (2)
// distributions including binary code include the above copyright notice and
// this paragraph in its entirety in the documentation or other materials
// provided with the distribution, and (3) all advertising materials mentioning
// features or use of this software display the following acknowledgement:
// ``This product includes software developed by the University of California,
// Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
// the University nor the names of its contributors may be used to endorse
// or promote products derived from this software without specific prior
// written permission.
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
// WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

#include "config.h"

#include <arpa/inet.h>

#include <stdlib.h>
#include <unistd.h>

#include "Net.h"
#include "Event.h"
#include "Timer.h"
#include "NetVar.h"
#include "Sessions.h"
#include "Active.h"

#include "FTP.h"
#include "Finger.h"
#include "Ident.h"
#include "HTTP.h"
#include "SMTP.h"
#include "Portmap.h"
#include "Telnet.h"
#include "Rlogin.h"
#include "DNS.h"
#include "NetbiosSSN.h"
#include "SSH.h"
#include "NTP.h"
#include "ICMP.h"

#include "SteppingStone.h"
#include "BackDoor.h"
#include "InterConn.h"
#include "Discard.h"
#include "RuleMatcher.h"

#include "PacketSort.h"

NetSessions* sessions;


class NetworkTimer : public Timer {
public:
	NetworkTimer(NetSessions* arg_sess, double arg_t) : Timer(arg_t)
		{ sess = arg_sess; }

	void Dispatch(double t, int is_expire);

protected:
	NetSessions* sess;
};

void NetworkTimer::Dispatch(double t, int is_expire)
	{
	if ( is_expire )
		return;

	sess->HeartBeat(t);
	}


NetSessions::NetSessions()
	{
	TypeList* t = new TypeList();
	t->Append(base_type(TYPE_COUNT));	// source IP address
	t->Append(base_type(TYPE_COUNT));	// dest IP address
	t->Append(base_type(TYPE_COUNT));	// source and dest ports

	ch = new CompositeHash(t);

	Unref(t);

	tcp_conns.SetDeleteFunc(bro_obj_delete_func);
	udp_conns.SetDeleteFunc(bro_obj_delete_func);
	fragments.SetDeleteFunc(bro_obj_delete_func);

	if ( reading_live && net_stats_update )
		timer_mgr->Add(new NetworkTimer(this, 1.0));

	if ( stp_correlate_pair )
		stp_manager = new SteppingStoneManager();
	else
		stp_manager = 0;

	discarder = new Discarder();
	if ( ! discarder->IsActive() )
		{
		delete discarder;
		discarder = 0;
		}

	packet_filter = 0;

	build_backdoor_analyzer =
		backdoor_stats || rlogin_signature_found ||
		telnet_signature_found || ssh_signature_found ||
		root_backdoor_signature_found || ftp_signature_found ||
		napster_signature_found || kazaa_signature_found ||
		http_signature_found || http_proxy_signature_found;

	dump_this_packet = 0;
	}

NetSessions::~NetSessions()
	{
	delete ch;
	}

void NetSessions::Done()
	{
	delete stp_manager;
	delete discarder;
	delete packet_filter;
	}

void NetSessions::NextPacket(double t, const struct pcap_pkthdr* hdr,
			     const u_char* const pkt, int hdr_size,
			     PacketSortElement* pkt_elem)
	{
	dump_this_packet = 0;

	if ( pkt_elem && pkt_elem->IPHdr() )
		// Fast path for "normal" IP packets if an IP_Hdr is
		// already extracted when doing PacketSort. Otherwise
		// the code below tries to extract the IP header, the
		// difference here is that header extraction in
		// PacketSort does not generate Weird events.

		DoNextPacket(t, hdr, pkt_elem->IPHdr(), pkt, hdr_size);

	else
		{
		uint32 caplen = hdr->caplen - hdr_size;
		if ( caplen < sizeof(struct ip) )
			{
			Weird("truncated_IP", hdr, pkt);
			return;
			}

		const struct ip* ip = (const struct ip*) (pkt + hdr_size);
		if ( ip->ip_v == 4 )
			{
			IP_Hdr ip_hdr(ip);
			DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
			}
		else
			{
#ifdef BROv6
			IP_Hdr ip_hdr((const struct ip6_hdr*) (pkt + hdr_size));
			DoNextPacket(t, hdr, &ip_hdr, pkt, hdr_size);
#else
			Weird("non_IPv4_packet", hdr, pkt);
			return;
#endif
			}
		}

	if ( dump_this_packet )
		DumpPacket(hdr, pkt);
	}

void NetSessions::DoNextPacket(double t, const struct pcap_pkthdr* hdr,
				const IP_Hdr* ip_hdr, const u_char* const pkt,
				int hdr_size)
	{
	uint32 caplen = hdr->caplen - hdr_size;
	const struct ip* ip4 = ip_hdr->IP4_Hdr();

	uint32 len = ip_hdr->TotalLen();
	if ( hdr->len < len + hdr_size )
		{
		Weird("truncated_IP", hdr, pkt);
		return;
		}

	// Ignore if packet matches packet filter.
	if ( packet_filter && packet_filter->Match(ip_hdr, len, caplen) )
		 return;

	int ip_hdr_len = ip_hdr->HdrLen();
	if ( ! ignore_checksums && ip4 &&
	     ones_complement_checksum((void*) ip4, ip_hdr_len, 0) != 0xffff )
		{
		Weird("bad_IP_checksum", hdr, pkt);
		return;
		}

	if ( discarder && discarder->NextPacket(ip_hdr, len, caplen) )
		return;

	int proto = ip_hdr->NextProto();
	if ( proto != IPPROTO_TCP && proto != IPPROTO_UDP &&
	     proto != IPPROTO_ICMP )
		{
		dump_this_packet = 1;
		return;
		}

	// Check for TTL/MTU problems from Active Mapping
#ifndef BROv6
#ifdef ACTIVE_MAPPING
	const NumericData* numeric;
	get_map_result(ip4->ip_dst.s_addr, numeric);

	if ( numeric->hops && ip4->ip_ttl < numeric->hops )
		{
		debug_msg("Packet destined for %s had ttl %d but there are %d hops to host.\n",
		inet_ntoa(ip4->ip_dst), ip4->ip_ttl, numeric->hops);
		return;
		}
#endif
#endif

	FragReassembler* f = 0;
	uint32 frag_field = ip_hdr->FragField();

#ifndef BROv6
#ifdef ACTIVE_MAPPING
	if ( numeric->path_MTU && (frag_field & IP_DF) )
		{
		if ( htons(ip4->ip_len) > numeric->path_MTU )
			{
			debug_msg("Packet destined for %s has DF flag but its size %d is greater than pmtu of %d\n",
			inet_ntoa(ip4->ip_dst), htons(ip4->ip_len), numeric->path_MTU);
			return;
			}
		}
#endif
#endif

	if ( (frag_field & 0x3fff) != 0 )
		{
		dump_this_packet = 1;	// always record fragments

		if ( caplen < len )
			{
			Weird("incompletely_captured_fragment", ip_hdr);
			return;	// don't try to reassemble, that's doomed
			}

		f = NextFragment(t, ip_hdr, pkt + hdr_size, frag_field);
		const IP_Hdr* ih = f->ReassembledPkt();
		if ( ! ih )
			// It didn't reassemble into anything yet.
			return;

		ip4 = ih->IP4_Hdr();
		ip_hdr = ih;

		caplen = len = ip_hdr->TotalLen();
		ip_hdr_len = ip_hdr->HdrLen();
		}

	len -= ip_hdr_len;	// remove IP header
	caplen -= ip_hdr_len;

	uint32 min_hdr_len = (proto == IPPROTO_TCP) ?  sizeof(struct tcphdr) :
		(proto == IPPROTO_UDP ? sizeof(struct udphdr) : ICMP_MINLEN);

	if ( len < min_hdr_len )
		{
		Weird("truncated_header", hdr, pkt);
		if ( f )
			Remove(f);	// ###
		return;
		}
	if ( caplen < min_hdr_len )
		{
		Weird("internally_truncated_header", hdr, pkt);
		if ( f )
			Remove(f);	// ###
		return;
		}

	// Where the data starts - if this is a protocol we know about,
	// this gets advanced past the transport header.
	const u_char* data = ip_hdr->Payload();

	ConnID id;
	id.src_addr = ip_hdr->SrcAddr();
	id.dst_addr = ip_hdr->DstAddr();
	Dictionary* d = 0;

	switch ( proto ) {
	case IPPROTO_TCP:
		{
		const struct tcphdr* tp = (const struct tcphdr *) data;
		id.src_port = tp->th_sport;
		id.dst_port = tp->th_dport;
		d = &tcp_conns;
		break;
		}

	case IPPROTO_UDP:
		{
		const struct udphdr* up = (const struct udphdr *) data;
		id.src_port = up->uh_sport;
		id.dst_port = up->uh_dport;

		d = &udp_conns;
		break;
		}

	case IPPROTO_ICMP:
		{
		const struct icmp* icmpp = (const struct icmp *) data;

		id.src_port = icmpp->icmp_type;
		id.dst_port = 0;

		d = &icmp_conns;
		break;
		}

	default:
		Weird(fmt("unknown_protocol %d", proto), hdr, pkt);
		return;
	}

	HashKey* h = id.BuildConnKey();
	if ( ! h )
		internal_error("hash computation failed");

	Connection* conn = (Connection*) d->Lookup(h);
	if ( ! conn )
		{
		conn = NewConn(h, t, &id, data, proto);
		if ( conn )
			d->Insert(h, conn);
		}
	else
		{
		if ( conn->IsReuse(t, data) )
			{
			conn->Event(connection_reused);
			Remove(conn, 0);
			conn = NewConn(h, t, &id, data, proto);
			if ( conn )
				d->Insert(h, conn);
			}
		else
			delete h;
		}

	if ( ! conn )
		{
		delete h;
		return;
		}

	int record_packet = 1;	// whether to record the packet at all
	int record_content = 1;	// whether to record its data

	int is_orig = addr_eq(id.src_addr, conn->OrigAddr()) &&
			id.src_port == conn->OrigPort();

	conn->NextPacket(t, is_orig, ip_hdr, len, caplen, data,
				record_packet, record_content,
			        hdr, pkt, hdr_size);

	int hdr_offset = data - ip_hdr->Payload();
	int contents_len = min(len, caplen) - hdr_offset;

	if ( packet_contents && contents_len > 0 )
		{
		BroString* cbs = new BroString(data, contents_len, 1);
		Val* contents = new StringVal(cbs);
		conn->Event(packet_contents, contents);
		}

	if ( f )
		{
		// Above we already recorded the fragment in its entirety.
		f->DeleteTimer();
		Remove(f);	// ###
		}

	else if ( record_packet && ! conn->RewritingTrace() )
		{
		if ( record_content )
			dump_this_packet = 1;	// save the whole thing

		else
			{
			int hdr_len = data - pkt;
			DumpPacket(hdr, pkt, hdr_len);	// just save the header
			}
		}
	}

FragReassembler* NetSessions::NextFragment(double t, const IP_Hdr* ip,
					const u_char* pkt, uint32 frag_field)
	{
	uint32 src_addr = uint32(ip->SrcAddr4());
	uint32 dst_addr = uint32(ip->DstAddr4());
	uint32 frag_id = ntohs(ip->ID4());	// we actually could skip conv.

	ListVal* key = new ListVal(TYPE_ANY);
	key->Append(new Val(src_addr, TYPE_COUNT));
	key->Append(new Val(dst_addr, TYPE_COUNT));
	key->Append(new Val(frag_id, TYPE_COUNT));

	HashKey* h = ch->ComputeHash(key, 1);
	if ( ! h )
		internal_error("hash computation failed");

	FragReassembler* f = fragments.Lookup(h);
	if ( ! f )
		{
		f = new FragReassembler(this, ip, pkt, frag_field, h, t);
		fragments.Insert(h, f);
		Unref(key);
		return f;
		}

	delete h;
	Unref(key);

	f->AddFragment(t, ip, pkt, frag_field);
	return f;
	}

Connection* NetSessions::FindConnection(Val* v)
	{
	if ( v->Type() != conn_id )
		return 0;

	val_list* vl = v->AsRecord();

	addr_type orig_addr = (*vl)[0]->AsAddr();
	addr_type resp_addr = (*vl)[2]->AsAddr();

	ConnID id;

#ifdef BROv6
	id.src_addr = orig_addr;
	id.dst_addr = resp_addr;
#else
	id.src_addr = &orig_addr;
	id.dst_addr = &resp_addr;
#endif

	PortVal* orig_portv = (*vl)[1]->AsPortVal();
	PortVal* resp_portv = (*vl)[3]->AsPortVal();

	id.src_port = htons((unsigned short) orig_portv->Port());
	id.dst_port = htons((unsigned short) resp_portv->Port());

	HashKey* h = id.BuildConnKey();
	if ( ! h )
		internal_error("hash computation failed");

	Dictionary* d;
	if ( orig_portv->IsTCP() )
		d = &tcp_conns;
	else
		d = &udp_conns;

	Connection* conn = (Connection*) d->Lookup(h);

	delete h;

	return conn;
	}

void NetSessions::Remove(Connection* c, int caller_is_timer)
	{
	HashKey* k = c->Key();
	if ( k )
		{
		c->CancelTimers(caller_is_timer);
		c->Done();
		if ( connection_state_remove )
			c->Event(connection_state_remove);

		// Zero out c's copy of the key, so that if c has been Ref()'d
		// up, we know on a future call to Remove() that it's no
		// longer in the dictionary.
		c->ClearKey();

		switch ( c->ConnType() ) {
		case CONN_TCP:
			if ( ! tcp_conns.RemoveEntry(k) )
				internal_error("connection missing");

			((TCP_Connection*) c)->AnalyzerDone();
			break;

		case CONN_UDP:
			if ( ! udp_conns.RemoveEntry(k) )
				internal_error("connection missing");
			break;

		case CONN_ICMP:
			if ( ! icmp_conns.RemoveEntry(k) )
				internal_error("connection missing");
			break;
		}

		Unref(c);
		delete k;
		}
	}

void NetSessions::Remove(FragReassembler* f)
	{
	HashKey* k = f->Key();
	if ( ! k )
		internal_error("fragment block not in dictionary");

	if ( ! fragments.RemoveEntry(k) )
		internal_error("fragment block missing");

	Unref(f);
	}

void NetSessions::Drain()
	{
	IterCookie* cookie = tcp_conns.InitForIteration();
	TCP_Connection* tc;

	while ( (tc = tcp_conns.NextEntry(cookie)) )
		{
		tc->Done();
		tc->Event(connection_state_remove);
		}

	cookie = udp_conns.InitForIteration();
	UDP_Connection* uc;

	while ( (uc = udp_conns.NextEntry(cookie)) )
		{
		uc->Done();
		uc->Event(connection_state_remove);
		}

	cookie = icmp_conns.InitForIteration();
	ICMP_Connection* ic;

	while ( (ic = icmp_conns.NextEntry(cookie)) )
		{
		ic->Done();
		ic->Event(connection_state_remove);
		}
	}

void NetSessions::HeartBeat(double t)
	{
	unsigned int recv = 0;
	unsigned int drop = 0;

	loop_over_list(pkt_srcs, i)
		{
		PktSrc* ps = pkt_srcs[i];

		struct pcap_stat stat;
		if ( pcap_stats(ps->PcapHandle(), &stat) < 0 )
			run_time("problem getting packet filter statistics: %s",
				ps->ErrorMsg());
		else
			{
			recv += stat.ps_recv;
			drop += stat.ps_drop;
			}
		}

	val_list* vl = new val_list;

	vl->append(new Val(t, TYPE_TIME));

	RecordVal* ns = new RecordVal(net_stats);
	ns->Assign(0, new Val(recv, TYPE_COUNT));
	ns->Assign(1, new Val(drop, TYPE_COUNT));

	// Turns out that stat.ps_ifdrop isn't even initialized.
	// ns->Assign(2, new Val(stat.ps_ifdrop, TYPE_COUNT));
	ns->Assign(2, new Val(0, TYPE_COUNT));

	vl->append(ns);

	mgr.QueueEvent(net_stats_update, vl);

	timer_mgr->Add(new NetworkTimer(this, t + heartbeat_interval));
	}

void NetSessions::GetStats(SessionStats& s) const
	{
	s.num_TCP_conns = tcp_conns.Length();
	s.num_UDP_conns = udp_conns.Length();
	s.num_ICMP_conns = icmp_conns.Length();
	s.num_fragments = fragments.Length();
	s.num_timers = timer_mgr->Size();

	s.max_TCP_conns = tcp_conns.MaxLength();
	s.max_UDP_conns = udp_conns.MaxLength();
	s.max_ICMP_conns = icmp_conns.MaxLength();
	s.max_fragments = fragments.MaxLength();
	s.max_timers = timer_mgr->PeakSize();
	}

Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
		const u_char* data, int proto)
	{
	switch ( proto ) {
	case IPPROTO_TCP:
		return NewConn(k, t, id, (const struct tcphdr *) data);

	case IPPROTO_UDP:
		return NewConn(k, t, id, (const struct udphdr *) data);

	case IPPROTO_ICMP:
		return NewConn(k, t, id, (const struct icmp *) data);

	default:
		internal_error("bad protocol in NetSessions::NewConn");
		return 0;
	}
	}

TCP_Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* orig_id,
			const struct tcphdr* tp)
	{
	TCP_Connection* c = 0;
	const ConnID* id = orig_id;
	ConnID flip_id;	// only used for flip_roles below

	if ( ! (tp->th_flags & TH_SYN) || (tp->th_flags & TH_ACK) )
		{
		// The new connection is starting either without a SYN,
		// or with a SYN ack.  This means it's a partial connection.

		if ( ! partial_connection_ok )
			return 0;

		uint32 src_port = ntohs(id->src_port);
		uint32 dst_port = ntohs(id->dst_port);

		if ( tp->th_flags & TH_SYN && ! tcp_SYN_ack_ok )
			return 0;

		// Try to guess true responder by the port numbers.
		// (We might also think that for SYN acks we could
		// safely flip the roles, but that doesn't work
		// for stealth scans.)
		int flip_roles = IsAnalyzedTCP_App(src_port) &&
				 ! IsAnalyzedTCP_App(dst_port);

		if ( flip_roles )
			{
			flip_id = *id;

			const uint32* ta = flip_id.src_addr;
			flip_id.src_addr = flip_id.dst_addr;
			flip_id.dst_addr = ta;

			uint32 t = flip_id.src_port;
			flip_id.src_port = flip_id.dst_port;
			flip_id.dst_port = t;

			id = &flip_id;
			}
		}

	int is_analyzed = 1;
	int service = ntohs(id->dst_port);
	switch ( service ) {
	// NOTE: Reflect changes here in IsAnalyzedTCP_App().
	case 21:
		if ( ftp_request || ftp_reply )
			c = new FTP_Conn(this, k, t, id, tp);
		break;

	case 22:
		if ( ssh_client_version || ssh_server_version )
			c = new SSH_Conn(this, k, t, id, tp);
		break;

	case 23:
		if ( login_failure || login_success ||
		     login_input_line || login_output_line )
			c = new TelnetConn(this, k, t, id, tp);
		break;

	case 25:
		if ( smtp_request || smtp_reply || smtp_data || smtp_unexpected )
			c = new SMTP_Conn(this, k, t, id, tp);
		break;

	case 513:
		if ( login_failure || login_success ||
		     login_input_line || login_output_line )
			c = new RloginConn(this, k, t, id, tp);
		break;

	case 79:
		if ( finger_request || finger_reply )
			c = new FingerConn(this, k, t, id, tp);
		break;

	case 113:
		if ( ident_request || ident_reply || ident_error )
			c = new IdentConn(this, k, t, id, tp);
		break;

	case 80:
	case 8080:
	case 8000:
	case 3128:	// Default port of Squid Proxy Cache.
		if ( http_request || http_reply )
			c = new HTTP_Conn(this, k, t, id, tp);
		break;

	case 53:
		if ( dns_request || dns_full_request )
			c = new TCP_DNS(this, k, t, id, tp);
		break;

	case 111:
		if ( pm_request )
			c = new PortmapperConn(this, k, t, id, tp);
		break;

	case 139:
		if ( netbios_session_request || netbios_session_accepted ||
		     netbios_session_rejected )
			c = new TCP_NetbiosSSN(this, k, t, id, tp);
		break;
	}

	if ( ! c )
		{
		TCPContentsType reass = TCP_CONTENTS_NONE;

		if ( new_connection_contents )
			reass = TCP_CONTENTS_BOTH;

		else if ( rule_matcher )
			{
			Val dst_port_val(ntohs(id->dst_port), TYPE_PORT);

			if ( tcp_reassembler_ports_orig->Lookup(&dst_port_val) )
				reass = TCP_CONTENTS_ORIG;

			if ( tcp_reassembler_ports_resp->Lookup(&dst_port_val) )
				reass = (TCPContentsType)
						(reass | TCP_CONTENTS_RESP);
			}

		if ( reass != TCP_CONTENTS_NONE )
			c = new TCP_ConnectionContents(this, k, t, id, tp, reass);
		else
			{
			c = new TCP_Connection(this, k, t, id, tp);
			is_analyzed = (non_analyzed_lifetime == 0.0);
			}
		}

	c->Init();

	if ( conn_stats )
		c->AddAnalyzer(new TCP_Stats(c));

	if ( stp_correlate_pair &&
	     (service == 22 || service == 23 || service == 513) )
		c->AddAnalyzer(new SteppingStoneAnalyzer(c, stp_manager));

	if ( build_backdoor_analyzer )
		c->AddAnalyzer(new BackDoorAnalyzer(c));

	if ( interconn_stats )
		c->AddAnalyzer(new InterConnAnalyzer(c));

	if ( new_connection )
		c->Event(new_connection);
	else if ( new_connection_contents )
		c->Event(new_connection_contents);

	if ( ! is_analyzed && ! c->HasAnalyzers() )
		c->SetLifetime(non_analyzed_lifetime);

	return c;
	}

int NetSessions::IsAnalyzedTCP_App(uint32 port) const
	{
	switch ( port ) {	// reflect changes here above
	case 21:
	case 22:
	case 23:
	case 25:
	case 513:
	case 79:
	case 113:
	case 80:
	case 8080:
	case 8000:
	case 3128:
	case 53:
	case 111:
	case 139:
		return 1;

	default:
		return 0;
	}
	}

UDP_Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* orig_id,
			const struct udphdr* up)
	{
	UDP_Connection* c = 0;
	const ConnID* id = orig_id;
	ConnID flip_id;	// only used for flip_roles below

	int src_h = ntohs(id->src_port);
	int dst_h = ntohs(id->dst_port);

	if ( (src_h == 53 || src_h == 111 || src_h == 123) &&
	     (dst_h != 53 && dst_h != 111 && dst_h != 123) )
		{
		// Make a guess that we're seeing the tail half of
		// an analyzable connection.
		flip_id = *id;

		const uint32* ta = flip_id.src_addr;
		flip_id.src_addr = flip_id.dst_addr;
		flip_id.dst_addr = ta;

		uint32 t = flip_id.src_port;
		flip_id.src_port = flip_id.dst_port;
		flip_id.dst_port = t;

		id = &flip_id;
		}

	switch ( ntohs(id->dst_port) ) {
	case 53:
		if ( dns_request || dns_full_request )
			c = new UDP_DNS(this, k, t, id, up);
		break;

	case 111:
		if ( pm_request )
			c = new UDP_Portmapper(this, k, t, id, up);
		break;

	case 123:
		if ( ntp_message )
			c = new NTP_Session(this, k, t, id, up);
		break;
	}

	if ( ! c )
		{
		c = new UDP_Connection(this, k, t, id, up);

		if ( non_analyzed_lifetime != 0.0 )
			c->SetLifetime(non_analyzed_lifetime);
		}

	return c;
	}

ICMP_Connection* NetSessions::NewConn(HashKey* k, double t, const ConnID* id,
			const struct icmp* icmpp)
	{
	switch ( icmpp->icmp_type ) {
	case ICMP_ECHO:
	case ICMP_ECHOREPLY:
		if ( icmp_echo_request || icmp_echo_reply )
			return new ICMP_Echo(this, k, t, id, icmpp);

	case ICMP_UNREACH:
		if ( icmp_unreachable )
			return new ICMP_Unreach(this, k, t, id, icmpp);

	default:
		ICMP_Connection* c = new ICMP_Connection(this, k, t, id, icmpp);

		if ( non_analyzed_lifetime != 0.0 )
			c->SetLifetime(non_analyzed_lifetime);

		return c;
	}
	}

void NetSessions::DumpPacket(const struct pcap_pkthdr* hdr,
				const u_char* pkt, int len)
	{
	if ( ! pkt_dump )
		return;

	if ( len == 0 )
		pcap_dump((u_char*) pkt_dump, hdr, pkt);
	else
		{
		struct pcap_pkthdr h = *hdr;
		h.caplen = len;
		if ( h.caplen > hdr->caplen )
			internal_error("bad modified caplen");
		pcap_dump((u_char*) pkt_dump, &h, pkt);
		}
	}

void NetSessions::Internal(const char* msg, const struct pcap_pkthdr* hdr,
				const u_char* pkt)
	{
	DumpPacket(hdr, pkt);
	internal_error(msg);
	}

void NetSessions::Weird(const char* name,
			const struct pcap_pkthdr* hdr, const u_char* pkt)
	{
	if ( hdr )
		dump_this_packet = 1;

	if ( net_weird )
		{
		val_list* vl = new val_list;
		vl->append(new StringVal(name));
		mgr.QueueEvent(net_weird, vl);
		}
	else
		fprintf(stderr, "weird: %.06f %s\n", network_time, name);
	}

void NetSessions::Weird(const char* name, const IP_Hdr* ip)
	{
	if ( flow_weird )
		{
		val_list* vl = new val_list;
		vl->append(new StringVal(name));
		vl->append(new AddrVal(ip->SrcAddr4()));
		vl->append(new AddrVal(ip->DstAddr4()));
		mgr.QueueEvent(flow_weird, vl);
		}
	else
		fprintf(stderr, "weird: %.06f %s\n", network_time, name);
	}
