diff options
Diffstat (limited to 'lib/MasterServer/TCP/Syncer.pm')
| -rwxr-xr-x | lib/MasterServer/TCP/Syncer.pm | 226 |
1 files changed, 115 insertions, 111 deletions
diff --git a/lib/MasterServer/TCP/Syncer.pm b/lib/MasterServer/TCP/Syncer.pm index d890f00..cf7ee5b 100755 --- a/lib/MasterServer/TCP/Syncer.pm +++ b/lib/MasterServer/TCP/Syncer.pm @@ -5,152 +5,156 @@ use warnings; use AnyEvent; use AnyEvent::Handle; use Exporter 'import'; - -our @EXPORT = qw| sync_with_master - process_sync_list |; +our @EXPORT = qw| synchronize + process_applet + process_syncer |; ################################################################################ -## Sends synchronization request to another 333networks based master server and -## receives the list of games. +## Synchronize with UCC Applets (Epic Megagames, Inc.) or other 333networks +## based masterservers. ################################################################################ -sub sync_with_master { - my ($self, $ms) = @_; - - # announce - $self->log("tcp", "Attempting to synchronize with $ms->{ip}"); +sub synchronize { + my ($self, $ms, $type) = @_; + my $ipbuflist = ""; - # list to store all IPs in. - my $sync_list = ""; - # connection handle - my $handle; - $handle = new AnyEvent::Handle( + my $handle; $handle = new AnyEvent::Handle( connect => [$ms->{ip} => $ms->{hostport}], timeout => $self->{timeout_time}, poll => 'r', - on_error => sub {$self->error($!, "$ms->{ip}:$ms->{hostport}"); $handle->destroy;}, - on_eof => sub {$self->process_sync_list($sync_list, $ms); $handle->destroy;}, + on_error => sub {$handle->destroy; $self->error($!, "$ms->{ip}:$ms->{hostport}");}, + on_eof => sub { + $handle->destroy; + if ($type eq "applet") {$self->process_applet($ipbuflist, $ms);} + if ($type eq "333nwm") {$self->process_syncer($ipbuflist, $ms);} + }, on_read => sub { - # receive and clear buffer my $m = $_[0]->rbuf; $_[0]->rbuf = ""; - # remove string terminator: sometimes trailing slashes, line endings or - # string terminators are added or forgotten by sender, so \secure\abcdef - # is actually \secure\abcdef{\0} - chop $m if $m =~ m/secure/; - # part 1: receive \basic\\secure\$key - if ($m =~ m/basic\\\\secure/) { + if ($m =~ m/\\basic\\\\secure\\/) { - # hash $m into %r - my %r = (); - $m =~ s/\\\\/\\undef\\/; - $m =~ s/\n//; - $m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg; + # use provided gamename for applet or 333networks for syncer + my $gamename = ""; + $gamename = $ms->{gamename} if ($type eq "applet"); + $gamename = "333networks" if ($type eq "333nwm"); - # respond to the validate challenge + # processess received data and respond to challenge + my $rx = $self->data2hashref($m); my $validate = $self->validate_string( - gamename => "333networks", - secure => $r{secure}, - enctype => $r{enctype} - ); + gamename => $gamename, + enctype => $rx->{enctype}, + secure => $rx->{secure} + ); + + # send challenge response + $handle->push_write("\\gamename\\$gamename\\location\\0\\validate\\$validate\\final\\"); + + # part 3a: also request the list \list\\gamename\ut + my $request = ""; + if ($type eq "applet") { + $request = "\\list\\\\gamename\\$ms->{gamename}\\final\\";} + # part 3b: request the list \sync\[gamenames] consisting of space-seperated game names or "all" + if ($type eq "333nwm") { + $request = "\\sync\\".($self->{sync_games}[0] == 0 ? "all" : $self->{sync_games}[1] )."\\final\\";} - # 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" - # compatibility note: old queries use "new", instead treat them as "all". - my $request = "\\sync\\" - . (($self->{sync_games}[0] == 0) ? ("all") : $self->{sync_games}[1]) - . "\\final\\"; - # push the request to remote host $handle->push_write($request); - - # clean up $m for future receivings - $m = ""; - - } # end secure + } - # part 4: receive the entire list in multiple steps - $sync_list .= $m; - }, + # part 4: receive the entire list in multiple steps. + # continue receiving data and adding to the buffer + else {$ipbuflist .= $m;} + } ); } ################################################################################ -## Process the list of addresses that was received after querying the UCC applet -## and store them in the pending list. +## Process the list of addresses received from the UCC applet masterserver and +## move new addresses to the pending list. ################################################################################ -sub process_sync_list { - my ($self, $m, $ms) = @_; - - # replace empty values for the string "undef" and replace line endings from netcatters - # parse hash {gamename => list of ips seperated by space} - my %r = (); - $m =~ s/\\\\/\\undef\\/; - $m =~ s/\n//; - $m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg; +sub process_applet { + my ($self, $buf, $ms) = @_; + my $new = 0; my $tot = 0; + + # database types such as SQLite are slow, therefore use transactions. + $self->{dbh}->begin_work; + + # parse $buf into an array of [ip, port] + foreach my $l (split /\\/, $buf) { + + # search for \ip\255.255.255.255:7778\ and capture ip and port + if (my ($address,$port) = $l =~ /([\.\w]+):(\d+)/ ) { + # check if address entry is valid + if ($self->valid_address($address,$port)) { + + # add server and count new/total addresses + $new += $self->insert_pending(ip => $address, port => $port); + $tot++; + } + # invalid address, log + else {$self->log("error", "invalid address found at $ms->{ip}:$ms->{hostport} > $l (applet)");} + } + } # end foreach - # counter - my $c = 0; + # complete transaction + $self->{dbh}->commit; + + # update time if successful applet query + $self->update_master_applet(ip => $ms->{ip}, port => $ms->{hostport}, gamename => $ms->{gamename} ) + if ($tot > 0); + + # print findings + $self->log("syncer","found $tot ($new new) addresses at $ms->{ip},$ms->{hostport} for $ms->{gamename} (applet)"); +} + +################################################################################ +## Process the list of addresses received from the 333networks masterserver and +## move new addresses to the pending list. +################################################################################ +sub process_syncer { + my ($self, $buf, $ms) = @_; + my $new = 0; my $tot = 0; + + # extract to hash: gamename => ( address list ) + my $rx = $self->data2hashref($buf); - if (exists $r{echo}) { - # remote address says... - $self->log("echo", "$ms->{ip} replied: $r{echo}"); - } + # use transactions for large numbers of ip/ports + $self->{dbh}->begin_work; # iterate through the gamenames and addresses - while ( my ($gn,$addr) = each %r) { - - # process all games wether we have a cipher for them. - if (defined $gn) { - - # some database types, such as SQLite, are slow - therefore use transactions. - $self->{dbh}->begin_work; - - # l(ocations, \label\ip:port\) split up in a(ddress) and p(ort) - foreach my $l (split(/ /, $addr)) { - - # search for \255.255.255.255:7778\, contains ':' - if ($l =~ /:/) { - my ($a,$p) = $l =~ /(.*):(.*)/; - - # check if address entry is valid - if ($self->valid_address($a,$p)) { - # count number of valid addresses - $c++; - - # add server - $self->syncer_add($a, $p, $gn, $self->secure_string()); - - # print address (debug) - # $self->log("add", "syncer added $gn\t$a\t$p"); - } - else { - # invalid address, log - $self->log("error", "invalid address found while syncing at $ms->{ip}: $l!"); - } - - } # endif ($l =~ /:/) - } # end for / / - - # end transaction, commit - $self->{dbh}->commit; + while ( my ($gamename,$addresslist) = each %{$rx}) { + + # parse $buf into an array of [ip, port] + foreach my $l (split / /, $addresslist) { + + # search for \ip\255.255.255.255:7778\ and capture ip and port + if (my ($address,$port) = $l =~ /([\.\w]+):(\d+)/ ) { - } # end defined $gn + # check if address entry is valid + if ($self->valid_address($address,$port)) { + # add server and count new/total addresses + $new += $self->insert_pending(ip => $address, port => $port); + $tot++; + + } + # invalid address, log + else {$self->log("error", "invalid address found at $ms->{ip}:$ms->{hostport} > $l (333nwm)");} + } + } # end foreach } # end while - # update this sync master in the gamelist with lastseen time - $self->update_server_list( - ip => $ms->{ip}, - port => $ms->{port}, - ) if ($c > 0); + # complete transaction + $self->{dbh}->commit; + + # update time if successful sync master query + $self->update_server(ip => $ms->{ip}, hostport => $ms->{hostport}) + if ($tot > 0); # end message - $self->log("sync-rx", "received $c addresses after syncing from $ms->{ip}:$ms->{hostport}"); + $self->log("syncer", "found $tot ($new new) addresses at $ms->{ip},$ms->{hostport} (333nwm)"); } 1; |
