1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
package MasterServer::UDP::UpLink;
use strict;
use warnings;
use Encode;
use AnyEvent::Handle::UDP;
use Socket qw(sockaddr_in inet_ntoa);
use Exporter 'import';
our @EXPORT = qw| send_heartbeats
do_uplink
process_uplink_response
process_udp_secure |;
################################################################################
## Broadcast heartbeats to other masterservers
##
################################################################################
sub send_heartbeats {
my $self = shift;
# 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}) {
# 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($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
## masterservers are shared in this way too.
################################################################################
sub do_uplink {
my ($self, $ip, $port) = @_;
# do not proceed if not all information is available
return unless (defined $ip && defined $port && $port > 0);
# 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(
connect => [$ip, $port],
timeout => $self->{timeout_time},
on_timeout => sub {$udp_client->destroy()},
on_error => sub {$udp_client->destroy()},
on_recv => sub {$self->process_uplink_response(@_)},
);
# Send heardbeat
$udp_client->push_send("\\heartbeat\\$self->{beacon_port}\\gamename\\333networks");
}
################################################################################
## Process requests received after uplinking
##
################################################################################
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\\/);
}
################################################################################
## 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
my %r;
$buf = encode('UTF-8', $buf);
$buf =~ s/\\\\/\\undef\\/;
$buf =~ s/\n//;
$buf =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
# response string
my $response = "";
# 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}"
. "\\location\\0"
. "\\hostname\\$self->{masterserver_hostname}"
. "\\hostport\\$self->{listen_port}";
}
# TODO: add queryid -- not because it's useful, but because protocol compliant
# support for secure/validate
if (defined $r{secure}) {
# generate response
$response .= "\\validate\\"
. $self->validate_string(gamename => "333networks",
enctype => 0,
secure => $r{secure});
}
# send the response
$udp->push_send("$response\\final\\", $pa);
}
1;
|