14 czerwca 2011

EVE Online i Skill Training Queue

Znowu się nudziłem i napisałem sobie mały projekt w PHP, który informuje mnie o kolejce skilli w EVE Online, bez potrzeby logowania się do gry.
Jeśli ktoś będzie zainteresowany źródłem, dorzucę do bloga.

Jeszcze pomyślę o "oklejeniu" tego jakimś javascriptem, aby dane wyglądały jeszcze bardziej "na bieżąco".

11 czerwca 2011

Ubiquiti cz.2

Dzisiaj rzutem na taśmę skaner do Ubiquiti. W odróżnieniu od Mikrotika, Ubiquiti odpowiada na adres z którego zostało wysłane zapytanie. Ale w tym skrypcie nasłuchujemy na dowolnym adresie, tak jak w skanerze dla Mikrotików. No i miały być szczegóły techniczne. Skaner rozsyła z portu 50122 na port 10001 paczkę 0x01000000 po broadcaście. Urządzenie wysyła odpowiedź na port 50122. Trochę dłubaniny było przy zmiennym formacie ramki ale dość łatwo było ominąć ten problem. Pierwszy rodzaj ramki:
@s = unpack("H6H2H6H12NH6H12H8(H2n/a*)*", $ramka);

$s[0]   sygnatura (0x010000)
$s[1]   nie pamiętam :)
$s[2]   ??
$s[3]   adres MAC
$s[4]   ??
$s[5]   ??
$s[6]   ??
$s[7]   ??
(
    $s[8]   typ pola
    X       długość łańcucha (automatycznie wchłaniane przez unpack)
    $s[9]   łańcuch
)
...
Drugi rodzaj ramki:
@s = unpack("H6H2H6H12NH6H12H14(H2n/a*)*", $ramka);

$s[0]   sygnatura (0x010000)
$s[1]   nie pamiętam :)
$s[2]   ??
$s[3]   adres MAC
$s[4]   ??
$s[5]   ??
$s[6]   ??
$s[7]   ?? (tutaj długość jest większa o 3 bajty)
(
    $s[8]   typ pola
    X       długość łańcucha (automatycznie wchłaniane przez unpack)
    $s[9]   łańcuch
)
...
Zidentyfikowane typy pól:
0x03 - wersja softu
0x0a - uptime
0x0b - nadana nazwa dla urządzenia
0x0c - model urządzenia
0x0d - SSID do którego jest podpięte urządzenie gdy pracuje w trybie klienta
No i skrypt:
#!/usr/bin/perl

###############################################################################
#
# scan-ubiquiti.pl v1.2.0
#
# scan utility for ubiquiti
#
# 2011-06-11
# przepisano engine na klasyczne sockety
#
# mindc.net
# 2011-03-20
###############################################################################

use strict;
use warnings;
use Socket;
use Time::HiRes qw(gettimeofday);

print "scan-ubiquiti.pl v1.2.0
    hw                 ip               board            sw   uptime name                     ssid
--------------------------------------------------------------------------------------------------------
";

my %dev;
open my $ph, "-|","ip a";
while ( <$ph> ) {
    $dev{$2} = $1 if m@\s(\S+)/\d+\s.*(eth\d+)@;
}
close $ph;

my $data = {};

foreach my $saddr ( values %dev ) {
    my $socket;
    socket($socket,PF_INET,SOCK_DGRAM,17);
    setsockopt($socket, SOL_SOCKET, SO_BROADCAST, 1 );
    my $sin = sockaddr_in(50122,inet_aton($saddr));
    bind($socket,$sin);
    my $sout = sockaddr_in(10001,INADDR_BROADCAST);
    send($socket,pack("H8","01000000"),0,$sout);
    send($socket,pack("H8","01000000"),0,$sout);
    send($socket,pack("H8","01000000"),0,$sout);
    close $socket;
}

my $socket;
socket($socket,PF_INET,SOCK_DGRAM,17) or die "socket error - $!";
my $sin = sockaddr_in(50122,INADDR_ANY);
bind($socket,$sin);

my $rin = "";
vec($rin,fileno($socket),1) = 1;

my $end = gettimeofday() + 3;
my $timeout = $end - gettimeofday();

