3 czerwca 2015

Aplikacja LINE, protokół Thrift i Perl

Wszystkie skrypty jakie znalazłem w sieci, które logowały się do LINE, były napisane głównie w języku Python, a ja niestety programuję w Perl. Więc po dwóch dniach walk, na podstawie znalezionego kodu w sieci, stworzyłem skrypt który loguje się do LINE i pobiera dane użytkownika. Inne funkcje można podejrzeć w pliku TalkService.pm
#!/usr/bin/perl

package customHttpClient;
use base qw(Thrift::HttpClient);

sub setCustomHeader {
    my $self = shift;
    push @{$self->{'customHeaders'}},[@_];
}

sub flush
{
    my $self = shift;

    my $ua = LWP::UserAgent->new('timeout' => 1);
    $ua->default_header('Accept' => 'application/x-thrift');
    $ua->default_header('Content-Type' => 'application/x-thrift');
    $ua->cookie_jar({}); # hash to remember cookies between redirects

    my $out = $self->{out};
    $out->setpos(0); # rewind
    my $buf = join('', <$out>);

    my $request = new HTTP::Request(POST => $self->{url}, undef, $buf);

    foreach ( @{$self->{'customHeaders'}} ) {
        $request->header(@$_);
    }

    my $response = $ua->request($request);
    my $content_ref = $response->content_ref;

    my $in = IO::String->new($content_ref);
    binmode($in);
    $self->{in} = $in;
    $in->setpos(0); # rewind

    # reset write buffer
    $out = IO::String->new;
    binmode($out);
    $self->{out} = $out;
}

package main;

use strict;
use warnings;
use Data::Dumper;
use Thrift::XS;
use TalkService;
use Try::Tiny;

use constant LINE_DOMAIN   => "http://125.209.252.15";
use constant LINE_HTTP_URL => LINE_DOMAIN . "/api/v4/TalkService.do";

my $ip          = "127.0.0.1";
my $version     = "4.0.2.366";
my $com_name    = "LineJ";

my $id = '<email>';
my $password = '<password>';

my $os_version = "6.2.9200-8-x64";
my $user_agent = sprintf("DESKTOP:WINDOWS:%s(%s)",$os_version,$version);
my $app = sprintf("DESKTOPWIN\t%s\tWINDOWS\t%s",$version, $os_version);

my $transport = new customHttpClient(LINE_HTTP_URL);
$transport->setCustomHeader('User-Agent' => $user_agent);
$transport->setCustomHeader('X-Line-Application' => $app);

my $protocol  = new Thrift::XS::CompactProtocol($transport);
my $client = new TalkServiceClient($protocol);

try {
    my $msg = $client->loginWithIdentityCredentialForCertificate(
        IdentityProvider::LINE, $id, $password, 0, $ip, $com_name, ""
    );
    $transport->setCustomHeader('X-Line-Access' => $msg->{authToken});
    print Dumper $client->getProfile();
} catch {
    print STDERR Dumper $_;
};

exit;
Moduł TalkService wygenerowałem na podstawie pliku line.thrift. Informacje jak wygenerować kod w Perlu (i nie tylko) znajdują się pod adresem https://thrift.apache.org/tutorial/.

8 stycznia 2014

Ubiquiti TOUGHSwitch PoE PRO i stany liczników (ruch) dla każdego portu

Odczytanie zawartości /proc/net/dev nic nie mówi o ruchu na portach, jedynie na wewnętrznych interfejsach switch'a. Aby uzyskać stany liczników (ruch) dla każdego z portów osobno, wystarczy otworzyć plik /var/tmp/status_json.

Przykładowa zawartość, format JSON:
{
   "stats":{
      "1":{
         "poe":"0",
         "portSpeed":"100",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":117754941658,
            "TxBroad":110690,
            "TxUni":111089346,
            "TxMulti":93541,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":27881914005,
            "RxBroad":46508,
            "RxUni":88242095,
            "RxMulti":1395,
            "RxFcsErr":1,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":40993599,
            "TRx128Byte":25693523,
            "TRx256Byte":3648856,
            "TRx512Byte":1990133,
            "TRx1024Byte":2362312,
            "TRxMaxByte":13601576
         }
      },
      "2":{
         "poe":"0",
         "portSpeed":"1000",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":163126309,
            "TxBroad":1432704,
            "TxUni":453,
            "TxMulti":567588,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":3241577,
            "RxBroad":0,
            "RxUni":1,
            "RxMulti":6191,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":34,
            "TRx128Byte":0,
            "TRx256Byte":0,
            "TRx512Byte":2471,
            "TRx1024Byte":3687,
            "TRxMaxByte":0
         }
      },
      "3":{
         "poe":"0",
         "portSpeed":"1000",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":163682077,
            "TxBroad":1433291,
            "TxUni":502,
            "TxMulti":567350,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":2740019,
            "RxBroad":0,
            "RxUni":0,
            "RxMulti":6511,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":6,
            "TRx128Byte":0,
            "TRx256Byte":0,
            "TRx512Byte":6505,
            "TRx1024Byte":0,
            "TRxMaxByte":0
         }
      },
      "4":{
         "poe":"0",
         "portSpeed":"1000",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":1393171085555,
            "TxBroad":1431210,
            "TxUni":1290915353,
            "TxMulti":573720,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":156045432312,
            "RxBroad":328,
            "RxUni":958413239,
            "RxMulti":4056,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":498122212,
            "TRx128Byte":318655134,
            "TRx256Byte":54161969,
            "TRx512Byte":17977988,
            "TRx1024Byte":23049878,
            "TRxMaxByte":46450443
         }
      },
      "5":{
         "poe":"0",
         "portSpeed":"1000",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":1336905838571,
            "TxBroad":241505,
            "TxUni":3710249743,
            "TxMulti":496286,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":12183648616197,
            "RxBroad":1178135,
            "RxUni":2229543132,
            "RxMulti":77472,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":657162412,
            "TRx128Byte":1339531801,
            "TRx256Byte":523701573,
            "TRx512Byte":313405085,
            "TRx1024Byte":246887664,
            "TRxMaxByte":3445300570
         }
      },
      "6":{
         "poe":"0",
         "portSpeed":"100",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":45602466100,
            "TxBroad":151096,
            "TxUni":42552269,
            "TxMulti":92342,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":3575597265,
            "RxBroad":26,
            "RxUni":29139075,
            "RxMulti":2768,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":17438436,
            "TRx128Byte":8498808,
            "TRx256Byte":1561161,
            "TRx512Byte":406953,
            "TRx1024Byte":563539,
            "TRxMaxByte":672972
         }
      },
      "7":{
         "poe":"0",
         "portSpeed":"1000",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":3208892838838,
            "TxBroad":1256802,
            "TxUni":2760667465,
            "TxMulti":400569,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":293912171364,
            "RxBroad":176646,
            "RxUni":2009603876,
            "RxMulti":173290,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":1068232579,
            "TRx128Byte":683150554,
            "TRx256Byte":96470449,
            "TRx512Byte":39277959,
            "TRx1024Byte":46666621,
            "TRxMaxByte":76155650
         }
      },
      "8":{
         "poe":"0",
         "portSpeed":"100",
         "linkStatus":"1",
         "duplex":"1",
         "hasVlans":"0",
         "maxFrameSize":"1518",
         "alertEnabled":"0",
         "stpState":"No spanning",
         "stats":{
            "TxByte":7436383703602,
            "TxBroad":1352989,
            "TxUni":2343144437,
            "TxMulti":267006,
            "TxExcCol":0,
            "TxLateCol":0,
            "RxGoodByte":873321777726,
            "RxBroad":32306,
            "RxUni":648949666,
            "RxMulti":310287,
            "RxFcsErr":0,
            "RxAllignErr":0,
            "RxJabber":0,
            "RxFragment":0,
            "RxOversize":0,
            "TRx64Byte":2512756819,
            "TRx128Byte":1695780622,
            "TRx256Byte":226916928,
            "TRx512Byte":98845703,
            "TRx1024Byte":121118002,
            "TRxMaxByte":289468496
         }
      }
   },
   "timestamp":1389173254.935458,
   "now":"2014-01-08 10:27:34",
   "uptime":"506585",
   "management":{
      "up":1,
      "speed":100
   }
}

