#!/usr/bin/perl -w

#AIM Sniff Copyright (C) 2002 Shawn Grimes
#This program is free software; you can redistribute it and/or
#modify it under the terms of the GNU General Public License as 
#published by the Free Software Foundation; version 2 of the License.
#This program is distributed in the hope that it will be useful, but 
#WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
#or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
#more details.
#You should have received a copy of the GNU General Public License along with 
#this program; if not, write to the 
#Free Software Foundation, Inc.
#59 Temple Place, Suite 330
#Boston, MA 02111-1307 USA

#You may also contact me directly with any questions at: 
#shawn@aimsniff.com
#OR on AIM: spittingfire101 (let me know you are talking about AIM Sniff)
#And of course the mailing list: aimsniff-devel@lists.sourceforge.net
#And the NEW website: www.aimsniff.com

#MODIFIED by Jason Healy (jhealy).  All modified lines commened with
#"jhealy".  See README.postgres for information about changes.

#FOR SUPPORT FOLLOW THESE STEPS:
#First scan the README and make sure you didn't miss anything
#Second, scan the FAQ section at the http://www.aimsniff.com forums 
#	(it's only like 3 entries to read)
#Third, Do a search on the forums
#Fourth and most importantly, If you use windows, don't even bother 
#	asking me any questions I don't use windows so I can't help 
#	you anyway.  Try the forums, there is a section just for you.
#Fifth, if you have done all of the above and still can't find an 
#	answer, go ahead and email me or IM me.  I will be very nice
#	so long as you have followed steps 1-4

use strict;
use Net::Pcap;
use NetPacket::Ethernet qw(:strip);
use NetPacket::IP qw(:strip);
use NetPacket::TCP;
use NetPacket::UDP;
use DBI;
use Unicode::String qw(utf8 latin1 utf16);
use Proc::Daemon;
use Proc::Simple;
use FileHandle;
use Unix::Syslog;
use GDBM_File;
use Fcntl;

autoflush STDOUT 1;

######################################################################
#########   User Defined Variables                           #########
######################################################################
my $debug=0;
my $debug2=1;
my $debugmsg="";
my $daemonMode=0;
my $driver="Pg";
my $database="aimsniff";
my $host="localhost";
my $user="aimsniff";
my $password="";
my $quiet=0;
my $dev='eth0';
my $filter_str='port 5190';
my $promisc=1;
my $to_ms=1000;
my $handleFile;
my $dumpHandles=0;
my $outputFile;
my $dumpfile;
my $SMB = 0;
my $nodb = 0;
my $childCPUMaxPct=80;  #Amount of cpu it can use before it restarts itself
my $parentPollTimeout=10;
my $useSyslog=0;
my $ident="aimSniff";
my $facility=Unix::Syslog::LOG_LOCAL1;
my $options=Unix::Syslog::LOG_PID | Unix::Syslog::LOG_PERROR;
my $priority=Unix::Syslog::LOG_INFO;
######################################################################
#########   End User Defined Variables--Modify nothing below #########
######################################################################

######################################################################
# Parent Global Variables
######################################################################
my $child = 0;
######################################################################
# End parent global variables
######################################################################

######################################################################
# Child global variables
######################################################################
my $msgCount = 0;
my $loginCount = 0;
my $fileCount = 0;
my $chatCount = 0;
my $buddyListCount = 0;
my $pcap_t;
my $err;
my $getHandles;
my $count = -1;

### These shouldn't be changed (yet)
my $optimize=0;
my $netmask=0;
my $snaplen=50000;
######################################################################
# End child global variables
######################################################################

my($ver)='0.9d';

print "\n\n#############################\n";
print "AIM Sniff v. $ver\n";
print "Developed by: Shawn Grimes\n";
print "#############################\n\n";




####
#Get command line arguments
####
&getOptions(@ARGV) || die "Could not get options\n";

sub kill_child()
{
	if (defined($child))
	{
		$child->kill("SIGQUIT");

		while ($child->poll() == 1) {
			sleep(1);  # wait for child to exit
		}
	}
}

sub ParentLeaveNow()
{
	kill_child();
	log_msg("Parent exiting");
	unlink '/var/run/aimsniff_parent.pid';
	exit;
}

sub launch_proc()
{
	$child = Proc::Simple->new();
	my $status = $child->start(\&start_AS);
	my $pid = $child->pid;
	log_msg("Started New Process, PID: $pid");
	open(PID, ">/var/run/aimsniff.pid");
	print PID $pid;
	close(PID);
}

sub save_parent_pid()
{
	open(PID, ">/var/run/aimsniff_parent.pid");
	print PID $$;
	close(PID);
}

sub open_syslog()
{
	if ($useSyslog == 1) {
		Unix::Syslog::openlog $ident, $options, $facility || die "Couldn't open Syslog";
	}
}

sub log_msg($)
{
	my ($msg) = @_;

	if ($useSyslog == 1){
		Unix::Syslog::syslog($priority, $msg);
	}
	else{
		print $msg . "\n";
	}
}

sub convert_to_sec($)
{
	my ($time) = @_;
	my $result = 0;
	if ($time =~ /(\d+):(\d+):(\d+)/) {
		my ($hr, $min, $sec) = ($1, $2, $3);
		$result = $hr * 3600 + $min * 60 + $sec;
	}

	$result;
}

if($daemonMode == 1){

	#Attempting to intercept ctrl-c
	$SIG{'QUIT'}=\&ParentLeaveNow;
	$SIG{'INT'}=\&ParentLeaveNow;

    my $status;

    Proc::Daemon::Init;
    &open_syslog();
	&save_parent_pid();

    log_msg("Running as daemon...");

	&launch_proc();
	my $lastUsage = 0;
	my $usageMax = $childCPUMaxPct == 0 ? 1 : $childCPUMaxPct / 100;
	log_msg("usageMax: $usageMax") if ($debug);

	while(1){
		sleep($parentPollTimeout);
		$status = $child->poll();
		if ($status == 0){
			log_msg("Missing child, starting a new one");
			&launch_proc();
		}

		my $pid = $child->pid;
		my $time=`ps -p $pid --no-header --format time`;
		my $cpuUsage = &convert_to_sec($time);
		my $usagePct = ($cpuUsage - $lastUsage) / $parentPollTimeout;

		log_msg("PID: $pid CPU: $cpuUsage, last CPU: $lastUsage, Usage %: $usagePct") if ($debug);

		$lastUsage = $cpuUsage;

		if($usagePct > $usageMax ){
			log_msg(
				"About to restart child due to excessive CPU utilization," .
				"PID: $pid CPU: $cpuUsage, last CPU: $lastUsage, Usage %: " .
				"$usagePct");

			&kill_child();
			&launch_proc();
			$lastUsage = 0;
		}
	}
}
else
{
	&start_AS;
}