my $i = 0;
while ( select(my $rout = $rin,undef,undef,$timeout) ) {
    $timeout = $end - gettimeofday();
    my $rtime = "";
    my $his = recv($socket,$rtime,1500,0) || die "recv: $!";
    my ($port,$hisaddr) = sockaddr_in($his);
    next if length($rtime) < 8; #skip self
    my $o = ubiquiti_header(inet_ntoa($hisaddr),$rtime);

    if ( not exists($data->{$o->{mac}})) {
        my $sw = parse_sw($o->{sw});
        printf("%2d. %17s  %-16s %-16s %-4s %6s %-24s %s\n",
            ++$i,
            $o->{mac},
            $o->{ip},
            models($o->{board}),
            $sw->{sw_version},
            sec2human($o->{uptime}),
            $o->{name},
            $o->{ssid}
        );
    }
    $data->{$o->{mac}}++;
}
close $socket;
print "\n" if keys %$data;
exit;

sub ubiquiti_header {
    my $ip = shift;
    my $frame = shift;

    my $template = "H6 H2 H6 H12 N H6 H12 H8 (H2n/a*)*";
    my @header = unpack($template,$frame);
    my $signature = shift @header;
    return undef if $signature ne '010000';
    my $t = shift @header;
    shift @header;
    my $mac = shift @header;
    shift @header;
    shift @header;
    shift @header;

    shift @header;

    if ( $header[0] ne '01' ) {
        $template = "H6 H2 H6 H12 N H6 H12 H14 (H2n/a*)*";
        @header = unpack($template, $frame);
        $signature = shift @header;
        return undef if $signature ne '010000';
        $t = shift @header;
        shift @header;
        $mac = shift @header;
        shift @header;
        shift @header;
        shift @header;
        shift @header;
    }

    my $out = {};
    @{$out}{qw(ip mac sw board key uptime ssid)} = ($ip,'','','','',0,'');

    for ( my $i = 0; $i < @header ; $i += 2 ) {
        my $type = $header[$i];
        my $value = $header[$i+1];

        $out->{sw} = $value if $type eq '03';
        $out->{board} = $value if $type eq '0c';
        $out->{name} = $value if $type eq '0b';
        $out->{uptime} = unpack("N",$value) if $type eq '0a';
        $out->{ssid} = $value if $type eq '0d';

    }

    $out->{mac} = uc join(":",unpack("(H2)6",pack("H*",$mac)));
    $out->{frame} = $frame;
    $out->{structure} = \@header;
    return $out;
}

sub sec2human {
    my $sec = shift;

    return "" if $sec <= 0;

    my $w = int($sec / 60 / 60 / 24 / 7);
    my $d = int($sec / 60 / 60 / 24 % 7);
    my $h = int($sec / 60 / 60 % 24);
    my $m = int($sec / 60 % 60);
    my $s = int($sec % 60);
    if ( $w ) {
        return sprintf("%dw%dd",$w,$d);
    } elsif ( $d ) {
        return sprintf("%dd%dh",$d,$h);
    } elsif ( $h ) {
        return sprintf("%dh%dm",$h,$m);
    } elsif ( $m ) {
        return sprintf("%dm%ds",$m,$s);
    } else {
        return "${s}s";
    }
}

sub parse_sw {
    my $sw = shift;
    return {} unless $sw;
    my @s = split /\./,$sw;
    $s[2] = $s[2] || '';
    $s[3] = $s[3] || '';
    $s[2] =~ s/v//g;
    my $out = {
    board => $s[0] || '',
    chipset => uc $s[1] || '',
    build => $s[4] || '',
    date => $s[5] || '',
    unkn => $s[6] || ''
    };

    $out->{sw_version} = $s[2].".".$s[3] if $s[2] && $s[3];

    return $out;
}

sub models {
    my $m = shift;
    return 'NanoBridgeM' if $m eq 'NB5';
    return 'WISPStation5' if $m eq 'MS5';
    return 'RocketM' if $m eq 'R5N';
    return 'AirGridM5' if $m eq 'AG5';
    return $m;
};

MNDP cz.4

Zaczął mnie irytować ten tcpdump. Ogarnąłem się trochę i przepisałem część kodu w zwykłych socketach.
#!/usr/bin/perl