25 października 2012

'iptables' i szybkie usuwanie wszystkich reguł...

Poniżej szybkie usuwanie wszystkich reguł z iptables. Zarówno z wbudowanych łańcuchów jak i z łańcuchów użytkownika.

#!/bin/bash

echo "*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
*filter
:INPUT ACCEPT [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
COMMIT
" | iptables-restore

22 października 2012

Ubiquiti, SSH i zmiana hasła

Zmianę hasła w Ubiquiti, można przeprowadzić w poniższych krokach:

  1. Najpierw znajdujemy zmieniane hasło dla danego użytkownika.

    # konsola Ubiquiti
    
    cat /tmp/system.cfg | grep users.1.password | cut -d= -f2
    
  2. Teraz ze starego hasła odzyskujemy salt, czyli w Ubiquiti dwa pierwsze znaki hasła.

    # Perl
    
    my $salt;
    ($salt) = $old_password =~ m/^(..)/; 
    # BASH
    
    SALT=`echo ${OLD_PASSWORD} | cut -c1,2`

  3. Generujemy nowe hasło.
    # Perl
    
    my $new_password_crypted = crypt($new_password,$salt);
  4. Mając wszystkie potrzebne dane, podmieniamy hasło.

    # konsola Ubiquiti
    
    sed -e 's/$old_password/$new_password_crypted/' /tmp/system.cfg
    
    # zapisujemy zmiany
    
    cfgmtd -w
    
  5. Gotowe

Powyższa procedura działa, jak na razie, na wszystkich dostępnych mi urządzeniach Ubiquiti.

Gotowy skrypt w Perlu: chpasswd-ubiquiti.pl

18 października 2012

Ubiquiti i zmiana MAC adresu

Aby zmienić adres MAC interfejsu radiowego w Ubiquiti, możemy go po prostu zmienić za pomocą polecenia ifconfig.

Jednak zmiany te, nie zachowują się po restarcie urządzenia. Należy więc zmieniać adres MAC po każdym starcie systemu, a prosty skrypt ustawiający nowy adres zapisać w pamięci nieulotnej.

Poniżej procedura zmiany adresu MAC:

  1. Edytujemy plik /etc/persistent/rc.poststart. Jeśli pliku brak, tworzymy go.
  2. Edytujemy zawartość pliku tak, aby zawierał poniższy kod:
    ifconfig ath0 down
    ifconfig ath0 hw ether XX:XX:XX:XX:XX:XX
    ifconfig ath0 up
    Wartość XX:XX:XX:XX:XX:XX należy zastąpić nowym adresem MAC
  3. Zapisujemy dane do pamięci nieulotnej, uwzględniając pliki w /etc, wykonując polecenie cfgmtd -w -p /etc/
  4. Gotowe

Po zrestartowaniu urządzenia, adres MAC będzie ustawiany na nową wartość

Ubiquiti airFiber 24G i SSH

W airFiber dane od interfejsach (liczniki) uzyskiwane przy pomocy ifconfig albo cat /proc/net/dev nie są adekwatne do oczekiwanych wartości.
Po krótkich poszukiwaniach okazało się, że wszystkie te dane można wyciągnąć przy pomocy komendy af

Pobieranie informacji odbywa się za pomocą komendy af get parametr

Poniżej pełna lista parametrów, wynik wywołania aflist. Zwracam uwagę na pogrubione nazwy parametrów:

-------------------
AirFiber State Dump
-------------------
  frequency.................24.2GHz
                   choices  {24.1GHz,24.2GHz}
  rxfrequency...............24.1GHz
                   choices  {24.1GHz,24.2GHz}
  txfrequency...............24.2GHz
                   choices  {24.1GHz,24.2GHz}
  rxgain....................high
                   choices  {low,high}
  txmodrate.................6x
  duplex....................full
                   choices  {half,full}
  dpstat....................1000Mbps-Full
  dpcntl....................auto
                   choices  {auto,10Mbps-Half,100Mbps-Half,10Mbps-Full,100Mbps-Full}
  gpspulse..................detected
                   choices  {none,detected}
  gpsfix....................3D fix
  gpslat....................5333.98849N
  gpslong...................02100.71451E
  gpsalt....................171.6
  gpsviewcnt................10
  gpsusedcnt................10
  gpshdop...................1.00
  rssi......................51
  rssi0.....................51
  rssi1.....................51
  rxpower...................-57
  rxpower0..................-57
  rxpower1..................-57
  led.......................power
                   choices  {rssi,power}
  temp0.....................34
  temp1.....................34
  getlist...................frequency,rxfrequency,txfrequency,rxgain,txmodrate,duplex,dpstat,dpcntl,gpspulse,gpsfix,gpslat,gpslong,gpsalt,gpsviewcnt,gpsusedcnt,gpshdop,rssi,rssi0,rssi1,rxpower,rxpower0,rxpower1,led,temp0,temp1,getlist,setlist,countrycode,countrydom,speed,modcontrol,gps,powerout,mode,state,status,mastertxstart,mastertxlength,masterrxstart,masterrxlength,slavetxstart,slavetxlength,slaverxstart,slaverxlength,tdelay,feet,miles,meters,kilometers,linkname,key,capacity,rxcapacity,txcapacity,rrxpower0,rrxpower1,rtxmodrate,rpowerout,linktest,caldcoff,caltcxo,caltxiq,calrxiq,caltxtc,calrxtc,caltxpower,calrxpower,calpodc,mac_0,mac_1,TxFramesOK,RxFramesOK,RxFrameCrcErr,RxAlignErr,TxOctetsOK,RxOctetsOK,TxPauseFrames,RxPauseFrames,RxErroredFrames,TxErroredFrames,RxValidUnicastFrames,RxValidMulticastFrames,RxValidBroadcastFrames,TxValidUnicastFrames,TxValidMulticastFrames,TxValidBroadcastFrames,RxDroppedMacErrFrames,RxTotalOctets,RxTotalFrames,RxLess64ByteFrames,RxOverLengthFrames,Rx64BytePackets,Rx65_127BytePackets,Rx128_255BytePackets,Rx256_511BytePackets,Rx512_1023BytePackets,Rx1024_1518BytesPackets,Rx1519PlusBytePackets,RxTooLongFrameCrcErr,RxTooShortFrameCrcErr,phy00,phy01,phy02,phy03,phy04,phy05,phy06,phy07,phy08,phy09,phy0a,phy0b,phy0c,phy0d,phy0e,phy0f,phy10,phy11,phy12,phy13,phy14,phy15,phy16,phy17,phy18,phy19,phy1a,phy1b,phy1c,phy1d,phy1e,phy1f,txqosoct0,txqosoct1,txqosoct2,txqosoct3,txqosoct4,txqosoct5,txqosoct6,txqosoct7,txqosoct8,txqosoct9,txqosocta,txqosoctb,txqosoctc,txqosoctd,txqosocte,txqosoctf,txqospkt0,txqospkt1,txqospkt2,txqospkt3,txqospkt4,txqospkt5,txqospkt6,txqospkt7,txqospkt8,txqospkt9,txqospkta,txqospktb,txqospktc,txqospktd,txqospkte,txqospktf,rxqosoct0,rxqosoct1,rxqosoct2,rxqosoct3,rxqosoct4,rxqosoct5,rxqosoct6,rxqosoct7,rxqosoct8,rxqosoct9,rxqosocta,rxqosoctb,rxqosoctc,rxqosoctd,rxqosocte,rxqosoctf,rxqospkt0,rxqospkt1,rxqospkt2,rxqospkt3,rxqospkt4,rxqospkt5,rxqospkt6,rxqospkt7,rxqospkt8,rxqospkt9,rxqospkta,rxqospktb,rxqospktc,rxqospktd,rxqospkte
  setlist...................frequency,rxfrequency,txfrequency,rxgain,duplex,dpcntl,led,countrycode,countrydom,speed,modcontrol,gps,powerout,mode,linkname,key,linktest,caldcoff,caltcxo,caltxiq,calrxiq,caltxtc,calrxtc,caltxpower,calrxpower,calpodc,phy00,phy04,phy07,phy09,phy10,phy12,phy14,phy18,phy19,phy1d,phy1e
  countrycode...............840
  countrydom................fcc
                   choices  {none,fcc,etsi}
  speed.....................6x
                   choices  {1x,2x,4x,6x,8x}
  modcontrol................automatic
                   choices  {manual,automatic}
  gps.......................on
                   choices  {off,on}
  powerout..................20
                     range  {0..33}
  mode......................slave
                   choices  {master,slave,reset}
  state.....................operational
  status....................slave-operational
  mastertxstart.............0
  mastertxlength............1895544
  masterrxstart.............1907506
  masterrxlength............1895544
  slavetxstart..............1901794
  slavetxlength.............1895544
  slaverxstart..............0
  slaverxlength.............1895544
  tdelay....................2856
  feet......................2809
  miles.....................0.532
  meters....................856
  kilometers................0.856
  linkname..................spy-nid.........................
  key.......................0000:0000:0000:0000:0000:0000:0000:0000
                     range  {0000:0000:0000:0000:0000:0000:0000:0000..ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff}
  capacity..................743339520
  rxcapacity................743339520
  txcapacity................743626240
  rrxpower0.................-59
  rrxpower1.................-60
  rtxmodrate................6x
  rpowerout.................20
  linktest..................rx,0,tx,0,No Data
                     range  {1..60}
  caldcoff..................TX0DCI=0x091F,TX0DCQ=0x0154,TX1DCI=0x0CD5,TX1DCQ=0xFDB0
  caltcxo...................TCXOcount=124999994
  caltxiq...................TX0AI=0x0F6F,TX0AQ=0x0409,TX1AI=0x0F67,TX1AQ=0x0403
  calrxiq...................RX0AI=0x0FFD,RX0AQ=0x0403,RX1AI=0x0FFE,RX1AQ=0x0409
  caltxtc...................TCTX0gm40=-2.12,TCTX0gm15=-1.76,TCTX0gp10=-0.98,TCTX0gp35=0.00,TCTX0gp60=1.64,TCTX0gp85=3.41,TCTX1gm40=-2.12,TCTX1gm15=-1.76,TCTX1gp10=-0.98,TCTX1gp35=0.00,TCTX1gp60=1.64,TCTX1gp85=3.41
  calrxtc...................TCRX0gm40=-5.20,TCRX0gm15=-2.93,TCRX0gp10=-1.58,TCRX0gp35=0.00,TCRX0gp60=1.92,TCRX0gp85=3.91,TCRX1gm40=-5.20,TCRX1gm15=-2.93,TCRX1gp10=-1.58,TCRX1gp35=0.00,TCRX1gp60=1.92,TCRX1gp85=3.91
  caltxpower................TX0P=0x1D,TX0PdBm=6.92,TX0PTEMP=48,TX1P=0x1D,TX1PdBm=7.41,TX1PTEMP=48
  calrxpower................RX0P=0x39,RX0PdBm=-50.00,RX0PTEMP=47,RX1P=0x39,RX1PdBm=-50.00,RX1PTEMP=48
  calpodc...................P0short=0.00,P0half=0.00,P0full=-2.00,P1short=0.00,P1half=0.00,P1full=-2.00
  mac_0.....................0
  mac_1.....................0
  TxFramesOK................2713099372
  RxFramesOK................1103621960
  RxFrameCrcErr.............0
  RxAlignErr................0
  TxOctetsOK................3728254389
  RxOctetsOK................1000856066
  TxPauseFrames.............0
  RxPauseFrames.............0
  RxErroredFrames...........1
  TxErroredFrames...........63692
  RxValidUnicastFrames......1099572717
  RxValidMulticastFrames....601489
  RxValidBroadcastFrames....3451960
  TxValidUnicastFrames......2691192694
  TxValidMulticastFrames....1670070
  TxValidBroadcastFrames....20308518
  RxDroppedMacErrFrames.....0
  RxTotalOctets.............3687124683
  RxTotalFrames.............1103629815
  RxLess64ByteFrames........0
  RxOverLengthFrames........0
  Rx64BytePackets...........2153589453
  Rx65_127BytePackets.......2333775463
  Rx128_255BytePackets......268202900
  Rx256_511BytePackets......145549651
  Rx512_1023BytePackets.....136029207
  Rx1024_1518BytesPackets...361452237
  Rx1519PlusBytePackets.....0
  RxTooLongFrameCrcErr......0
  RxTooShortFrameCrcErr.....0
  phy00.....................0x1000
  phy01.....................0x796d
  phy02.....................0x004d
  phy03.....................0xd072
  phy04.....................0x1de1
  phy05.....................0xc5e1
  phy06.....................0x000f
  phy07.....................0x2801
  phy08.....................0x0000
  phy09.....................0x0200
  phy0a.....................0x3800
  phy0b.....................0x0000
  phy0c.....................0x0000
  phy0d.....................0x0000
  phy0e.....................0x0000
  phy0f.....................0xa000
  phy10.....................0x0862
  phy11.....................0xbc5c
  phy12.....................0x0000
  phy13.....................0x7401
  phy14.....................0x082c
  phy15.....................0x0000
  phy16.....................0x04e8
  phy17.....................0x0000
  phy18.....................0x3200
  phy19.....................0x3000
  phy1a.....................0x0000
  phy1b.....................0x063e
  phy1c.....................0x0000
  phy1d.....................0x000b
  phy1e.....................0x2c20
  phy1f.....................0x8100
  txqosoct0.................0x0000000000000000
  txqosoct1.................0x0000000000000000
  txqosoct2.................0x0000000000000000
  txqosoct3.................0x000000ed33db2544
  txqosoct4.................0x0000000000000000
  txqosoct5.................0x0000000000000000
  txqosoct6.................0x0000000000000000
  txqosoct7.................0x0000000000000000
  txqosoct8.................0x0000000000000000
  txqosoct9.................0x0000000000000000
  txqosocta.................0x0000000000000000
  txqosoctb.................0x0000000000000000
  txqosoctc.................0x0000000000000ea7
  txqosoctd.................0x0000000000000000
  txqosocte.................0x0000000000000000
  txqosoctf.................0x0000000000000000
  txqospkt0.................0x0000000000000000
  txqospkt1.................0x0000000000000000
  txqospkt2.................0x0000000000000000
  txqospkt3.................0x0000000141c446c5
  txqospkt4.................0x0000000000000000
  txqospkt5.................0x0000000000000000
  txqospkt6.................0x0000000000000000
  txqospkt7.................0x0000000000000000
  txqospkt8.................0x0000000000000000
  txqospkt9.................0x0000000000000000
  txqospkta.................0x0000000000000000
  txqospktb.................0x0000000000000000
  txqospktc.................0x0000000000000017
  txqospktd.................0x0000000000000000
  txqospkte.................0x0000000000000000
  txqospktf.................0x0000000000000000
  rxqosoct0.................0x0000000000000000
  rxqosoct1.................0x0000000004a95cdf
  rxqosoct2.................0x0000000000000000
  rxqosoct3.................0x000006da147ca526
  rxqosoct4.................0x0000000000000000
  rxqosoct5.................0x0000000000000000
  rxqosoct6.................0x0000000000000000
  rxqosoct7.................0x0000000000000000
  rxqosoct8.................0x0000000000000000
  rxqosoct9.................0x0000000000000000
  rxqosocta.................0x0000000012b24e9c
  rxqosoctb.................0x0000000000000000
  rxqosoctc.................0x000000001159c6c1
  rxqosoctd.................0x0000000000000000
  rxqosocte.................0x0000000000000000
  rxqosoctf.................0x0000000000000000
  rxqospkt0.................0x0000000000000000
  rxqospkt1.................0x000000000003b870
  rxqospkt2.................0x0000000000000000
  rxqospkt3.................0x00000001a1741f99
  rxqospkt4.................0x0000000000000000
  rxqospkt5.................0x0000000000000000
  rxqospkt6.................0x0000000000000000
  rxqospkt7.................0x0000000000000000
  rxqospkt8.................0x0000000000000000
  rxqospkt9.................0x0000000000000000
  rxqospkta.................0x00000000001f35e9
  rxqospktb.................0x0000000000000000
  rxqospktc.................0x0000000000209e5a
  rxqospktd.................0x0000000000000000
  rxqospkte.................0x0000000000000000

Więc aby wyciągnąć dane z liczników i wykorzystać je na przykład w rrdtool, wystarczy odczytać wyniki dwóch komend:

af get TxOctetsOK
af get RxOctetsOK

5 października 2012

Skaner Ubiquiti

Skaner do urządzeń Ubiquiti dla konsoli linuksowej, napisany w Perlu.

Historia zmian:

  • 2013-06-07
    dodano nowy kod hardware'u: 'WM5'
  • 2012-12-17
    dodano nowy kod hardware'u: 'NB2'
  • 2012-10-05
    dodano nowy kod hardware'u: 'AF24'
    dodano możliwość wybrania interfejsu do skanowania, podawany jako parametr wywołania skryptu
  • 2012-08-24
    dodano rozpoznawanie kodow hardware'u: 'NS3','B2N','BS2'
  • 2012-08-22
    poprawiono parsowanie odbieranego pakietu
    poprawiono parsowanie wersji software
  • 2011-06-11
    przepisano engine na klasyczne sockety
  • 2011-03-20
    pierwsza wersja

Najbardziej aktualna wersja skanera: scan-ubiquiti.pl

Opisy:

22 sierpnia 2012

Ubiquiti cz.3

Dzisiaj coś mi nie pasowało w moim skanerze, okazało się że za bardzo zakombinowałem z formatem ramki.
Poprawny format ramki:
@s = unpack("H6 H2 H6 H12 N (H2n/a*)*", $ramka);
No i sam scanner:
#!/usr/bin/perl

###############################################################################
#
# scan-ubiquiti.pl v2.1.0
#
# scan utility for ubiquiti
#
# 2012-08-24
# dodano rozpoznawanie kodow hardware'u: 'NS3','B2N','BS2'
#
# 2012-08-22
# poprawiono parsowanie odbieranego pakietu
# poprawiono parsowanie wersji software
#
# 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 v2.0.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);
setsockopt($socket, SOL_SOCKET, SO_BROADCAST, 0 );


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("%3d. %17s  %-16s %-16s %-6s %6s %-32s %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 (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;

    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 @d = $sw =~ m/(\S{2,3})\.(ar\d+)\.v(\S+)\.(\d{4,5})\.(\d{6})\.(\d{4})/;

    my $out = {
    board => $d[0] || '',
    chipset => uc $d[1] || '',
    sw_version => $d[2],
    build => $d[3] || '',
    date => $d[4] || '',
    unkn => $d[5] || ''
    };
    return $out;
}

sub models {
    my $m = shift;
    return 'NanoBridge M5' if $m eq 'NB5';
    return 'WispStation5' if $m eq 'MS5';
    return 'Rocket M5' if $m eq 'R5N';
    return 'AirGrid M5' if $m eq 'AG5';
    return 'NanoStation M3' if $m eq 'NS3';
    return 'Bullet M2' if $m eq 'B2N';
    return 'Bullet 2' if $m eq 'BS2';
    return $m;
};

11 kwietnia 2012

Uruchamianie aplikacji w przeglądarce...

...czyli rejestrowanie własnego protokołu URL

Kilka razy podchodziłem do tego problemu. A to kombinowałem z zakręconym JavaScriptem (bezskutecznie), a to z pluginem do Firefoxa (z powodzeniem). Aż trafiłem na artykuł o rejestrowaniu własnych protokołów, tak jak wbudowany http://

Aby przeglądarka rozpoznawała wywołanie typu winbox:192.168.88.1;admin;admin; należy ten protokół zarejestrować w systemie.
Osobiście potrzebowałem kilku takich protokołów, przygotowałem mały zestaw skrypt+aplikacja aby ten proces zautomatyzować.

Plik konfiguracyjny url_launcher.config:
(Nazwa pliku jest ważna, na sztywno wpisana w aplikacji url_launcher.exe)

winbox;c:\bin\winbox.exe
putty;C:\BIN\putty.exe
quake;d:\games\Quake III Arena\quake3.exe

Pierwsza pozycja to nazwa protokołu, druga pozycja (oddzielona średnikiem) to ścieżka do pliku wykonywalnego obsługującego nasz protokół. Odpowiednie wpisy do rejestru wykonuje poniższy skrypt, wpisy są uzależnione od wpisów w pliku konfiguracyjnym.

Skrypt url_launcher.cmd
@echo off
set URL_LAUNCHER_PATH=%~dp0
set URL_LAUNCHER_PATH=%URL_LAUNCHER_PATH:\=\\%

echo Windows Registry Editor Version 5.00 > url_launcher.temp.reg
FOR /F "eol= tokens=1,2 delims=;" %%I in (url_launcher.config) do (
 (
 echo [HKEY_CLASSES_ROOT\%%I]
 echo @="Winbox URI"
 echo "URL Protocol"=""
 echo [HKEY_CLASSES_ROOT\%%I\DefaultIcon]
 echo @="\"%%J\",0"
 echo [HKEY_CLASSES_ROOT\%%I\shell]
 echo @="open"
 echo [HKEY_CLASSES_ROOT\%%I\shell\open]
 echo [HKEY_CLASSES_ROOT\%%I\shell\open\command]
 echo @="\"%URL_LAUNCHER_PATH%url_launcher.exe\" \"%%1\""
 ) >> url_launcher.temp.reg
)
regedit /s url_launcher.temp.reg
del url_launcher.temp.reg

Do tego wszystkiego, potrzebny jest mały programik, który obrobi dane wejściowe i wywoła nam aplikację docelową url_launcher.exe

Wracając do naszego protokołu winbox:192.168.88.1;admin;admin, po wpisaniu go w pasku adresu przeglądarki i uruchomieniu, zostanie uruchomiony program z parametrami c:\bin\winbox.exe 192.168.88.1 admin admin. Analogicznie z putty:8.8.8.8;-l;root zostanie uruchomiony c:\bin\putty.exe 8.8.8.8 -l root

Aby wszystko zadziałało bezproblemowo, wszystkie pliki powinny znajdować się w tym samym katalogu.

I na zakończenie: to rozwiązanie jest uniwersalne, działa w każdej przeglądarce.

5 kwietnia 2012

Speedtest.net Mini - automatyczna aktualizacja

Czasami zdarzało się, że wchodząc na mój własny speedtest okazywało się, że jest nieaktualny... czasami taki nieaktualny wisiał ponad miesiąc...
Napisałem więc prościusieńki skrypt w BASHu który robi to za mnie
#!/bin/bash

cd <pełna_ścieżka_do_katalogu_z_twoim_speedtest.net_mini>
if ! wget -N http://c.speedtest.net/mini/mini.zip 2>&1 | grep -q "not retrieving";then
    unzip -o mini.zip
    cp -fR ./mini/* .
fi
Uruchamiamy co jakiś czas z crona i zapominamy o pilnowaniu i aktualizowaniu naszego Speedtest.net Mini.

6 marca 2012

Clean Drive

Tak sobie przypomniałem o pewnej bardzo prostej komendzie, którą można czyścić dyski ze zbędnych śmieci (chodzi o zajęte sektory po usuniętych plikach). Bardzo przydatne przy tworzeniu obrazów dysków/partycji w systemach plików nie obsługiwanych przez Wasz program do archiwizacji (tryb 'sektor po sektorze'). A wszystko po to, aby zminimalizować rozmiar skompresowanego archiwum...
dd if=/dev/zero of=/0bits bs=20M;rm -f /0bits
dd if=/dev/zero of=/home/0bits bs=20M;rm -f /home/0bits
dd if=/dev/zero of=/var/0bits bs=20M;rm -f /var/0bits
dd if=/dev/zero of=/usr/0bits bs=20M;rm -f /usr/0bits
dd if=/dev/zero of=/tmp/0bits bs=20M;rm -f /tmp/0bits
Oczywiście parametry wywołań będą różnić się od ilości i miejsca montowań partycji.
Przy okazji zmierzymy prędkość zapisu na danej partycji :)

10 sierpnia 2011

Szablon XSL - słownie złotych

Przy okazji uruchamiania systemu generującego PDFy z fakturami, potrzebowałem czegoś, co zamieni kwoty przedstawione za pomocą liczb na odpowiadający im opis słowny. Tak powstał ten szablon XSL, gotowy do dołączenia w projektach.
Pierwowzorem był znaleziony w necie, pewien algorytm napisany w C#, który to w międzyczasie przerobiłem na Javascript (tego użyłem w innym projekcie związanym z PDF).
I tak oto powstał ten szbalon.
<xsl:template name="get-gr">
  <xsl:param name="amount"/>
  <xsl:variable name="a1" select="floor($amount)"/>
  <xsl:variable name="len" select="string-length($a1)"/>
  <xsl:variable name="a2" select="substring($a1,$len,1)"/>
  <xsl:choose>
    <xsl:when test="$a1 = 1"><xsl:value-of select="document('amount-in-words.xml')/amount-in-words/grosze[1]"/></xsl:when>
    <xsl:when test="$a2 > 1 and $a2 < 5"><xsl:value-of select="document('amount-in-words.xml')/amount-in-words/grosze[2]"/></xsl:when>
    <xsl:otherwise><xsl:value-of select="document('amount-in-words.xml')/amount-in-words/grosze[3]"/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="get-zl">
  <xsl:param name="amount"/>
  <xsl:variable name="a1" select="floor($amount)"/>
  <xsl:variable name="len" select="string-length($a1)"/>
  <xsl:variable name="a2" select="substring($a1,$len,1)"/>
  <xsl:choose>
    <xsl:when test="$a1 = 1"><xsl:value-of select="document('amount-in-words.xml')/amount-in-words/zlote[1]"/></xsl:when>
    <xsl:when test="$a2 > 1 and $a2 < 5"><xsl:value-of select="document('amount-in-words.xml')/amount-in-words/zlote[2]"/></xsl:when>
    <xsl:otherwise><xsl:value-of select="document('amount-in-words.xml')/amount-in-words/zlote[3]"/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="get-words">
  <xsl:param name="amount"/>
  <xsl:variable name="zl" select="floor($amount)"/>
  <xsl:variable name="len" select="string-length($zl)"/>
  <xsl:if test="$len > 6">
    <xsl:variable name="million2" select="substring($zl,$len -6,1)"/>
    <xsl:variable name="million">
        <xsl:if test="$len > 8"><xsl:value-of select="substring($zl,$len -8,1)"/></xsl:if>
        <xsl:if test="$len > 7"><xsl:value-of select="substring($zl,$len -7,1)"/></xsl:if>
        <xsl:value-of select="substring($zl,$len -6,1)"/>
    </xsl:variable>
    <xsl:if test="$million > 1">
      <xsl:call-template name="get-words">
        <xsl:with-param name="amount" select="$million"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:choose>
      <xsl:when test="$million = 1">
        <xsl:value-of select="document('amount-in-words.xml')//millions[1]"/>
      </xsl:when>
      <xsl:when test="$million > 1 and $million < 5">
        <xsl:value-of select="document('amount-in-words.xml')//millions[2]"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="document('amount-in-words.xml')//millions[3]"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>

  <xsl:if test="$len > 3">
    <xsl:variable name="thousend2" select="substring($zl,$len -3,1)"/>
    <xsl:variable name="thousend">
      <xsl:if test="$len > 5"><xsl:value-of select="substring($zl,$len -5,1)"/></xsl:if>
      <xsl:if test="$len > 4"><xsl:value-of select="substring($zl,$len -4,1)"/></xsl:if>
      <xsl:value-of select="substring($zl,$len -3,1)"/>
    </xsl:variable>

    <xsl:if test="$thousend > 1">
      <xsl:call-template name="get-words">
        <xsl:with-param name="amount" select="$thousend"/>
      </xsl:call-template>
    </xsl:if>

    <xsl:choose>
      <xsl:when test="$thousend = 1">
        <xsl:value-of select="document('amount-in-words.xml')//thousends[1]"/>
      </xsl:when>
      <xsl:when test="$thousend2 > 1 and $thousend2 < 5">
        <xsl:value-of select="document('amount-in-words.xml')//thousends[2]"/>
      </xsl:when>
      <xsl:when test="$thousend = '000' and $len > 6">
        <xsl:text></xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="document('amount-in-words.xml')//thousends[3]"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>

  <xsl:if test="$len > 2">
    <xsl:if test="substring($zl,$len -2,1) != 0">
      <xsl:value-of select="document('amount-in-words.xml')//hundrets[substring($zl,$len -2,1) + 1]"/>
    </xsl:if>
  </xsl:if>

  <xsl:if test="$len > 0">
    <xsl:variable name="to99">
      <xsl:choose>
        <xsl:when test="$len = 1">
          <xsl:value-of select="substring($zl,$len,1)"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="substring($zl,$len -1,2)"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>

    <xsl:choose>
      <xsl:when test="$to99 < 20">
        <xsl:choose>
          <xsl:when test="substring($to99,1,1) = 0">
            <xsl:if test="substring($to99,2,1) != 0">
              <xsl:value-of select="document('amount-in-words.xml')//units[substring($to99,2,1) + 1]"/>
            </xsl:if>
          </xsl:when>
          <xsl:otherwise>
            <xsl:if test="$to99 != 0">
              <xsl:value-of select="document('amount-in-words.xml')//units[$to99 + 1]"/>
            </xsl:if>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="document('amount-in-words.xml')//tens[substring($to99,1,1)+1]"/>
        <xsl:if test="substring($to99,2,2) != 0">
          <xsl:value-of select="document('amount-in-words.xml')//units[substring($to99,2,2) + 1]"/>
        </xsl:if>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:if>

  <xsl:if test="$zl = 0">
    <xsl:value-of select="document('amount-in-words.xml')//units[1]"/>
  </xsl:if>
</xsl:template>

<xsl:template name="amount-in-words">
  <xsl:param name="amount"/>
  <xsl:call-template name="get-words"><xsl:with-param name="amount" select="$amount"/></xsl:call-template>
  <xsl:call-template name="get-zl"><xsl:with-param name="amount" select="$amount"/></xsl:call-template>
  <xsl:call-template name="get-words"><xsl:with-param name="amount" select="round(($amount - floor($amount))*100)"/></xsl:call-template>
  <xsl:call-template name="get-gr"><xsl:with-param name="amount" select="round(($amount - floor($amount))*100)"/></xsl:call-template>
</xsl:template>
Powyższy fragment kody należy umieścić w dowolnym miejscu istniejącego szablonu. Dodatkowo w katalogu z szablonem, należy umieścić poniższy plik o nazwie amount-in-words.xml:
<?xml version="1.0" encoding="UTF-8"?>    
<amount-in-words>
<grosze>grosz </grosze>
<grosze>grosze </grosze>
<grosze>groszy </grosze>
<zlote>złoty </zlote>
<zlote>złote </zlote>
<zlote>złotych </zlote>
<units>zero </units>
<units>jeden </units>
<units>dwa </units>
<units>trzy </units>
<units>cztery </units>
<units>pięć </units>
<units>sześć </units>
<units>siedem </units>
<units>osiem </units>
<units>dziewięć </units>
<units>dziesięć </units>
<units>jedenaście </units>
<units>dwanaście </units>
<units>trzynaście </units>
<units>czternaście </units>
<units>piętnaście </units>
<units>szesnaście </units>
<units>siedemnaście </units>
<units>osiemnaście </units>
<units>dziewiętnaście </units>
<tens></tens>
<tens>dziesięć </tens>
<tens>dwadzieścia </tens>
<tens>trzydzieści </tens>
<tens>czterdzieści </tens>
<tens>pięćdziesiąt </tens>
<tens>sześćdziesiąt </tens>
<tens>siedemdziesiąt </tens>
<tens>osiemdziesiąt </tens>
<tens>dziewięćdziesiąt </tens>
<hundrets></hundrets>
<hundrets>sto </hundrets>
<hundrets>dwieście </hundrets>
<hundrets>trzysta </hundrets>
<hundrets>czterysta </hundrets>
<hundrets>pięćset </hundrets>
<hundrets>sześćset </hundrets>
<hundrets>siedemset </hundrets>
<hundrets>osiemset </hundrets>
<hundrets>dziewięćset </hundrets>
<thousends>tysiąc </thousends>
<thousends>tysiące </thousends>
<thousends>tysięcy </thousends>
<millions>milion </millions>
<millions>miliony </millions>
<millions>milionów </millions>
</amount-in-words>            
Wywołanie jest proste, polega na wstawieniu poniższego fragmentu w interesującym nas miejscu szblonu:
<xsl:call-template name="amount-in-words">
  <xsl:with-param name="amount" select="value"/>
</xsl:call-template>
Jak na razie, u mnie ma to status beta, jeśli ktoś znajdzie jakieś błędy, zaproponuje poprawki, zapraszam do dyskusji.

5 lipca 2011

cwRsync portable

Często używam rsync do robienia backupów albo do synchronizowania zawartości folderów na wielu serwerach. Ostatnio postanowiłem, że kupię sobie dysk przenośny o dużej pojemności i wrzucę tam wszystkie najważniejsze swoje rzeczy. Chciałem mieć możliwość robienia tego z dowolnego miejsca przez net, więc pomyślałem o rsync.

Wszystko chciałem uruchamiać pod Windows, na dowolnym kompie do którego się podepnę. Stworzyłem prostą strukturę katalogów w głównym folderze dysku:
\bin
\backup
Zawartość z katalogu bin z folderu instalacyjnego cwRsync wrzuciłem do katalogu bin na dysku przenośnym.

Dorzuciłem też plik cwrsync.cmd do głównego katalogu. Poniżej zawartość pliku:
@echo off
setlocal
set CWRSYNCHOME=%~d0\bin
set CYGWIN=nontsec
set HOME=%HOMEDRIVE%%HOMEPATH%
set PATH=%~d0\bin;%PATH%

cygpath -a backup > backup.d
set /p BACKUP= < backup.d
del backup.d

rsync -rtlv --compress user@host::moduł %BACKUP%/ < %~d0\plik_z_hasłem
Problem z rsync polegał na tym, że ścieżka docelowa musi być absolutną ścieżką w formacie cygwin, np.: /cygdrive/h/backup. Wszelkie próby z relatywną ścieżką zawodziły.
Wykorzystałem także możliwość prostej autoryzacji w rsync, jedynym sposobem przekazania hasła, było proste przekierowanie go z zewnętrznego pliku.

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";
    }
}

20 maja 2011

APE (Ajax Push Engine) i SSL

Po długich poszukiwaniach rozwiązania tego problemu... utknąłem... W jednym miejscu piszą, że to działa tylko ze starą wersją stunnel (u mnie to nie działało) gdzie indziej robią sztuczki z przekierowaniami na Apachu (siedzę od jakiegoś czasu na lighttpd i nie wnikałem)... Po wnikliwej analizie co się dzieje w momencie negocjacji przez stunnel doszedłem, że chodzi o coś z timeoutami... Jedna z opcji wydawała się obiecująca... Poniżej działający bezproblemowo z APE, stunnel.conf
output = /var/log/stunnel.log
pid = /var/run/stunnel.pid
foreground = no

[ape]
TIMEOUTclose = 0
cert = <ścieżka do certyfikatu>
accept = <host na którym nasłuchujemy>:4443
connect = <ape serwer>:6969
Pogrubiłem opcję, która wszystko "naprawiła". Należy pamiętać także o zmianach w pliku config.js
APE.Config.baseUrl = '<serwer z którego pobieramy biblioteki>'; //APE JSF
APE.Config.domain = '<domena jak w pliku ape.conf>';
APE.Config.server = '<ape serwer>:4443'; //APE server URL
APE.Config.secure = true;
I możemy się cieszyć APE z SSLem. Należy tylko pamiętać o "wildcardowych" certyfikatach...

5 maja 2011

Google Maps API v2 + zumi.pl

Dzisiaj wracam do tematu map z zumi.pl. Ten mały projekt powstał jeszcze za czasów, gdy w Google Maps, zdjęcia satelitarne były mało szczegółowe. Wpadłem więc na pomysł, aby połączyć Google Maps API z kafelkami map z zumi.pl. Pierwszy był skrypt w perlu który... ściągał wszystkie kafelki z zumi.pl, dla każdego przybliżenia. Ale po ściągnięciu ich wszystkich stwierdziłem, że wykorzystanie tego jest bezcelowe. Drugim podejściem było połączenie właśnie z API Google, które to już trochę poznałem i wykorzystywałem do własnych celów. Problemem z połączeniem tych dwóch map polegał na tym, że odwzorowanie map, a raczej "pocięcie" całej mapy na kafelki znacznie różniło się w obu tych przypadkach. Kluczem do sukcesu było poznanie algorytmu kafelkowania map w zumi.pl. Myślałem, że wyjaśnię co z czego wynika, ale nie pamiętam już za wiele. Po prostu trzeba zajrzeć w źródła :) Stronka ta, to okrojona wersja czegoś co używam na co dzień, więc mogą tam być jeszcze jakieś niepotrzebne "śmieci". Kluczowym elementem jest funkcja getTileUrl, która na podstawie podanej szerokości i długości geograficznej oraz przybliżenia, wylicza nam, który kafelek ma być pobrany z serwera zumi.pl. Reszta to funkcje wyświetlające to wszystko.

20 kwietnia 2011

Apache FOP

Znowu wróciłem do FOP'a. Pomyślałem sobie, że jak znajdę sposób na obliczenie szerokości kolumn w tabeli na podstawie znajdujących się tam danych i ustawię tą szerokość na sztywno, to będzie można spokojnie obejść problem table-layout="fixed". W PHP mamy do dyspozycji funkcję imagettfbbox, dzięki której można wyliczyć szerokość tekstu przy zastosowaniu określonej czcionki. Najprościej można to zrobić tak:
function get_text_width($font,$font_size,$text) {
    $box = imagettfbbox($font_size,0,$font,$text);
    return $box[4];
}
Gdzie: $font - ścieżka do użytej czcionki $font_size - rozmiar czcionki dla której ma być wyliczona szerokość tekstu $text - tekst dla którego wyliczymy szerokość. Przed wygenerowanie właściwej tabeli w XSL-FO, wyliczam maksymalne szerokości dla danych z każdej kolumny i na tej podstawie generuję tagi <fo:table-column column-width="??"/>.

16 kwietnia 2011

MNDP cz.3

poniżej zamieszcza skrypt, skanujący za pomocą MNDP otoczenie sieciowe
#!/usr/bin/perl -w

###############################################################################
#
# scan-mikrotik.pl v2.6.0
#
# scan utility for mikrotik
#
# 2011-03-27
# dodano uptime
#
# 2011-03-20
# mindc.net
###############################################################################

use strict;
use IO::Socket;
use Data::Dumper;
use Time::HiRes qw( usleep );

$|=1;

my %links;

print "scan-mikrotik.pl v2.6.0
link  hw                 ip               board      sw      uptime identity
--------------------------------------------------------------------------------
wait for 3 sec...\r";

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

foreach my $link ( keys %links ) {
    unless ( fork ) {
        my $socket = IO::Socket::INET->new(
            Proto => 'UDP',
            PeerPort => 5678,
            PeerAddr => inet_ntoa(INADDR_BROADCAST),
            LocalAddr => $links{$link},
            Broadcast => 1,
            Reuse => 1
        ) or die "cannot bind socket $!\n";
        my $data = {};
        eval {
            my $out = {};
            local $SIG{ALRM} = sub {
                foreach ( sort keys %$data ) {
                    my $o = $data->{$_};
                    printf("%s  %17s  %-16s %-10s %-7s %6s %s\n",
                        $link,
                        $o->{mac},
                        $o->{ip},
                        $o->{board},
                        $o->{sw},
                        sec2human($o->{uptime}),
                        $o->{identity}
                    );
                }
                print "\n" if keys %$data;
                exit;
            };
            alarm 3;
            open (STDIN,"tcpdump -i $link -lnqxt -s 1024 src port 5678 and proto UDP 2>/dev/null |");
            usleep(100000);
            $socket->send(pack("H8",0));
            usleep(100000);
            $socket->send(pack("H8",0));
            usleep(100000);
            $socket->send(pack("H8",0));
            close $socket;
            my $buffer = '';
            my $i = 0;
            while (<>) {
                s/IP \S+.5678 > \S+ UDP, length \d+.*//g;
                s/0x\d\d\d\d://g;
                s/\s+//g;
                $buffer .= $_;
                if ( $_ =~ /^$/ && $buffer ne '' ) {
                    $i++;
                    my $out = mikrotik_header($buffer);
                    $data->{$out->{mac}} = $out;
                    $buffer = '';
                }
            }
            alarm 0;
        };
        exit;
    }
}

