<?php

// Configurable data
define('EXPECTED_HEADER'"\xDA\x31\x41\x59");
define('EXPECTED_TAG_HOSTS'"\xDAHS\x01");
define('EXPECTED_TAG_GRAPH'"\xDAGR\x01");

define('EXPECTED_PROTO_IP'"P");
define('EXPECTED_PROTO_TCP'"T");
define('EXPECTED_PROTO_UDP'"U");

$versions = array(=> "HST\x01"=> "HST\x02"=> "HST\x03");

$protocols = array(=> 'tcp'17 => 'udp'=> 'icmp');
// End configurable data


// Utility functions
function readaddr_ipv4($db) {
    
$ip = array();

    for(
$i 0$i 4$i++) {
        
$ip[] = reset(unpack('C'fread($db1)));
    }

    return 
implode('.'$ip);
}

function 
readaddr_ipv6($db) {
    
$ip = array();
    
    for(
$i 0$i 8$i++) {
        
$ip[] = bin2hex(fread($db8));
    }
    
    return 
implode(':'$ip);
}

function 
readmac($db) {
    
$mac = array();
    
    for(
$i 0$i 6$i++) {
        
$mac[] = bin2hex(fread($db1));
    }
    
    return 
strtoupper(implode(':'$mac));
}

function 
read64_uint($db) {
    
$first reset(unpack('N'fread($db4)));
    
$second reset(unpack('N'fread($db4)));
    
    return (
$first << 32) + $second;
}

function 
read64_int($db) {
    
$first reset(unpack('N'fread($db4)));
    
$second reset(unpack('N'fread($db4)));
    
    
// Get whether -ve (& 1000...)
    
$negative = (bool)($first pow(231));
    
    
// Strip the sign (& 0111...)
    
$first &= (pow(231) - 1);
    
    
// Add the two
    
$result = ($first << 32) + $second;
    
    
// Make it -ve if we need to
    
if ($negative)
        
$result *= -1;
        
    
// Return
    
return $result;
}
// End functions


// Main function
function parse_darkstat_db($filename) {
    
// Get versions
    
global $versions$protocols;
    
    
// Open the DB
    
$db fopen($filename'r');
    
    
// Check the header
    
if (fread($db4) !== EXPECTED_HEADER)
        die(
'This is not a darkstat.db that I understand');
    
    if (
fread($db4) !== EXPECTED_TAG_HOSTS)
        die(
'This is not a darkstat.db that I understand');
        
    
// Get the count
    
$count reset(unpack('N'fread($db4)));
    
    
// Where to store our info
    
$info = array();
    
    
// Loop the count
    
for($i 0$i $count$i++) {
        
$tmp = array();
        
        
// Get the entry version
        
$ver array_search(fread($db4), $versions);
        
        
// Die if invalid version
        
if (!$ver)
            die(
'Invalid host header');
            
        
// v3 supports ipv4 and ipv6
        
if ($ver == 3) {
            
$family fread($db1);
            
            if (
$family == 6) {
                
$tmp['addr'] = readaddr_ipv6($db);
            }
            elseif (
$family == 4) {
                
$tmp['addr'] = readaddr_ipv4($db);
            }
            else {
                die(
'I don\'t know this address family');
            }
        }
        
// v1 and v2 only support ipv4
        
else {
            
$tmp['addr'] = readaddr_ipv4($db);
        }
        
        
// Get the last seen time
        
if ($ver 1) {
            
$tmp['last_seen'] = read64_int($db);
        }
        
        
// Get the host MAC
        
$tmp['mac'] = readmac($db);
        
        
// Get the length of the hostname
        
$host_len reset(unpack('C'fread($db1)));
        
        
// Get the hostname
        
$tmp['hostname'] = $host_len fread($db$host_len) : null;
        
        
// Get the inbound
        
$tmp['in'] = read64_uint($db);
        
        
// Get the outbound
        
$tmp['out'] = read64_uint($db);
        
        
// Sum these for a total
        
$tmp['total'] = $tmp['in'] + $tmp['out'];
        
        
// Make array for IP data
        
$tmp['ip'] = array();
        
        
// Read the IP proto header
        
if (fread($db1) != EXPECTED_PROTO_IP)
            die(
'IP proto header not found');
            
        
// Read the count
        
$ip_count reset(unpack('C'fread($db1)));
        
        
// Loop the count
        
for($j 0$j $ip_count$j++) {
            
$ip_tmp = array();
            
            
// Get the proto
            
$ip_tmp['proto_num'] = reset(unpack('C'fread($db1)));
            
            
// Map to a protocol name
            
$ip_tmp['proto_name'] = isset($protocols[$ip_tmp['proto_num']]) ? $protocols[$ip_tmp['proto_num']] : null;
            
            
// Get the in
            
$ip_tmp['in'] = read64_uint($db);
            
            
// Get the out
            
$ip_tmp['out'] = read64_uint($db);
            
            
// Sum these for total
            
$ip_tmp['total'] = $ip_tmp['in'] + $ip_tmp['out'];
            
            
// Add to parent array
            
$tmp['ip'][] = $ip_tmp;
        }
        
        
// Make array for TCP data
        
$tmp['tcp'] = array();
        
        
// Read the TCP proto header
        
if (fread($db1) != EXPECTED_PROTO_TCP)
            die(
'TCP proto header not found');
            
        
// Read the count
        
$tcp_count reset(unpack('n'fread($db2)));
        
        
// Loop the count
        
for($j 0$j $tcp_count$j++) {
            
$tcp_tmp = array();
            
            
// Get the port
            
$tcp_tmp['port'] = reset(unpack('n'fread($db2)));
            
            
// Get the syn count
            
$tcp_tmp['syn_count'] = read64_uint($db);
            
            
// Get the in
            
$tcp_tmp['in'] = read64_uint($db);
            
            
// Get the out
            
$tcp_tmp['out'] = read64_uint($db);
            
            
// Sum these for total
            
$tcp_tmp['total'] = $tcp_tmp['in'] + $tcp_tmp['out'];
            
            
// Add to parent array
            
$tmp['tcp'][] = $tcp_tmp;
        }
        
        
// Make array for UDP data
        
$tmp['udp'] = array();
        
        
// Read the UDP protocol header
        
if (fread($db1) != EXPECTED_PROTO_UDP)
            die(
'UDP proto header not found');
            
        
// Get count
        
$udp_count reset(unpack('n'fread($db2)));
        
        
// Loop count
        
for($j 0$j $udp_count$j++) {
            
$udp_tmp = array();
            
            
// Get the port
            
$udp_tmp['port'] = reset(unpack('n'fread($db2)));
            
            
// Get the in
            
$udp_tmp['in'] = read64_uint($db);
            
            
// Get the out
            
$udp_tmp['out'] = read64_uint($db);
            
            
// Sum these for total
            
$udp_tmp['total'] = $udp_tmp['in'] + $udp_tmp['out'];
            
            
// Add to parent array
            
$tmp['udp'][] = $udp_tmp;
        }
        
        
// Add to parent array
        
$info[] = $tmp;
    }
    
    return 
$info;
}

// Test run
var_dump(parse_darkstat_db('darkstat.db'));