/usr/lib/crossfire/crossfire/metaserver.pl is in crossfire-server 1.71.0+dfsg1-1build1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
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 | #!/usr/bin/perl
# $Id$
# Copyright 2000 by Mark Wedel.
# This script follows the same license as crossfire (GPL).
use Socket;
use English;
# We periodically generate a nice HTML file that people can't put their
# web browser at. This is the location of that file.
$HTML_FILE="/var/apache/htdocs/metaserver.html";
# Cache file to keep data we ahve collected. This is used so that if
# the metaserver program crashes/dies, it still has some old data.
# You may want to set this to a location that will survive across system
# reboots.
$CACHE_FILE="/var/tmp/meta_xfire.cache";
# We remove a server after $REMOVE_SERVER number of seconds of no updates.
# 600 is 10 minutes - if we haven't gotten an update that fast, the server
# is almost certainly gone/not available. This reduces congestion when
# someone on a dhcp connection keeps running a server and it fills
# up a bunch of the slots.
$REMOVE_SERVER=600;
# UPDATE_SYNC determines how often we update the HTML_FILE and CACHE_FILE.
$UPDATE_SYNC=300;
# IP_INTERVAL is how often (in seconds) same IP can request metaserver
# info. This is to prevent DOS attacks.
$IP_INTERVAL=5;
# For gathering some anonymous statistics. You probably want to use
# MRTG/RRDTOOL for generating statistics from the file.
# -- Heikki Hokkanen, 2005-03-26
my $STATS_FILE="/var/tmp/meta_xfire.stats";
my $stats_updatehits = 0; # COUNTER
my $stats_requesthits = 0; # COUNTER
my $stats_totalplayers = 0; # GAUGE
my $stats_totalservers = 0; # GAUGE
socket(SOCKET, PF_INET, SOCK_DGRAM, getprotobyname("udp")) ||
die("$0: can not open udp socket: $OS_ERROR\n");
bind(SOCKET, sockaddr_in(13326, INADDR_ANY)) ||
die("$0: Can not bind to socket: $OS_ERROR\n");
# Socket1 is used for incoming requests - if we get a connection on this,
# we dump out our data to that socket in an easily parsible form.
socket(SOCKET1, PF_INET, SOCK_STREAM, getprotobyname("tcp")) ||
die("$0: can not open tcp socket: $OS_ERROR\n");
# errors on this not that critical
setsockopt(SOCKET1, SOL_SOCKET, SO_REUSEADDR, 1);
bind(SOCKET1, sockaddr_in(13326, INADDR_ANY)) ||
die("$0: Can not bind to socket: $OS_ERROR\n");
listen(SOCKET1, 10) ||
die("$0: Can not listen on socket: $OS_ERROR\n");
vec($rin, fileno(SOCKET), 1)=1;
vec($rin, fileno(SOCKET1), 1)=1;
if (open(CACHE,"<$CACHE_FILE")) {
while (<CACHE>) {
chomp;
($ip, $rest) = split /\|/, $_, 2;
$data{$ip} = $_;
}
}
close(CACHE);
$last_sync=time;
while (1) {
$nfound=select($rout=$rin, undef, undef, 60);
$cur_time=time;
if ($nfound) {
if (vec($rout, fileno(SOCKET),1)) {
$ipaddr = recv(SOCKET, $data, 256, 0) ||
print STDERR "$0: error on recv call: $OS_ERROR\n";
($port, $ip) = sockaddr_in($ipaddr);
$host = inet_ntoa $ip;
($name, $rest) = split /\|/, $data;
if ($name ne "put.your.hostname.here") {
$data{$host} = "$host|$cur_time|$data";
$stats_updatehits++;
}
}
if (vec($rout, fileno(SOCKET1),1)) {
# This is overly basic - if there are enough servers
# where the output won't fit in the outgoing socket,
# this will block. However, if we fork, we open
# ourselves up to someone intentionally designing something
# that causes these to block, and then have us fork a huge
# number of process potentially filling up our proc table.
if ($ipaddr=accept NEWSOCKET, SOCKET1) {
($port, $ip ) = sockaddr_in( $ipaddr );
$dq = join('.',unpack('C4', $ip));
if ($ip_times{$dq} > time) {
close(NEWSOCKET);
} else {
$ip_times{$dq} = time + $IP_INTERVAL;
foreach $i (keys %data) {
# Report idle time to client, and not last update
# as we received in seconds since epoch.
($ip, $time, $rest) = split /\|/, $data{$i}, 3;
$newtime = $cur_time - $time;
print NEWSOCKET "$ip|$newtime|$rest\n";
}
close(NEWSOCKET);
$stats_requesthits++;
}
}
}
}
# Need to generate some files. This is also where we remove outdated
# hosts.
if ($last_sync+$UPDATE_SYNC < $cur_time) {
$last_sync = $cur_time;
open(CACHE,">$CACHE_FILE");
open(HTML,">$HTML_FILE");
print HTML
'<title>Crossfire Server List</title>
<h1 align=center>Crossfire Server List</h1><p>
<table border=1 align=center cellpadding=5>
<tr>
<th>IP Address</th><th>Last Update Date/Time</th><th>Last Update Minutes Elapsed</th>
<th>Hostname</th><th>Number of Players</th><th>Version</th><th>Comment</th>
</tr>
';
$stats_totalplayers = 0;
$stats_totalservers = 0;
foreach $i (keys %data) {
$stats_totalservers++;
($ip, $time, @rest) = split /\|/, $data{$i};
if ($time+$REMOVE_SERVER<$cur_time) {
delete $data{$i};
} else {
print CACHE "$data{$i}\n";
$elapsed = int(($cur_time - $time)/60);
$gmtime = gmtime($time);
print HTML "<tr><td>$i</td><td>$gmtime</td><td>$elapsed</td>";
print HTML "<td>$rest[0]</td><td>$rest[1]</td><td>$rest[2]</td><td>$rest[3]</td></tr>\n";
$stats_totalplayers += int($rest[2]);
}
}
$gmtime = gmtime($cur_time);
print HTML "
</table><p>
The server name is reported by the server, while the ip address is determined by
the incoming data packet. These values may not resolve to the same thing in the
case of multi homed hosts or multi ip hosts.<p>
All times are in GMT.<p>
<font size=-2>Last Updated: $gmtime<font size=+2><p>";
close(HTML);
close(CACHE);
open(STATS,">$STATS_FILE");
print STATS "$stats_updatehits:$stats_requesthits:$stats_totalservers:$stats_totalplayers\n";
close(STATS);
}
}
|