sub start_AS {
	&open_syslog();

	if ($daemonMode == 1)
	{
		$SIG{'QUIT'}=\&LeaveNow;  
		$SIG{'INT'}=\&LeaveNow;
		$SIG{'HUP'}=\&dump_child_stats;
	}

	tieNameIp();

	###
	#Set counts=0
	###
	$msgCount=0;
	$loginCount=0;
	$fileCount=0;
	$chatCount=0;
	$buddyListCount=0;
	##################
	
	if(defined($getHandles) && $getHandles == 1){
	    &getFromHandle || die "Could not get SMB Names\n"; ##Used to find out who messages are coming from in the database.  
	}else{
	    if(defined($dumpfile)){ #work in offline mode?
		log_msg("Working in Offline Mode");
		log_msg("nReading File: $dumpfile");
		$pcap_t = Net::Pcap::open_offline($dumpfile, \$err);
		die "Error opening file: $err\n" if(!defined($pcap_t));
	
	    }else{  #actively sniff
		log_msg("Beginning Sniff...") if(!$quiet);
		log_msg("FILTER: $filter_str");
	
		$pcap_t=Net::Pcap::open_live($dev, $snaplen, $promisc, $to_ms, \$err) || die "Error opening pcap: $err\n"; #start sniffing
		my $filter_t;
		my $result=Net::Pcap::compile($pcap_t, \$filter_t, $filter_str,$optimize,$netmask); #compile filter_str
		Net::Pcap::setfilter($pcap_t, $filter_t); #apply filter
	    }
	    Net::Pcap::loop($pcap_t, $count, \&process_pkt,"xyz"); #start to process the packets that are received
	    &LeaveNow;
	}
}

