SHOW:
|
|
- or go back to the newest paste.
1 | <?php | |
2 | ||
3 | /* | |
4 | ||
5 | Version history | |
6 | ||
7 | 1 - Initial release. | |
8 | 2 - Added option to use the official domain name instead of IPs and retry if | |
9 | a timeout occurs. | |
10 | 2.1 - Added more master servers. | |
11 | 2.1.1 - Fixed a bug, program didn't terminate if all master servers time out. | |
12 | 2.1.2 - Removed a now redundant variable. | |
13 | 2.2 - Seeding for long server lists. | |
14 | - Removed the domain name because it's often better to try different | |
15 | servers. | |
16 | 2.2.1 - Bug fix: Timeout showed wrong master server IP. | |
17 | - Bug fix: Check whether the new seed starts with a duplicate or not. | |
18 | - Bug fix: Don't close socket twice. | |
19 | 2.3 - Retry queries on timeout if the first query was successful. | |
20 | 2.3.1 - Reading the whole address again if it starts with a zero. | |
21 | - Increased timeout to avoid a communication issue. | |
22 | - Removed maximum number of timeouts for testing. | |
23 | 2.4 - Again using hl1master.steampowered.com because IPs change often. | |
24 | - Endless retries because this server often times out. | |
25 | 2.5 - Trying following ports on timeout or if the response wasn't correct. | |
26 | 2.5.3 - Cosmetic changes (defines, variable name). | |
27 | 2.5.4 - Code optimisation. | |
28 | 2.6 - Repeat until list isn't empty. | |
29 | */ | |
30 | ||
31 | // Currently uses ports 27011-27013 and 27015. | |
32 | $master_servers = array("hl2master.steampowered.com"); | |
33 | ||
34 | define("MIN_PORT", 27011); // Range of port numbers which the master servers | |
35 | define("MAX_PORT", 27015); // potentially use. | |
36 | define("FILTER", '\gamedir\tfc'); // game = Team Fortress Classic | |
37 | define("REGION", "\xFF"); // region = world | |
38 | define("TIMEOUT", 2.0); // 2s timeout | |
39 | ||
40 | function query_timeout($socket, $seed) | |
41 | { | |
42 | echo "Sending query to master server.\n"; | |
43 | stream_set_timeout($socket, TIMEOUT); | |
44 | if (!fwrite($socket, "1".REGION."$seed\0".FILTER."\0")) | |
45 | { | |
46 | fclose($socket); | |
47 | exit("fwrite error\n"); | |
48 | } | |
49 | ||
50 | echo "Reading response header.\n"; | |
51 | stream_set_timeout($socket, TIMEOUT); | |
52 | $s = bin2hex(fread($socket, 6)); | |
53 | $info = stream_get_meta_data($socket); | |
54 | if ($info['timed_out']) | |
55 | echo "Master server timed out.\n"; | |
56 | else | |
57 | { | |
58 | if ($s !== "ffffffff660a") | |
59 | { | |
60 | fclose($socket); | |
61 | if ($s == "") | |
62 | echo "Expected ff ff ff ff 66 0a (hex) but got nothing.\n"; | |
63 | else | |
64 | echo "Expected ff ff ff ff 66 0a (hex) but got $s.\n"; | |
65 | return True; | |
66 | } | |
67 | } | |
68 | return $info['timed_out']; | |
69 | } | |
70 | ||
71 | // Connect to master server, return timeout info. | |
72 | // The socket is passed as reference and thus returned as well. | |
73 | ||
74 | function master_server_timeout(&$socket, $ip) | |
75 | { | |
76 | $port = MIN_PORT; | |
77 | do { | |
78 | echo "Connecting to master server \"$ip:$port\".\n"; | |
79 | $socket = fsockopen("udp://$ip", $port, $errno, $errstr, TIMEOUT); | |
80 | if (!$socket) exit("Error $errno : $errstr \n"); | |
81 | $timeout = query_timeout($socket, "0.0.0.0:0"); | |
82 | $port = $port + 1; | |
83 | } while ($timeout && ($port <= MAX_PORT)); | |
84 | return $timeout; | |
85 | } | |
86 | ||
87 | // Read list with server addresses (IP:port). | |
88 | ||
89 | function list_not_empty($socket) | |
90 | { | |
91 | $count = 0; | |
92 | $old_a1 = 0; $old_a2 = 0; $old_a3 = 0; $old_a4 = 0; $old_a5 = 0; | |
93 | $max_timeouts = 6; | |
94 | do | |
95 | { | |
96 | stream_set_timeout($socket, TIMEOUT); | |
97 | $a1 = ord(fread($socket,1)); | |
98 | $info = stream_get_meta_data($socket); | |
99 | if ($info['timed_out']) | |
100 | { | |
101 | $seed = "$old_a1.$old_a2.$old_a3.$old_a4:$old_a5"; | |
102 | echo "Seed: $seed\n"; | |
103 | while (query_timeout($socket, $seed)); | |
104 | stream_set_timeout($socket, TIMEOUT); | |
105 | $a1 = ord(fread($socket,1)); | |
106 | $info = stream_get_meta_data($socket); | |
107 | if ($info['timed_out']) | |
108 | { | |
109 | echo "Timeout occured.\n"; | |
110 | break; | |
111 | } | |
112 | $check_for_duplicate = 1; | |
113 | } | |
114 | else | |
115 | $check_for_duplicate = 0; | |
116 | ||
117 | // Let's always read the rest of the address (even if it starts with 0) in | |
118 | // order to empty the master server's write buffer. This may avoid subsequent | |
119 | // problems, but I'm paranoid here. | |
120 | $a2 = ord(fread($socket,1)); | |
121 | $a3 = ord(fread($socket,1)); | |
122 | $a4 = ord(fread($socket,1)); | |
123 | $a5 = ord(fread($socket,1))*256 + ord(fread($socket,1)); | |
124 | ||
125 | if ($a1 != 0) | |
126 | { | |
127 | if (($check_for_duplicate==0)||($a1!=$old_a1)||($a2!=$old_a2)|| | |
128 | ($a3!=$old_a3)||($a4!=$old_a4)||($a5!=$old_a5)) | |
129 | { | |
130 | $count++; | |
131 | echo "$count $a1.$a2.$a3.$a4:$a5\n"; | |
132 | } | |
133 | $old_a1 = $a1; $old_a2 = $a2; $old_a3 = $a3; $old_a4 = $a4; $old_a5 = $a5; | |
134 | } | |
135 | } while ($a1 != 0); | |
136 | fclose($socket); | |
137 | ||
138 | if ($count != 0) echo "Retrieved $count server addresses.\n"; | |
139 | return ($count != 0); | |
140 | } | |
141 | ||
142 | // Try all master servers until we find one that isn't timing out. | |
143 | ||
144 | foreach ($master_servers as $ip) | |
145 | if ($timeout=master_server_timeout($socket, $ip)) | |
146 | fclose($socket); | |
147 | else | |
148 | if (list_not_empty($socket)) break; | |
149 | ||
150 | ?> | |
151 |