aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/TCP
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MasterServer/TCP')
-rwxr-xr-xlib/MasterServer/TCP/BrowserHost.pm5
-rwxr-xr-xlib/MasterServer/TCP/Handler.pm82
-rwxr-xr-xlib/MasterServer/TCP/ListCompiler.pm34
-rwxr-xr-xlib/MasterServer/TCP/Syncer.pm100
-rwxr-xr-xlib/MasterServer/TCP/UCCAppletQuery.pm72
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;