aboutsummaryrefslogtreecommitdiff
path: root/lib/MasterServer/UDP/BeaconChecker.pm
blob: 5fc2b38b04e3b31decc7cdf457d9a3823106186c (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

package MasterServer::UDP::BeaconChecker;

use strict;
use warnings;
use Encode;
use AnyEvent::Handle::UDP;
use Exporter 'import';

our @EXPORT = qw| beacon_checker query_udp_server|;

################################################################################
##
##   Beacon Checker Module
##
##   When addresses are stored in the 'pending' list, they are supposed to be
##   queried immediately with the secure/validate challenge to testify that
##   the server is genuine and alive.
##
##   Some servers do not support the secure-challenge on the Uplink port. These
##   servers are verified with a secure-challenge on their heartbeat ports, 
##   which are designed to respond to secure queries, as well as status queries.
##
##   Querying pending servers should only happen when beacons are already 
##   several seconds old. We do not want to interfere with the existing 
##   construction where servers respond immediately.
##
##   Addresses collected by other scripts, whether from the UCC applet or manual
##   input via the website, are added to the pending query table. It is more 
##   important to verify pending beacons and new server addresses, than to 
##   update the status of existing addresses. Therefore, pending addresses are
##   prioritized.
##
################################################################################
sub beacon_checker {
  my $self = shift;
  $self->log("load", "UDP Beacon Checker is loaded.");

  # queue -- which address is next in line?
  my %q = ( pending_id    => 0,
            server_id     => 0,
            start_time    => time+$self->{beacon_checker_time}[0]-1, #time+grace
          );

  # go through all servers one by one, new and old
  my $server_info = AnyEvent->timer (
    after     => $self->{beacon_checker_time}[0],
    interval  => $self->{beacon_checker_time}[1],
    cb        => sub {
        
        # first of all, check whether we exceeded our time cap limit
        if ( (time - $q{start_time}) >= $self->{beacon_checker_time}[2] ){
          # reset queue variables          
          $q{pending_id} = 0;
          $q{server_id}  = 0;
          $q{start_time} = time;
        }
        
        # id, ip, port reference of the server to be queried
        my $n;
        
        #
        # Pending Servers
        #
        # See if there are pending servers, and use existing secure string for
        # the challenge.
        $n = $self->get_next_pending($q{pending_id});
        
        # if any entries were found, proceed
        if ( $n->[0] ) {
          
          # next pending id will be > $n
          $q{pending_id} = $n->[0];
          
          # query the server
          $self->query_udp_server($n->[1], $n->[2], $n->[3]);
          
          # work done. Wait for the next round for the next task.
          return;
        }
        
        # if no pending servers left, update the other entries
        $n = $self->get_next_server($q{server_id});  
        
        # if any entries were found, proceed
        if ( $n->[0] ) {

          # next server id will be > $n
          $q{server_id} = $n->[0];
          
          # query the server
          $self->query_udp_server($n->[1], $n->[2], "");
          
          # work done. Wait for the next round for the next task.
          return;
        }
        
        # At this point, we are out of server entries. When new servers are 
        # added, they are immediately queried on the next round.
        # From here on, just count down until the cycle is complete.

        # DEBUG (spams badly)
        $self->log("debug_spam", "Checker timer: t=".(time - $q{start_time}));
    }
  );
  
  $self->log("info", "Verifying servers every $self->{beacon_checker_time}[2] seconds.");
  
  # return the timer to keep it alive outside of scope
  return $server_info;
}


################################################################################
##
##   UDP Server Query function
##
##   Get the server status from any server over UDP and store the 
##   received information in the database.
##
##   $secure determines the type of query: secure/pending or information
##
##   Args: $ip, $port, payload
##   Function is called by AnyEvent->timer() above
################################################################################
sub query_udp_server {
  my ($self, $ip, $port, $secure) = @_;
  my $buf = "";
  
  # debug spamming
  $self->log("debug_spam", "Query server $ip:$port");
  
  # connect with UDP server
  my $udp_client; $udp_client = AnyEvent::Handle::UDP->new(
    # Bind to this host and port
    connect   => [$ip, $port],
    timeout   => 1,
    on_timeout => sub {$udp_client->destroy();}, # don't bother reporting timeouts
    on_error   => sub {$udp_client->destroy();}, # or errors
    on_recv   => sub {

      # add packet to buffer
      $buf .= $_[0];
      
      # if validate, assume that we sent a \basic\secure request.
      if ($buf =~ m/\\validate\\/){
        $self->process_udp_validate($buf, $ip, undef, $port);
      }
      # if gamename, ver, hostname and hostport are available, it should have been \basic\info
      elsif ($buf =~ m/\\gamename\\/ && $buf =~ m/\\gamever\\/ 
          && $buf =~ m/\\hostname\\/ && $buf =~ m/\\hostport\\/) {
        $self->process_query_response($buf, $ip, $port);
      }
      # else partial information received. wait for more.
      else{ }
    },
  );

  #
  # Send secure message or status, depending on provided variables
  #  
  
  # secure servers enabled and secure key provided
  if ($secure ne "" && $self->{require_secure_beacons} > 0) {
    # send secure
    $udp_client->push_send("\\basic\\\\secure\\$secure");
    
    # and log that we sent it
    $self->log("udp", "sending secure=\"$secure\" to $ip:$port");
  }
  else {
    # send information request
    $udp_client->push_send("\\basic\\\\info\\");  
    
    # and log that we sent it
    $self->log("udp","sending basic request to $ip:$port");
  }
}

1;