#!/usr/bin/perl use strict; use warnings; use Encode; use AnyEvent; use AnyEvent::Handle; use IP::Country::Fast; use Socket; use POSIX qw/strftime/; our %S; ################################################################################ # Verify whether a given domain name or IP address and port are valid. # returns the valid ip-address + port, or 0 when not. ################################################################################ sub valid_address { my $h = shift; # split up address and port my ($a, $p) = ($h =~ m/:/) ? $h =~ /(.*):(.*)/ : ($h,0); return (undef,undef) unless ($a && $p); # check if IP and port are in valid range $a = ($a =~ '\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b') ? $a : 0; $p = (0 < $p && $p <= 65535) ? $p : 0; # exclude addresses where we don't want people sniffing for (qw|192.168.(.\d*).(.\d*) 127.0.(.\d*).(.\d*) 10.0.(.\d*).(.\d*)|){$a = 0 if ($a =~ m/$_/)} return ($a, $p); } sub host2ip { my $name = shift; my $unpack = inet_aton($name) if $name; return inet_ntoa($unpack) if $unpack; } sub query_master { my $ms = shift; for my $g (@{$ms->{games}}) { my $cv = AnyEvent->condvar; my $master_list = ""; my $handle; $handle = new AnyEvent::Handle( connect => [$ms->{ip} => $ms->{port}], timeout => 15, poll => 'r', on_error => sub { #print "($ms->{ip}, $g) $!\n"; $handle->destroy; $cv->send; }, on_eof => sub { process_received_data($master_list, $g, $ms); $handle->destroy; $cv->send; }, on_read => sub { my $m = $_[0]->rbuf; $_[0]->rbuf = ""; # part 1: receive \basic\\secure\$key if ($m =~ m/\\basic\\\\secure\\/) { # received data my %r; $m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg; # respond to challenge my $validate = get_validate_string($S{game}->{$g}->{key}, $r{secure}, $r{enctype}||0); # print and send response $handle->push_write("\\gamename\\$g\\location\\0\\validate\\$validate\\final\\"); #part 3: also request the list \list\gamename\ut -- skipped in UCC applets $handle->push_write("\\list\\\\gamename\\$g\\final\\"); } # part 3b: receive the entire list in multiple steps. if ($m =~ m/\\ip\\/) { $master_list .= $m; } } ); $cv->recv; } } sub process_received_data { my ($buf, $g, $ms) = @_; $buf = encode('UTF-8', $buf); #counter my $c = 0; # parse $buf into an array of [ip, port] foreach my $l (split(/\\/, $buf)) { # search for \ip\255.255.255.255:7778\, contains ':' if ($l =~ /:/) { my ($a,$p) = valid_address($l); next unless ($a && $p); db_add_server(ip => $a, port => $p); $c++; } } # log addresses per game per master #print "found $c \t$g \taddresses at $ms->{ip}.\n" if ($c > 0 ); # print only responsive gamenames (useful for making selections) print "$g " if ($c > 0 ); } 1;