tcp-proxy.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #!/usr/bin/env python3
  2. import sys
  3. import re
  4. from twisted.internet import defer
  5. from twisted.internet import protocol
  6. from twisted.internet import reactor
  7. from twisted.python import log
  8. from twisted.enterprise import adbapi
  9. import pendulum
  10. from config import *
  11. # Connect to:
  12. # HOST = "twgs"
  13. # PORT = 2002
  14. # Listen on:
  15. # LISTEN_PORT = 2002
  16. # LISTEN_ON = "0.0.0.0"
  17. cleaner = re.compile(r"\x1b\[[0-9;]*[A-Zmh]")
  18. makeNL = re.compile(r"\x1b\[[0-9;]*[J]")
  19. def treatAsNL(line):
  20. global makeNL
  21. return makeNL.sub("\n", line)
  22. def cleanANSI(line):
  23. global cleaner
  24. return cleaner.sub("", line)
  25. # return re.sub(r'\x1b\[([0-9,A-Z]{1,2}(;[0-9]{1,2})?(;[0-9]{3})?)?[m|K]?', '', line)
  26. class PlayerProtocol(protocol.Protocol):
  27. def __init__(self):
  28. self.user = None
  29. self.dbinit = False
  30. self.db = None
  31. self.buffer = ""
  32. def connectionMade(self):
  33. log.msg("Client: connected to peer")
  34. self.queue_twgs = self.factory.queue_twgs
  35. self.queue_twgs.get().addCallback(self.serverDataReceived)
  36. def serverDataReceived(self, chunk):
  37. # rlogin looks like this: \x00 password \x00 username \x00 terminal/speed \x00
  38. # b'\x00up2lat3\x00bugz\x00ansi-bbs/115200\x00'
  39. # We're looking for 4 \x00!
  40. # TODO: Line processing, and line cleaning (remove ANSI color codes)
  41. if chunk is False:
  42. self.queue_twgs = None
  43. log.msg("Client: disconnecting from peer")
  44. self.factory.continueTrying = False
  45. self.transport.loseConnection()
  46. else:
  47. # self.buffer += chunk.decode("utf-8", 'ignore')
  48. if self.user is None:
  49. self.buffer += chunk.decode("utf-8", "ignore")
  50. # Ok, process this
  51. # self.buffer += chunk.decode('utf-8')
  52. # We don't have the username yet
  53. parts = self.buffer.split("\x00")
  54. if len(parts) >= 5:
  55. # Got it!
  56. self.user = parts[1]
  57. log.msg("User: {0}".format(self.user))
  58. # Reset buffer -- remove everything before last \x00
  59. zpos = self.buffer.rindex("\x00")
  60. self.buffer = self.buffer[zpos + 1 :]
  61. self.buffer = ""
  62. # init sqlite db using the username
  63. self.factory.getUser(self.user)
  64. # else:
  65. # process the buffer
  66. # Handle backspaces by deleting previous character.
  67. # Send the received data into the linereader for "automatic" line processing.
  68. #
  69. #
  70. # Strip out ANSI color codes
  71. # self.buffer = re.sub(r'\x1b[\d;?\d+m', '', self.buffer)
  72. # Process lines ...
  73. self.transport.write(chunk)
  74. self.queue_twgs.get().addCallback(self.serverDataReceived)
  75. # elif b"$" == chunk:
  76. # self.factory.svr_queue.put(b"HELLO.\r\n")
  77. # self.cli_queue.get().addCallback(self.serverDataReceived)
  78. # elif self.cli_queue:
  79. # log.msg("Client: writing %d bytes to peer" % len(chunk))
  80. # log.msg(">>", repr(chunk))
  81. # self.transport.write(chunk)
  82. # self.cli_queue.get().addCallback(self.serverDataReceived)
  83. # else:
  84. # self.factory.cli_queue.put(chunk)
  85. def dataReceived(self, chunk):
  86. # log.msg("Client: %d bytes received from peer" % len(chunk))
  87. # clean, strip ANSI, etc.
  88. # log.msg("<<", chunk.decode("utf-8", "ignore"))
  89. # log.msg("<<", repr(chunk))
  90. self.factory.queue_client.put(chunk)
  91. def connectionLost(self, why):
  92. log.msg("connectionLost because: %s" % why)
  93. # if self.cli_queue:
  94. # self.cli_queue = None
  95. # log.msg("Client: peer disconnect unexpectedly")
  96. self.factory.queue_client.put(False)
  97. self.queue_twgs = None
  98. self.transport.loseConnection()
  99. class GlueFactory(protocol.ClientFactory):
  100. # class GlueFactory(protocol.Factory):
  101. maxDelay = 10
  102. protocol = PlayerProtocol
  103. def __init__(self, queue_client, queue_twgs, twgs):
  104. self.queue_client = queue_client
  105. self.queue_twgs = queue_twgs
  106. self.twgs = twgs
  107. self.fpRaw = None
  108. self.fpLines = None
  109. def closeIt(self):
  110. log.msg("closeIt")
  111. self.queue_client.put(False)
  112. def getUser(self, user):
  113. log.msg("getUser( %s )" % user)
  114. self.twgs.logUser(user)
  115. # This was needed when I replaced ClientFactory with Factory.
  116. # def clientConnectionLost(self, connector, why):
  117. # log.msg("clientconnectionlost: %s" % why)
  118. # self.queue_client.put(False)
  119. def clientConnectionFailed(self, connector, why):
  120. log.msg("connectionFailed: %s" % why)
  121. self.queue_client.put(b"Sorry! I'm Unable to connect to the game server.\r\n")
  122. # syncterm gets cranky/locks up if we close this here.
  123. # (Because it is still sending rlogin information?)
  124. reactor.callLater(2, self.closeIt)
  125. # ProxyServer is created for each connection
  126. class TWGSServer(protocol.Protocol):
  127. def __init__(self):
  128. self.buffer = ""
  129. self.fpRaw = None
  130. self.fpLines = None
  131. def connectionMade(self):
  132. self.queue_twgs = defer.DeferredQueue()
  133. self.queue_client = defer.DeferredQueue()
  134. self.queue_client.get().addCallback(self.clientDataReceived)
  135. factory = GlueFactory(self.queue_client, self.queue_twgs, self)
  136. reactor.connectTCP(HOST, PORT, factory, 5)
  137. def logUser(self, user):
  138. now = pendulum.now()
  139. filename = now.format("YYYY-MM-DD_HHmm") + "-" + user.lower()
  140. self.fpRaw = open(filename + ".raw", "ab")
  141. self.fpLines = open(filename + ".lines", "a")
  142. # print("Log created:", now.to_rss_string(), "\n", file=self.fpRaw)
  143. print("Log created:", now.to_rss_string(), "\n", file=self.fpLines)
  144. def gotLine(self, line):
  145. # log.msg(">>> [{0}]".format(line.decode("utf-8", "ignore")))
  146. log.msg(">>> [{0}]".format(line))
  147. if self.fpLines is not None:
  148. print(line, file=self.fpLines)
  149. def clientDataReceived(self, chunk):
  150. if chunk is False:
  151. self.transport.loseConnection()
  152. else:
  153. if self.fpRaw is not None:
  154. self.fpRaw.write(chunk)
  155. self.buffer += chunk.decode("utf-8", "ignore")
  156. # Process any backspaces in the buffer
  157. while "\x08" in self.buffer:
  158. part = self.buffer.partition("\x08")
  159. self.buffer = part[0][:-1] + part[2]
  160. # Treat some ANSI codes as a newline (for purposes of Lines)
  161. self.buffer = treatAsNL(self.buffer)
  162. # Break the buffer into lines
  163. while "\n" in self.buffer:
  164. part = self.buffer.partition("\n")
  165. line = part[0].replace("\r", "")
  166. # Clean ANSI codes from the line
  167. line = cleanANSI(line)
  168. self.gotLine(line)
  169. self.buffer = part[2]
  170. # log.msg("Server: writing %d bytes to original client" % len(chunk))
  171. self.transport.write(chunk)
  172. self.queue_client.get().addCallback(self.clientDataReceived)
  173. def dataReceived(self, chunk):
  174. # log.msg("Server: %d bytes received" % len(chunk))
  175. self.queue_twgs.put(chunk)
  176. def connectionLost(self, why):
  177. log.msg("lost connection %s" % why)
  178. self.queue_twgs.put(False)
  179. if self.fpRaw is not None:
  180. self.fpRaw.close()
  181. self.fpRaw = None
  182. if self.fpLines is not None:
  183. if self.buffer != "":
  184. print(self.buffer, file=self.fpLines)
  185. self.fpLines.close()
  186. self.fpLines = None
  187. def connectionFailed(self, why):
  188. log.msg("connectionFailed: %s" % why)
  189. if __name__ == "__main__":
  190. log.startLogging(sys.stdout)
  191. factory = protocol.Factory()
  192. factory.protocol = TWGSServer
  193. reactor.listenTCP(LISTEN_PORT, factory, interface=LISTEN_ON)
  194. reactor.run()