aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/MasterServer/Core/Core.pm32
-rwxr-xr-xlib/MasterServer/Core/LoadConfig.pm150
-rwxr-xr-xlib/MasterServer/Core/Logging.pm30
-rwxr-xr-xlib/MasterServer/Core/Schedulers.pm117
-rwxr-xr-xlib/MasterServer/Core/Secure.pm73
-rwxr-xr-xlib/MasterServer/Core/Stats.pm4
-rwxr-xr-xlib/MasterServer/Core/Util.pm11
-rwxr-xr-xlib/MasterServer/Core/Version.pm7
-rwxr-xr-xlib/MasterServer/Database/Pg/dbAddServers.pm18
-rwxr-xr-xlib/MasterServer/Database/Pg/dbAppletActions.pm92
-rwxr-xr-xlib/MasterServer/Database/Pg/dbCiphers.pm18
-rwxr-xr-xlib/MasterServer/Database/Pg/dbCore.pm33
-rwxr-xr-xlib/MasterServer/Database/Pg/dbMaintenance.pm2
-rwxr-xr-xlib/MasterServer/Database/Pg/dbUTServerInfo.pm1
-rwxr-xr-xlib/MasterServer/Database/SQLite/dbAddServers.pm13
-rwxr-xr-xlib/MasterServer/Database/SQLite/dbAppletActions.pm92
-rwxr-xr-xlib/MasterServer/Database/SQLite/dbCiphers.pm18
-rwxr-xr-xlib/MasterServer/Database/SQLite/dbCore.pm38
-rwxr-xr-xlib/MasterServer/Database/SQLite/dbMaintenance.pm2
-rwxr-xr-xlib/MasterServer/Database/SQLite/dbUTServerInfo.pm1
-rwxr-xr-xlib/MasterServer/TCP/BrowserHost.pm9
-rwxr-xr-xlib/MasterServer/TCP/Handler.pm22
-rwxr-xr-xlib/MasterServer/TCP/ListCompiler.pm2
-rwxr-xr-xlib/MasterServer/TCP/Syncer.pm69
-rwxr-xr-xlib/MasterServer/TCP/UCCAppletQuery.pm16
-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
-rwxr-xr-xlib/MasterServer/Util/KFStatsWatcher.pm4
31 files changed, 753 insertions, 431 deletions
diff --git a/lib/MasterServer/Core/Core.pm b/lib/MasterServer/Core/Core.pm
index be0646b..4d1e47b 100755
--- a/lib/MasterServer/Core/Core.pm
+++ b/lib/MasterServer/Core/Core.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Core;
use strict;
@@ -6,6 +5,7 @@ use warnings;
use AnyEvent;
use Exporter 'import';
use DBI;
+$|++;
our @EXPORT = qw | halt select_database_type main |;
@@ -47,7 +47,7 @@ sub select_database_type {
if ( "Pg SQLite mysql" =~ m/$db_type[1]/i) {
# inform us what DB we try to load
- $self->log("load","Loading $db_type[1] database module.");
+ $self->log("debug","Loading $db_type[1] database module.");
# load dbd and tables/queries for this db type
MasterServer::load_recursive("MasterServer::Database::$db_type[1]");
@@ -74,11 +74,11 @@ sub main {
# condition var prevents or allows the program from ending
$self->{must_halt} = AnyEvent->condvar;
- # force version info
+ # load version info
$self->version();
# print startup
- print "Running...\n";
+ print "Running 333networks Master Server Application...\n";
# keep several objects alive outside their original scope
$self->{scope} = ();
@@ -96,14 +96,24 @@ sub main {
# determine the type of database and load the appropriate module
$self->select_database_type();
+ ###
#
- # Prepare necessary tasks for running the masterserver
+ # execute necessary tasks for running the masterserver
#
+ ###
+
+ # load the list with ciphers from the config file if no ciphers were detected
+ # update manually with util/tools/db_load_ciphers.pl
+ # then unload the game variables from masterserver memory
+ $self->load_ciphers() unless $self->check_cipher_count();
+ $self->{game} = undef;
- # (re)load the list with ciphers from the config file, into the database
- $self->load_ciphers();
+ # (re)load the list with masterservers and master applets from config
+ # does not clear out old entries, but resets "last_updated" to now
+ $self->load_sync_masters();
+ $self->load_applet_masters();
- # set first run flag to avoid ignoring servers after downtime
+ # set first run flag to avoid ignoring/deleting servers after downtime
$self->{firstrun} = undef;
$self->{firstruntime} = time;
@@ -134,10 +144,14 @@ sub main {
# provide server lists to clients with the browser host server
$self->{scope}->{browser_host} = $self->browser_host();
+ ###
+ #
# all modules loaded. Running...
+ #
+ ###
$self->log("info", "All modules loaded. Masterserver is now running.");
- # prevent main program from ending prematurely
+ # prevent main program from ending as long as no fatal errors occur
$self->{must_halt}->recv;
}
diff --git a/lib/MasterServer/Core/LoadConfig.pm b/lib/MasterServer/Core/LoadConfig.pm
new file mode 100755
index 0000000..e209848
--- /dev/null
+++ b/lib/MasterServer/Core/LoadConfig.pm
@@ -0,0 +1,150 @@
+package MasterServer::Core::LoadConfig;
+
+use strict;
+use warnings;
+use AnyEvent;
+use POSIX qw/strftime/;
+use Exporter 'import';
+use DBI;
+
+our @EXPORT = qw | load_applet_masters
+ load_sync_masters
+ add_sync_master |;
+
+################################################################################
+## Load configuration variables to the database, helper functions
+################################################################################
+sub load_applet_masters {
+ my $self = shift;
+
+ # loop through config entries
+ foreach my $master_applet (@{$self->{master_applet}}) {
+ # master_applet contains
+ # address --> domain
+ # port --> tcp port
+ # games --> array of gamenames
+
+ # iterate through all games per entry
+ for my $gamename (@{$master_applet->{games}}) {
+
+ # resolve domain names
+ my $applet_ip = $self->host2ip($master_applet->{address});
+
+ # check if all credentials are valid
+ if ($applet_ip &&
+ $master_applet->{port} &&
+ $gamename)
+ {
+ # add to database
+ $self->add_master_applet(
+ ip => $applet_ip,
+ port => $master_applet->{port},
+ gamename => $gamename,
+ );
+
+ #log
+ $self->log("add", "added applet $master_applet->{address}:$master_applet->{port} for $gamename");
+
+ } # else: insufficient info available
+ else {
+ $self->log("fail", "Could not add master applet: ".
+ ($applet_ip || "unknown ip"). ", ".
+ ($master_applet->{port} || "0"). ", ".
+ ($gamename || "game"). "."
+ );
+ }
+ } # end gamename
+ } # end master_applet
+
+ # reset added/updated time to last current time
+ $self->reset_master_applets();
+
+ # clear out the original variable, we don't use it anymore
+ $self->{master_applet} = ();
+
+ # report
+ $self->log("info", "Applet database successfully updated!");
+
+}
+
+################################################################################
+## There are three ways to load new masterservers to sync with.
+## 1: from the config file; address, port and beaconport are provided
+## 2: from a heartbeat; this automatically parses like all other servers
+## 3: from another sync request. Add if sufficient info is available
+################################################################################
+sub load_sync_masters {
+ my $self = shift;
+
+ # loop through config entries
+ foreach my $sync_host (@{$self->{sync_masters}}) {
+
+ # add them to database
+ $self->add_sync_master($sync_host);
+ }
+
+ # clear out the original variable, we don't use it anymore
+ $self->{sync_masters} = ();
+
+ # report
+ $self->log("info", "Sync server database successfully updated!");
+
+}
+
+################################################################################
+## Add a sync master according to cases 1 and 3.
+## Check for valid IP, port and/or beaconport
+################################################################################
+sub add_sync_master {
+ my ($self, $sync_host) = @_;
+
+ # sync_host contains
+ # address --> domain
+ # port --> tcp port
+ # beacon --> udp port
+
+ # resolve domain names
+ my $sync_ip = $self->host2ip($sync_host->{address});
+
+ # check if all credentials are valid
+ if ($sync_ip &&
+ $sync_host->{beacon} &&
+ $sync_host->{port})
+ {
+ # select sync master from serverlist
+ my $entry = $self->get_server(ip => $sync_ip,
+ port => $sync_host->{beacon})->[0];
+
+ # was found, update the entry
+ if (defined $entry) {
+ # update the serverlist with
+ my $sa = $self->update_server_list(
+ ip => $sync_ip,
+ port => $sync_host->{beacon},
+ hostport => $sync_host->{port},
+ gamename => "333networks",
+ );
+ }
+ # was not found, insert clean entry
+ else {
+ my $sa = $self->add_server_list(
+ ip => $sync_ip,
+ port => $sync_host->{beacon},
+ hostport => $sync_host->{port},
+ gamename => "333networks",
+ );
+
+ #log
+ $self->log("add", "added sync $sync_host->{address}:$sync_host->{port},$sync_host->{beacon}");
+ }
+ } # else: insufficient info available
+ else {
+ $self->log("fail", "Could not add sync master: ".
+ ($sync_ip || "ip"). ", ".
+ ($sync_host->{beacon} || "0"). ", ".
+ ($sync_host->{port} || "0"). "."
+ );
+ }
+}
+
+1;
diff --git a/lib/MasterServer/Core/Logging.pm b/lib/MasterServer/Core/Logging.pm
index e8631de..416a97f 100755
--- a/lib/MasterServer/Core/Logging.pm
+++ b/lib/MasterServer/Core/Logging.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Logging;
use strict;
@@ -6,6 +5,7 @@ use warnings;
use Switch;
use POSIX qw/strftime/;
use Exporter 'import';
+$|++;
our @EXPORT = qw| log error |;
@@ -46,41 +46,34 @@ sub error {
}
}
-
################################################################################
## Log to file and print to screen.
## args: $self, message_type, message
################################################################################
sub log {
my ($self, $type, $msg) = @_;
-
- # flush
- $| = 1;
-
- # parse time of log entry and prep for rotating log
- my $time = strftime('%Y-%m-%d %H:%M:%S',localtime);
- my $yearly = strftime('-%Y',localtime);
- my $monthly = strftime('-%Y-%m',localtime);
- my $weekly = strftime('-%Y-week%U',localtime);
- my $daily = strftime('-%Y-%m-%d',localtime);
-
+
# is the message suppressed in config?
return if (defined $type && $self->{suppress} =~ m/$type/i);
+
+ # parse time of log entry and prep for rotating log
+ my $time = strftime('%Y-%m-%d %H:%M:%S',localtime);
# determine filename
my $f = "MasterServer";
# rotate log filename according to config
- $f .= $daily if ($self->{log_rotate} =~ /^daily$/i );
- $f .= $weekly if ($self->{log_rotate} =~ /^weekly$/i );
- $f .= $monthly if ($self->{log_rotate} =~ /^monthly$/i );
- $f .= $yearly if ($self->{log_rotate} =~ /^yearly$/i );
+ $f .= strftime('-%Y-%m-%d',localtime) if ($self->{log_rotate} =~ /^daily$/i );
+ $f .= strftime('-%Y-week%U',localtime) if ($self->{log_rotate} =~ /^weekly$/i );
+ $f .= strftime('-%Y-%m',localtime) if ($self->{log_rotate} =~ /^monthly$/i);
+ $f .= strftime('-%Y',localtime) if ($self->{log_rotate} =~ /^yearly$/i );
$f .= ".log";
# put log filename together
my $logfile = $self->{log_dir}.((substr($self->{log_dir},-1) eq "/")?"":"/").$f;
- print "[$time] [$type] > $msg\n" if $self->{printlog};
+ # print to stdout if enabled
+ print "[$time]\t[$type]\t$msg\n" if $self->{printlog};
# temporarily disable the warnings-to-log, to avoid infinite recursion if
# this function throws a warning.
@@ -99,5 +92,4 @@ sub log {
$SIG{__WARN__} = $old;
}
-
1;
diff --git a/lib/MasterServer/Core/Schedulers.pm b/lib/MasterServer/Core/Schedulers.pm
index 97e45a5..cee4e5c 100755
--- a/lib/MasterServer/Core/Schedulers.pm
+++ b/lib/MasterServer/Core/Schedulers.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Schedulers;
use strict;
@@ -18,75 +17,90 @@ our @EXPORT = qw |
################################################################################
sub long_periodic_tasks {
my $self = shift;
- my $num = 0;
+ my $prev = 0;
return AnyEvent->timer (
- after => 300, # 5 minutes grace time
- interval => 1800, # execute every half hour
+ after => 30, # 30 seconds grace time
+ interval => 3600, # execute every hour
cb => sub {
- ## update Killing Floor stats
- $self->read_kfstats();
+ # update Killing Floor stats
+ $self->read_kfstats() if $self->{kfstats_enabled};
+
+ # delete old masterserver applets that have been unresponsive for a while now
+ $self->remove_unresponsive_applets() if (defined $self->{firstrun});
# time spacer
my $t = 0;
# clean out handles from the previous round (executed or not)
$self->{scope}->{sync} = ();
-
- ## Query Epic Games'-based UCC applets periodically to get an additional
- ## list of online UT, Unreal (or other) game servers.
- if ($self->{master_applet_enabled}) {
- for my $ms (@{$self->{master_applet}}) {
+
+ # Synchronize with all other 333networks masterservers that are uplinking,
+ # added by synchronization or manually listed.
+ if ($self->{sync_enabled}) {
+
+ # get serverlist
+ my $masterserverlist = $self->get_server(
+ updated => 3600,
+ gamename => "333networks",
+ );
- # add 3 second delay to spread network/server load
+ foreach my $ms (@{$masterserverlist}) {
+ # add 5 second delay to spread network/server load
$self->{scope}->{sync}->{$t} = AnyEvent->timer(
- after => 3*$t++,
- cb => sub{$self->query_applet($ms)}
- );
+ after => 5*$t++,
+ cb => sub{$self->sync_with_master($ms)}
+ ) if ($ms->{hostport} > 0);
}
}
+
+ # do NOT reset $t, keep padding time -- you should not have more than 300
+ # entries in applets/syncer in total anyway.
- # do NOT reset $t, keep padding time -- you should not have more than 600
- # entries in applets/syncer in total.
-
- ## Request the masterlist for selected or all games from other
- ## 333networks-based masterservers that uplinked to us and otherwise made
- ## our list (config, manual entry, etc)
- if ($self->{sync_enabled}) {
- foreach my $ms (values %{$self->masterserver_list()}) {
+ # Query Epic Games-based UCC applets periodically to get an additional
+ # list of online UT, Unreal and other game servers.
+ if ($self->{master_applet_enabled}) {
- # add 3 second delay to spread network/server load
+ # get applet list
+ my $appletlist = $self->get_masterserver_applets();
+
+ for my $ms (@{$appletlist}) {
+
+ # add 5 second delay to spread network/server load
$self->{scope}->{sync}->{$t} = AnyEvent->timer(
- after => 3*$t++,
- cb => sub{$self->sync_with_master($ms) if ($ms->{tcp} > 0)}
+ after => 5*$t++,
+ cb => sub{$self->query_applet($ms)}
);
}
}
#
- # Also very long-running tasks, like once per day:
+ # very long-running tasks, like database dumps
+ # interval from config
#
- if ($num++ >= 47) {
- # reset counter
- $num = 0;
-
- #
- # do database dump
- #
- my $time = strftime('%Y-%m-%d-%H-%M',localtime);
-
- # read db type from db login
- my @db_type = split(':', $self->{dblogin}->[0]);
- $db_type[2] =~ s/dbname=//;
+ my $curr = 0;
+ $curr = strftime('%d',localtime) if ($self->{dump_db} =~ /^daily$/i );
+ $curr = strftime('%U',localtime) if ($self->{dump_db} =~ /^weekly$/i );
+ $curr = strftime('%m',localtime) if ($self->{dump_db} =~ /^monthly$/i);
+ $curr = strftime('%Y',localtime) if ($self->{dump_db} =~ /^yearly$/i );
+
+ # on change, execute
+ if ($prev < $curr) {
- if ($db_type[1] eq "Pg") {
- # use pg_dump to dump Postgresql databases
- system("pg_dump $db_type[2] -U $self->{dblogin}->[1] > $self->{root}/data/dumps/$db_type[1]-$time.db");
- $self->log("dump", "Dumping database to /data/dumps/$db_type[1]-$time.db");
+ # skip on first run
+ if ($prev == 0) {
+ # update timer and loop
+ $prev = $curr;
+ return;
}
+
+ # dump db
+ $self->dump_database();
+
+ # update timekeeper
+ $prev = $curr;
}
-
},
);
}
@@ -102,22 +116,19 @@ sub short_periodic_tasks {
interval => 120,
cb => sub {
- ## update stats on direct beacons and total number of servers
+ # update stats on direct beacons and total number of servers
$self->update_stats();
- ## determine whether servers are still uplinking to us. If not, toggle.
+ # determine whether servers are still uplinking to us. If not, toggle.
$self->write_direct_beacons() if (defined $self->{firstrun});
- ## delete old servers from the "pending" list (except for the first run)
+ # delete old servers from the "pending" list (except for the first run)
$self->delete_old_pending() if (defined $self->{firstrun});
- ## uplink to other 333networks masterservers with heartbeats,
- ## that way we can index other masterservers too
+ # uplink to other 333networks masterservers with heartbeats, so other
+ # masterservers can find us too
$self->send_heartbeats();
-
- #
- # more short tasks?
- #
+
},
);
}
diff --git a/lib/MasterServer/Core/Secure.pm b/lib/MasterServer/Core/Secure.pm
index 51d1832..6d05f82 100755
--- a/lib/MasterServer/Core/Secure.pm
+++ b/lib/MasterServer/Core/Secure.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Secure;
use strict;
@@ -25,7 +24,7 @@ sub load_ciphers {
# first delete the old cipher database
$self->clear_ciphers();
- # start inserting ciphers (lots of 'em)
+ # start inserting ciphers (use transactions for slow systems)
$self->{dbh}->begin_work;
# iterate through the game list
@@ -36,10 +35,11 @@ sub load_ciphers {
$opt{gamename} = lc $_;
$opt{cipher} = $self->{game}->{$_}->{key};
$opt{description} = $self->{game}->{$_}->{label} || 'Unknown Game';
- $opt{default_qport} = $self->{game}->{$_}->{port} || 0;
+ $opt{default_qport} = $self->{game}->{$_}->{port} || 0;
# insert the game/cipher in the db or halt on error
if ($self->insert_cipher(%opt) < 0) {
+ # failure causes a fatal error and exits
$self->{dbh}->rollback;
$self->halt();
}
@@ -48,19 +48,14 @@ sub load_ciphers {
# commit
$self->{dbh}->commit;
$self->log("info", "Cipher database successfully updated!");
-
- # unload the game variables from masterserver memory
- $self->{game} = undef;
-
}
-
################################################################################
# generate a random string of 6 characters long for the \secure\ challenge
# returns string
################################################################################
sub secure_string {
- # spit out a random string, only uppercase characters
+ # generate a random string, only uppercase characters
my @c = ('A'..'Z');
my $s = "";
$s .= $c[rand @c] for 1..6;
@@ -82,23 +77,21 @@ sub compare_challenge {
# secure string too long? (because vulnerable in UE)
return 0 if (length $o{secure} > 16);
- # additional conditions to skip checking provided?
- $o{ignore} = "" unless $o{ignore};
-
# ignore this game if asked to do so
- if ($o{ignore} =~ m/$o{gamename}/i){
- $self->log("secure", "ignored beacon validation for $o{gamename}");
+ if ($self->{ignore_browser_key} =~ m/$o{gamename}/i){
+ $self->log("ignore", "ignored beacon validation for $o{gamename}");
return 1;
}
# enctype given?
$o{enctype} = 0 unless $o{enctype};
-
- # get cipher corresponding with the gamename
- my $cip = $self->get_game_props($o{gamename})->{cipher};
-
+
# calculate validate string
- my $val = get_validate_string($cip, $o{secure}, $o{enctype});
+ my $val = get_validate_string(
+ $self->get_game_props($o{gamename})->{cipher},
+ $o{secure},
+ $o{enctype}
+ );
# return whether or not they match
return ($val eq $o{validate});
@@ -136,33 +129,33 @@ sub validate_string {
# conversion and modification of the algorithm by Darkelarious, June 2014 with
# explicit, written permission of Luigi Auriemma.
#
+# use pre-built rotations for enctype
+# -- see GSMSALG 0.3.3 reference for copyright and more information
+my @enc_chars = ( qw |
+ 001 186 250 178 081 000 084 128 117 022 142 142 002 008 054 165
+ 045 005 013 022 082 007 180 034 140 233 009 214 185 038 000 004
+ 006 005 000 019 024 196 030 091 029 118 116 252 080 081 006 022
+ 000 081 040 000 004 010 041 120 081 000 001 017 082 022 006 074
+ 032 132 001 162 030 022 071 022 050 081 154 196 003 042 115 225
+ 045 079 024 075 147 076 015 057 010 000 004 192 018 012 154 094
+ 002 179 024 184 007 012 205 033 005 192 169 065 067 004 060 082
+ 117 236 152 128 029 008 002 029 088 132 001 078 059 106 083 122
+ 085 086 087 030 127 236 184 173 000 112 031 130 216 252 151 139
+ 240 131 254 014 118 003 190 057 041 119 048 224 043 255 183 158
+ 001 004 248 001 014 232 083 255 148 012 178 069 158 010 199 006
+ 024 001 100 176 003 152 001 235 002 176 001 180 018 073 007 031
+ 095 094 093 160 079 091 160 090 089 088 207 082 084 208 184 052
+ 002 252 014 066 041 184 218 000 186 177 240 018 253 035 174 182
+ 069 169 187 006 184 136 020 036 169 000 020 203 036 018 174 204
+ 087 086 238 253 008 048 217 253 139 062 010 132 070 250 119 184
+|);
+#
# args: game cipher, 6-char challenge string, encryption type
# returns: validate string (usually 8 characters long)
# !! requires cipher hash to be configured in config! (imported or otherwise)
################################################################################
sub get_validate_string {
my ($cipher_string, $secure_string, $enctype) = @_;
-
- # use pre-built rotations for enctype
- # -- see GSMSALG 0.3.3 reference for copyright and more information
- my @enc_chars = ( qw |
- 001 186 250 178 081 000 084 128 117 022 142 142 002 008 054 165
- 045 005 013 022 082 007 180 034 140 233 009 214 185 038 000 004
- 006 005 000 019 024 196 030 091 029 118 116 252 080 081 006 022
- 000 081 040 000 004 010 041 120 081 000 001 017 082 022 006 074
- 032 132 001 162 030 022 071 022 050 081 154 196 003 042 115 225
- 045 079 024 075 147 076 015 057 010 000 004 192 018 012 154 094
- 002 179 024 184 007 012 205 033 005 192 169 065 067 004 060 082
- 117 236 152 128 029 008 002 029 088 132 001 078 059 106 083 122
- 085 086 087 030 127 236 184 173 000 112 031 130 216 252 151 139
- 240 131 254 014 118 003 190 057 041 119 048 224 043 255 183 158
- 001 004 248 001 014 232 083 255 148 012 178 069 158 010 199 006
- 024 001 100 176 003 152 001 235 002 176 001 180 018 073 007 031
- 095 094 093 160 079 091 160 090 089 088 207 082 084 208 184 052
- 002 252 014 066 041 184 218 000 186 177 240 018 253 035 174 182
- 069 169 187 006 184 136 020 036 169 000 020 203 036 018 174 204
- 087 086 238 253 008 048 217 253 139 062 010 132 070 250 119 184
- |),
# convert to array of characters
my @cip = split "", $cipher_string;
diff --git a/lib/MasterServer/Core/Stats.pm b/lib/MasterServer/Core/Stats.pm
index 25044e8..8e9eb95 100755
--- a/lib/MasterServer/Core/Stats.pm
+++ b/lib/MasterServer/Core/Stats.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Stats;
use strict;
@@ -31,9 +30,8 @@ sub update_stats {
$self->write_stat(%opt);
}
- #done
+ # done
$self->log("stat", "Updated all game statistics.");
-
}
1;
diff --git a/lib/MasterServer/Core/Util.pm b/lib/MasterServer/Core/Util.pm
index 4f64fe1..682335c 100755
--- a/lib/MasterServer/Core/Util.pm
+++ b/lib/MasterServer/Core/Util.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Util;
use strict;
@@ -24,10 +23,10 @@ sub ip2country {
################################################################################
sub host2ip {
my ($self, $name) = @_;
- return inet_ntoa(inet_aton($name)) if $name;
+ my $unpack = inet_aton($name) if $name;
+ return inet_ntoa($unpack) if $unpack;
}
-
################################################################################
## Verify whether a given domain name or IP address and port are valid.
## returns 1/0 if valid/invalid ip + port. IPv4 ONLY!
@@ -36,8 +35,8 @@ sub valid_address {
my ($self, $a, $p) = @_;
# check if ip and port are in valid range
- my $val_addr = ($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') if $a;
- my $val_port = (0 < $p && $p <= 65535) if $p;
+ my $val_addr = ($a =~ '^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$') if $a;
+ my $val_port = ($p =~ m/^\d+$/ && 0 < $p && $p <= 65535) if $p;
# exclude addresses where we don't want people sniffing
for (qw|192.168.(.\d*).(.\d*) 127.0.(.\d*).(.\d*) 10.0.(.\d*).(.\d*)|){$val_addr = 0 if ($a =~ m/$_/)}
@@ -72,7 +71,7 @@ sub db_all {
}
################################################################################
-# sqlprint (TUWF):
+# sqlprint (TUWF, Yorhel):
# ? normal placeholder
# !l list of placeholders, expects arrayref
# !H list of SET-items, expects hashref or arrayref: format => (bind_value || \@bind_values)
diff --git a/lib/MasterServer/Core/Version.pm b/lib/MasterServer/Core/Version.pm
index 718b8c6..f87ea8d 100755
--- a/lib/MasterServer/Core/Version.pm
+++ b/lib/MasterServer/Core/Version.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Core::Version;
use strict;
@@ -27,16 +26,16 @@ sub version {
#
# master type
- $self->{build_type} = "333networks Masterserver-Perl pre-release";
+ $self->{build_type} = "333networks Masterserver-Perl Multidb";
# version
- $self->{build_version} = "2.2.5";
+ $self->{build_version} = "2.3.0";
# short version for uplinks
$self->{short_version} = "MS-perl $self->{build_version}";
# date yyyy-mm-dd
- $self->{build_date} = "2016-11-19";
+ $self->{build_date} = "2017-05-13";
#author, email
$self->{build_author} = "Darkelarious, darkelarious\@333networks.com";
diff --git a/lib/MasterServer/Database/Pg/dbAddServers.pm b/lib/MasterServer/Database/Pg/dbAddServers.pm
index 33785da..31deb08 100755
--- a/lib/MasterServer/Database/Pg/dbAddServers.pm
+++ b/lib/MasterServer/Database/Pg/dbAddServers.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Database::Pg::dbAddServers;
use strict;
@@ -26,7 +25,10 @@ sub add_server_new {
$o{direct} ? ( 'b333ms = CAST(? AS BOOLEAN)' => $o{direct}) : (),
$o{updated} ? ( 'updated = to_timestamp(?)' => $o{updated}) : (),
$o{beacon} ? ( 'beacon = to_timestamp(?)' => $o{beacon}) : (),
- $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (),
+
+ # some applets have incorrect gamename lists, let udpticker update this
+ # entry instead. this way, applets don't overwrite with incorrect data
+ #$o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (),
);
my($q, @p) = sqlprint("UPDATE serverlist !H
@@ -37,12 +39,10 @@ sub add_server_new {
# if serverlist was updated
return 0 if ($n > 0);
-
# try updating it in pending
%H = (
$o{added} ? ( 'added = ?' => $o{added}) : (),
$o{secure} ? ( 'secure = ?' => $o{secure}) : (),
- $o{gamename} ? ( 'gamename = ?' => lc $o{gamename}) : (),
$o{beaconport} ? ('beaconport = ?' => $o{beaconport}) : (),
);
@@ -119,7 +119,7 @@ sub syncer_add {
# if address is in the list AND up to date,
# acknowledge its existance but don't do anything with it
my $u = $self->{dbh}->do(
- "SELECT * FROM serverlist
+ "SELECT count(*) FROM serverlist
WHERE ip = ?
AND port = ?
AND updated > to_timestamp(?)",
@@ -136,8 +136,8 @@ sub syncer_add {
AND heartbeat = ?",
undef, $secure, $ip, $port);
- # notify
- $self->log("update","$ip:$port was updated by syncer") if ($u > 0);
+ # notify (debug)
+ #$self->log("update","$ip:$port was updated by syncer") if ($u > 0);
# return 1 if found
return 1 if ($u > 0);
@@ -148,8 +148,8 @@ sub syncer_add {
SELECT ?, ?, ?, ?",
undef, $ip, $port, lc $gamename, $secure);
- # notify
- $self->log("add","beacon: $ip:$port was added for $gamename after sync") if ($u > 0);
+ # notify (debug)
+ #$self->log("add","beacon: $ip:$port was added for $gamename after sync") if ($u > 0);
# return 2 if added new
return 2 if ($u > 0);
diff --git a/lib/MasterServer/Database/Pg/dbAppletActions.pm b/lib/MasterServer/Database/Pg/dbAppletActions.pm
new file mode 100755
index 0000000..dc7d941
--- /dev/null
+++ b/lib/MasterServer/Database/Pg/dbAppletActions.pm
@@ -0,0 +1,92 @@
+package MasterServer::Database::Pg::dbAppletActions;
+
+use strict;
+use warnings;
+use Exporter 'import';
+
+our @EXPORT = qw| add_master_applet
+ update_master_applet
+ reset_master_applets
+ get_masterserver_applets
+ remove_unresponsive_applets |;
+
+################################################################################
+## Add a remote master server applet
+################################################################################
+sub add_master_applet {
+ my $self = shift;
+ my %o = @_;
+
+ my $u = $self->{dbh}->do(
+ "SELECT * FROM appletlist
+ WHERE ip = ?
+ AND port = ?
+ AND gamename = ?",
+ undef, $o{ip}, $o{port}, lc $o{gamename});
+
+ # return if found
+ return if ($u > 0);
+
+ # insert applet data
+ return $self->{dbh}->do("INSERT INTO appletlist (ip, port, gamename)
+ SELECT ?, ?, ?", undef,
+ $o{ip}, $o{port}, lc $o{gamename});
+}
+
+################################################################################
+## reset added/updated time after restart
+################################################################################
+sub reset_master_applets {
+ my $self = shift;
+ return $self->{dbh}->do("UPDATE appletlist
+ SET added = to_timestamp(?),
+ updated = to_timestamp(?)",
+ undef, time, time);
+}
+
+################################################################################
+## update time on master applet
+################################################################################
+sub update_master_applet {
+ my ($self, %o) = @_;
+
+ return $self->{dbh}->do("UPDATE appletlist
+ SET updated = to_timestamp(?)
+ WHERE ip = ?
+ AND port = ?
+ AND gamename = ?",
+ undef, time, $o{ip}, $o{port}, lc $o{gamename});
+}
+
+################################################################################
+## get a list of master server applets that were online in the past week
+##
+################################################################################
+sub get_masterserver_applets {
+ my $self = shift;
+
+ return $self->db_all(
+ "SELECT *
+ FROM appletlist
+ WHERE updated > to_timestamp(?)",
+ time-604800);
+}
+
+################################################################################
+## Clear out applet entries that have been unresponsive or without servers for
+## more than a week. Servers with multiple entries only have the entry of this
+## specific gamename removed.
+################################################################################
+sub remove_unresponsive_applets {
+ my $self = shift;
+
+ # remove entries
+ my $u = $self->{dbh}->do(
+ "DELETE FROM appletlist
+ WHERE updated < to_timestamp(?)", undef, time-604800);
+
+ # notify
+ $self->log("delete", "Removed $u entries from applet list.") if ($u > 0);
+}
+
+1;
diff --git a/lib/MasterServer/Database/Pg/dbCiphers.pm b/lib/MasterServer/Database/Pg/dbCiphers.pm
index e2a5784..e099f88 100755
--- a/lib/MasterServer/Database/Pg/dbCiphers.pm
+++ b/lib/MasterServer/Database/Pg/dbCiphers.pm
@@ -1,15 +1,23 @@
-
package MasterServer::Database::Pg::dbCiphers;
use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw| clear_ciphers
+our @EXPORT = qw| check_cipher_count
+ clear_ciphers
insert_cipher
get_game_props |;
################################################################################
+## Check if ciphers exist
+################################################################################
+sub check_cipher_count {
+ my $self = shift;
+ return $self->db_all('SELECT count(*) as num from games')->[0]->{num};
+}
+
+################################################################################
## Clear all existing ciphers from the database
################################################################################
sub clear_ciphers {
@@ -43,15 +51,13 @@ sub insert_cipher {
################################################################################
## get the cipher, description and default port that goes with given gamename
+## returns only the first item if multiple items
################################################################################
sub get_game_props {
my ($self, $gn) = @_;
# get cipher from db if gamename exists
- return $self->{dbh}->selectall_arrayref(
- 'SELECT * FROM games WHERE gamename = ?',
- {Slice=>{}},
- lc $gn)->[0];
+ return $self->db_all('SELECT * FROM games WHERE gamename = ?', lc $gn)->[0];
}
1;
diff --git a/lib/MasterServer/Database/Pg/dbCore.pm b/lib/MasterServer/Database/Pg/dbCore.pm
index 90899f7..0891012 100755
--- a/lib/MasterServer/Database/Pg/dbCore.pm
+++ b/lib/MasterServer/Database/Pg/dbCore.pm
@@ -1,11 +1,11 @@
-
package MasterServer::Database::Pg::dbCore;
use strict;
use warnings;
+use POSIX qw/strftime/;
use Exporter 'import';
-our @EXPORT = qw| database_login |;
+our @EXPORT = qw| database_login dump_database |;
################################################################################
## login to the database with credentials provided in the config file.
@@ -19,6 +19,9 @@ sub database_login {
# get db info
my @db_type = split(':', $self->{dblogin}->[0]);
+
+ # inform what db we try to load
+ $self->log("info","Database: $db_type[1], $db_type[2]");
# create the dbi object
my $dbh = DBI->connect(@{$self->{dblogin}}, {PrintError => 1});
@@ -47,4 +50,30 @@ sub database_login {
return undef;
}
+################################################################################
+## Dump the database in the data/dump folder.
+## useful for backups, historical data
+################################################################################
+sub dump_database {
+ my $self = shift;
+
+ # filename / time
+ my $time = strftime('%Y-%m-%d-%H-%M',localtime);
+
+ # FIXME
+ # separate absolute path and relative path,
+ # split database filename for dump filename.
+
+ # read db credentials from db login
+ my @db_type = split(':', $self->{dblogin}->[0]);
+ $db_type[2] =~ s/dbname=//;
+
+ # use pg_dump to dump Postgresql databases
+ system("pg_dump $db_type[2] -U $self->{dblogin}->[1] > $self->{root}/data/dumps/Pg-$time-$db_type[2].db");
+
+ # log
+ $self->log("dump", "Dumping database to /data/dumps/$db_type[1]-$time.db");
+}
+
+
1;
diff --git a/lib/MasterServer/Database/Pg/dbMaintenance.pm b/lib/MasterServer/Database/Pg/dbMaintenance.pm
index 90e4d34..7a4fc23 100755
--- a/lib/MasterServer/Database/Pg/dbMaintenance.pm
+++ b/lib/MasterServer/Database/Pg/dbMaintenance.pm
@@ -5,7 +5,7 @@ use warnings;
use Exporter 'import';
our @EXPORT = qw| delete_old_pending
- remove_pending |;
+ remove_pending |;
################################################################################
## delete unresponsive servers from the pending list
diff --git a/lib/MasterServer/Database/Pg/dbUTServerInfo.pm b/lib/MasterServer/Database/Pg/dbUTServerInfo.pm
index 12ba05f..0bf005e 100755
--- a/lib/MasterServer/Database/Pg/dbUTServerInfo.pm
+++ b/lib/MasterServer/Database/Pg/dbUTServerInfo.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Database::Pg::dbUTServerInfo;
use strict;
diff --git a/lib/MasterServer/Database/SQLite/dbAddServers.pm b/lib/MasterServer/Database/SQLite/dbAddServers.pm
index 9fccead..592ab7b 100755
--- a/lib/MasterServer/Database/SQLite/dbAddServers.pm
+++ b/lib/MasterServer/Database/SQLite/dbAddServers.pm
@@ -26,7 +26,10 @@ sub add_server_new {
$o{direct} ? ( 'b333ms = CAST(? AS BOOLEAN)' => $o{direct}) : (),
$o{updated} ? ( 'updated = datetime(?, \'unixepoch\')' => $o{updated}) : (),
$o{beacon} ? ( 'beacon = datetime(?, \'unixepoch\')' => $o{beacon}) : (),
- $o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (),
+
+ # some applets have incorrect gamename lists, let udpticker update this
+ # entry instead. this way, applets don't overwrite with incorrect data
+ #$o{gamename} ? ('gamename = ?' => lc $o{gamename}) : (),
);
my($q, @p) = sqlprint("UPDATE serverlist !H
@@ -37,12 +40,10 @@ sub add_server_new {
# if serverlist was updated
return 0 if ($n > 0);
-
# try updating it in pending
%H = (
$o{added} ? ( 'added = ?' => $o{added}) : (),
$o{secure} ? ( 'secure = ?' => $o{secure}) : (),
- $o{gamename} ? ( 'gamename = ?' => lc $o{gamename}) : (),
$o{beaconport} ? ('beaconport = ?' => $o{beaconport}) : (),
);
@@ -119,7 +120,7 @@ sub syncer_add {
# if address is in the list AND up to date,
# acknowledge its existance but don't do anything with it
my $u = $self->{dbh}->do(
- "SELECT * FROM serverlist
+ "SELECT count(*) FROM serverlist
WHERE ip = ?
AND port = ?
AND updated > datetime(?, 'unixepoch')",
@@ -137,7 +138,7 @@ sub syncer_add {
undef, $secure, $ip, $port);
# notify
- $self->log("update","$ip:$port was updated by syncer") if ($u > 0);
+ #$self->log("update","$ip:$port was updated by syncer") if ($u > 0);
# return 1 if found
return 1 if ($u > 0);
@@ -149,7 +150,7 @@ sub syncer_add {
undef, $ip, $port, lc $gamename, $secure);
# notify
- $self->log("add","beacon: $ip:$port was added for $gamename after sync") if ($u > 0);
+ #$self->log("add","beacon: $ip:$port was added for $gamename after sync") if ($u > 0);
# return 2 if added new
return 2 if ($u > 0);
diff --git a/lib/MasterServer/Database/SQLite/dbAppletActions.pm b/lib/MasterServer/Database/SQLite/dbAppletActions.pm
new file mode 100755
index 0000000..d2421fc
--- /dev/null
+++ b/lib/MasterServer/Database/SQLite/dbAppletActions.pm
@@ -0,0 +1,92 @@
+package MasterServer::Database::SQLite::dbAppletActions;
+
+use strict;
+use warnings;
+use Exporter 'import';
+
+our @EXPORT = qw| add_master_applet
+ update_master_applet
+ reset_master_applets
+ get_masterserver_applets
+ remove_unresponsive_applets |;
+
+################################################################################
+## Add a remote master server applet
+################################################################################
+sub add_master_applet {
+ my $self = shift;
+ my %o = @_;
+
+ my $u = $self->{dbh}->do(
+ "SELECT * FROM appletlist
+ WHERE ip = ?
+ AND port = ?
+ AND gamename = ?",
+ undef, $o{ip}, $o{port}, lc $o{gamename});
+
+ # return if found
+ return if ($u > 0);
+
+ # insert applet data
+ return $self->{dbh}->do("INSERT INTO appletlist (ip, port, gamename)
+ SELECT ?, ?, ?", undef,
+ $o{ip}, $o{port}, lc $o{gamename});
+}
+
+################################################################################
+## reset added/updated time after restart
+################################################################################
+sub reset_master_applets {
+ my $self = shift;
+ return $self->{dbh}->do("UPDATE appletlist
+ SET added = datetime(?, \'unixepoch\'),
+ updated = datetime(?, \'unixepoch\')",
+ undef, time, time);
+}
+
+################################################################################
+## update time on master applet
+################################################################################
+sub update_master_applet {
+ my ($self, %o) = @_;
+
+ return $self->{dbh}->do("UPDATE appletlist
+ SET updated = datetime(?, \'unixepoch\')
+ WHERE ip = ?
+ AND port = ?
+ AND gamename = ?",
+ undef, time, $o{ip}, $o{port}, lc $o{gamename});
+}
+
+################################################################################
+## get a list of master server applets that were online in the past week
+##
+################################################################################
+sub get_masterserver_applets {
+ my $self = shift;
+
+ return $self->db_all(
+ "SELECT *
+ FROM appletlist
+ WHERE updated > datetime(?, \'unixepoch\')",
+ time-604800);
+}
+
+################################################################################
+## Clear out applet entries that have been unresponsive or without servers for
+## more than a week. Servers with multiple entries only have the entry of this
+## specific gamename removed.
+################################################################################
+sub remove_unresponsive_applets {
+ my $self = shift;
+
+ # remove entries
+ my $u = $self->{dbh}->do(
+ "DELETE FROM appletlist
+ WHERE updated < datetime(?, \'unixepoch\')", undef, time-604800);
+
+ # notify
+ $self->log("delete", "Removed $u entries from applet list.") if ($u > 0);
+}
+
+1;
diff --git a/lib/MasterServer/Database/SQLite/dbCiphers.pm b/lib/MasterServer/Database/SQLite/dbCiphers.pm
index c98eb05..5b88944 100755
--- a/lib/MasterServer/Database/SQLite/dbCiphers.pm
+++ b/lib/MasterServer/Database/SQLite/dbCiphers.pm
@@ -1,15 +1,23 @@
-
package MasterServer::Database::SQLite::dbCiphers;
use strict;
use warnings;
use Exporter 'import';
-our @EXPORT = qw| clear_ciphers
+our @EXPORT = qw| check_cipher_count
+ clear_ciphers
insert_cipher
get_game_props |;
################################################################################
+## Check if ciphers exist
+################################################################################
+sub check_cipher_count {
+ my $self = shift;
+ return $self->db_all('SELECT count(*) as num from games')->[0]->{num};
+}
+
+################################################################################
## Clear all existing ciphers from the database
################################################################################
sub clear_ciphers {
@@ -43,15 +51,13 @@ sub insert_cipher {
################################################################################
## get the cipher, description and default port that goes with given gamename
+## returns only the first item if multiple items
################################################################################
sub get_game_props {
my ($self, $gn) = @_;
# get cipher from db if gamename exists
- return $self->{dbh}->selectall_arrayref(
- 'SELECT * FROM games WHERE gamename = ?',
- {Slice=>{}},
- lc $gn)->[0];
+ return $self->db_all('SELECT * FROM games WHERE gamename = ?', lc $gn)->[0];
}
1;
diff --git a/lib/MasterServer/Database/SQLite/dbCore.pm b/lib/MasterServer/Database/SQLite/dbCore.pm
index f58d535..0772a4e 100755
--- a/lib/MasterServer/Database/SQLite/dbCore.pm
+++ b/lib/MasterServer/Database/SQLite/dbCore.pm
@@ -1,11 +1,11 @@
-
package MasterServer::Database::SQLite::dbCore;
use strict;
use warnings;
+use POSIX qw/strftime/;
use Exporter 'import';
-our @EXPORT = qw| database_login |;
+our @EXPORT = qw| database_login dump_database |;
################################################################################
## login to the database with credentials provided in the config file.
@@ -19,7 +19,10 @@ sub database_login {
# get db info
my @db_type = split(':', $self->{dblogin}->[0]);
-
+
+ # inform what db we try to load
+ $self->log("info","Database: $db_type[1], $db_type[2]");
+
# check if database file exists
my $db_file = [split(':', $self->{dblogin}->[0])]->[2];
$db_file =~ s/dbname=//i;
@@ -31,10 +34,7 @@ sub database_login {
# end program
$self->halt();
}
-
- # inform what DB we try to load
- # $self->log("info","Database: $db_type[1]");
-
+
# create the dbi object
my $dbh = DBI->connect(@{$self->{dblogin}}, {PrintError => 1});
@@ -72,4 +72,28 @@ sub database_login {
return undef;
}
+################################################################################
+## Dump the database in the data/dump folder.
+## useful for backups, historical data
+################################################################################
+sub dump_database {
+ my $self = shift;
+
+ # filename / time
+ my $time = strftime('%Y-%m-%d-%H-%M',localtime);
+
+ # read db credentials from db login
+ my @db_type = split ':', $self->{dblogin}->[0];
+ $db_type[2] =~ s/dbname=//;
+
+ # split db path
+ my @db_path = split '/', $db_type[2];
+
+ # use pg_dump to dump Postgresql databases
+ system("cp $db_type[2] $self->{root}/data/dumps/SQLite-$time-$db_path[-1]");
+
+ # log
+ $self->log("dump", "Dumping database to /data/dumps/SQLite-$time-$db_path[-1]");
+}
+
1;
diff --git a/lib/MasterServer/Database/SQLite/dbMaintenance.pm b/lib/MasterServer/Database/SQLite/dbMaintenance.pm
index 06a8db4..019f31f 100755
--- a/lib/MasterServer/Database/SQLite/dbMaintenance.pm
+++ b/lib/MasterServer/Database/SQLite/dbMaintenance.pm
@@ -5,7 +5,7 @@ use warnings;
use Exporter 'import';
our @EXPORT = qw| delete_old_pending
- remove_pending |;
+ remove_pending |;
################################################################################
## delete unresponsive servers from the pending list
diff --git a/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm b/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm
index f1577b2..119900b 100755
--- a/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm
+++ b/lib/MasterServer/Database/SQLite/dbUTServerInfo.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Database::SQLite::dbUTServerInfo;
use strict;
diff --git a/lib/MasterServer/TCP/BrowserHost.pm b/lib/MasterServer/TCP/BrowserHost.pm
index 855b2c0..c53ae42 100755
--- a/lib/MasterServer/TCP/BrowserHost.pm
+++ b/lib/MasterServer/TCP/BrowserHost.pm
@@ -1,4 +1,3 @@
-
package MasterServer::TCP::BrowserHost;
use strict;
@@ -24,7 +23,7 @@ sub browser_host {
my $auth = 0;
# debug -- new connection opened
- $self->log("tcp","New connection from $a:$p");
+ #$self->log("tcp","New connection from $a:$p");
# prep a challenge
my $secure = $self->secure_string();
@@ -33,9 +32,9 @@ sub browser_host {
my $h; $h = AnyEvent::Handle->new(
fh => $fh,
poll => 'r',
- timeout => 5,
- on_eof => sub {$self->clean_tcp_handle(@_)},
- on_error => sub {$self->clean_tcp_handle(@_)},
+ timeout => $self->{timeout_time},
+ on_eof => sub {$self->log("tcp","eof on $a:$p" ); $self->clean_tcp_handle(@_)},
+ on_error => sub {$self->error($!, "browser $a:$p"); $self->clean_tcp_handle(@_)},
on_read => sub {$self->read_tcp_handle($h, $a, $p, $secure, @_)},
);
diff --git a/lib/MasterServer/TCP/Handler.pm b/lib/MasterServer/TCP/Handler.pm
index 1a075bf..4e174c2 100755
--- a/lib/MasterServer/TCP/Handler.pm
+++ b/lib/MasterServer/TCP/Handler.pm
@@ -1,4 +1,3 @@
-
package MasterServer::TCP::Handler;
use strict;
@@ -28,14 +27,12 @@ sub read_tcp_handle {
# did the client validate already?
my $val = $self->{browser_clients}->{$h}[1];
- # in case of errors, save the original message
+ # in case of errors, log the original message
my $rxbuf = $m;
+ #$self->log("debug","$a:$p sent $rxbuf");
# 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
@@ -75,14 +72,13 @@ sub read_tcp_handle {
"Contact us via 333networks.com\\final\\");
# and log it
- $self->log("error","invalid request from Browser $a:$p with unknown message \"$rxbuf\".");
+ $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 "");
}
}
-
################################################################################
## The master server opens the connection with the \secure\ challenge. The
## client should respond with basic information about itself and the
@@ -95,7 +91,8 @@ sub handle_validate {
my $val = 0;
# pass or fail the secure challenge
- if (exists $r->{gamename} && length $self->get_game_props(lc $r->{gamename})->{cipher} > 1 ) {
+ if (exists $r->{gamename} && $self->get_game_props($r->{gamename})) {
+
# game exists and we have the key to verify the response
$val = $self->compare_challenge(
gamename => $r->{gamename},
@@ -113,7 +110,7 @@ sub handle_validate {
$self->log("support", "received unknown gamename request \"$r->{gamename}\" from $a:$p.");
}
- # log (the spam!)
+ # log (debug)
#$self->log("secure","$a:$p validated with $val for $r->{gamename}, $secure, $r->{validate}");
# return auth status
@@ -132,6 +129,7 @@ sub handle_validate {
##
## NOTE: client does not need to validate to be allowed to perform this
## query.
+## TODO: deprecate about query -- info will now be in udp query!
################################################################################
sub handle_about {
my ($self, $about, $a, $p) = @_;
@@ -197,7 +195,7 @@ sub handle_list {
my $data = "";
# determine the return format
- if ($self->{hex_format} =~ m/$r->{gamename}/i or $r->{list} =~ /^cmp$/i) {
+ if ($r->{list} =~ /^cmp$/i) {
# return addresses as byte format (ip=ABCD port=EF)
$data .= $self->compile_list_cmp($r->{gamename});
}
@@ -216,6 +214,7 @@ sub handle_list {
$self->log("list","$a:$p successfully retrieved the list for $r->{gamename}.");
# clean and close the connection
+ #$self->log("tcp","closing $a:$p");
$self->clean_tcp_handle($c);
}
@@ -229,6 +228,7 @@ sub handle_list {
$self->log("error","browser $a:$p failed validation for $r->{gamename}");
# clean and close the connection
+ #$self->log("tcp","closing $a:$p");
$self->clean_tcp_handle($c);
}
}
@@ -256,6 +256,7 @@ sub handle_sync {
$self->log("sync-tx","$a:$p successfully synced.");
# clean and close the connection
+ #$self->log("tcp","closing $a:$p");
$self->clean_tcp_handle($c);
}
@@ -270,6 +271,7 @@ sub handle_sync {
$self->log("error","$a:$p failed synchronization.");
# clean and close the connection
+ #$self->log("tcp","closing $a:$p");
$self->clean_tcp_handle($c);
}
}
diff --git a/lib/MasterServer/TCP/ListCompiler.pm b/lib/MasterServer/TCP/ListCompiler.pm
index a5571d0..97ef541 100755
--- a/lib/MasterServer/TCP/ListCompiler.pm
+++ b/lib/MasterServer/TCP/ListCompiler.pm
@@ -1,9 +1,7 @@
-
package MasterServer::TCP::ListCompiler;
use strict;
use warnings;
-
use Exporter 'import';
our @EXPORT = qw| compile_list compile_list_cmp compile_sync |;
diff --git a/lib/MasterServer/TCP/Syncer.pm b/lib/MasterServer/TCP/Syncer.pm
index 3e903e0..d890f00 100755
--- a/lib/MasterServer/TCP/Syncer.pm
+++ b/lib/MasterServer/TCP/Syncer.pm
@@ -1,4 +1,3 @@
-
package MasterServer::TCP::Syncer;
use strict;
@@ -8,8 +7,7 @@ use AnyEvent::Handle;
use Exporter 'import';
our @EXPORT = qw| sync_with_master
- process_sync_list
- masterserver_list |;
+ process_sync_list |;
################################################################################
## Sends synchronization request to another 333networks based master server and
@@ -27,18 +25,20 @@ sub sync_with_master {
# connection handle
my $handle;
$handle = new AnyEvent::Handle(
- connect => [$ms->{ip} => $ms->{tcp}],
- timeout => 4,
+ connect => [$ms->{ip} => $ms->{hostport}],
+ timeout => $self->{timeout_time},
poll => 'r',
- on_error => sub {$self->error($!, "$ms->{ip}:$ms->{port}"); $handle->destroy;},
- on_eof => sub {$self->process_sync_list($sync_list, $ms); $handle->destroy;},
+ on_error => sub {$self->error($!, "$ms->{ip}:$ms->{hostport}"); $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;
$_[0]->rbuf = "";
- # remove string terminator: sometimes trailing slashes are added or
- # forgotten by sender, so \secure\abcdef is actually \secure\abcdef{\0}
+ # 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
@@ -63,7 +63,7 @@ sub sync_with_master {
# 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" or "new") : $self->{sync_games}[1])
+ . (($self->{sync_games}[0] == 0) ? ("all") : $self->{sync_games}[1])
. "\\final\\";
# push the request to remote host
@@ -99,8 +99,7 @@ sub process_sync_list {
if (exists $r{echo}) {
# remote address says...
- $self->log("error", "$ms->{ip} replied: $r{echo}");
-
+ $self->log("echo", "$ms->{ip} replied: $r{echo}");
}
# iterate through the gamenames and addresses
@@ -127,8 +126,8 @@ sub process_sync_list {
# add server
$self->syncer_add($a, $p, $gn, $self->secure_string());
- # print address
- $self->log("add", "syncer added $gn\t$a\t$p");
+ # print address (debug)
+ # $self->log("add", "syncer added $gn\t$a\t$p");
}
else {
# invalid address, log
@@ -144,42 +143,14 @@ sub process_sync_list {
} # end defined $gn
} # end while
- # end message
- $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;
+ # update this sync master in the gamelist with lastseen time
+ $self->update_server_list(
+ ip => $ms->{ip},
+ port => $ms->{port},
+ ) if ($c > 0);
- # 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;
+ # end message
+ $self->log("sync-rx", "received $c addresses after syncing from $ms->{ip}:$ms->{hostport}");
}
1;
diff --git a/lib/MasterServer/TCP/UCCAppletQuery.pm b/lib/MasterServer/TCP/UCCAppletQuery.pm
index 7637e9f..2c32de9 100755
--- a/lib/MasterServer/TCP/UCCAppletQuery.pm
+++ b/lib/MasterServer/TCP/UCCAppletQuery.pm
@@ -1,4 +1,3 @@
-
package MasterServer::TCP::UCCAppletQuery;
use strict;
@@ -18,7 +17,7 @@ sub query_applet {
my ($self, $ms) = @_;
# be nice to notify
- $self->log("tcp","start querying $ms->{ip}:$ms->{port} for '$ms->{game}' games");
+ $self->log("tcp","start querying $ms->{ip}:$ms->{port} for '$ms->{gamename}' games");
# list to store all IPs in.
my $master_list = "";
@@ -27,7 +26,7 @@ sub query_applet {
my $handle;
$handle = new AnyEvent::Handle(
connect => [$ms->{ip} => $ms->{port}],
- timeout => 5,
+ timeout => $self->{timeout_time},
poll => 'r',
on_error => sub {$self->error($!, "$ms->{ip}:$ms->{port}"); $handle->destroy;},
on_eof => sub {$self->process_ucc_applet_query($master_list, $ms); $handle->destroy;},
@@ -36,7 +35,7 @@ sub query_applet {
# receive and clear buffer
my $m = $_[0]->rbuf;
$_[0]->rbuf = "";
-
+
# remove string terminator
chop $m if $m =~ m/secure/;
@@ -48,20 +47,21 @@ sub query_applet {
$m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
# respond to challenge
- my $validate = $self->validate_string(gamename => $ms->{game},
+ my $validate = $self->validate_string(gamename => $ms->{gamename},
enctype => $r{enctype}||0,
secure => $r{secure});
# send response
- $handle->push_write("\\gamename\\$ms->{game}\\location\\0\\validate\\$validate\\final\\");
+ $handle->push_write("\\gamename\\$ms->{gamename}\\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\\");
+ $handle->push_write("\\list\\\\gamename\\$ms->{gamename}\\final\\");
}
# part 3b: receive the entire list in multiple steps.
- if ($m =~ m/\\ip\\/) {
+ # $m contains \ip\ or part of that string
+ else {
# add buffer to the list
$master_list .= $m;
}
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;
diff --git a/lib/MasterServer/Util/KFStatsWatcher.pm b/lib/MasterServer/Util/KFStatsWatcher.pm
index 916446e..6601aa3 100755
--- a/lib/MasterServer/Util/KFStatsWatcher.pm
+++ b/lib/MasterServer/Util/KFStatsWatcher.pm
@@ -1,4 +1,3 @@
-
package MasterServer::Util::KFStatsWatcher;
use strict;
@@ -49,11 +48,10 @@ sub read_kfstats {
}
}
- #notify
+ # notify
$self->log("kfstat", "Updated Killing Floor player stats.");
}
);
}
-
1;