File Coverage

blib/lib/NetStumbler/Stumbler.pm
Criterion Covered Total %
statement 35 270 12.9
branch 8 70 11.4
condition 1 3 33.3
subroutine 6 20 30.0
pod 17 17 100.0
total 67 380 17.6


line stmt bran cond sub pod time code
1             package NetStumbler::Stumbler;
2            
3 1     1   24764 use strict;
  1         2  
  1         39  
4 1     1   5 use warnings;
  1         2  
  1         34  
5 1     1   5 use Carp qw(cluck carp croak);
  1         7  
  1         3840  
6            
7             require NetStumbler::Wap;
8             require Exporter;
9            
10             our @ISA = qw(Exporter);
11            
12             #
13             # We do not Export anything
14             #
15            
16             our $VERSION = '0.07';
17             our $wapinfo = NetStumbler::Wap->new();
18            
19             =head1 Object Methods
20            
21             =head2 new()
22            
23             Returns a new Stumbler object.
24            
25             =cut
26            
27             sub new
28             {
29              
30 1     1 1 12 my $proto = shift;
31 1   33     9 my $class = ref($proto) || $proto;
32 1         3 my $self = {};
33 1         3 bless ($self, $class);
34 1         9 $wapinfo->initialize();
35 1         4218 return $self;
36             }
37            
38            
39             =head2 parseNSSummaryLine($line)
40            
41             Params:
42             -string A line from a summary file
43             Returns:
44             an array of seperated values corresponding to output of a NetStumbler summary export
45             **NOTE**
46            
  • Conversion of the verbose GPS data to doubles in standard GPS format
  • 47            
  • Blank SSID will be set to "Hidden"
  • 48            
  • The time data will have GMT stripped off
  • 49            
  • If the line is not correctly formed return an empty list
  • 50             Example:
    51             my @line = $obj->parseNSSummaryLine($line);
    52             print "Line [@line]\n";
    53            
    54             =cut
    55            
    56            
    57             sub parseNSSummaryLine
    58             {
    59 0     0 1 0 my $self = shift;
    60 0         0 my $line = shift;
    61 0         0 my($d1,$lat,$d2,$lon,$ssid,$type,$mac,$time,$snr,$sig,$noise,$name,$flags,$chanbits,$bcninterval,$datarate,$lastchannel);
    62 0         0 my ($nlat,$nlon);
    63 0 0       0 if($line =~ /^#/)
    64             {
    65 0         0 return [];
    66             }
    67 0 0       0 if($line =~ /(\w).(\d+\.\d+)\t(\w).(\d+\.\d+)\t\(.(.*).\)\t(\S+)\t\(.(\w+\:\w+\:\w+\:\w+\:\w+\:\w+).\)\t(.*)\[.(\w+).(\w+).(\w+).\]\t\#.\((.*)\)\t(\d+)\t(\d+)\t(\d+)\t(\d+)\t(\d+)/)
    68             {
    69 0         0 $d1 = $1;
    70 0         0 $lat = $2;
    71 0         0 $d2 = $3;
    72 0         0 $lon = $4;
    73 0         0 $ssid = $5;
    74 0         0 $type = $6;
    75 0         0 $mac = $7;
    76 0         0 $time = $8;
    77 0 0       0 if($time)
    78             {
    79 0         0 $time =~ s/\(GMT\)//;
    80             }
    81 0         0 chomp($time);
    82 0 0       0 if($mac)
    83             {
    84 0         0 $mac =~ s/\://g;
    85             }
    86 0         0 $snr = $9;
    87 0         0 $sig = $10;
    88 0         0 $noise = $11;
    89 0         0 $name = $12;
    90 0 0       0 unless($name) { $name = "";};
      0         0  
    91 0         0 $flags = hex($13);
    92 0         0 $chanbits = hex($14);
    93 0         0 $bcninterval = $15;
    94 0 0       0 unless($16) { $datarate = $16/10; };
      0         0  
    95 0         0 $lastchannel = $17;
    96 0 0       0 if(!$ssid){ $ssid = "Hidden";}
      0         0  
    97 0 0       0 if(!$lat){ $lat = 0.00; }
      0         0  
    98 0 0       0 if(!$lon){ $lon = 0.00; }
      0         0  
    99 0 0       0 if(!$d1){ $d1 = "N";}
      0         0  
    100 0 0       0 if(!$d2){ $d2 = "W";}
      0         0  
    101 0 0       0 if($d1 =~ /[sS]/){$lat = "-$lat";}
      0         0  
    102 0 0       0 if($d2 =~ /[wW]/){$lon = "-$lon";}
      0         0  
    103 0         0 return ($lat,$lon,$ssid,$type,$mac,$time,$snr,$sig,$noise,$flags,$chanbits,$datarate,$lastchannel);
    104             }
    105             else
    106             {
    107 0         0 return [];
    108             }
    109             }
    110            
    111             =head2 isSummary($file)
    112            
    113             Params:
    114             -string fully qualified filename
    115             Returns:
    116             true if the file is in NetStumbler Summary format
    117             Example:
    118             if($obj->isSummary($file))
    119             {
    120             # do something here
    121             }
    122            
    123             =cut
    124            
    125             sub isSummary
    126             {
    127 1     1 1 3 my $self = shift;
    128 1         2 my $file = shift;
    129 1 50       50 open(FD,$file) or cluck "Failed to open input file $!\n";
    130 1         3 my $found = 0;
    131 1         23 while()
    132             {
    133 2 50       9 if(/^#/)
    134             {
    135 2 100       9 if(/wi-scan summary with extensions/)
    136             {
    137 1         2 $found = 1;
    138             }
    139             }
    140 2 100       7 if($found)
    141             {
    142 1         2 last;
    143             }
    144             }
    145 1         14 close(FD);
    146 1         5 return $found;
    147             }
    148            
    149             =head2 isNS1($file)
    150            
    151             Params:
    152             -string fully qualified filename
    153             Returns:
    154             true if the file is in NetStumbler NS1 file
    155             Example:
    156             if($obj->isNS1($file))
    157             {
    158             # do something here
    159             }
    160            
    161             =cut
    162            
    163             sub isNS1
    164             {
    165 1     1 1 8 my $self = shift;
    166 1         2 my $file = shift;
    167 1 50       45 open(FD,$file) or cluck "Failed to open input file $!\n";
    168 1         2 my $magic;
    169 1         9 binmode(FD);
    170 1         25 read(FD,$magic,4);
    171 1 50       4 if($magic eq 'NetS') { return 1; }
      1         9  
    172 0           else { return 0; }
    173             }
    174            
    175             =head2 isKismetCSV($file)
    176            
    177             Params:
    178             -string fully qualified filename
    179             Returns:
    180             true if the file is in Kismet CSV file
    181             Example:
    182             if($obj->isKismetCSV($file))
    183             {
    184             # do something here
    185             }
    186            
    187             =cut
    188            
    189             sub isKismetCSV
    190             {
    191 0     0 1   my $self = shift;
    192 0           my $file = shift;
    193 0 0         open(FD,$file) or cluck "Failed to open input file $!\n";
    194 0           my $magic;
    195 0           binmode(FD);
    196 0           read(FD,$magic,7);
    197 0 0         if($magic eq 'Network') { return 1; }
      0            
    198 0           else { return 0; }
    199             }
    200            
    201             =head2 parseKismetCSV($file)
    202            
    203             Params:
    204             -string fully qualified filename
    205             Returns:
    206             list of lists each item in the sublist corresponds to a list from kismet summary file
    207             Example:
    208             $ref = $obj->parseKismetCSV($file);
    209             # The list is as follows
    210             0 Network
    211             1 NetType
    212             2 ESSID
    213             3 BSSID
    214             4 Info
    215             5 Channel
    216             6 Cloaked
    217             7 WEP
    218             8 Decrypted
    219             9 MaxRate
    220             10 MaxSeenRate
    221             11 Beacon
    222             12 LLC
    223             13 Data
    224             14 Crypt
    225             15 Weak
    226             16 Total
    227             17 Carrier
    228             18 Encoding
    229             19 FirstTime
    230             20 LastTime
    231             21 BestQuality
    232             22 BestSignal
    233             23 BestNoise
    234             24 GPSMinLat
    235             25 GPSMinLon
    236             26 GPSMinAlt
    237             27 GPSMinSpd
    238             28 GPSMaxLat
    239             29 GPSMaxLon
    240             30 GPSMaxAlt
    241             31 GPSMaxSpd
    242             32 GPSBestLat
    243             33 GPSBestLon
    244             34 GPSBestAlt
    245             35 DataSize
    246             36 IPType
    247             37 IP
    248             #
    249            
    250             =cut
    251            
    252             sub parseKismetCSV
    253             {
    254 0     0 1   my $self = shift;
    255 0           my $file = shift;
    256 0           my $fh;
    257 0           open(FH,$file);
    258 0           $fh = \*FH;
    259 0           my $line;
    260 0           <$fh>;
    261 0           $line = <$fh>;
    262 0           my @list;
    263 0           while(<$fh>)
    264             {
    265 0           $line = $_;
    266 0           push(@list,[split(/;/,$line)]);
    267             }
    268 0           return @list;
    269             }
    270            
    271             =head2 parseNS1($file)
    272            
    273             Params:
    274             -string fully qualified filename
    275             Returns:
    276             list of lists each item in the sublist corresponds to a list from parseNSSummary
    277             Example:
    278             $ref = $obj->parseNS1($file);
    279            
    280             =cut
    281            
    282             sub parseNS1
    283             {
    284 0     0 1   my $self = shift;
    285 0           my $file = shift;
    286 0           my $fh;
    287 0           open(FH,$file);
    288 0           $fh = \*FH;
    289 0           my ($sig,$ver,$apCount);
    290 0           my $line;
    291 0           read($fh,$line,12);
    292 0           ($sig,$ver,$apCount) = unpack("A4LL",$line);
    293 0 0         unless($sig =~ /NetS/) { return []; }
      0            
    294 0 0         unless($ver > 6) { carp "Version $ver not supported!\n"; return []; }
      0            
      0            
    295 0           my @list;
    296 0           for(my $i=0;$i<$apCount;$i++)
    297             {
    298 0           push(@list, [ readAPInfo($fh,$ver) ]);
    299             }
    300 0           return @list;
    301             }
    302            
    303             =head1 Private Methods
    304            
    305             =head2 readAPInfo($fileHandle,$fileVersion)
    306            
    307             Params:
    308             reference - Filehandle reference
    309             number - NS1 Version
    310             Returns:
    311             list - smae format as parseNSSummary
    312            
    313             =cut
    314            
    315            
    316             sub readAPInfo
    317             {
    318 0     0 1   my @apData;
    319 0           my $fh = shift;
    320 0           my $ver = shift;
    321 0           my $sl = readUint8($fh);
    322 0           my $sid = readChars($fh,$sl);
    323 0           my $mac;
    324             my @ml;
    325 0           for(my $ms=0;$ms<6;$ms++)
    326             {
    327 0           push(@ml,readUint8($fh));
    328             }
    329 0           $mac = sprintf("%02x:%02x:%02x:%02x:%02x:%02x",@ml);
    330 0           my($mSig,$mNoi,$mSnr,$flags,$beacon,$fs,$ls,$blat,$blon,$dCount);
    331 0           $mSig = readint32($fh);
    332 0           $mNoi = readint32($fh);
    333 0           $mSnr = readint32($fh);
    334 0           $flags = readUint32($fh);
    335 0           $beacon = readUint32($fh);
    336 0           $fs = readint64($fh);
    337 0           $ls = readint64($fh);
    338 0           $blat = readDouble($fh);
    339 0           $blon = readDouble($fh);
    340 0           $dCount = readUint32($fh);
    341 0           push(@apData,$blat);
    342 0           push(@apData,$blon);
    343 0           push(@apData,$sid);
    344 0 0         if($wapinfo->isInfrastructure($flags))
    345             {
    346 0           push(@apData,"BSS");
    347             }
    348             else
    349             {
    350 0           push(@apData,"Ad-Hoc");
    351             }
    352 0           push(@apData,$mac);
    353 0           push(@apData,$fs);
    354 0           push(@apData,$mSnr);
    355 0           push(@apData,$mSig);
    356 0           push(@apData,$mNoi);
    357            
    358 0           for(my $xl=0;$xl<$dCount;$xl++)
    359             {
    360 0           my $rc = readAPData($fh,$ver);
    361             }
    362 0           my $nl = readUint8($fh);
    363 0           my $name = readChars($fh,$nl);
    364 0           push(@apData,$flags);
    365 0           push(@apData,$name);
    366 0 0         if($ver > 6)
    367             {
    368 0           my ($channels,$lchan,$ip,$min,$maxNoise,$dr,$ipsub,$ipmask,$pflags,$ieLength);
    369 0           $channels = readint64($fh);
    370 0           $lchan = readUint32($fh);
    371 0           $ip = readUint8($fh);
    372 0           $ip .= "." . readUint8($fh);
    373 0           $ip .= "." . readUint8($fh);
    374 0           $ip .= "." . readUint8($fh);
    375 0           $min = readint32($fh);
    376 0           $maxNoise = readint32($fh);
    377 0           $dr = readUint32($fh);
    378 0           $ipsub = readUint8($fh);
    379 0           $ipsub .= "." . readUint8($fh);
    380 0           $ipsub .= "." . readUint8($fh);
    381 0           $ipsub .= "." . readUint8($fh);
    382 0           push(@apData,$channels);
    383 0           push(@apData,$beacon);
    384 0           push(@apData,$dr);
    385 0           push(@apData,$lchan);
    386 0 0         if($ver > 8)
    387             {
    388 0           $ipmask = readUint8($fh);
    389 0           $ipmask .= "." . readUint8($fh);
    390 0           $ipmask .= "." . readUint8($fh);
    391 0           $ipmask .= "." . readUint8($fh);
    392             }
    393 0 0         if($ver > 11)
    394             {
    395 0           $pflags = readUint32($fh);
    396 0           $ieLength = readUint32($fh);
    397 0 0         if($ieLength > 0)
    398             {
    399 0           for(my $iel=0;$iel < $ieLength;$iel++)
    400             {
    401 0           readUint8($fh);
    402             }
    403             }
    404             }
    405             }
    406 0           return @apData;
    407             }
    408            
    409             =head2 readAPData($fileHandle,$fileVersion)
    410            
    411             Params:
    412             reference - Filehandle reference
    413             number - NS1 Version
    414             Returns:
    415             nothing
    416             TODO:
    417             Add a return value to this method to build graphs
    418            
    419             =cut
    420            
    421             sub readAPData
    422             {
    423 0     0 1   my $fh = shift;
    424 0           my $ver = shift;
    425 0           my ($time,$sig,$noise,$loc);
    426 0           $time = readint64($fh);
    427 0           $sig = readint32($fh);
    428 0           $noise = readint32($fh);
    429 0           $loc = readint32($fh);
    430 0 0         if($loc > 0)
    431             {
    432 0           readGPSData($fh);
    433             }
    434             }
    435            
    436             =head2 readGPSData($fileHandle)
    437            
    438             Params:
    439             reference - Filehandle reference
    440             Returns:
    441             nothing
    442             TODO:
    443             Add a return value to this method to build graphs
    444            
    445             =cut
    446            
    447             sub readGPSData
    448             {
    449 0     0 1   my $fh = shift;
    450 0           my ($lat,$lon,$alt,$numSat,$speed,$track,$magVar,$hdop);
    451 0           $lat = readDouble($fh);
    452 0           $lon = readDouble($fh);
    453 0           $alt = readDouble($fh);
    454 0           $numSat = readUint32($fh);
    455 0           $speed = readDouble($fh);
    456 0           $track = readDouble($fh);
    457 0           $magVar = readDouble($fh);
    458 0           $hdop = readDouble($fh);
    459             }
    460            
    461             =head2 readint64($fileHandle)
    462            
    463             Params:
    464             reference - Filehandle reference
    465             Returns:
    466             a 64bit number
    467            
    468             =cut
    469            
    470             sub readint64
    471             {
    472 0     0 1   my $fh = shift;
    473             #my $l;
    474             #my ($p,$t);
    475             #$p = tell($fh);
    476             #my $r = read($fh,$l,8);
    477             #$t = tell($fh);
    478             #ensurePos($fh,$p,$t,8);
    479             #if($r != 8){die "Failed to read int64 $r $!\n";}
    480             #return unpack("L2",$l);
    481 0           return (readint32($fh) << 32) + readint32($fh);
    482             }
    483            
    484             =head2 readDouble($fileHandle)
    485            
    486             Params:
    487             reference - Filehandle reference
    488             Returns:
    489             a double
    490            
    491             =cut
    492            
    493             sub readDouble
    494             {
    495 0     0 1   my $fh = shift;
    496 0           my $l;
    497 0           my ($p,$t);
    498 0           $p = tell($fh);
    499 0           my $r = read($fh,$l,8);
    500 0           $t = tell($fh);
    501 0           ensurePos($fh,$p,$t,8);
    502 0 0         if($r != 8){die "Failed to read double $r $!\n";}
      0            
    503 0           return unpack("d",$l);
    504             }
    505            
    506             =head2 readint32($fileHandle)
    507            
    508             Params:
    509             reference - Filehandle reference
    510             Returns:
    511             a 32bit number
    512            
    513             =cut
    514            
    515             sub readint32
    516             {
    517 0     0 1   my $fh = shift;
    518 0           my $l;
    519 0           my ($p,$t);
    520 0           $p = tell($fh);
    521 0           my $r = read($fh,$l,4);
    522 0           $t = tell($fh);
    523 0           ensurePos($fh,$p,$t,4);
    524 0 0         if($r != 4){die "Failed to read int32 $r $!\n";}
      0            
    525 0           return unpack("l",$l);
    526             }
    527            
    528             =head2 readUint32($fileHandle)
    529            
    530             Params:
    531             reference - Filehandle reference
    532             Returns:
    533             an unsigned 32bit number
    534            
    535             =cut
    536            
    537             sub readUint32
    538             {
    539 0     0 1   my $fh = shift;
    540 0           my $l;
    541 0           my ($p,$t);
    542 0           $p = tell($fh);
    543 0           my $r = read($fh,$l,4);
    544 0           $t = tell($fh);
    545 0           ensurePos($fh,$p,$t,4);
    546 0 0         if($r != 4){die "Failed to read Uint32 $r $!\n";}
      0            
    547 0           return unpack("L",$l);
    548             }
    549            
    550             =head2 readUint8($fileHandle)
    551            
    552             Params:
    553             reference - Filehandle reference
    554             Returns:
    555             an unsigned 8bit number
    556            
    557             =cut
    558            
    559             sub readUint8
    560             {
    561 0     0 1   my $fh = shift;
    562 0           my $l;
    563 0           my ($p,$t);
    564 0           $p = tell($fh);
    565 0           my $r = read($fh,$l,1);
    566 0           $t = tell($fh);
    567 0           ensurePos($fh,$p,$t,1);
    568 0 0         if($r != 1){die "Failed to read Uint8 $r $!\n";}
      0            
    569 0           return unpack("C",$l);
    570             }
    571            
    572             =head2 readChars($fileHandle,$length)
    573            
    574             Params:
    575             reference - Filehandle reference
    576             length - number of bytes to read
    577             Returns:
    578             a string
    579            
    580             =cut
    581            
    582             sub readChars
    583             {
    584 0     0 1   my $fh = shift;
    585 0           my $length = shift;
    586 0           my $l;
    587 0           my ($p,$t);
    588 0           $p = tell($fh);
    589 0           my $r = read($fh,$l,$length);
    590 0           $t = tell($fh);
    591 0           ensurePos($fh,$p,$t,$length);
    592 0 0         if($r != $length) { die "Failed to read $length ($r) $!\n";}
      0            
    593 0           return unpack("A*",$l);
    594             }
    595            
    596             =head2 ensurePos($fileHandle,$prePosition,$postPosition,$amountNeeded)
    597            
    598             This method was aadded due to an odd behavior with Perl5.8 read would sometimes
    599             put the file pointer 1 byte beyond where it was supposed to be. This method fixes that issue
    600             Params:
    601             reference - Filehandle reference
    602             number - Pre read position of the file
    603             number - Post position of the file
    604             number - Correct amount to data that was supposed to be read
    605            
    606             =cut
    607            
    608             sub ensurePos
    609             {
    610 0     0 1   my ($fh,$prePos,$postPos,$amt) = @_;
    611 0           my $diff = ($postPos-$prePos);
    612 0 0         if($diff != $amt)
    613             {
    614 0           $diff -= $amt;
    615 0           $postPos -= $diff;
    616 0           seek($fh,$postPos,0);
    617             }
    618             }
    619            
    620             1;
    621             __END__