#Process the packet
sub process_pkt {
#CHANGED
#Mark Anacker's suggestion so that output can be piped through something else
#    STDOUT->autoflush;
#    STDOUT->autoflush(STDOUT);
	autoflush STDOUT 1;
###

    my($pktuser, $hdr, $pkt) = @_;
    if (!defined($hdr) or !defined($pkt)) {
	log_msg("Bad args passed to callback");
	log_msg("Bad user data"), if ($user ne "xyz");
	log_msg("Bad pkthdr"), if (!defined($hdr));
	log_msg("Bad pkt data"), if (!defined($pkt));
	log_msg("not ok");
	exit;
    }
    my(%sqlAdd, $msg,$handle,$country,$language,$format);
#get datetimestamp of packet
    my ($sec, $min, $hour, $mday, $mon, $year)=localtime($hdr->{tv_sec});
    $year+=1900;
    $mon+=1;	
    my $datestamp="$year-$mon-$mday $hour:$min:$sec";

#Strip Ethernet portion of packet off
    my $ip_obj=NetPacket::IP->decode(eth_strip($pkt));
    my $srcip=$ip_obj->{src_ip};
    my $dstip=$ip_obj->{dest_ip};
    my $proto=$ip_obj->{proto};
    my ($tcp_obj, $udp_obj, $flags, $srcport, $dstport, $dataset);

    if($proto==6){
    	$tcp_obj=NetPacket::TCP->decode(ip_strip(eth_strip($pkt)));
    	$flags=$tcp_obj->{flags};  #make sure packet contains ACK PUSH
    	#return if(!($flags==24 || $flags==10));
    	$srcport=$tcp_obj->{src_port};
    	$dstport=$tcp_obj->{dest_port};
    	$dataset=$tcp_obj->{data};
	}elsif($proto==17){
		$udp_obj=NetPacket::UDP->decode(ip_strip(eth_strip($pkt)));
		$srcport=$udp_obj->{src_port};
		$dstport=$udp_obj->{dest_port};
		$dataset=$udp_obj->{data};
	}
	
	my ($family, $chanid)=undef;
	my ($destHandle, $fromHandle) = ("", "");


    # if($srcport=='1863' || $dstport=='1863'){
	# The start of MSN protocol analysis
	# &MSNparse($dataset);
    # }else{
		#AIM Parse
		($family,$dataset)=&familyFind($dataset); #get Family ID and SubID
		return if(!defined($family)||!defined($dataset));
    # }

    #Have to better modularize so that plugins can be easily added
    #AIM Messages
    if ($family eq '0003000b' || $family eq '0003000c' ||
    	$family eq '0004000b' || $family eq '0004000c' ||
    	$family eq '0013000e' || $family eq '000b0002' || 
    	$family eq '0001001e' || $family eq '0001000e' )
    {
    	print("$family -- $dataset\n") if ($debug);
    	return;
    }
    
    	my $known=0;
    	my @knownFamilies=qw(00040006 00040007 00170006 001700060170002 000e 000d0008 00130003 00130006 00020015 00020006);
    	foreach my $testFamily(@knownFamilies){
    		$known=1 if($family=~/^$testFamily/);
	}
	if(!$known){
		print "Unknown family: $family\n" if($debug);
		return;
    	}

    if(($family=~/00040006/) or ($family=~/00040007/)){ #AIM Message
    	$debugmsg="AIM Message";
	($chanid,$dataset)=&idFind($dataset); #Get 8 byte id before handle, not sure what it's for yet
	if($chanid == '0001'){
	    ($handle,$dataset)=&handleFind($dataset); #Get AIM Handle
	    $msg=&msgFind($dataset); #Get the message

	    # log_msg("\$chanid=$chanid defined(\$handle)=" . defined($handle) . 
	    # 	" defined(\$dataset)=" . defined($dataset) .
	    #	" defined(\$msg)=" . defined($msg));

	    if($family == '00040007' and $handle and $msg){
	    	$debugmsg="Incoming Message";
			#Incoming messages
			$destHandle=getNameIp($dstip);
			%sqlAdd=('table','logs','ts',$datestamp,'fromHandle',$handle,'handle',$destHandle,'direction',$family, 'message', $msg,'ip', $dstip);
			$msg="AIM##TYPE##Incoming Message##TS##$datestamp##FAMILY##$family##FROM##$handle##DESTHANDLE##$destHandle##DESTIP##$dstip##MESSAGE##$msg";
			$msgCount++;
    	}elsif($family=='00040006' and $handle and $msg){
	    	$debugmsg="Outgoing Message";
			#Outgoing messages
			$fromHandle=getNameIp($srcip);
			%sqlAdd=('table',"logs",'ts',$datestamp,'fromHandle',$fromHandle,'handle',$handle, 'direction',$family, 'message', $msg,'ip', $srcip) if($handle and $msg);
			$msg="AIM##TYPE##Outgoing Message##TS##$datestamp##FAMILY##$family##FROM##$fromHandle##DESTHANDLE##$handle##SRCIP##$srcip##MESSAGE##$msg";
			$msgCount++;
	    }
	}elsif($chanid == '0002'){
		$debugmsg="File Xfer";
	    my($handle, $capString, $lang, $format, $fileMsg, $ip, $port, $filename)=&getFile($dataset);
	    if($handle and $ip and $port and $filename){
		$msg="File Xfer Detected:<BR>\nCapabilities: $capString<BR>\nLanguage: $lang<BR>\nFormat:$format<BR>\nMSG: $fileMsg<BR>\nIP: $ip<BR>\nPort: $port<BR>\nFile: $filename<BR>\n";
		%sqlAdd=('table','logs','ts',$datestamp,'handle',$handle,'direction',$family,'message',$msg,'ip',$srcip);
		$msg="AIM##TYPE##File Xfer##TS##$datestamp##FAMILY##$family##SRCIP##$ip##PORT##$port##CAPSTRING##$capString##LANG##$lang##FORMAT##$format##HANDL##$handle##MESSAGE##$fileMsg##FILE##$filename";
	        $fileCount++;
	    }
	}
	#AIM Logins
    }elsif($family eq '00170006'){ #AIM Login
    	$debugmsg="AIM Login";
	$handle=&getSignon($dataset); #Get AIM Handle
	setNameIp($srcip, $handle);
	if($SMB == 1){
	    log_msg("Getting smb info for $srcip") if ($debug);
	    my ($SMBMachine, $SMBUser)=&SMBInfo($srcip);
		log_msg("SMBMachine=$SMBMachine") if ($debug && $SMBMachine);
		log_msg("SMBUser=$SMBUser") if ($debug && $SMBUser);
		log_msg("handle=$handle") if ($debug && $handle);
		$SMBMachine="UNKNOWN" if(!$SMBMachine);
		$SMBUser="UNKNOWN" if(!$SMBUser);
	    if($handle){
		   %sqlAdd=('table',"handles",'ts',$datestamp,'handle',$handle, 'username',$SMBUser,'machine',$SMBMachine,'ip',$srcip);
		    $msg="AIM##TYPE##Login##TS##$datestamp##FAMILY##$family##SRCIP##$srcip##HANDLE##$handle##SMBUser##$SMBUser##SMBMachine##$SMBMachine";
	    }
	}else{
	    	if($handle){
	    		%sqlAdd=('table',"handles",'ts',$datestamp,'handle',$handle,'ip',$srcip);
	    		$msg="AIM##TYPE##Login##TS##$datestamp##FAMILY##$family##SRCIP##$srcip##HANDL##$handle";
	    	}
	}
	$loginCount++;
    #AIM Version Information
    }elsif($family eq '001700060170002'){
	$debugmsg="Version Information";
	my ($version, $country, $language);
	($handle, $version, $country, $language)=&getVersion($dataset);
	$debugmsg="Got Version";
	if($handle and $version){
	    $msg="Version Information:\n$handle is using $version with country=$country and language=$language\n\n";
	    %sqlAdd=('table',"versions",'ts',$datestamp,'ip',$srcip, 'handle',$handle, 'version', $version,'country', $country, 'language',$language) if($handle and $version);
	    $msg="AIM##TYPE##Version Information##TS##$datestamp##FAMILY##$family##SRCIP##$srcip##HANDL##$handle##VERSION##$version##COUNTRY##$country##LANGUAGE##$language";
	    
	}

    #Chat Information
}elsif($family=~/^000e/){
	$debugmsg="Chat Info";
	#Format a %sqlAdd statement
	#Format a @printOUT statement
	($chanid,$dataset)=&idFind($dataset);
	($handle,$format,$language,$msg)=&getChats($family,$dataset);
	$debugmsg="Got Chats";
	my ($direction, $ip);
	if($handle eq '<NONE>'){
	    $direction=$srcip . '-> Chatroom';
	    $ip=$srcip;
	}else{
	    $direction=$handle . '->' . $dstip;
	    $ip=$dstip;
	}
	
	$msg=&msgClean($msg);
	if($msg){
	    $msg="AIM##TYPE##Chat Message##TS##$datestamp##FAMILY##$family##IP##$ip##HANDL##$handle##FORMAT##$format##LANGUAGE##$language##MESSAGE##$msg";
	    $chatCount++;
	}

#Chat room join
    }elsif($family=~/^000d0008/){
	$debugmsg="Chat join";
	($chanid,$dataset)=&idFind($dataset);
	my $room;
	($format, $language, $room)=&getChatJoin($dataset);
	$debugmsg="Got chat join";
	if($format and $language and $room){
	    $msg="***CHAT ROOM JOIN****<BR>\n$srcip joined $room<BR>\nFORMAT: $format<BR>\nLANGUAGE: $language<BR>\n\n";
	    %sqlAdd=('table', "logs",'ts',$datestamp,'handle',"",'direction',$family,'message',$msg,'ip',$srcip);
	    $msg="AIM##TYPE##Chat Room Join##TS##$datestamp##FAMILY##$family##SRCIP##$srcip##FORMAT##$format##LANGUAGE##$language##ROOM##$room";
	}
#Buddy List
    }elsif($family=~/^00130003/){
	$debugmsg="Get Buddies";
	$dataset=~s/^([a-f0-9]{4})//;
	$flags=$1;
	$dataset=~s/^([a-f0-9]{8})//;
	my $SNACreqID=$1;
	if($dataset=~s/^.*2a02[a-f0-9]{8}00130006//){
	    %sqlAdd=('table','buddies','ts',$datestamp,'ip',$dstip);
	    my %buddylist=&getBuddies($dataset);
	    $debugmsg="Got Buddies";
	    $msg="AIM##TYPE##Buddy List##TS##$datestamp##FAMILY##00130006##DSTIP##$dstip";
	    my $x = 0;
	    my $key;
	    foreach $key(keys(%buddylist)){
		$x++;
		$sqlAdd{$key}=substr($buddylist{$key},1);
		$msg.="##GROUP$x##$key:" . substr($buddylist{$key},1);
	    }
	    $buddyListCount++;
	}
#Buddy list too (sometimes I found that the buddy list is sent with the 0013003 family as a sub packet (see above elsif statemetn)
    }elsif($family=~/^00130006/){
	 $debugmsg="Get other buddies";
	 %sqlAdd=('table','buddies','ts',$datestamp,'ip',$dstip);
	 my %buddylist=&getBuddies($dataset);
	 $debugmsg="got buddies";
	 $msg="AIM##TYPE##Buddy List##TS##$datestamp##FAMILY##00130006##DSTIP##$dstip";
	 my $x = 0;
	 my $key;
	 foreach $key(keys(%buddylist)){
	     $x++;
	     $sqlAdd{$key}=substr($buddylist{$key},1);
	     $msg.="##GROUP$x##$key:" . substr($buddylist{$key},1);
	 }
	 $buddyListCount++;
     }elsif($family=~/^00020015/){
	 	if($dataset=~s/0000[a-f0-9]{4}001500000001([a-f0-9]{2})//){
			my $buddySize=hex($1)*2;
			$dataset=~s/^([a-f0-9]{$buddySize})//;
			my $buddy=&convertHex($1);
			$msg="AIM##TYPE##Buddy Info Request##TS##$datestamp##FAMILY##00020015##SRCIP##$srcip##BUDDY##$buddy" if($buddy) ;
			%sqlAdd=('table','logs','ts',$datestamp,'ip',$srcip,'direction','00020015','message',"****Buddy Info Request****<BR>\n&nbsp;&nbsp;&nbsp;$buddy\n") if($buddy);
		}
     }elsif($family=~/^00020006/){
	 if($dataset=~s/^000000[a-f0-9]{2}0015([a-f0-9]{2})//){
	     my $buddySize=hex($1)*2;
	     $dataset=~s/^([a-f0-9]{$buddySize})//;
	     my $buddy=&convertHex($1);
	     if($dataset=~s/.*00030004[a-f0-9]{8}0001001f[a-f0-9]{62}0002([a-f0-9]{4})//){
		 my $infoSize=hex($1)*2;
		 $dataset=~s/^([a-f0-9]{$infoSize})//;
		 my $info=&convertHex($1);
		 %sqlAdd=('table','logs','ts',$datestamp,'ip',$dstip,'direction','00020006','message',"****Buddy Info Reply****<BR>\n&nbsp;&nbsp;&nbsp;$buddy<BR>\n&nbsp;&nbsp;&nbsp;$info\n") if($info && $buddy);
		 $info=&msgClean($info);
		 $msg="AIM##TYPE##Buddy Info##TS##$datestamp##FAMILY##00020006##DSTIP##$dstip##BUDDY##$buddy##INFO##$info" if($info && $buddy);
	     }
	 }
	 	

     }
 
    &pgsqlAdd(%sqlAdd) if(!$nodb and $sqlAdd{'table'});
    &printOUT($quiet, $msg, $outputFile) if($msg);
	
}



