#!/usr/bin/perl

######
# bluetraces.pl: Monitors and records visible Bluetooth devices. When 
# visible for the first time, a detail scan is done for each target.
#
# Dec. 2006, Armin Hornung
# based on "braces" from "The Shmoo Group" / Blackhat '04
######

# arguments:
$argc = $#ARGV + 1;
if ( $argc != 3 || !(($ARGV[2] eq "g") || ($ARGV[2] eq "p")))
{
    print "usage: bluetraces.pl <seconds_sleep> <sensor_no> <g|p>
            \n g: runs on gumstix w. microperl and non-verbose, 
            \n p: PC, regular operation\n";
    exit(0);
}
$SLEEP_TIME = $ARGV[0];
$SENSOR = $ARGV[1];
$OPMODE = $ARGV[2];

if ($OPMODE eq "g"){
    # in Gumstix-mode: use microperl, and don't verbose output
    $BPRINT = 'microperl bp.pl';
    $VERBOSE=0;
}

else {
    # in PC-mode: use perl, and verbose output
    $BPRINT = 'perl bp.pl';
    $VERBOSE=1;
}

my $HCITOOL='hcitool';
my $SDPTOOL='sdptool';
my @macs;

# list of devices that were already scanned "in detail"
my @knowndev;

#trace: MAC addr. and timestamp
my $OUT_TRACE='trace.sql';
#device details
my $OUT_DEV = 'dev.sql';

# output a new line in the SQL files:
$newl = "-- new scan\n";
open( OUTPUT, ">>$OUT_TRACE" ) || print "error opening file!: ($OUT_TRACE)\n";
print OUTPUT $newl;
close( OUTPUT );
open( OUTPUT, ">>$OUT_DEV" ) || print "error opening file!: ($OUT_DEV)\n";
print OUTPUT $newl;
close( OUTPUT );

# Start the new output:
while(1)
{
    
    if ( $VERBOSE ) { print ">>> scanning for local, discoverable devices..."; }

    # run hcitool to find discoverable devices.
    open(HCI_PIPE, "$HCITOOL scan | grep -E \"..:..:\"|" );
    $count = 0;

    # timestamp of inquiry:
    ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
    $timestamp= ($year+1900).'-'.($mon+1).'-'.$mday." $hour:$min:$sec";

    while (<HCI_PIPE>)
    {
        chomp;

        @scan = split( ' ', $_, 2 );
        # mac address
        $mac = $scan[0];
        if ( $VERBOSE ) { print "\n   * found device mac=$mac... "}
        $inq_incomplete = 0;
        
        # space-saving: inq for details only once:
        if (!($knowndev{$mac})) {
            
            # assigned BT-name, escaped:
            $name = $scan[1];
            $name =~ s/'/\\'/g;
            
            # error in inquiry (e.g. timeout):
            if ($name =~ m/^n\/a$/){
                if ($VERBOSE) {print "No BT-Name obtained: \"". $name ."\", skipping details"};
                $inq_incomplete = 1;
            }
            
            if (!$inq_incomplete){
                # getting Blueprint with  bp.pl, see trifinite.org:
                open(BPRINT,"$SDPTOOL browse --tree --l2cap $mac | $BPRINT $mac -mkdb|");
                while (<BPRINT>){
                    chomp;
                    $blueprint = $_;
                }
                close(BPRINT);
                
                # error in inquiry (e.g. timeout): no services blueprint
                if ($blueprint =~ m/\@0$/){
                    if ($VERBOSE) {print "No valid Blueprint obtained: \"".$blueprint."\", skipping details"};
                    $inq_incomplete = 1;    
                }
            }
            
            if (!$inq_incomplete){
                # obtain device class:
                open( HCI_CLASS, "$HCITOOL inq $mac | grep \"$mac\"|" );
                while (<HCI_CLASS>){
                    chomp;
                    @cl_scan = split( ' ', $_, 6 );
                    $class = $cl_scan[5];
                    $class =~ s/0x//; # remove 0x marking the number as Hex
                
                }
                close(HCI_CLASS);
                
                $content_det = "('$mac','$name','$class','$blueprint'),\n";
                if ( $VERBOSE )
                    { print "details: name=$name; class=$class; BPrint=$blueprint; saving..."; }
                save_details();
                $knowndev{$mac} = true;
                if ( $VERBOSE ) { print "(done)"; }
            }
        }
        else {if ($VERBOSE && !$inq_incomplete)
            {print "already known.";}}

	$mac =~ s/://g;  # remove ":" divider
	# Array of all Mac-Adresses found in this scan:
        push(@macs, $mac);
	$count++;
    }

    close( HCI_PIPE );

    if ($VERBOSE) { print "\n\n>>> Inquiry found $count devices, saving..."; }
    # clean up:
    $|=1;
    save_sql();
    @macs=();
    if ( $VERBOSE ) 
        { print "(done).\n \n----------------------------------- 
            \n sleeping for $SLEEP_TIME sec..."; }

    $|=1;
    sleep($SLEEP_TIME);
    if ( $VERBOSE ) { print "(done)\n"; }
}


###
# Saves the found BT-trace to a line of a pgSQL query.
# (rest of SQL added later for smaller filesize)
###
sub save_sql
{   
    $content=();
    foreach $mac (@macs)
    {
        #The SQL query (shortened) for the trace:
        $content = $content. "('$mac','$timestamp','$SENSOR'), \n";
    }
    open( OUTPUT, ">>$OUT_TRACE" ) || print "error opening file!: ($OUT_TRACE)\n";
    print OUTPUT $content;
    close( OUTPUT );
}

###
# Saves the found BT-trace to a line of a pgSQL query.
# (rest of SQL added later for smaller filesize)
###
sub save_details
{   
    open( OUTPUT, ">>$OUT_DEV" ) || print "error opening file!: ($OUT_DEV)\n";
    print OUTPUT $content_det;
    close(OUTPUT);
}



