query_gsquery.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #! /usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. # LinuxGSM query_gsquery.py module
  4. # Author: Daniel Gibbs
  5. # Contributors: http://linuxgsm.com/contrib
  6. # Website: https://linuxgsm.com
  7. # Description: Allows querying of various game servers.
  8. import argparse
  9. import socket
  10. import sys
  11. engine_types = ('protocol-valve', 'protocol-quake2', 'protocol-quake3', 'protocol-gamespy1',
  12. 'protocol-unreal2', 'ut3', 'minecraft', 'minecraftbe', 'jc2m', 'mumbleping', 'soldat', 'teeworlds')
  13. class gsquery:
  14. server_response_timeout = 2
  15. default_buffer_length = 1024
  16. sourcequery = ('protocol-valve', 'avalanche3.0', 'barotrauma', 'madness', 'quakelive', 'realvirtuality',
  17. 'refractor', 'source', 'goldsrc', 'spark', 'starbound', 'unity3d', 'unreal4', 'wurm')
  18. idtech2query = ('protocol-quake2', 'idtech2', 'quake', 'iw2.0')
  19. idtech3query = ('protocol-quake3', 'iw3.0', 'ioquake3', 'qfusion')
  20. minecraftquery = ('minecraft', 'lwjgl2')
  21. minecraftbequery = ('minecraftbe',)
  22. jc2mquery = ('jc2m',)
  23. mumblequery = ('mumbleping',)
  24. soldatquery = ('soldat',)
  25. twquery = ('teeworlds',)
  26. unrealquery = ('protocol-gamespy1', 'unreal')
  27. unreal2query = ('protocol-unreal2', 'unreal2')
  28. unreal3query = ('ut3', 'unreal3')
  29. def __init__(self, arguments):
  30. self.argument = arguments
  31. #
  32. if self.argument.engine in self.sourcequery:
  33. self.query_prompt_string = b'\xFF\xFF\xFF\xFFTSource Engine Query\0'
  34. elif self.argument.engine in self.idtech2query:
  35. self.query_prompt_string = b'\xff\xff\xff\xffstatus\x00'
  36. elif self.argument.engine in self.idtech3query:
  37. self.query_prompt_string = b'\xff\xff\xff\xffgetstatus'
  38. elif self.argument.engine in self.jc2mquery:
  39. self.query_prompt_string = b'\xFE\xFD\x09\x10\x20\x30\x40'
  40. elif self.argument.engine in self.minecraftquery:
  41. self.query_prompt_string = b'\xFE\xFD\x09\x3d\x54\x1f\x93'
  42. elif self.argument.engine in self.minecraftbequery:
  43. self.query_prompt_string = b'\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x12\x34\x56\x78\x00\x00\x00\x00\x00\x00\x00\x00'
  44. elif self.argument.engine in self.mumblequery:
  45. self.query_prompt_string = b'\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07\x08'
  46. elif self.argument.engine in self.soldatquery:
  47. self.query_prompt_string = b'\x69\x00'
  48. elif self.argument.engine in self.twquery:
  49. self.query_prompt_string = b'\x04\x00\x00\xff\xff\xff\xff\x05' + \
  50. bytearray(511)
  51. elif self.argument.engine in self.unrealquery:
  52. self.query_prompt_string = b'\x5C\x69\x6E\x66\x6F\x5C'
  53. elif self.argument.engine in self.unreal2query:
  54. self.query_prompt_string = b'\x79\x00\x00\x00\x00'
  55. elif self.argument.engine in self.unreal3query:
  56. self.query_prompt_string = b'\xFE\xFD\x09\x00\x00\x00\x00'
  57. self.connected = False
  58. self.response = None
  59. @staticmethod
  60. def fatal_error(error_message, error_code=1):
  61. sys.stderr.write('ERROR: ' + str(error_message) + '\n')
  62. sys.exit(error_code)
  63. @staticmethod
  64. def exit_success(success_message=''):
  65. sys.stdout.write('OK: ' + str(success_message) + '\n')
  66. sys.exit(0)
  67. def responding(self):
  68. # Connect.
  69. connection = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  70. connection.settimeout(self.server_response_timeout)
  71. try:
  72. self.connected = connection.connect(
  73. (self.argument.address, int(self.argument.port)))
  74. except socket.timeout:
  75. self.fatal_error('Request timed out', 1)
  76. except Exception:
  77. self.fatal_error('Unable to connect', 1)
  78. # Send.
  79. connection.send(self.query_prompt_string)
  80. # Receive.
  81. try:
  82. self.response = connection.recv(self.default_buffer_length)
  83. except socket.error:
  84. self.fatal_error('Unable to receive', 2)
  85. connection.close()
  86. # Response.
  87. if self.response is None:
  88. self.fatal_error('No response', 3)
  89. if len(self.response) < 5:
  90. sys.exit('Short response.', 3)
  91. else:
  92. self.exit_success(str(self.response))
  93. def parse_args():
  94. parser = argparse.ArgumentParser(
  95. description='Allows querying of various game servers.',
  96. usage='usage: python3 %(prog)s [options]',
  97. add_help=False
  98. )
  99. parser.add_argument(
  100. '-a', '--address',
  101. type=str,
  102. required=True,
  103. help='The IPv4 address of the server.'
  104. )
  105. parser.add_argument(
  106. '-p', '--port',
  107. type=int,
  108. required=True,
  109. help='The IPv4 port of the server.'
  110. )
  111. parser.add_argument(
  112. '-e', '--engine',
  113. metavar='ENGINE',
  114. choices=engine_types,
  115. help='Engine type: ' + ' '.join(engine_types)
  116. )
  117. parser.add_argument(
  118. '-v', '--verbose',
  119. action='store_true',
  120. help='Display verbose output.'
  121. )
  122. parser.add_argument(
  123. '-d', '--debug',
  124. action='store_true',
  125. help='Display debugging output.'
  126. )
  127. parser.add_argument(
  128. '-V', '--version',
  129. action='version',
  130. version='%(prog)s 0.0.1',
  131. help='Display version and exit.'
  132. )
  133. parser.add_argument(
  134. '-h', '--help',
  135. action='help',
  136. help='Display help and exit.'
  137. )
  138. return parser.parse_args()
  139. def main():
  140. arguments = parse_args()
  141. server = gsquery(arguments)
  142. server.responding()
  143. if __name__ == '__main__':
  144. main()