sub familyFind{
#Find family id
	my($dataset)=@_;
	my ($family, $dataportion);

    	return 0 if(!(length($dataset) > 0)); #Exit if $dataset is not > 0
    	
	if (length($dataset) % 2 != 0) {
		$dataset .= "0";
	}

	$dataportion=utf16($dataset);
	$dataportion=$dataportion->hex;
	$dataportion=~s/U\+//g;
	if($dataportion=~/^([0-9a-f]{4}\s){20}2a02/){
		$dataportion=~s/^([0-9a-f]{4}\s){20}//;
	}

	if($dataportion=~/^2a02/){
	    if ($dataportion=~s/^2a02 [0-9a-f]{4} [0-9a-f]{4} (00[a-f0-9]{2} 00[a-f0-9]{2})//){
			$family=$1;
			$family=~s/\s//g;
	    }
	    $dataportion=~s/\s//g;
	}else{
	    return 0;
	}

	return($family,$dataportion);
}

sub getSignon{
#Find AIM Handle
    my($dataset)=@_;
    my($handle, $lengthHandle)="";
    $dataset=~s/^.*000100([0-9a-f]{2})//;
    $lengthHandle=hex($1);
    $dataset=~s/^(([a-f0-9]{2}){$lengthHandle})//;
    $handle=&convertHex($1);
    $handle=~s/\s//;
    return $handle;
}

sub SMBInfo{
#Get SMB machine name and username
    my($ip)=@_;
    my(@SMBInfo)=`nmblookup -A $ip`;
    my($SMBInfo, $SMBName, $username);
    if($SMBInfo[1]!~/ACTIVE/){
	log_msg("Didn't find nmblookup entry: $SMBInfo[1]") if ($debug);
	$SMBName="";
	$username="";
    }else{
	$SMBName=$SMBInfo[1];
	$SMBName=~/(.*)\<.*/;
	$SMBName=$1;
	$SMBName=~s/\s//;
	$SMBName=~s/\<.*//;
	$SMBName=utf8($SMBName);
	$SMBName=$SMBName->hex;
	$SMBName=~s/U\+00//g;
	$SMBName=~s/\s20//g;
	my @digis=split(/\s/,$SMBName);
	$SMBName="";
	my ($digi, $te);
	foreach $digi(@digis){
	    $te=chr(hex($digi));
	    $SMBName.=$te;
	}
			    
	##Get user name
	$count=2;
	if($SMBInfo){
		while($SMBInfo[$SMBInfo-$count]=~/SMS/ || $SMBInfo[$SMBInfo-$count]!~/03/){ #Don't use SMS accounts
		    $count++;
		}
		$username=$SMBInfo[$SMBInfo-$count];
		$username=~/(.*)\s*\<03\>.*/;
		$username=$1;
		$username=~s/\s//;
		$username=utf8($username);
		$username=$username->hex;
		$username=~s/U\+00//g;
		$username=~s/\s20//g;
		@digis=split(/\s/,$username);
		$username="";
		foreach $digi(@digis){
		    $te=chr(hex($digi));
		    $username.=$te;
		}
	    }
	}
    return $SMBName, $username;
}

sub idFind{
    #Gets 8 byte id before handle length and handle is sent
    my($dataset)=@_;
    $dataset=~s/^[0-9a-f]{28}([a-f0-9]{4})//;
    my $chanid=$1;
    return $chanid, $dataset;
}
    

sub handleFind{
#Find AIM handle
    my($dataset)=@_;
    my($handle, $lengthHandle);
    $handle="";
    $dataset=~s/^([a-f0-9]{2})//;
    $lengthHandle=hex($1)*2;
    $dataset=~s/^([a-f0-9]{$lengthHandle})//;
    $handle=&convertHex($1);
    $handle=~s/\s//;
    return $handle, $dataset;
}

sub msgFind{
#Find AIM msg
	my($dataset)=@_;
	my $msg="";
    my $hexcomb=$dataset;
	$hexcomb=~s/.*01010?2?010100([a-f0-9]{2})//;
	my $length=hex($1)-4;
	$hexcomb=~s/00000000(.*)[00.*]?//;
	$msg=$1;
	$msg=~s/00.*//;
	$msg=&convertHex($msg);
	return $msg
}

sub msgClean{
#Remove HTML tags & NULL Chars from message
    my($msg)=@_;
    my $null=pack('@',0); #Thanks to Zoli
    $msg=~s/<([^>]|\n)*>//g;
    $msg=~s/\<\/.*//;
    $msg=~s/$null//g;
    return $msg;
}