###############################################################################
#
# scan-mikrotik.pl v2.8.0
#
# scan utility for mikrotik
#
# 2011-06-11
# przepisano engine na klasyczne sockety
#
# 2011-03-27
# dodano uptime
#
# mindc.net
# 2011-03-20
###############################################################################

use strict;
use warnings;
use Socket;
use Time::HiRes qw(gettimeofday);

print "scan-mikrotik.pl v2.8.0
    hw                 ip               board      sw      uptime identity
--------------------------------------------------------------------------------
";

my %dev;
open my $ph, "-|","ip a";
while ( <$ph> ) {
    $dev{$2} = $1 if m@\s(\S+)/\d+\s.*(eth\d+)@;
}
close $ph;

my $data = {};

foreach my $saddr ( values %dev ) {
    my $socket;
    socket($socket,PF_INET,SOCK_DGRAM,17);
    setsockopt($socket, SOL_SOCKET, SO_BROADCAST, 1 );
    my $sin = sockaddr_in(5678,inet_aton($saddr));
    bind($socket,$sin);
    my $sout = sockaddr_in(5678,INADDR_BROADCAST);
    send($socket,pack("H8",0),0,$sout);
    send($socket,pack("H8",0),0,$sout);
    send($socket,pack("H8",0),0,$sout);
    close $socket;
}

my $socket;
socket($socket,PF_INET,SOCK_DGRAM,17) or die "socket error - $!";
my $sin = sockaddr_in(5678,INADDR_ANY);
bind($socket,$sin);

my $rin = "";
vec($rin,fileno($socket),1) = 1;

my $end = gettimeofday() + 3;
my $timeout = $end - gettimeofday();

my $i = 0;
while ( select(my $rout = $rin,undef,undef,$timeout) ) {
    $timeout = $end - gettimeofday();
    my $rtime = "";
    my $his = recv($socket,$rtime,1500,0) || die "recv: $!";
    my ($port,$hisaddr) = sockaddr_in($his);
    next if length($rtime) < 5; #skip self
    my $o = mikrotik_header(inet_ntoa($hisaddr),$rtime);
    if ( not exists($data->{$o->{mac}})) {
        printf("%2d. %17s  %-16s %-10s %-7s %6s %s\n",
            ++$i,
            $o->{mac},
            $o->{ip},
            $o->{board},
            $o->{sw},
            sec2human($o->{uptime}),
            $o->{identity}
        );
    }
    $data->{$o->{mac}}++;
}
close $socket;
print "\n" if keys %$data;
exit;

sub mikrotik_header {
    my $ip = shift;
    my $buffer = shift;

    my @header = unpack("H2H2S(H4n/a*)*", $buffer);

    shift @header; #?
    shift @header; #?
    shift @header; #age?

    my $out = {};
    @{$out}{qw(ip mac identity vendor sw board key uptime)} = ($ip,'','','','','','',0);

    for ( my $i = 0; $i < @header ; $i += 2 ) {
        my $type = $header[$i];
        my $value = $header[$i+1];

        $out->{mac} = uc join(":",unpack("(H2)6",$value)) if $type eq '0001';
        $out->{identity} = $value if $type eq '0005';
        $out->{vendor} = $value if $type eq '0008';
        $out->{sw} = $value if $type eq '0007';
        $out->{board} = $value if $type eq '000c';
        $out->{key} = $value if $type eq '000b';
        $out->{uptime} = unpack("L",$value) if $type eq '000a';
#       $out->{unknown2} = $value if $type eq '000d';
    }
    return $out;
}

sub sec2human {
    my $sec = shift;

    return "" if $sec <= 0;

    my $w = int($sec / 60 / 60 / 24 / 7);
    my $d = int($sec / 60 / 60 / 24 % 7);
    my $h = int($sec / 60 / 60 % 24);
    my $m = int($sec / 60 % 60);
    my $s = int($sec % 60);
    if ( $w ) {
        return sprintf("%dw%dd",$w,$d);
    } elsif ( $d ) {
        return sprintf("%dd%dh",$d,$h);
    } elsif ( $h ) {
        return sprintf("%dh%dm",$h,$m);
    } elsif ( $m ) {
        return sprintf("%dm%ds",$m,$s);
    } else {
        return "${s}s";
    }
}