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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
package MasterServer::TCP::Handler;
use strict;
use warnings;
use AnyEvent::Socket;
use AnyEvent::Handle;
use Exporter 'import';
our @EXPORT = qw| read_tcp_handle
handle_validate
handle_about
handle_list
handle_sync |;
################################################################################
## wait for incoming TCP connections from game clients and other masterservers.
## respond with secure/validate, contact info and/or server lists.
## allow other masterservers to synchronize
################################################################################
sub read_tcp_handle {
my ($self, $h, $a, $p, $secure, $c) = @_;
# clear the buffer
my $m = $c->rbuf;
$c->rbuf = "";
# did the client validate already?
my $val = $self->{browser_clients}->{$h}[1];
# in case of errors, save the original message
my $rxbuf = $m;
# allow multiple blocks to add to the response string
my $response = "";
# replace empty values for the string "undef" and replace line endings from netcatters
# parse the received data and extrapolate all the query commands found
my %r = ();
$m =~ s/\\\\/\\undef\\/;
$m =~ s/\n//;
$m =~ s/\\([^\\]+)\\([^\\]+)/$r{$1}=$2/eg;
# secure/validate challenge
# part 2: receive \gamename\ut\location\0\validate\$validate\final\
$val = $self->handle_validate(\%r, $h, $secure, $a, $p)
if (exists $r{validate} && !$val);
# about query
$response .= $self->handle_about($r{about}, $a, $p) if (exists $r{about});
# return address list
# part 3: wait for the requested action: \list\gamename\ut\
$self->handle_list($val, \%r, $c, $a, $p) if (exists $r{list} && exists $r{gamename});
# Sync request from another 333networks-based masterserver. Respond with list
# of requested games (or all games).
$self->handle_sync($val, \%r, $c, $a, $p) if (exists $r{sync});
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# improper syntax/protocol -- no valid commands found
# respond with an error.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
if ($m =~ m/!(about|sync|validate|list)/) {
# error message to client
$c->push_write("\\echo\\333networks did not understand your request. ".
"Contact us via 333networks.com\\final\\");
# and log it
$self->log("error","invalid request from Browser $a:$p with unknown message \"$rxbuf\"", $self->{log_settings}->{handler_error});
} # 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
## \validate\ response. In this code block we verify the challenge/response.
################################################################################
sub handle_validate {
my ($self, $r, $h, $secure, $a, $p) = @_;
# auth var init
my $val = 0;
# pass or fail the secure challenge
if (exists $r->{gamename} && exists $self->{game}->{$r->{gamename}}) {
# game exists and we have the key to verify the response
$val = $self->validated_request($r->{gamename}, $secure, $r->{enctype}, $r->{validate});
# update for future queries
$self->{browser_clients}->{$h}[1] = $val;
}
elsif (exists $r->{gamename}) {
# log
$self->log("support", "received unknown gamename request \"$r->{gamename}\" from $a:$p");
}
# log
$self->log("secure","$a:$p validated with $val for $r->{gamename}, $secure, $r->{validate}");
# return auth status
return $val;
}
################################################################################
## \about\ query.
## Can contain the values "contact", "build", "Address" or "support", where
## - contact: return contact information; see config.pl
## - build: build info, also version.pm
## - address: address and ports
## - support: return a list of games currently in the database
## - undef: all of the above
##
## NOTE: client does not need to validate to be allowed to perform this
## query.
################################################################################
sub handle_about {
my ($self, $about, $a, $p) = @_;
my $response = "";
#
# contact info
#
if ($about =~ /^contact$/i or $about =~ /^undef$/i) {
$response .= "\\about\\$self->{contact_details}";
# log/print
$self->log("about","communicating to $a:$p my contact information.");
}
#
# build info
#
if ($about =~ /^build$/i or $about =~ /^undef$/i) {
$response .= "\\build\\$self->{build_type} $self->{build_version} written "
. "by $self->{build_author}, released $self->{build_date}";
# log/print
$self->log("about","telling $a:$p my build info.");
}
#
# address info
#
if ($about =~ /^address$/i or $about =~ /^undef$/i) {
$response .= "\\address\\$self->{masterserver_address}"
. "\\listen_port\\$self->{listen_port}"
. "\\beacon_port\\$self->{beacon_port}";
# log/print
$self->log("about","telling $a:$p my address/config info.");
}
#
# support info
#
if ($about =~ /^support$/i or $about =~ /^undef$/i) {
# string games in database
my $sg = $self->get_gamenames();
my $sgs = "";
for (@{$sg}) {
$sgs .= " " if (length $sgs > 0);
$sgs .= $_->[0];
}
# print response
$response .= "\\support\\$sgs";
#log/print
$self->log("about","telling $a:$p which games are supported.");
}
# return response string
return $response;
}
################################################################################
## At this point, the client should be validated and ready to request with
## the \secure\ command and is allowed to ask for the list.
## The \list\ cmd is NOT compatible with combined queries
## (like \about\contact\list\)
################################################################################
sub handle_list {
my ($self, $val, $r, $c, $a, $p) = @_;
# confirm validation
if ($val && exists $r->{gamename}) {
# prepare the list
my $data = "";
# determine the return format
if ($self->{hex_format} =~ m/$r->{gamename}/i or $r->{gamename} =~ /^cmp$/i) {
# return addresses as byte format (ip=ABCD port=EF)
$data .= $self->compile_list_cmp($r->{gamename});
}
else {
# return addresses as regular \ip\127.0.0.1:7777\ format
$data .= $self->compile_list($r->{gamename});
}
# finalize response string
$data .= "\\final\\";
# immediately send to client
$c->push_write($data);
# log successful (debug)
$self->log("list","$a:$p successfully retrieved the list for $r->{gamename}.");
# clean and close the connection
$self->clean_tcp_handle($c);
}
# proper syntax/protocol, but incorrect validation. Therefore respond with
# an 'empty' list, returning only \final\.
else {
# return error/empty list
$c->push_write("\\echo\\333networks failed to validate your request. Use the correct authorization cipher!\\final\\");
# log it too
$self->log("error","browser $a:$p failed validation for $r->{gamename}");
# clean and close the connection
$self->clean_tcp_handle($c);
}
}
################################################################################
## Respond to \sync\ requests from other 333networks-based masterservers. After
## validation, sync behaves in much the same way as \list\,
################################################################################
sub handle_sync {
my ($self, $val, $r, $c, $a, $p) = @_;
# alternate part 3: wait for the requested action: \sync\(all|list of games)\sender\domainname
$self->log("tcp","Sync request from $a:$p found");
if ($val && exists $r->{sync}) {
# compile list of addresses
my $data = $self->compile_sync($r->{sync});
$data .= "\\final\\";
# send to remote client
$c->push_write($data);
# log successful (debug)
if (exists $r->{sender}) {$self->log("sync","$r->{sender} successfully synced.");}
else {$self->log("sync","$a:$p successfully synced.");}
# clean and close the connection
$self->clean_tcp_handle($c);
}
# proper syntax/protocol, but incorrect validation. Therefore respond with
# an 'empty' list, returning only \final\.
else {
# return error/empty list
$c->push_write("\\echo\\333networks failed to validate your request. Use a proper authorization key!\\final\\");
# log it too
$self->log("error","$a:$p failed synchronization.");
# clean and close the connection
$self->clean_tcp_handle($c);
}
}
1;
|