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
|
package MasterServer::UDP::BeaconChecker;
use strict;
use warnings;
use AnyEvent::Handle::UDP;
use Exporter 'import';
our @EXPORT = qw| beacon_checker query_udp_server|;
################################################################################
## 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.
##
## Addresses collected by other scripts, whether from the UCC applet or manual
## input via the website, are added to the pending list. 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;
}
# See if there are pending servers, and use existing secure string for
# the challenge.
my $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 timer tick.
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 (no secure string)
$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}));
}
);
# at the start of the module, remind host how often this happens
$self->log("info", "Verifying servers every $self->{beacon_checker_time}[2] seconds.");
# return the timer object to keep it alive outside of this scope
return $server_info;
}
################################################################################
## 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.
################################################################################
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;
|