query_gsquery.py 5.4 KB

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