1 while ( wait() != -1);
system "killall tcpdump";

exit;

sub mikrotik_header {
    my $buffer = shift;
                         #ip       #udp  #mikrotik
    my @header = unpack("H4nH8H8NN nnnH4 H2H2S(H4n/a*)*", pack("H*",$buffer));

    #ip header
    shift @header; #ip1
    shift @header; #ip_lenght
    shift @header; #ip2
    shift @header; #ip3
    my $ip = shift @header; #ip_src
    shift @header; #ip_dst
    #udp header
    shift @header; #udp_src_port
    shift @header; #udp_dst_port
    shift @header; #udp_lenght
    shift @header; #udp_checksum

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

    my $out = {};

    $out->{mac} = '';
    $out->{identity} = '';
    $out->{vendor} = '';
    $out->{sw} = '';
    $out->{board} = '';
    $out->{key} = '';
    $out->{uptime} = 0;

    $out->{ip} = join(".",unpack("C4",pack("N",$ip)));

    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";
    }
}
Działanie jest proste. Skrypt wysyła na każdym z interfejsów zapytanie 0x00000000 i zaczyna nasłuchiwać na odpowiedź w formacie MNDP. Warunkiem działania skrytpu jest istnienie co najmniej jednego adresu IP na danym interfejsie. Niezbyt eleganckie rozwiązanie z killall tcpdump, ale nie mogłem sobie z tym poradzić, gdy nie zamykam STDIN. A może by tak select.