aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/UDP/UpLink.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/MasterServer/UDP/UpLink.pm')
-rwxr-xr-xlib/MasterServer/UDP/UpLink.pm140
1 files changed, 85 insertions, 55 deletions
diff --git a/lib/MasterServer/UDP/UpLink.pm b/lib/MasterServer/UDP/UpLink.pm
index 63cbefb..01e806e 100755
--- a/lib/MasterServer/UDP/UpLink.pm
+++ b/lib/MasterServer/UDP/UpLink.pm
@@ -9,8 +9,11 @@ use Exporter 'import';
our @EXPORT = qw| send_heartbeats
do_uplink
- process_uplink_response
- process_udp_secure |;
+ process_udp_secure
+ handle_status_query |;
+
+# for compliance, query ID
+my $query_id = 0;
################################################################################
## Broadcast heartbeats to other masterservers
@@ -57,7 +60,11 @@ sub do_uplink {
timeout => $self->{timeout_time},
on_timeout => sub {$udp_client->destroy()},
on_error => sub {$udp_client->destroy()},
- on_recv => sub {$self->process_uplink_response(@_)},
+ on_recv => sub {
+ my ($self, $buf, $udp, $pa) = @_;
+ $self->handle_status_query($udp, $pa, $buf)
+ if ($buf =~ m/secure/);
+ },
);
# Send heardbeat
@@ -65,78 +72,101 @@ sub do_uplink {
}
################################################################################
-## Process requests received after uplinking
-##
+## Respond to status-like queries. Supported queries are basic, info, rules,
+## players, status.
+## Note: this replaces the \about\ query in the TCP handler!
################################################################################
-sub process_uplink_response {
- # $self, beacon address, handle, packed client address
- my ($self, $b, $udp, $pa) = @_;
-
- # unpack ip from packed client address
- my ($port, $iaddr) = sockaddr_in($pa);
- my $peer_addr = inet_ntoa($iaddr);
-
- # 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");
-
- # truncate and try to continue
- $b = substr $b, 0, 64;
- }
-
- # check if this is a secure challenge
- $self->process_udp_secure($udp, $pa, $b, $peer_addr)
- if ($b =~ m/\\secure\\/);
-}
+sub handle_status_query {
+ my ($self, $udp, $pa, $buf) = @_;
-
-################################################################################
-## 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\wookie
+ # hotfix for one-word queries
+ $buf .= "\\dummy\\";
my %r;
-
+
$buf = encode('UTF-8', $buf);
- $buf =~ s/\\\\/\\undef\\/;
$buf =~ s/\n//;
+ $buf =~ s/\\\\/\\undef\\/g; # where to add the +? seperate perl script!
$buf =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
# response string
my $response = "";
- # compile basic string
+ # for compliance, query ids between 0-99
+ $query_id = ($query_id >= 99) ? 1 : ++$query_id;
+ my $sub_id = 1;
- # provide basic information if asked for
- if (defined $r{basic} || defined $r{status} || defined $r{info}) {
+ # get database info to present game stats as players, where num_total > 0
+ my $maxgames = $self->check_cipher_count();
+ my $gameinfo = $self->get_game_props(
+ num_gt => 1,
+ sort => "num_total",
+ reverse => 1
+ );
- # format: \gamename\ut\gamever\348\minnetver\348\location\0\final\\queryid\16.1
+ # secure challenge
+ if (defined $r{secure}) {
+ $response .= "\\validate\\"
+ . $self->validate_string(
+ gamename => "333networks",
+ enctype => 0,
+ secure => $r{secure}
+ );
+ }
+
+ # basic query
+ if (defined $r{basic} || defined $r{status}) {
$response .= "\\gamename\\333networks"
. "\\gamever\\$self->{short_version}"
. "\\location\\0"
- . "\\hostname\\$self->{masterserver_hostname}"
- . "\\hostport\\$self->{listen_port}";
+ . "\\queryid\\$query_id.".$sub_id++;
+ }
+
+ # info query
+ if (defined $r{info} || defined $r{status}) {
+ $response .= "\\hostname\\$self->{masterserver_hostname}"
+ . "\\hostport\\$self->{listen_port}"
+ . "\\gametype\\MasterServer"
+ . "\\numplayers\\". scalar @{$gameinfo}
+ . "\\maxplayers\\$maxgames"
+ . "\\gamemode\\openplaying"
+ . "\\queryid\\$query_id.".$sub_id++;
+ }
+
+ # rules query
+ if (defined $r{rules} || defined $r{status}) {
+ $response .= "\\mutators\\333networks synchronization, master applet synchronization"
+ . "\\AdminName\\$self->{masterserver_name}"
+ . "\\AdminEMail\\$self->{masterserver_contact}"
+ . "\\queryid\\$query_id.".$sub_id++;
}
- # TODO: add queryid -- not because it's useful, but because protocol compliant
+ # players query
+ if (defined $r{players} || defined $r{status}) {
+ # list game stats as if they were players, with game description as
+ # player_$, gamename as skin_$, total servers as frags_$ and number of
+ # direct uplinks as deaths_$
+ my $c = 0;
- # support for secure/validate
- if (defined $r{secure}) {
- # generate response
-
- $response .= "\\validate\\"
- . $self->validate_string(gamename => "333networks",
- enctype => 0,
- secure => $r{secure});
+ foreach my $p (@{$gameinfo}) {
+ $c++; # count players
+ $response .= "\\player_$c\\$p->{description}"
+ . "\\skin_$c\\$p->{gamename}"
+ . "\\frags_$c\\$p->{num_total}"
+ . "\\deaths_$c\\$p->{num_uplink}";
+ }
+ $response .= "\\queryid\\$query_id.".$sub_id++;
}
- # send the response
- $udp->push_send("$response\\final\\", $pa);
+ # close query with final tag
+ $response .= "\\final\\";
+
+ # split the response in chunks of 512 bytes and send
+ while (length $response > 512) {
+ my $chunk = substr $response, 0, 512, '';
+ $udp->push_send($chunk, $pa);
+ }
+ # last <512 chunk
+ $udp->push_send($response, $pa);
}
1;