sub pgsqlAdd{
    my(%values)=@_;
    my($table)=delete $values{'table'};
    my($sql)="";
    my ($key, $value, $sth);

    my $dsn="DBI:$driver:dbname=$database;host=$host";
    my $dbh=DBI->connect($dsn, $user, $password) || return 0;
    $dbh->{RaiseError}=0;

    # jhealy: revamped to work with postgres.
    # also cleaned up to use positional parameters

#Needed special case for buddy list
    if($table eq 'buddies') {
#		$sql="DELETE * FROM buddies WHERE ip='$values{'ip'}'";
#		$sth=$dbh->prepare($sql) || log_msg("Error preparing $DBI::errstr");

		my($datestamp)=delete $values{'ts'};
		my($ip)=delete $values{'ip'};

		# jhealy: create base parameterized SQL statement
		$sql='INSERT INTO ' . $table .
		  '(ts, ip, buddygroup, buddylist) VALUES (?, ?, ?, ?)';

		# jhealy: prepare the statement
		$sth=$dbh->prepare($sql) || log_msg("Error preparing $DBI::errstr");

		# jhealy: now call the statement for each buddylist entry
		foreach $key(keys %values){
		    $sth->execute($datestamp, $ip, $key, $values{$key}) ||
		      log_msg("Error executing $DBI::errstr");
#		    log_msg("$sql");
		}

#		log_msg("$sql");
    } else {

	my @columns = ();
	my @params = ();

	# scrub the data values
	foreach $key(keys %values){
	    my $value=$values{$key};
	    $value=~s/\\0//g;
	    push(@columns, $key);
	    push(@params, $value);
	}

	# build the parameterized SQL statement
	$sql='INSERT INTO ' . $table . '(' .
	  join(", ", @columns) . ') VALUES (?' . (', ?' x $#columns) . ')';

	$sth=$dbh->prepare($sql) || log_msg("problem preparing: ".$dbh->errstr);
	$sth->execute(@params) || log_msg("Error executing $DBI::errstr");
	}

#    log_msg("SQL: $sql") if ($debug);
}

sub printOUT{
    my($quiet, $msg, $outputFile)=@_;
    my($count,%printValues)="";
    if(defined($outputFile)){
		open(OUTPUT, ">>$outputFile") || die "Could not open file: $^E\n";
		print OUTPUT "$msg\n" || die "Could not print to file\n";
		close(OUTPUT);
    }
    if(!$quiet){
	#parse $msg to print it in a friendly manner
	my($key,$value)='';
	my($x)=0;
	#print "$msg\n";
	my ($heading, $part);
	while($msg=~/\#\#/){
	    $msg=~s/\#\#(.*)//;
	    if($x==0){
		$heading=$msg;
	    }else{
		if(($x%2)==0){
		    $value=&msgClean($msg);
		    $printValues{$key}=$value;
		}else{
		    $key=$msg;
		}
		
	    }
	    $part=$1;
	    $msg=$part;
	    $x++;
	}
	$printValues{$key}=&msgClean($part);
	print "$heading\n\tType: $printValues{'TYPE'}\n\tTimestamp: $printValues{'TS'}\n";
	delete $printValues{'TS'};
	delete $printValues{'TYPE'};
	foreach $key(keys(%printValues)){
	    if($key=~/GROUP/){
		$printValues{$key}=~s/(.*)://;
		my $buddyGroup=$1;
		print "\tGROUP: $buddyGroup\n";
		my @members=split(/,/,$printValues{$key});
		my $member;
		foreach $member(@members){
		    print "\t\t\t$member\n";
		}
	    }else{
		print "\t$key: $printValues{$key}\n";
	    }
	}
	print "\n\n";
        return;

	my @values=split(/\#\#/,$msg);
	if($values[1] eq '00040006' or $values[1] eq '00040007'){
	    $count=@values;
	    if($count > 5){ #file xfer
		print "\n\n****FILE XFER****\n";
		print "\t$values[0]\n";
		print "\tFrom: $values[2]:$values[3]\n";
		if($values[1]=~/0006/){
		    print "\tTO: $values[7]\n";
		}else{
		    print "\tFROM: $values[7]\n";
		}
		print "\tMSG: " . &msgClean($values[8]) . "\n";
		print "\tFileName: $values[9]\n";
	    }else{
		print "\n\n######MESSAGE#####\n$values[1]\n";
		print "\t$values[0]\n";
		if($values[1]=~/0006/){
		    print "\tFROM: $values[3]\n";
		    print "\tTO: $values[2]\n";
		}else{
  		    print "\tFROM: $values[2]\n";
		    print "\tTO: $values[3]\n";
		}
		print "\tMSG: " . &msgClean($values[4]) . "\n";
	    }
	}elsif($values[1] eq '00170006'){
	    print "\n\n!!!!!!!AIM LOGIN!!!!!!!!\n";
	    print "\tAt $values[0]\n";
	    if($values[4]){
		print "\t$values[4] on $values[5] ($values[2]) logged on as $values[3]\n";
	    }else{
		print "\t$values[3] is logging on from $values[2]\n";
	    }
	}elsif($values[1] eq '00170002'){
	    print "\n\n@@@@@\@AIM Version@@@@@@\n";
	    print "\tAt $values[0]\n";
	    print "\t$values[3] on $values[2] is using $values[4]\n";
	    print "\tCountry: $values[5]\tLanguage: $values[6]\n";
	}elsif($values[1]=~/000e/){
	    print "\n\n$$$$$\$Chat Message$$$$$\n";
	    print "\tAt $values[0]\n";
	    if($values[3] eq '<NONE>'){
		print "\t$values[2] sent a message to a chatroom\n";
		print "\tMSG: " . &msgClean($values[6]) . "\n";
	    }else{
		print "\t$values[3] received:\n";
		print "\t" . &msgClean($values[6]) . "\n";
	    }
	}elsif($values[1] eq '000d0008'){
	    print "\n\n%%%%%%%\%Chatroom Login%%%%%%\n";
	    print "\tAt $values[0]\n";
	    print "\t$values[2] joined $values[5]\n";
 	}elsif($values[1] eq '00130006'){
	    print "\n\n^^^^^^^Buddy List^^^^^^^^\n";
	    print "\tAt $values[0]\n";
	    print "\t$values[2] got the following list of buddies:\n";
	    splice(@values,0,3);
	    foreach $value (@values){
		my @group=split(":",$value);
		my @buddies=split(",",$group[1]);
		my $buddy;
		print "\t\t$group[0]:\n";
		foreach $buddy(@buddies){
		    print "\t\t\t$buddy\n";
		}
	    } 
	}else{
	    print "\n\nUNKOWNK FAMILY\n";
	    foreach $value(@values){
		print "$value\n";
	    }
	}
	
    }
	
}

sub getFromHandle{
    log_msg("Beginning handle lookups");
    my $dsn="DBI:$driver:dbname=$database;host=$host";
    my $dbh=DBI->connect($dsn, $user, $password) || return 0;
    $dbh->{RaiseError}=0;
   
    my $sql="SELECT id, ip, ts FROM logs WHERE direction='00040006' and fromHandle IS NULL OR fromHandle=''";
    my $sth=$dbh->prepare($sql) || die "problem preparing: ",$dbh->errstr,"\n";
    my ($id, $ip, $ts, @values, $handle, $sql2, $sth2);
    $sth->execute || die "Error executing $DBI::errstr\n";
    $sth->bind_col(1, \$id);
    $sth->bind_col(2, \$ip);
    $sth->bind_col(3, \$ts);
    while(@values=$sth->fetchrow_array()){
	$handle="";
	# jhealy parameterized
	$sql2='SELECT handle FROM handles WHERE ip=? AND ts<? ORDER BY ts DESC LIMIT 1';
	$sth2=$dbh->prepare($sql2) || die "Problem preparing: ",$dbh->errstr,"\n";
	$sth2->execute($ip, $ts) || die "Error executing $DBI::errstr\n";
	$sth2->bind_col(1, \$handle);
	my @row=$sth2->fetchrow_array();
	if($handle){
	    $sql2='UPDATE logs SET fromHandle=? WHERE id=?'; #Get the last handle logged in from that ip and before that timestamp
	    $sth2=$dbh->prepare($sql2) || die "Problem preparing: ",$dbh->errstr,"\n";
	    $sth2->execute($handle, $id) || die "Error executing $DBI::errstr\n";
	}
    }

    $sql="SELECT id, ip, ts FROM logs WHERE (direction='00040007' or direction='00020006' or direction='00020015') and handle is NULL or handle=''";
    $sth=$dbh->prepare($sql) || die "problem preparing: ",$dbh->errstr,"\n";
    $sth->execute || die "Error executing $DBI::errstr\n";
    $sth->bind_col(1, \$id);
    $sth->bind_col(2, \$ip);
    $sth->bind_col(3, \$ts);
    while(@values=$sth->fetchrow_array()){
	$handle="";
	$sql2='SELECT handle FROM handles WHERE ip=? AND ts<? ORDER BY ts DESC LIMIT 1';
	$sth2=$dbh->prepare($sql2) || die "Problem preparing: ",$dbh->errstr,"\n";
	$sth2->execute($ip, $ts) || die "Error executing $DBI::errstr\n";
	$sth2->bind_col(1, \$handle);
	my @row=$sth2->fetchrow_array();
	if($handle){
	    $sql2='UPDATE logs SET handle=? WHERE id=?'; #Get the last handle logged in from that ip and before that timestamp
	    $sth2=$dbh->prepare($sql2) || die "Problem preparing: ",$dbh->errstr,"\n";
	    $sth2->execute($handle, $id) || die "Error executing $DBI::errstr\n";
	}
    }
    log_msg("Lookups complete");
    return 1;
}

sub getVersion{
    my($dataset)=@_;
    my ($handle, $hlength, $version, $language, $country, $type, $value)="";
    $dataset=~s/^[a-f0-9]{12}//;
    while( ($type!~/4a/) && ($type ne "")){ #Changed thanks to Zoli
		$dataset=~s/^([a-f0-9]{4})00([a-f0-9]{2})//;
        $type=$1;
        my $len=(hex($2) * 2);
		$dataset=~s/([a-f0-9]{$len})//;
		$value=$1;
		if($type=~/0001/){
		    $handle=&convertHex($value);
		}elsif($type=~/0003/){
		    $version=&convertHex($value);
		}elsif($type=~/000f/){
		    $language=&convertHex($value);
		}elsif($type=~/000e/){
		    $country=&convertHex($value);
		}
    }
    return $handle, $version, $country, $language;
    
}

sub getFile{
    my($dataset)=@_;
    my($handle, $lenHandle, $ip,$msg,$filename,$lang,$format,$type,$len,$value)="";
    $dataset=~s/^([a-f0-9]{2})//;
    $lenHandle=hex($1) * 2;
    $dataset=~s/^([a-f0-9]{$lenHandle})//;
    $handle=&convertHex($1);
    $dataset=~s/0005(00[a-f0-9]{2})//;
#CHANGED
#commented out to get rid of error message
#    $lenPacket=hex($1);
    $dataset=~s/([a-f0-9]{4})//;
    my $flags=$1;
    if($flags=='0000'){
	$dataset=~s/([a-f0-9]{16})//;
	my $cookie=$1;
	my $port;

#check if it is a file xfer
	my $capString=$1 if($dataset=~s/.*(0946[a-f0-9]*53540000)//);
	if($capString){
	    $type='1';
	    while($type && $dataset && (length($dataset)>8)){
		$dataset=~s/^([a-f0-9]{4})([a-f0-9]{4})//;
		$type=$1;$len=(hex($2) * 2);
		#log_msg("TYPE: $type\tVALUE: $len");
		#log_msg("DATA: $dataset");

#error checking to make sure that the length is not longer then what is left 
		if($type!~/000f/ && ($len<=length($dataset))){
		    $dataset=~s/([a-f0-9]{$len})//;
		    $value=$1;
		}
		if($type=~/000e/){ #Language
		    $lang=&convertHex($value);
		}elsif($type=~/000d/){ #Format
		    $format=&convertHex($value);
		}elsif($type=~/2711/){ #File Name
		    $value=~s/^00010001[a-f0-9]{8}//;
		    $value=~s/00.*//;
		    $filename=&convertHex($value);
		}elsif($type=~/0005/){ #Port Number
		    $port=hex($value);
		}elsif($type=~/0003/){ #IP Address
		    my @octets=split('([a-f0-9]{2})',$value);
		    my $octet;
		    foreach $octet(@octets){
			$octet=hex($octet) if ($octet);
			$ip.=$octet . "." if($octet);
		    }
		    $ip=~s/\.$//;
		}elsif($type=~/000c/){ #Message
		    $msg=&convertHex($value);
#exit out 
		}elsif(length($dataset)<9){
		    $dataset='';
		}
	    }		
	}	 
	return ($handle, $capString, $lang, $format, $msg, $ip, $port, $filename);
    }else{
	return 0;
    }
}


#function to handle Chat events
sub getChats{
    my($family, $dataset)=@_;
    my($lenMsg,$handleset,$lenHandle,$handle,$type,$len,$value,$format,$language,$msg)='';
    if($family=~/0006/){
	#incoming message
	$dataset=~s/^0003([a-f0-9]{4})//;
	$lenMsg=hex($1)*2;
	$dataset=~s/^([a-f0-9]{$lenMsg})//;
	$handleset=$1;
	$handleset=~s/^([a-f0-9]{2})//;
	$lenHandle=hex($1)*2;
	$handleset=~s/^([a-f0-9]{$lenHandle})//;
	$handle=&convertHex($1);
    }elsif($family=~/0005/){
	#outgoing message
	$dataset=~s/[a-f0-9]{24}//;
	$handle='<NONE>';
    }else{
	return 0; #not ready for any other chat commands yet
    }
    $type=1;
    while($type && $dataset){
	$dataset=~s/^([a-f0-9]{4})([a-f0-9]{4})//;
	$type=$1;$len=(hex($2)*2);
	if($type!~/0005/ && ($len<=length($dataset))){
	    $dataset=~s/([a-f0-9]{$len})//;
	    $value=$1;
	}
	if($type=~/0002/){
	    $format=&convertHex($value);
	}elsif($type=~/0003/){
	    $language=&convertHex($value);
	}elsif($type=~/0001/){
	    $msg=&convertHex($value);
	}elsif(length($dataset)<9){
	    $dataset='';
	}
    }
    return ($handle,$format,$language,$msg)
}

sub getChatJoin{
    my($dataset)=@_;
    my($room,$language,$format,$value)='';
    my $type=1;
    while($type && $dataset){
	$dataset=~s/^([a-f0-9]{4})([a-f0-9]{4})//;
	$type=$1;
	my $len=(hex($2)*2);
	if($type!~/ff01/ && ($len<=length($dataset))){
	    $dataset=~s/([a-f0-9]{$len})//;
	    $value=$1;
	}
	if($type=~/00d6/){
	    $format=&convertHex($value);
	}elsif($type=~/00d7/){
	    $language=&convertHex($value);
	}elsif($type=~/00d3/){
	    $room=&convertHex($value);
	}elsif(length($dataset)<9){
	    $dataset='';
	}
    }
    if($format and $language and $room){
	return ($format, $language, $room);
    }else{
	return 1;
    }
}

#added this function to convert hex to string for me
sub convertHex{
    my($value)=@_;
    my($incr,$bit,$msg)="";
    for($incr=0;$incr<length($value);$incr+=2){
	$bit=substr($value,$incr,2);
#CHANGED
#Only print printable characters, duh! should have been done a long time ago
	if(hex($bit)>=32 && hex($bit)<=126){ 
	    $msg.=chr(hex($bit));
	}
    }		 
    return $msg;
}

#Function to get buddy list
sub getBuddies{
    my($dataset)=@_;
    my($verSSI,$revNum,$lenItem,$nameItem,$groupID,$buddyID,$typeItem,$lenAddData,$AddData,$type,$len,$AddValue,$key,$buddy,%buddylist,%groups)="";
    $dataset=~s/^[a-f0-9]{12}([a-f0-9]{2})//;
    $verSSI=$1;
    $dataset=~s/^([a-f0-9]{4})//;
    $revNum=$1;
	    #Now comes list of items
    while(length($dataset)>4){ #Changed thanks to Zoli
	$dataset=~s/^([a-f0-9]{4})//;
	$lenItem=hex($1) * 2;
	if($lenItem<length($dataset)){
	    $dataset=~s/^([a-f0-9]{$lenItem})//;
	    $nameItem=$1;
	    $dataset=~s/^([a-f0-9]{4})//;
	    $groupID=$1;
	    $dataset=~s/^([a-f0-9]{4})//;
	    $buddyID=$1;
	    $dataset=~s/^([a-f0-9]{4})//;
	    $typeItem=$1;
	    $dataset=~s/^([a-f0-9]{4})//;
	    $lenAddData=hex($1)*2;
	    if($typeItem eq '0000'){ #Buddy
		#Add to buddy list
		$buddylist{$groups{$groupID}}.=",".&convertHex($nameItem);
	    }elsif($typeItem eq '0001'){ #GROUP
		#Add to groups Assoc Array
		$groups{$groupID}= &convertHex($nameItem)  if($groupID ne '0000');
	    }elsif($typeItem eq '0002'){
#			    log_msg("This is the allow list");
	    }
	    if($lenAddData<length($dataset)){
		$dataset=~s/^([a-f0-9]{$lenAddData})//;
		$AddData=$1;
		$type=1;
		while($AddData && $type && (!length($AddData)>8)){
		    $AddData=~s/^([a-f0-9]{4})([a-f0-9]{4})//;
		    $type=$1;$len=hex($2) * 2;
		    if($len<=length($AddData)){
			$AddData=~s/^([a-f0-9]{$len})//;
			$AddValue=$1;
		    }
		}
	    }
	}else{
	    return(%buddylist);
	}
	    
    }
    return(%buddylist);
 }

sub print_config()
{
	print "Running with:\n";
	print "\tdumpfile=$dumpfile\n" if defined($dumpfile);
	print "\thandleFile=$handleFile\n" if defined($handleFile);
	print "\tdumpHandles=$dumpHandles\n" if defined($dumpHandles);
	print "\tpacketCount=$count\n" if defined($count);
	print "\tdev=$dev\n" if defined($dev);
	print "\tfilter=$filter_str\n" if defined($filter_str);
	print "\tpromisc=$promisc\n" if defined($promisc);
	print "\ttimeout=$to_ms\n" if defined($to_ms);
	print "\tSMB=$SMB\n" if defined($SMB);
	print "\tDaemon Mode=$daemonMode\n" if defined($daemonMode);
	print "\tnodb=$nodb\n" if defined($nodb);
	print "\tquiet=$quiet\n" if defined($quiet);
	print "\thost=$host\n" if defined($host);
	print "\tuser=$user\n" if defined($user);
	print "\tpassword=****\n" if defined($password);
	print "\tgetHandles Mode=$getHandles\n" if defined($getHandles);
	print "\tuseSyslog=$useSyslog\n" if defined($useSyslog);
	print "\tparentPollTimeout=$parentPollTimeout\n" if defined($parentPollTimeout);
	print "\tchildCPUMaxPct=$childCPUMaxPct\n" if defined($childCPUMaxPct);
}

sub getOptions{
    my(@ARGV)=@_;
	my $arg;
    if(scalar(@ARGV) > 0 && $ARGV[0]=~/-C=(.*)/){
	my $configfile=$1;
	open(CONFIG,"$configfile") || log_msg("Error opening config file, using defaults instead.");
	while(<CONFIG>){
	    $arg=$_;
	    if($arg!~/^\#/){
	       $handleFile=$1 if($arg=~/handlefile=(.*)/);
	       $dumpHandles=$1 if($arg=~/dumpHandles=(.*)/);
	       $dumpfile=$1 if($arg=~/dumpfile=(.*)/);
	       $outputFile=$1 if($arg=~/outputFile=(.*)/);
	       $count=$1 if($arg=~/packetCount=(.*)/);
	       $dev=$1 if($arg=~/dev=(.*)/);
	       $filter_str=$1 if($arg=~/filter='(.*)'/);
	       $promisc=1 if ($arg=~/promisc=1/);
	       $to_ms=$1 if ($arg=~/timeout=(.*)/);
	       $nodb=1 if ($arg=~/nodb=1/);
	       $quiet=1 if ($arg=~/quiet=1/);
	       $SMB=1 if ($arg=~/SMB=1/);
	       $daemonMode=1 if ($arg=~/daemon=1/);
	       $host=$1 if ($arg=~/host=(.*)/);
	       $user=$1 if ($arg=~/user=(.*)/);
	       $debug=$1 if ($arg=~/debug=(.*)/);
	       $password=$1 if ($arg=~/password=(.*)/);
	       $database=$1 if($arg=~/database=(.*)/);
	   	  $useSyslog=$1 if ($arg=~/useSyslog=(.*)/);
	   	  $parentPollTimeout=$1 if ($arg=~/parentPollTimeout=(.*)/);
	   	  $childCPUMaxPct=$1 if ($arg=~/childCPUMaxPct=(.*)/);
	   }
	}
	if (scalar(@ARGV) > 1 && $ARGV[1] =~ /--getHandles/){
		$getHandles=1;
		$daemonMode=0;  # don't bother b/c we want to exit when we're done
	}
	close(CONFIG);
	print_config();
    }else{
	foreach $arg (@ARGV){
	    $getHandles=1 if($arg=~/--getHandles/);
	    $dumpHandles=$1 if($arg=~/--dumpHandles/);
	    $dumpfile=$1 if($arg=~/-r=(.*)/);
	    $handleFile=$1 if($arg=~/-HF=(.*)/);
	    $outputFile=$1 if($arg=~/-O=(.*)/);
	    $count=$1 if($arg=~/-c=(.*)/);
	    $dev=$1 if($arg=~/-d=(.*)/);
	    $filter_str=$1 if($arg=~/-f=(.*)/);
	    $promisc=1 if ($arg=~/-p/);
	    $to_ms=$1 if ($arg=~/-to=(.*)/);
	    $nodb=1 if ($arg=~/--nodb/);
	    $quiet=1 if ($arg=~/--quiet/);
	    $SMB=1 if ($arg=~/--SMB/);
	    $daemonMode=1 if ($arg=~/-D/);
	    if($arg=~/--h/ or $arg=~/-h/){
		print "**************\nAimSniff $ver\n**************\n";
		print "AIM Sniff Copyright (C) 2002 Shawn Grimes\n";
		print "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2.\n";
		print "This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.\n";
		print "You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n";
		print "You may also contact me directly with any questions at: grimessh\@users.sourceforge.net\n";
		print "\n###########\nTo use:\n";
		print "\t-C=filename <-Get AIM Sniff options from a config file\n";
		print "\t-r=filename <-Read a PCAP file instead of doing a live capture\n";
		print "\t-O=filename <-Output to a file\n";
		print "\t-HF=filename <-Output handle/ip hash\n";
		print "\t-c=integer <-The number of packets to read before quitting\n";
		print "\t-d=dev <-The device to capture packets from\n";
		print "\t-f='filter string' <-String to filter on enclosed in single quotes\n\t\t(DEFAULT: 'tcp and port 5190') -- Should only have to be specified if you think AIM is running on a different port\n";
		print "\t-p <-Place the device into promiscuous mode\n";
		print "\t-to=integer <-Read timeout in ms\n";
		print "\t--SMB <-Turn SMB lookups 'on' to get NT domain usernames with AIM logins, Off by default\n";
		print "\t--nodb <-Do not dump to a DB, only dump to STDOUT\n";
		print "\t--quiet <-Do not print anything but errors to STDOUT\n";
		print "\t-D <-Run as daemon\n";
		print "\t--getHandles <-Do not do anything with PCAP but populate the fromHandle field in the logs table\n";
		print "\t--dumpHandles <--Dumps the hash containing AIM Handles and IP Addresses to syslog\n";
		print "\nDefaults:\n";
		print "\t-d=eth0\n\t-f='tcp and port 5190'\n\t-p\n\t-to=1000\n";
		exit;
	    }
	}
    }
    $daemonMode=0 if($dumpfile);
    return 1;
}

sub MSNparse{
    my($dataset)=@_;
    my($dataportion,@values,$type,%msnHash)="";
    #Need to convert to hex to parse easier
    if((length($dataset) > 0) or ((length($dataset) % 2) != 0)){
	$dataportion=utf16($dataset);
	$dataportion=$dataportion->hex;
	$dataportion=~s/U\+//g;
	$dataportion=~s/\s//g; 
    }
    if($dataportion=~s/0d0a/ /g){
	if($dataportion=~s/\s\s(.*)//){
	    $msnHash{'Message'}=&convertHex($1);
	}
	@values=split(/\s/,$dataportion);
	$type=shift(@values);
	$type=&convertHex($type);

	my $value;
	foreach $value(@values){
	    $value=~/(.*)3a20(.*)/;
	    my $key=$1;
	    $value=$2;
	    $key=&convertHex($key);
	    $value=&convertHex($value);
	    $msnHash{$key}=$value;
	}
	if(!$msnHash{'TypingUser'}){
	    log_msg("TYPE: $type");
	    my $key;
	    foreach $key (keys(%msnHash)){
		log_msg("$key=$msnHash{$key}");
	    }
	    log_msg("#######################################");
	    log_msg("#######################################");
	}
    }
#    while($dataset){
#	$dataset=~s/(.*)
}

sub dump_child_stats()
{
    my %stats;
    Net::Pcap::stats($pcap_t, \%stats);
    log_msg("Packets Received: $stats{'ps_recv'}");
    log_msg("Packets Dropped: $stats{'ps_drop'}");
    log_msg("Packets Dropped by interface: $stats{'ps_ifdrop'}");
    log_msg("Messages found: $msgCount");
    log_msg("Logins found: $loginCount");
    log_msg("Buddy List Captured: $buddyListCount");
    log_msg("File Xfers: $fileCount");
    log_msg("Chat Messages: $chatCount");
}

sub LeaveNow{
    log_msg("Child exiting");
    if($debug2){
    	unlink("/tmp/AS.log"); sysopen(LOG,'/tmp/AS.log',O_WRONLY|O_EXCL|O_CREAT,0600);
    	print LOG "$debugmsg\n";
    	close(LOG);
    }

	&dump_child_stats();

	untieNameIp();
	unlink '/var/run/aimsniff.pid';
    exit;
}

my %nameIPs;

sub tieNameIp()
{
	if (defined($handleFile)) {
		tie(%nameIPs, 'GDBM_File', $handleFile, &GDBM_WRCREAT, 0600, ) || 
			die "Can't tie nameIPs: $!";
	}

	if ($dumpHandles == 1)
	{
		log_msg('Handle File Dump Follows...');
		my ($key, $val);
		while (($key, $val) = each %nameIPs)
		{
			log_msg($key . ' = ' . $val);
		}
	}
}

sub untieNameIp()
{
	untie %nameIPs if tied(%nameIPs);
}

sub setNameIp($$)
{
	my ($ip, $name) = @_;
	$nameIPs{$ip} = $name;
	log_msg("Set $name for IP $ip") if ($debug);
}

sub getNameIp($)
{
	my ($ip) = @_;
	my $name = (defined $nameIPs{$ip} ? $nameIPs{$ip} : '');
	log_msg("Returning \"$name\" for $ip") if ($debug);
	$name;
}

