Skip to content

Instantly share code, notes, and snippets.

@maikelwever
Created February 9, 2016 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maikelwever/5c4ca5f5fa9e72685812 to your computer and use it in GitHub Desktop.
Save maikelwever/5c4ca5f5fa9e72685812 to your computer and use it in GitHub Desktop.
UT4 server query in Python
#!/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