aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/TCP/Handler.pm
blob: 1a075bf84e8607a085182c75d75043f463b9e151 (plain)
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

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 = "";
  
  # print debug values
  $self->log("debug","$a:$p sent $rxbuf");
     
  # 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/\\$/\\undef\\/;
  $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});
  
  #
  # Support echo: doesn't do anything but print to log if not suppressed.
  $self->log("echo","($a:$p): $r{echo}") if $r{echo};
  
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  # improper syntax/protocol -- no valid commands found
  # respond with an error.
  # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  if ("about sync validate list" =~ m/\Q$response\E/i) {
    
    # 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\".");
  } # 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} && length $self->get_game_props(lc $r->{gamename})->{cipher} > 1 ) {
    # game exists and we have the key to verify the response
    $val = $self->compare_challenge(
                gamename => $r->{gamename},
                secure   => $secure,
                enctype  => $r->{enctype},
                validate => $r->{validate},
                ignore   => $self->{ignore_browser_key},
              );    

    # 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 (the spam!)
  #$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->{masterserver_hostname}, contact: $self->{masterserver_contact}";
    $self->log("about","communicating to $a:$p my contact information.");
  }
    
  # build/version info
  if ($about =~ /^build$/i or $about =~ /^version$/i or $about =~ /^undef$/i) {
    $response .= "\\build\\$self->{build_type} $self->{build_version} written "
              .  "by $self->{build_author}, released $self->{build_date}";
    $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}";
    $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];
    }
    $response .= "\\support\\$sgs";
    $self->log("about","telling $a:$p which games are supported.");
  }

  # unsupported query
    if ("contact build address support version undef" !~ m/$about/i) {
    $response .= "\\echo\\incorrect query usage, supported queries are: contact build version address support.";
    $self->log("about","incorrect query \"$about\", telling $a:$p the supported \"about\" queries.");
  }
  
  # 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->{list} =~ /^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
    $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)
    $self->log("sync-tx","$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;