diff options
Diffstat (limited to 'lib/MasterServer/TCP')
| -rwxr-xr-x | lib/MasterServer/TCP/BrowserHost.pm | 5 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/Handler.pm | 82 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/ListCompiler.pm | 34 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/Syncer.pm | 100 | ||||
| -rwxr-xr-x | lib/MasterServer/TCP/UCCAppletQuery.pm | 72 |
5 files changed, 191 insertions, 102 deletions
diff --git a/lib/MasterServer/TCP/BrowserHost.pm b/lib/MasterServer/TCP/BrowserHost.pm index 3eb22eb..855b2c0 100755 --- a/lib/MasterServer/TCP/BrowserHost.pm +++ b/lib/MasterServer/TCP/BrowserHost.pm @@ -16,9 +16,6 @@ our @EXPORT = qw| browser_host clean_tcp_handle|; ################################################################################ sub browser_host { my $self = shift; - - # log: TCP host is active - $self->log("load","Loading TCP Browser Host."); my $browser = tcp_server undef, $self->{listen_port}, sub { my ($fh, $a, $p) = @_; @@ -51,7 +48,7 @@ sub browser_host { }; # startup of TCP server complete - $self->log("load", "Listening for TCP connections on port $self->{listen_port}."); + $self->log("info", "Listening for TCP connections on port $self->{listen_port}."); return $browser; } diff --git a/lib/MasterServer/TCP/Handler.pm b/lib/MasterServer/TCP/Handler.pm index 2fb05f5..1a075bf 100755 --- a/lib/MasterServer/TCP/Handler.pm +++ b/lib/MasterServer/TCP/Handler.pm @@ -33,47 +33,53 @@ sub read_tcp_handle { # allow multiple blocks to add to the response string my $response = ""; + + # print debug values + $self->log("debug","$a:$p sent $rxbuf"); # replace empty values for the string "undef" and replace line endings from netcatters # parse the received data and extrapolate all the query commands found my %r = (); $m =~ s/\\\\/\\undef\\/; - $m =~ s/\n//; + $m =~ s/\\$/\\undef\\/; $m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg; # secure/validate challenge # part 2: receive \gamename\ut\location\0\validate\$validate\final\ $val = $self->handle_validate(\%r, $h, $secure, $a, $p) if (exists $r{validate} && !$val); - + # about query $response .= $self->handle_about($r{about}, $a, $p) if (exists $r{about}); # return address list - # part 3: wait for the requested action: \list\gamename\ut\ + # part 3: wait for the requested action: \list\\gamename\ut\ $self->handle_list($val, \%r, $c, $a, $p) if (exists $r{list} && exists $r{gamename}); # Sync request from another 333networks-based masterserver. Respond with list # of requested games (or all games). $self->handle_sync($val, \%r, $c, $a, $p) if (exists $r{sync}); + # + # Support echo: doesn't do anything but print to log if not suppressed. + $self->log("echo","($a:$p): $r{echo}") if $r{echo}; + # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # improper syntax/protocol -- no valid commands found # respond with an error. # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # - if ($m =~ m/!(about|sync|validate|list)/) { + if ("about sync validate list" =~ m/\Q$response\E/i) { # error message to client $c->push_write("\\echo\\333networks did not understand your request. ". "Contact us via 333networks.com\\final\\"); # and log it - $self->log("error","invalid request from Browser $a:$p with unknown message \"$rxbuf\"", $self->{log_settings}->{handler_error}); + $self->log("error","invalid request from Browser $a:$p with unknown message \"$rxbuf\"."); } # end if weird query else { $c->push_write($response . "\\final\\") if ($response ne ""); } - } @@ -87,22 +93,28 @@ sub handle_validate { # auth var init my $val = 0; - + # pass or fail the secure challenge - if (exists $r->{gamename} && length $self->get_cipher(lc $r->{gamename}) > 1 ) { + if (exists $r->{gamename} && length $self->get_game_props(lc $r->{gamename})->{cipher} > 1 ) { # game exists and we have the key to verify the response - $val = $self->validated_request($r->{gamename}, $secure, $r->{enctype}, $r->{validate}); - + $val = $self->compare_challenge( + gamename => $r->{gamename}, + secure => $secure, + enctype => $r->{enctype}, + validate => $r->{validate}, + ignore => $self->{ignore_browser_key}, + ); + # update for future queries $self->{browser_clients}->{$h}[1] = $val; } elsif (exists $r->{gamename}) { # log - $self->log("support", "received unknown gamename request \"$r->{gamename}\" from $a:$p"); + $self->log("support", "received unknown gamename request \"$r->{gamename}\" from $a:$p."); } - # log - $self->log("secure","$a:$p validated with $val for $r->{gamename}, $secure, $r->{validate}"); + # log (the spam!) + #$self->log("secure","$a:$p validated with $val for $r->{gamename}, $secure, $r->{validate}"); # return auth status return $val; @@ -124,47 +136,30 @@ sub handle_validate { sub handle_about { my ($self, $about, $a, $p) = @_; my $response = ""; - - # + # contact info - # if ($about =~ /^contact$/i or $about =~ /^undef$/i) { - $response .= "\\about\\$self->{contact_details}"; - - # log/print + $response .= "\\about\\$self->{masterserver_hostname}, contact: $self->{masterserver_contact}"; $self->log("about","communicating to $a:$p my contact information."); } - # - # build info - # - if ($about =~ /^build$/i or $about =~ /^undef$/i) { - + # build/version info + if ($about =~ /^build$/i or $about =~ /^version$/i or $about =~ /^undef$/i) { $response .= "\\build\\$self->{build_type} $self->{build_version} written " . "by $self->{build_author}, released $self->{build_date}"; - - # log/print $self->log("about","telling $a:$p my build info."); } - # # address info - # if ($about =~ /^address$/i or $about =~ /^undef$/i) { - $response .= "\\address\\$self->{masterserver_address}" . "\\listen_port\\$self->{listen_port}" . "\\beacon_port\\$self->{beacon_port}"; - - # log/print $self->log("about","telling $a:$p my address/config info."); } - # # support info - # if ($about =~ /^support$/i or $about =~ /^undef$/i) { - # string games in database my $sg = $self->get_gamenames(); my $sgs = ""; @@ -172,13 +167,15 @@ sub handle_about { $sgs .= " " if (length $sgs > 0); $sgs .= $_->[0]; } - - # print response $response .= "\\support\\$sgs"; - - #log/print $self->log("about","telling $a:$p which games are supported."); } + + # unsupported query + if ("contact build address support version undef" !~ m/$about/i) { + $response .= "\\echo\\incorrect query usage, supported queries are: contact build version address support."; + $self->log("about","incorrect query \"$about\", telling $a:$p the supported \"about\" queries."); + } # return response string return $response; @@ -200,7 +197,7 @@ sub handle_list { my $data = ""; # determine the return format - if ($self->{hex_format} =~ m/$r->{gamename}/i or $r->{gamename} =~ /^cmp$/i) { + if ($self->{hex_format} =~ m/$r->{gamename}/i or $r->{list} =~ /^cmp$/i) { # return addresses as byte format (ip=ABCD port=EF) $data .= $self->compile_list_cmp($r->{gamename}); } @@ -215,7 +212,7 @@ sub handle_list { # immediately send to client $c->push_write($data); - # log successful (debug) + # log successful $self->log("list","$a:$p successfully retrieved the list for $r->{gamename}."); # clean and close the connection @@ -244,7 +241,7 @@ sub handle_sync { my ($self, $val, $r, $c, $a, $p) = @_; # alternate part 3: wait for the requested action: \sync\(all|list of games)\sender\domainname - $self->log("tcp","Sync request from $a:$p found"); + $self->log("tcp","Sync request from $a:$p found."); if ($val && exists $r->{sync}) { @@ -256,8 +253,7 @@ sub handle_sync { $c->push_write($data); # log successful (debug) - if (exists $r->{sender}) {$self->log("sync","$r->{sender} successfully synced.");} - else {$self->log("sync","$a:$p successfully synced.");} + $self->log("sync-tx","$a:$p successfully synced."); # clean and close the connection $self->clean_tcp_handle($c); diff --git a/lib/MasterServer/TCP/ListCompiler.pm b/lib/MasterServer/TCP/ListCompiler.pm index 1863757..a5571d0 100755 --- a/lib/MasterServer/TCP/ListCompiler.pm +++ b/lib/MasterServer/TCP/ListCompiler.pm @@ -3,6 +3,7 @@ package MasterServer::TCP::ListCompiler; use strict; use warnings; + use Exporter 'import'; our @EXPORT = qw| compile_list compile_list_cmp compile_sync |; @@ -15,7 +16,10 @@ sub compile_list { my ($self, $gamename) = @_; # get the list from database - my $serverlist = $self->get_game_list($gamename); + my $serverlist = $self->get_server( + updated => 3600, + gamename => $gamename, + ); # prepare empty return string my $response_string = ""; @@ -24,9 +28,9 @@ sub compile_list { for (@{$serverlist}){ # append \ip\ip:port to string - $response_string .= "\\ip\\$_->[0]:$_->[1]"; + $response_string .= "\\ip\\$_->{ip}:$_->{port}"; } - + # return the string with data return $response_string; } @@ -39,7 +43,10 @@ sub compile_list_cmp { my ($self, $gamename) = @_; # get the list from database - my $serverlist = $self->get_game_list($gamename); + my $serverlist = $self->get_server( + updated => 3600, + gamename => $gamename, + ); # prepare empty return string my $response_string = ""; @@ -48,8 +55,8 @@ sub compile_list_cmp { for (@{$serverlist}){ # convert ip address to ABCDEF mode - my ($A, $B, $C, $D) = ($_->[0] =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/); - my ($E, $F) = ($_->[1] >> 8, $_->[1] & 0xFF); + my ($A, $B, $C, $D) = ($_->{ip} =~ /(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/); + my ($E, $F) = ($_->{port} >> 8, $_->{port} & 0xFF); # print as chr string of 6 bytes long my $bin = ""; $bin .= (chr $A) . (chr $B) . (chr $C) . (chr $D) . (chr $E) . (chr $F); @@ -87,20 +94,23 @@ sub compile_sync { } # only get unique values from array - @games = map { $_ => 1 } @games; + my %games = map { $_ => 1 } @games; # get the list for every requested gamename - for my $g (@games) { - + for my $g (keys %games) { + # $g is now a gamename -- check if it's supported. Else ignore. - if (length $self->get_cipher(lc $g) > 1) { + if ($self->get_game_props($g)) { # get list from database - my $list = $self->get_game_list($g); + my $list = $self->get_server( + updated => 7200, + gamename => $g, + ); # add all games to string separated by spaces my $gamestring = ""; - foreach $_ (@{$list}) {$gamestring .= "$_->[0]:$_->[1] ";} + foreach $_ (@{$list}) {$gamestring .= "$_->{ip}:$_->{port} ";} # if it contains at least one entry, add the list to the response list $response_string .= "\\$g\\$gamestring" if (length $gamestring >= 7); diff --git a/lib/MasterServer/TCP/Syncer.pm b/lib/MasterServer/TCP/Syncer.pm index 2ba52ca..bfe3d78 100755 --- a/lib/MasterServer/TCP/Syncer.pm +++ b/lib/MasterServer/TCP/Syncer.pm @@ -7,39 +7,9 @@ use AnyEvent; use AnyEvent::Handle; use Exporter 'import'; -our @EXPORT = qw| syncer_scheduler sync_with_master process_sync_list|; - -################################################################################ -## Syncer Scheduler -## Request the masterlist for selected or all games from other -## 333networks-based masterservers. -################################################################################ -sub syncer_scheduler { - my $self = shift; - - # log active - $self->log("load", "Synchronisation module active."); - - # go through the list of provided addresses - my $i = 0; - return AnyEvent->timer ( - after => $self->{sync_time}[0], - interval => $self->{sync_time}[1], - cb => sub { - # check if there's a master server entry to be synced. If not, return - # to zero and go all over again. - $i = 0 unless $self->{sync_masters}[$i]; - return if (!defined $self->{sync_masters}[$i]); - - # synchronze with master $i - $self->log("tcp", "Attempting to synchronize with $self->{sync_masters}[$i]->{address}"); - $self->sync_with_master($self->{sync_masters}[$i]); - - #increment counter - $i++; - } - ); -} +our @EXPORT = qw| sync_with_master + process_sync_list + masterserver_list |; ################################################################################ ## Sends synchronization request to another 333networks based master server and @@ -47,6 +17,9 @@ sub syncer_scheduler { ################################################################################ sub sync_with_master { my ($self, $ms) = @_; + + # announce + $self->log("tcp", "Attempting to synchronize with $ms->{ip}"); # list to store all IPs in. my $sync_list = ""; @@ -54,11 +27,11 @@ sub sync_with_master { # connection handle my $handle; $handle = new AnyEvent::Handle( - connect => [$ms->{address} => $ms->{port}], - timeout => 3, + connect => [$ms->{ip} => $ms->{tcp}], + timeout => 4, poll => 'r', - on_error => sub {$self->log("error","$! on $ms->{address} $ms->{port}"); $handle->destroy;}, - on_eof => sub {$self->process_sync_list($sync_list, $ms); $handle->destroy;}, + on_error => sub {$self->log("error","$! on $ms->{ip} $ms->{tcp}"); $handle->destroy;}, + on_eof => sub {$self->process_sync_list($sync_list, $ms); $handle->destroy;}, on_read => sub { # receive and clear buffer my $m = $_[0]->rbuf; @@ -78,13 +51,20 @@ sub sync_with_master { $m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg; # respond to the validate challenge - my $validate = $self->validate_string("333networks", $r{secure}, $r{enctype}); + my $validate = $self->validate_string( + gamename => "333networks", + secure => $r{secure}, + enctype => $r{enctype} + ); # part 2: send \gamename\ut\location\0\validate\$validate\final\ $handle->push_write("\\gamename\\333networks\\location\\0\\validate\\$validate\\final\\"); # part 3: request the list \sync\gamenames consisting of space-seperated game names or "all" - my $request = "\\sender\\$self->{masterserver_address}\\sync\\".(($self->{sync_games}[0] == 0) ? "all" : $self->{sync_games}[1])."\\final\\"; + # compatibility note: old queries use "new", instead treat them as "all". + my $request = "\\sync\\" + . (($self->{sync_games}[0] == 0) ? ("all" or "new") : $self->{sync_games}[1]) + . "\\final\\"; # push the request to remote host $handle->push_write($request); @@ -119,7 +99,7 @@ sub process_sync_list { if (exists $r{echo}) { # remote address says... - $self->log("error", "$ms->{address} replied: $r{echo}"); + $self->log("error", "$ms->{ip} replied: $r{echo}"); } @@ -152,7 +132,7 @@ sub process_sync_list { } else { # invalid address, log - $self->log("error", "invalid address found while syncing at $ms->{address}: $l!"); + $self->log("error", "invalid address found while syncing at $ms->{ip}: $l!"); } } # endif ($l =~ /:/) @@ -165,7 +145,41 @@ sub process_sync_list { } # end while # end message - $self->log("sync", "received $c addresses after syncing from $ms->{address}"); + $self->log("sync-rx", "received $c addresses after syncing from $ms->{ip}:$ms->{tcp}"); } - + +################################################################################ +## Determine a list of all unique 333networks-compatible masterservers +## and return this list. Join the brotherhood! +################################################################################ +sub masterserver_list { + my $self = shift; + my %brotherhood; + + # start with the masterservers defined in our configuration file + for my $ms (@{$self->{sync_masters}}) { + my $ip = $self->host2ip($ms->{address}); + $brotherhood{"$ip:$ms->{port}"} = {ip => $ip, tcp => $ms->{port}, udp => $ms->{beacon}} if $ip; + } + + # get the list of uplinking masterservers + my $serverlist = $self->get_server( + updated => 3600, + gamename => "333networks", + limit => 50, # more would be ridiculous.. right?.. + ); + + # overwrite existing entries, add new + for my $ms (@{$serverlist}) { + $brotherhood{"$ms->{ip}:$ms->{hostport}"} = {ip => $ms->{ip}, tcp => $ms->{hostport}, udp => $ms->{port}}; + } + + # masterservers that sync with us can not be derived directly, but by reading + # the server log we can add them manually. Lot of work, little gain, as those + # syncing masterservers will most likely be uplinking as well between now and + # a few weeks/months. + + return \%brotherhood; +} + 1; diff --git a/lib/MasterServer/TCP/UCCAppletQuery.pm b/lib/MasterServer/TCP/UCCAppletQuery.pm new file mode 100755 index 0000000..e3eb587 --- /dev/null +++ b/lib/MasterServer/TCP/UCCAppletQuery.pm @@ -0,0 +1,72 @@ + +package MasterServer::TCP::UCCAppletQuery; + +use strict; +use warnings; +use AnyEvent; +use AnyEvent::Handle; +use Exporter 'import'; + +our @EXPORT = qw| query_applet |; + +################################################################################ +## The UCC Applet (Epic Megagames, Inc.) functions as a master server for one +## single game. However, it does not always follow the defined protocol. +## This module connects with UCC masterserver applets to receive the list. +################################################################################ +sub query_applet { + my ($self, $ms) = @_; + + # be nice to notify + $self->log("tcp","start querying $ms->{ip}:$ms->{port} for '$ms->{game}' games"); + + # list to store all IPs in. + my $master_list = ""; + + # connection handle + my $handle; + $handle = new AnyEvent::Handle( + connect => [$ms->{ip} => $ms->{port}], + timeout => 5, + poll => 'r', + on_error => sub {$self->log("error", "$! on $ms->{ip}:$ms->{port}."); $handle->destroy;}, + on_eof => sub {$self->process_ucc_applet_query($master_list, $ms); $handle->destroy;}, + on_read => sub { + + # receive and clear buffer + my $m = $_[0]->rbuf; + $_[0]->rbuf = ""; + + # remove string terminator + chop $m if $m =~ m/secure/; + + # 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 = $self->validate_string(gamename => $ms->{game}, + enctype => $r{enctype}||0, + secure => $r{secure}); + + # send response + $handle->push_write("\\gamename\\$ms->{game}\\location\\0\\validate\\$validate\\final\\"); + + # part 3: also request the list \list\gamename\ut -- skipped in UCC applets + $handle->push_write("\\list\\\\gamename\\$ms->{game}\\final\\"); + + } + + # part 3b: receive the entire list in multiple steps. + if ($m =~ m/\\ip\\/) { + # add buffer to the list + $master_list .= $m; + } + } + ); +} + +1; |
