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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
package MasterServer::TCP::Syncer;
use strict;
use warnings;
use AnyEvent;
use AnyEvent::Handle;
use Exporter 'import';
our @EXPORT = qw| sync_with_master
process_sync_list
masterserver_list |;
################################################################################
## Sends synchronization request to another 333networks based master server and
## receives the list of games.
################################################################################
sub sync_with_master {
my ($self, $ms) = @_;
# announce
$self->log("tcp", "Attempting to synchronize with $ms->{ip}");
# list to store all IPs in.
my $sync_list = "";
# connection handle
my $handle;
$handle = new AnyEvent::Handle(
connect => [$ms->{ip} => $ms->{tcp}],
timeout => 4,
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_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}
chop $m if $m =~ m/secure/;
# part 1: receive \basic\\secure\$key
if ($m =~ m/basic\\\\secure/) {
# hash $m into %r
my %r = ();
$m =~ s/\\\\/\\undef\\/;
$m =~ s/\n//;
$m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
# respond to the validate challenge
my $validate = $self->validate_string(
gamename => "333networks",
secure => $r{secure},
enctype => $r{enctype}
);
# part 2: send \gamename\ut\location\0\validate\$validate\final\
$handle->push_write("\\gamename\\333networks\\location\\0\\validate\\$validate\\final\\");
# 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])
. "\\final\\";
# push the request to remote host
$handle->push_write($request);
# clean up $m for future receivings
$m = "";
} # end secure
# part 4: receive the entire list in multiple steps
$sync_list .= $m;
},
);
}
################################################################################
## Process the list of addresses that was received after querying the UCC applet
## and store them in the pending list.
################################################################################
sub process_sync_list {
my ($self, $m, $ms) = @_;
# replace empty values for the string "undef" and replace line endings from netcatters
# parse hash {gamename => list of ips seperated by space}
my %r = ();
$m =~ s/\\\\/\\undef\\/;
$m =~ s/\n//;
$m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
# counter
my $c = 0;
if (exists $r{echo}) {
# remote address says...
$self->log("error", "$ms->{ip} replied: $r{echo}");
}
# iterate through the gamenames and addresses
while ( my ($gn,$addr) = each %r) {
# process all games wether we have a cipher for them.
if (defined $gn) {
# some database types, such as SQLite, are slow - therefore use transactions.
$self->{dbh}->begin_work;
# l(ocations, \label\ip:port\) split up in a(ddress) and p(ort)
foreach my $l (split(/ /, $addr)) {
# search for \255.255.255.255:7778\, contains ':'
if ($l =~ /:/) {
my ($a,$p) = $l =~ /(.*):(.*)/;
# check if address entry is valid
if ($self->valid_address($a,$p)) {
# count number of valid addresses
$c++;
# add server
$self->syncer_add($a, $p, $gn, $self->secure_string());
# print address
$self->log("add", "syncer added $gn\t$a\t$p");
}
else {
# invalid address, log
$self->log("error", "invalid address found while syncing at $ms->{ip}: $l!");
}
} # endif ($l =~ /:/)
} # end for / /
# end transaction, commit
$self->{dbh}->commit;
} # 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;
# 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;
}
1;
|