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
|
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 |;
################################################################################
## 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->{hostport}],
timeout => $self->{timeout_time},
poll => 'r',
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, 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
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") : $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("echo", "$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 (debug)
# $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
# update this sync master in the gamelist with lastseen time
$self->update_server_list(
ip => $ms->{ip},
port => $ms->{port},
) if ($c > 0);
# end message
$self->log("sync-rx", "received $c addresses after syncing from $ms->{ip}:$ms->{hostport}");
}
1;
|