Created
February 9, 2016 13:54
-
-
Save maikelwever/5c4ca5f5fa9e72685812 to your computer and use it in GitHub Desktop.
UT4 server query in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# | |
# UT4 Server query script by maikoool (github.com/maikelwever) | |
# | |
# With many thanks to Sp0ngeb0b from the UT forums which made the initial Perl version this was copied from. | |
# https://forums.unrealtournament.com/showthread.php?19208-UT4-Server-HUB-Query-Perl-PHP-(HUB-Query-added!) | |
# | |
import binascii | |
import socket | |
RECV_AMOUNT = 512 | |
RECV_TIMEOUT = 0.1 | |
OUTDATA = [ | |
b"\x00\x80\x05\x00\x10\x40\x60\x00\x20\x20\xc5\xbc\x05\x20", | |
b"\x01\x00\x01\x00\x08\x10\x64\x20\xc0\xd4\x01\x00\xd0\x78\x00\x00\x00\xa8\xa2\x9a\x2a\x93\xb3\x2b\x93\x13\x2a\x0b\x1b\x7b\x73\x03\x08", # noqa | |
b"\x02\x40\x00\x00\x01\x00\x0c\x10\x50\xe0\x78\x00\x00\x00\xa8\xa2\x9a\x2a\x93\xb3\x2b\x93\x13\x2a\x0b\x1b\x7b\x73\x03\x18\x00\x04", # noqa | |
b"\x03\x40\x00\xc0\x00\x80\x02\x00\x02\xd0\x02\xac\xbb\x00\x10", | |
b"\x04\x40\x01\x80\x02\x00\x04\xd0\x02\xb4\xfb\x00\x10", | |
b"\x05\xc0\x01\x20\x01\xb0\x00\x08", | |
b"\x06\x40\x02\x60\x01\xa0\x02\x00\x20\x20\x00\x10" | |
] | |
TYPE_A_HEX_MAP = { | |
# Lowercase | |
"c2": "a", | |
"c4": "b", | |
"c6": "c", | |
"c8": "d", | |
"ca": "e", | |
"cc": "f", | |
"ce": "g", | |
"d0": "h", | |
"d2": "i", | |
"d4": "j", | |
"d6": "k", | |
"d8": "l", | |
"da": "m", | |
"dc": "n", | |
"de": "o", | |
"e0": "p", | |
"e2": "q", | |
"e4": "r", | |
"e6": "s", | |
"e8": "t", | |
"ea": "u", | |
"ec": "v", | |
"ee": "w", | |
"f0": "x", | |
"f2": "y", | |
"f4": "z", | |
# Uppercase | |
"82": "A", | |
"84": "B", | |
"86": "C", | |
"88": "D", | |
"8a": "E", | |
"8c": "F", | |
"8e": "G", | |
"90": "H", | |
"92": "I", | |
"94": "J", | |
"96": "K", | |
"98": "L", | |
"9a": "M", | |
"9c": "N", | |
"9e": "O", | |
"a0": "P", | |
"a2": "Q", | |
"a4": "R", | |
"a6": "S", | |
"a8": "T", | |
"aa": "U", | |
"ac": "V", | |
"ae": "W", | |
"b0": "X", | |
"b2": "Y", | |
"b4": "Z", | |
# Numbers | |
"60": "0", | |
"62": "1", | |
"64": "2", | |
"66": "3", | |
"68": "4", | |
"6a": "5", | |
"6c": "6", | |
"6e": "7", | |
"70": "8", | |
"72": "9", | |
# Symbols | |
"40": " ", | |
"42": "!", | |
"44": '"', | |
"46": "#", | |
"48": '$', | |
"4a": "%", | |
"4c": "&", | |
"4e": "'", | |
"50": "(", | |
"52": ")", | |
"54": "*", | |
"56": '+', | |
"58": ',', | |
"5a": "-", | |
"5c": ".", | |
"5e": "/", | |
"74": ":", | |
"76": ";", | |
"78": "<", | |
"7a": "=", | |
"7c": ">", | |
"7e": "?", | |
"b6": "[", | |
"b8": "\\", | |
"ba": "]", | |
"bc": "^", | |
"be": "_", | |
"c0": "`", | |
"fc": "~", | |
} | |
def chunker(seq, size): | |
""" | |
Chunks the source data 'seq' into bits of 'size' | |
:param seq: The source iterable to chunk | |
:param size: The size of the chunks | |
:return: iterable of chunks | |
""" | |
return (seq[pos:pos + size] for pos in range(0, len(seq), size)) | |
def make_connection(server_ip, server_query_port=7787): | |
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # DGRAM is udp | |
s.settimeout(RECV_TIMEOUT) | |
s.connect((server_ip, server_query_port)) | |
return s | |
def receive(connection): | |
result = [] | |
try: | |
data = connection.recv(RECV_AMOUNT) | |
result.append(data) | |
while data: | |
data = connection.recv(RECV_AMOUNT) | |
result.append(data) | |
except socket.timeout: | |
pass | |
return result | |
def query_server(server_ip="127.0.0.1", server_port=7787): | |
connection = make_connection(server_ip, server_query_port=server_port) | |
result = [] | |
for packet in OUTDATA: | |
connection.send(packet) | |
data = receive(connection) | |
result.extend(binascii.hexlify(bits) for bits in data) # Transforms result to string representation | |
connection.close() | |
return decode_packet(result) | |
def decode_packet(datalist): | |
current_data = datalist[4] | |
head = current_data[0:12] # This determines the packet type | |
byte = current_data[12:20] # I dont really have a clue what this is. # noqa | |
current_data = current_data[24:-6] # For some reason the last few bytes are rubbish | |
if head == b'04c001800200': | |
if len(datalist[5]) <= 24: | |
return decode_packet_type_a(current_data) | |
else: | |
return decode_packet_type_b(current_data) | |
else: | |
raise Exception("Packet with header {0} is not supported yet".format(head)) | |
def decode_packet_type_a(data): | |
groups = [] | |
tmp_list = [] | |
tmp_val = '' | |
for single_hex in chunker(data, 2): | |
single_hex = single_hex.decode('ascii') | |
if single_hex == '00': # Data separator | |
if tmp_val: # A Value separator is not added if there's only one value in the data block. | |
tmp_list.append(tmp_val) | |
tmp_val = '' | |
if tmp_list: | |
groups.append(tmp_list) | |
tmp_list = [] | |
elif single_hex == '12': # Value separator | |
if tmp_val: | |
tmp_list.append(tmp_val) | |
tmp_val = '' | |
else: | |
tmp_val += TYPE_A_HEX_MAP.get(single_hex, "") | |
if tmp_val: | |
tmp_list.append(tmp_val) | |
if tmp_list: | |
groups.append(tmp_list) | |
return groups | |
def decode_packet_type_b(data): | |
pass | |
if __name__ == "__main__": | |
print(query_server()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment