aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/UDP
diff options
context:
space:
mode:
authorDarkelarious <darkelarious@333networks.com>2017-05-13 14:18:28 +0200
committerDarkelarious <darkelarious@333networks.com>2017-05-13 14:20:49 +0200
commit34a2c7390ea9662d33258d384e72fff1912343ff (patch)
treed96ea33c0107e4906a152aa1de4b5c75b81ba0a8 /lib/MasterServer/UDP
parent84af66aba26d2088d5d95c240d176f3edaf17b58 (diff)
downloadMasterServer-Perl-2.3.0.tar.gz
MasterServer-Perl-2.3.0.zip
revised synchronization methods, config settings and bug fixesv2.3.0
Diffstat (limited to 'lib/MasterServer/UDP')
-rwxr-xr-xlib/MasterServer/UDP/BeaconCatcher.pm21
-rwxr-xr-xlib/MasterServer/UDP/BeaconChecker.pm29
-rwxr-xr-xlib/MasterServer/UDP/DatagramProcessor.pm116
-rwxr-xr-xlib/MasterServer/UDP/UDPTicker.pm51
-rwxr-xr-xlib/MasterServer/UDP/UpLink.pm93
5 files changed, 130 insertions, 180 deletions
diff --git a/lib/MasterServer/UDP/BeaconCatcher.pm b/lib/MasterServer/UDP/BeaconCatcher.pm
index 06fd38a..5c4ff5f 100755
--- a/lib/MasterServer/UDP/BeaconCatcher.pm
+++ b/lib/MasterServer/UDP/BeaconCatcher.pm
@@ -1,4 +1,3 @@
-
package MasterServer::UDP::BeaconCatcher;
use strict;
@@ -45,8 +44,7 @@ sub on_beacon_receive {
my ($port, $iaddr) = sockaddr_in($pa);
my $peer_addr = inet_ntoa($iaddr);
- # if the beacon has a length longer than a certain amount, assume it is
- # a fraud or crash attempt
+ # assume fraud/crash attempt if response too long
if (length $b > 64) {
# log
$self->log("attack","length exceeded in beacon: $peer_addr:$port sent $b");
@@ -55,17 +53,20 @@ sub on_beacon_receive {
$b = substr $b, 0, 64;
}
- # if a heartbeat format was detected...
- $self->process_udp_beacon($udp, $pa, $b, $peer_addr, $port)
- if ($b =~ m/\\heartbeat\\/ && $b =~ m/\\gamename\\/);
+ # FIXME: note to self: order is important when having combined queries!
+ # TODO: find a more elegant and long-time solution for this.
- # or if this is a secure response, verify the response
+ # if this is a secure response, verify the response
$self->process_udp_validate($b, $peer_addr, $port, undef)
if ($b =~ m/\\validate\\/);
+
+ # if a heartbeat format was detected...
+ $self->process_udp_beacon($udp, $pa, $b, $peer_addr, $port)
+ if ($b =~ m/\\heartbeat\\/ && $b =~ m/\\gamename\\/);
- # or if other masterservers check if we're still alive
- $self->process_udp_basic($udp, $pa, $b, $peer_addr)
- if ($b =~ m/\\basic\\/ || $b =~ m/\\status\\/ || $b =~ m/\\info\\/);
+ # if other masterservers check if we're still alive
+ $self->process_udp_secure($udp, $pa, $b, $peer_addr)
+ if ($b =~ m/\\secure\\/ || $b =~ m/\\basic\\/ || $b =~ m/\\status\\/ || $b =~ m/\\info\\/);
}
1;
diff --git a/lib/MasterServer/UDP/BeaconChecker.pm b/lib/MasterServer/UDP/BeaconChecker.pm
index f74378d..73220cf 100755
--- a/lib/MasterServer/UDP/BeaconChecker.pm
+++ b/lib/MasterServer/UDP/BeaconChecker.pm
@@ -1,4 +1,3 @@
-
package MasterServer::UDP::BeaconChecker;
use strict;
@@ -6,7 +5,7 @@ use warnings;
use AnyEvent::Handle::UDP;
use Exporter 'import';
-our @EXPORT = qw| query_udp_server|;
+our @EXPORT = qw| query_udp_server |;
################################################################################
## Get the server status from any server over UDP and store the received
@@ -17,20 +16,28 @@ sub query_udp_server {
my ($self, $id, $ip, $port, $secure, $message_type) = @_;
my $buf = "";
- # debug spamming
- $self->log("udp", "Query server $id ($ip:$port)");
+ # debug logging
+ # $self->log("debug", "Query server $id ($ip:$port)");
# connect with UDP server
my $udp_client; $udp_client = AnyEvent::Handle::UDP->new(
- # Bind to this host and port
connect => [$ip, $port],
- timeout => 1,
- on_timeout => sub {$udp_client->destroy();}, # don't bother reporting timeouts
+ timeout => $self->{timeout_time},
+ on_timeout => sub {$udp_client->destroy();}, # do not report timeouts
on_error => sub {$udp_client->destroy();}, # or errors
- on_recv => sub {
+ on_recv => sub {
# add packet to buffer
$buf .= $_[0];
+
+ # FIXME: note to self: order is important when having combined queries!
+ # TODO: find a more elegant and long-time solution for this.
+
+ # message type 1: \basic\\secure\wookie
+ # if validate, assume that we sent a \basic\secure request.
+ if ($buf =~ m/\\validate\\/){
+ $self->process_udp_validate($buf, $ip, undef, $port);
+ }
# message type 0: \basic\\info\
# if gamename, ver, hostname and hostport are available, but NOT the value
@@ -42,12 +49,6 @@ sub query_udp_server {
$self->process_query_response($buf, $ip, $port);
}
- # message type 1: \basic\\secure\wookie
- # if validate, assume that we sent a \basic\secure request.
- if ($buf =~ m/\\validate\\/){
- $self->process_udp_validate($buf, $ip, undef, $port);
- }
-
# message type 2: \status\
# contains same info as \basic\\info, but also "listenserver". Only for UT.
if ($buf =~ m/\\gamename\\ut/ &&
diff --git a/lib/MasterServer/UDP/DatagramProcessor.pm b/lib/MasterServer/UDP/DatagramProcessor.pm
index d2a3333..2a26763 100755
--- a/lib/MasterServer/UDP/DatagramProcessor.pm
+++ b/lib/MasterServer/UDP/DatagramProcessor.pm
@@ -38,18 +38,18 @@ sub process_udp_beacon {
# if no entry exists, report error.
if (defined $game_props) {
-
- # some games (like bcommander) have a default port and don't send a heartbeat port.
- $r{heartbeat} = $game_props->{heartbeat} if ($r{heartbeat} == 0);
+
+ # validate heartbeat data
+ my $heartbeat = ($r{heartbeat} || $game_props->{default_qport});
#
# verify valid server address (ip+port)
- if ($self->valid_address($peer_addr,$r{heartbeat})) {
+ if ($self->valid_address($peer_addr,$heartbeat)) {
# check if the entry already was not added within the last 5 seconds, throttle otherwise
my $throttle = $self->get_pending(
ip => $peer_addr,
- heartbeat => $r{heartbeat},
+ heartbeat => $heartbeat,
gamename => $r{gamename},
after => 5,
sort => "added",
@@ -64,7 +64,7 @@ sub process_udp_beacon {
# or add to pending with new secure string.
my $auth = $self->add_server_new(ip => $peer_addr,
beaconport => $port,
- heartbeat => $r{heartbeat},
+ heartbeat => $heartbeat,
gamename => $r{gamename},
secure => $secure,
direct => 1,
@@ -77,8 +77,8 @@ sub process_udp_beacon {
# verify that this is a legitimate client by sending the "secure" query
$udp->push_send("\\secure\\$secure\\final\\", $pa);
- # log this as a new beacon
- $self->log("secure", "challenged new beacon $peer_addr:$port with $secure.");
+ # log this as a new beacon (debug)
+ #$self->log("secure", "challenged new beacon $peer_addr:$port with $secure.");
}
}
@@ -91,7 +91,7 @@ sub process_udp_beacon {
}
# unknown game
else {
- $self->log("invalid","$peer_addr tries to identify as unknown game \"$r{gamename}\".");
+ $self->log("support","$peer_addr tries to identify as unknown game \"$r{gamename}\".");
}
}
@@ -100,13 +100,7 @@ sub process_udp_beacon {
# be extrapolated from the heartbeat
else {
# log
- $self->log("support", "received unknown beacon from $peer_addr --> $raw");
- #
- # TODO: more practical way to log this to the database: new table
- # named "unsupported" where messages are logged by ip, port, gamename (if
- # applicable) and TEXT raw message.
- #
-
+ $self->log("support", "received unknown beacon from $peer_addr --> '$raw'");
}
}
@@ -138,7 +132,7 @@ sub process_udp_validate {
# was either removed by the BeaconChecker or cleaned out in maintenance (after X hours).
if (defined $pending) {
- #determine if it uses any enctype
+ # determine if it uses any enctype
my $enc = (defined $r{enctype}) ? $r{enctype} : 0;
# database may not contain the correct gamename (ucc applet, incomplete beacon, other game)
@@ -169,7 +163,7 @@ sub process_udp_validate {
# remove the entry from pending if successfully added
$self->remove_pending($pending->{id}) if ( $sa >= 0);
}
- # was not found, insert clean and remove from pending
+ # was not found in serverlist, insert clean and remove from pending
else {
my $sa = $self->add_server_list(
ip => $pending->{ip},
@@ -183,37 +177,51 @@ sub process_udp_validate {
else {
# else failed validation
# calculate expected result for log
- my $validate_string = $self->validate_string(
- gamename => $pending->{gamename},
- secure => $pending->{secure}
+
+ my $validate_string = "";
+ if ($pending->{gamename} && $pending->{secure}) {
+ $validate_string = $self->validate_string(
+ gamename => $pending->{gamename},
+ secure => $pending->{secure}
+ );
+ }
+ $self->log("secure","$pending->{id} for ".
+ ($pending->{gamename} || "empty_p_gamename")
+ ." sent: '". ($pending->{secure} || "empty_p_secure")
+ ."', expected '". ($validate_string || "empty_v_string")
+ ."', got '". ($r{validate} || "empty_r_validate")
+ ."'"
);
- $self->log("secure","$pending->{id} for $pending->{gamename} sent: $pending->{secure}, got $r{validate}, expected $validate_string");
}
}
# if no entry found in pending list
else {
- # 404 not found
- $self->log("error","server not found in pending for $peer_addr:",
- ($heartbeat ? $heartbeat : "" ), ($port ? $port : "" ), " !");
+ # not found
+ $self->log("error","server not found in pending for ".
+ ($peer_addr || "ip") .":".
+ ($heartbeat || "0") .",".
+ ($port || "0") ." !");
}
}
################################################################################
## Process query data that was obtained with \basic\ and/or \info\ from the
## beacon checker module.
+## FIXME: error checking and data processing. ($_ || "default") instead.
################################################################################
sub process_query_response {
# $self, udp data, ip, port
my ($self, $buf, $ip, $port) = @_;
- #process datastream
- my %s;
+ # process datastream
+ my %s = ();
$buf = encode('UTF-8', $buf);
$buf =~ s/\\([^\\]+)\\([^\\]+)/$s{$1}=$2/eg;
+
+
# check whether the gamename is supported in our db
- if (defined $s{gamename} &&
- length $self->get_game_props($s{gamename})->{cipher} > 1) {
+ if (exists $s{gamename} && $self->get_game_props($s{gamename})) {
# parse variables
my %nfo = ();
@@ -224,19 +232,22 @@ sub process_query_response {
# some mor0ns have hostnames longer than 200 characters
$nfo{hostname} = substr $nfo{hostname}, 0, 199 if (length $nfo{hostname} >= 199);
- # log results
- $self->log("hostname", "$ip:$port is now known as $nfo{hostname}");
+ # log results (debug)
+ # $self->log("hostname", "$ip:$port is now known as $nfo{hostname}");
# add or update in serverlist (assuming validation is complete)
- $self->update_server_list(
+ my $result = $self->update_server_list(
ip => $ip,
port => $port,
gamename => $s{gamename},
%nfo);
-
+
# if address is in pending list, remove it
my $pen = $self->get_pending(ip => $ip, heartbeat => $port)->[0];
$self->remove_pending($pen->{id}) if $pen;
+
+ # log potential error
+ $self->log("support", "no entries were updated for $ip:$port ($s{gamename}), but it was still removed from pending!") if ($result == 0 && $pen);
}
}
@@ -248,7 +259,7 @@ sub process_status_response {
# $self, udp data, ip, port
my ($self, $buf, $ip, $port) = @_;
- #process datastream
+ # process datastream
my %s;
$buf = encode('UTF-8', $buf);
$buf =~ s/\\([^\\]+)\\([^\\]+)/$s{$1}=$2/eg;
@@ -312,16 +323,15 @@ sub process_status_response {
# write to db
$self->insert_utplayer($serverlist_id->{id}, %player);
}
-
- #
- # Prevent null concatenation in logging
- $s{numplayers} ||= 0;
- $s{maxplayers} ||= 0;
- $s{mapname} ||= "Unknown map";
- $s{hostname} ||= "Unknown hostname";
-
- # log results
- $self->log("utserver", "$serverlist_id->{id}, $ip:$port,\t $s{numplayers}/$s{maxplayers} players, $s{mapname}, $s{hostname}");
+
+ # log results (debug)
+ #$self->log("utserver",
+ # "$serverlist_id->{id}, $ip:$port,\t".
+ # ($s{numplayers} || "0") ."/".
+ # ($s{maxplayers} || "0") ."players, ".
+ # ($s{mapname} || "mapname") .",".
+ # ($s{hostname} || "hostname")
+ #);
}
}
@@ -351,28 +361,34 @@ sub process_ucc_applet_query {
# count number of valid addresses
$c++;
- # print address
- $self->log("add", "applet query added $ms->{game}\t$a\t$p");
+ # print address (debug)
+ # $self->log("add", "applet query added $ms->{gamename}\t$a\t$p");
# add server
$self->add_server_new(ip => $a,
beaconport => $p,
heartbeat => $p,
- gamename => $ms->{game},
+ gamename => $ms->{gamename},
secure => $self->secure_string(),
updated => time);
}
# invalid address, log
- else {$self->log("error", "invalid address found at master applet $ms->{ip}: $l!");}
+ else {$self->log("error", "invalid address found at master applet $ms->{ip}, $l!");}
}
}
# end transaction, commit
$self->{dbh}->commit;
+
+ # update time if successful applet query
+ $self->update_master_applet(
+ ip => $ms->{ip},
+ port => $ms->{port},
+ gamename => $ms->{gamename},
+ ) if ($c > 0);
# print findings
- $self->log("applet-rx","found $c addresses at $ms->{ip} for $ms->{game}.");
-
+ $self->log("applet-rx","found $c addresses at $ms->{ip} for $ms->{gamename}.");
}
1;
diff --git a/lib/MasterServer/UDP/UDPTicker.pm b/lib/MasterServer/UDP/UDPTicker.pm
index 5a34a8f..6b3a681 100755
--- a/lib/MasterServer/UDP/UDPTicker.pm
+++ b/lib/MasterServer/UDP/UDPTicker.pm
@@ -1,10 +1,10 @@
-
package MasterServer::UDP::UDPTicker;
use strict;
use warnings;
use AnyEvent::Handle::UDP;
use Exporter 'import';
+use Data::Dumper 'Dumper';
our @EXPORT = qw| udp_ticker |;
@@ -45,7 +45,7 @@ sub udp_ticker {
# go through all servers that need querying
my $server_info = AnyEvent->timer (
- after => 75, # first give beacons a chance to uplink
+ after => 120, # first give beacons a chance to uplink
interval => 0.2, # 5 addresses per second is fast enough
cb => sub {
@@ -71,7 +71,7 @@ sub udp_ticker {
if (time - $ut_serv{start} > $ut_serv{limit}) {
if ($ut_serv{c} > 0) {
# done within defined time, reset
- %ut_serv = (%ut_serv, %reset)
+ %ut_serv = (%ut_serv, %reset);
}
}
@@ -89,48 +89,6 @@ sub udp_ticker {
%oldserv = (%oldserv, %reset);
}
}
-
- #
- # else { print "Making overtime!" }
-
-=pod
- # FIXME remove this if above works
-
- # debug: detect premature resets
- if (time - $pending{start} > $pending{limit}) {
- if ($pending{c} == 0) {
- print "Premature pending reset\n" ;
- }
- else{$pending{c} = 0;}
- }
-
- if (time - $updater{start} > $updater{limit}) {
- if ($updater{c} == 0) {
- print "Premature updater reset\n" ;
- }
- else{$updater{c} = 0;}
- }
-
- if (time - $ut_serv{start} > $ut_serv{limit}) {
- if ($ut_serv{c} == 0) {
- print "Premature ut_serv reset\n" ;
- }
- else{$ut_serv{c} = 0;}
- }
-
- if (time - $oldserv{start} > $oldserv{limit}) {
- if ($oldserv{c} == 0) {
- print "Premature oldserv reset\n" ;
- }
- else{$oldserv{c} = 0;}
- }
-
- # are we making overtime on any of the counters yet?
- %pending = (%pending, %reset) if (time - $pending{start} > $pending{limit});
- %updater = (%updater, %reset) if (time - $updater{start} > $updater{limit});
- %ut_serv = (%ut_serv, %reset) if (time - $ut_serv{start} > $ut_serv{limit});
- %oldserv = (%oldserv, %reset) if (time - $oldserv{start} > $oldserv{limit});
-=cut
}
#
@@ -242,6 +200,7 @@ sub udp_ticker {
$n = $self->get_server(
next_id => $oldserv{id},
before => 7200,
+ (defined $self->{firstrun}) ? () : (updated => 86400), # FIXME long firstrun time fixed now?
sort => "id",
limit => 1,
)->[0] if $self->{beacon_checker_enabled};
@@ -271,7 +230,7 @@ sub udp_ticker {
if (!defined $self->{firstrun}) {
# inform that first run is completed
my $t = time-$self->{firstruntime};
- my $t_readable = ($t > 60) ? (($t/60). ":". ($t%60). "minutes") : ($t. "seconds");
+ my $t_readable = ($t > 60) ? (int($t/60). " minutes ". ($t%60). " seconds") : ($t. " seconds");
$self->log("info", "First run completed after $t_readable.");
$self->{firstrun} = 0;
diff --git a/lib/MasterServer/UDP/UpLink.pm b/lib/MasterServer/UDP/UpLink.pm
index e5c703b..63cbefb 100755
--- a/lib/MasterServer/UDP/UpLink.pm
+++ b/lib/MasterServer/UDP/UpLink.pm
@@ -10,8 +10,7 @@ use Exporter 'import';
our @EXPORT = qw| send_heartbeats
do_uplink
process_uplink_response
- process_udp_secure
- process_udp_basic |;
+ process_udp_secure |;
################################################################################
## Broadcast heartbeats to other masterservers
@@ -23,16 +22,21 @@ sub send_heartbeats {
# in order to be permitted to sync, you need to share your address too so
# others can sync from you too.
if ($self->{sync_enabled}) {
-
- # uplink to every entry of the masterserver brotherhood list
- foreach my $uplink (values %{$self->masterserver_list()}) {
+
+ # get serverlist
+ my $masterserverlist = $self->get_server(
+ updated => 3600,
+ gamename => "333networks",
+ );
+
+ # uplink to every 333networks-based masterserver
+ foreach my $ms (@{$masterserverlist}) {
# send uplink
- $self->do_uplink($uplink->{ip}, $uplink->{udp});
+ $self->do_uplink($ms->{ip}, $ms->{port});
}
- }
+ }
}
-
################################################################################
## Do an uplink to other 333networks-based masterservers so we can be shared
## along the 333networks synchronization protocol. Other 333networks-based
@@ -44,16 +48,15 @@ sub do_uplink {
# do not proceed if not all information is available
return unless (defined $ip && defined $port && $port > 0);
- # debug spamming
+ # report uplinks to log
$self->log("uplink", "Uplink to Masterserver $ip:$port");
# connect with UDP server
my $udp_client; $udp_client = AnyEvent::Handle::UDP->new(
- # Bind to this host and port
connect => [$ip, $port],
- timeout => 5,
- on_timeout => sub {$udp_client->destroy();}, # don't bother reporting timeouts
- on_error => sub {$udp_client->destroy();}, # or errors
+ timeout => $self->{timeout_time},
+ on_timeout => sub {$udp_client->destroy()},
+ on_error => sub {$udp_client->destroy()},
on_recv => sub {$self->process_uplink_response(@_)},
);
@@ -73,8 +76,7 @@ sub process_uplink_response {
my ($port, $iaddr) = sockaddr_in($pa);
my $peer_addr = inet_ntoa($iaddr);
- # if the beacon has a length longer than a certain amount, assume it is
- # a fraud or crash attempt
+ # assume fraud/crash attempt if response too long
if (length $b > 64) {
# log
$self->log("attack","length exceeded in uplink response: $peer_addr:$port sent $b");
@@ -91,27 +93,28 @@ sub process_uplink_response {
################################################################################
## Process the received secure query and respond with the correct response
-##
+## TODO: expand queries with support for info, rules, players, status, etc
################################################################################
sub process_udp_secure {
# $self, handle, packed address, udp data, peer ip address, $port
my ($self, $udp, $pa, $buf, $peer_addr) = @_;
-
- # received secure in $buf: \basic\secure\l8jfVy
+
+ # received secure in $buf: \basic\\secure\wookie
my %r;
- my $raw = $buf; # raw buffer for logging if necessary
+
$buf = encode('UTF-8', $buf);
$buf =~ s/\\\\/\\undef\\/;
$buf =~ s/\n//;
$buf =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
- # scope
+ # response string
my $response = "";
- # provide basic information if asked for (not uncommon)
- if (defined $r{basic}) {
- # compile basic string (identical to process_udp_basic)
-
+ # compile basic string
+
+ # provide basic information if asked for
+ if (defined $r{basic} || defined $r{status} || defined $r{info}) {
+
# format: \gamename\ut\gamever\348\minnetver\348\location\0\final\\queryid\16.1
$response .= "\\gamename\\333networks"
. "\\gamever\\$self->{short_version}"
@@ -119,51 +122,21 @@ sub process_udp_secure {
. "\\hostname\\$self->{masterserver_hostname}"
. "\\hostport\\$self->{listen_port}";
}
+
+ # TODO: add queryid -- not because it's useful, but because protocol compliant
- # we only respond with gamename = 333networks
+ # support for secure/validate
if (defined $r{secure}) {
- # get response
+ # generate response
+
$response .= "\\validate\\"
. $self->validate_string(gamename => "333networks",
enctype => 0,
secure => $r{secure});
}
- # send the response to the \basic\\secure\wookie query
+ # send the response
$udp->push_send("$response\\final\\", $pa);
}
-################################################################################
-## Respond to basic or status queries
-## TODO: abstract function for this -- otherwise these functions pile up.
-################################################################################
-sub process_udp_basic {
- # $self, handle, packed address, udp data, peer ip address, $port
- my ($self, $udp, $pa, $buf, $peer_addr) = @_;
-
- # received basic or status in $buf: \basic\ or \status\
- my %r;
- $buf = encode('UTF-8', $buf);
- $buf =~ s/\\\\/\\undef\\/;
- $buf =~ s/\n//;
- $buf =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
-
- # scope
- my $basic = "";
- # provide basic information
-
- if (defined $r{basic} || defined $r{status} || defined $r{info}) {
- # compile basic string (identical to process_udp_basic)
-
- # format: \gamename\ut\gamever\348\minnetver\348\location\0\final\\queryid\16.1
- $basic = "\\gamename\\333networks"
- . "\\gamever\\$self->{short_version}"
- . "\\location\\0"
- . "\\hostname\\$self->{masterserver_hostname}"
- . "\\hostport\\$self->{listen_port}";
- }
-
- # send the response to the \basic\ or \status\
- $udp->push_send("$basic\\final\\", $pa);
-}
1;