diff options
Diffstat (limited to 'lib')
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; |
