Просмотр исходного кода

Moved example code into examples directory.

Steve Thielemann 5 лет назад
Родитель
Сommit
1e24349a06

+ 0 - 0
defer.py → examples/defer.py


+ 0 - 0
defer_alreadycallederror.py → examples/defer_alreadycallederror.py


+ 0 - 0
defer_errback.py → examples/defer_errback.py


+ 0 - 0
echo.py → examples/echo.py


+ 0 - 0
fib.py → examples/fib.py


+ 155 - 0
examples/sshsimpleclient.py

@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+
+# Copyright (c) Twisted Matrix Laboratories.
+# See LICENSE for details.
+from __future__ import print_function
+
+from twisted.conch.ssh import transport, userauth, connection, common, keys, channel
+from twisted.internet import defer, protocol, reactor
+from twisted.python import log
+import struct, sys, getpass, os
+
+"""
+Example of using a simple SSH client.
+
+It will try to authenticate with a SSH key or ask for a password.
+
+Re-using a private key is dangerous, generate one.
+For this example you can use:
+
+$ ckeygen -t rsa -f ssh-keys/client_rsa
+"""
+
+# Replace this with your username.
+# Default username and password will match the sshsimpleserver.py
+USER = b'user'
+HOST = 'localhost'
+PORT = 5022
+
+USER = b'bugz'
+HOST = 'bbs.red-green.com'
+PORT = 4022
+
+# SERVER_FINGERPRINT = b'55:55:66:24:6b:03:0e:f1:ec:f8:66:c3:51:df:27:4b'
+
+# Path to RSA SSH keys accepted by the server.
+# CLIENT_RSA_PUBLIC = 'ssh-keys/client_rsa.pub'
+# Set CLIENT_RSA_PUBLIC to empty to not use SSH key auth.
+# CLIENT_RSA_PUBLIC = ''
+# CLIENT_RSA_PRIVATE = 'ssh-keys/client_rsa'
+
+
+class SimpleTransport(transport.SSHClientTransport):
+    def verifyHostKey(self, hostKey, fingerprint):
+        print('Server host key fingerprint: %s' % fingerprint)
+        if SERVER_FINGERPRINT == fingerprint:
+            return defer.succeed(True)
+        else:
+            print('Bad host key. Expecting: %s' % SERVER_FINGERPRINT)
+            return defer.fail(Exception('Bad server key'))
+
+    def connectionSecure(self):
+        self.requestService(
+            SimpleUserAuth(USER,
+                SimpleConnection()))
+
+class SimpleUserAuth(userauth.SSHUserAuthClient):
+
+    def getPassword(self):
+        return defer.succeed(getpass.getpass("%s@%s's password: " % (USER, HOST)))
+
+    def getGenericAnswers(self, name, instruction, questions):
+        print(name)
+        print(instruction)
+        answers = []
+        for prompt, echo in questions:
+            if echo:
+                answer = raw_input(prompt)
+            else:
+                answer = getpass.getpass(prompt)
+            answers.append(answer)
+        return defer.succeed(answers)
+
+
+    def getPublicKey(self):
+        if (
+            not CLIENT_RSA_PUBLIC or
+            not os.path.exists(CLIENT_RSA_PUBLIC) or
+            self.lastPublicKey
+                ):
+            # the file doesn't exist, or we've tried a public key
+            return
+        return keys.Key.fromFile(filename=CLIENT_RSA_PUBLIC)
+
+
+    def getPrivateKey(self):
+        """
+        A deferred can also be returned.
+        """
+        return defer.succeed(keys.Key.fromFile(CLIENT_RSA_PRIVATE))
+
+
+
+class SimpleConnection(connection.SSHConnection):
+    def serviceStarted(self):
+        self.openChannel(TrueChannel(2**16, 2**15, self))
+        self.openChannel(FalseChannel(2**16, 2**15, self))
+        self.openChannel(CatChannel(2**16, 2**15, self))
+
+
+
+class TrueChannel(channel.SSHChannel):
+    name = b'session' # needed for commands
+
+    def openFailed(self, reason):
+        print('true failed', reason)
+
+    def channelOpen(self, ignoredData):
+        self.conn.sendRequest(self, 'exec', common.NS('true'))
+
+    def request_exit_status(self, data):
+        status = struct.unpack('>L', data)[0]
+        print('true status was: %s' % status)
+        self.loseConnection()
+
+class FalseChannel(channel.SSHChannel):
+    name = b'session'
+
+    def openFailed(self, reason):
+        print('false failed', reason)
+
+    def channelOpen(self, ignoredData):
+        self.conn.sendRequest(self, 'exec', common.NS('false'))
+
+    def request_exit_status(self, data):
+        status = struct.unpack('>L', data)[0]
+        print('false status was: %s' % status)
+        self.loseConnection()
+
+class CatChannel(channel.SSHChannel):
+    name = b'session'
+
+    def openFailed(self, reason):
+        print('echo failed', reason)
+
+    def channelOpen(self, ignoredData):
+        self.data = b''
+        d = self.conn.sendRequest(self, 'exec', common.NS('cat'), wantReply = 1)
+        d.addCallback(self._cbRequest)
+
+    def _cbRequest(self, ignored):
+        self.write(b'hello conch\n')
+        self.conn.sendEOF(self)
+
+    def dataReceived(self, data):
+        self.data += data
+
+    def closed(self):
+        print('got data from cat: %s' % repr(self.data))
+        self.loseConnection()
+        reactor.stop()
+
+
+log.startLogging(sys.stdout)
+protocol.ClientCreator(reactor, SimpleTransport).connectTCP(HOST, PORT)
+reactor.run()

+ 0 - 0
task.py → examples/task.py


+ 176 - 0
examples/tcp-proxy-tried-lineReceiver.py

@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+
+from twisted.internet import defer
+from twisted.internet import protocol
+from twisted.internet import reactor
+from twisted.python import log
+from twisted.enterprise import adbapi
+from twisted.protocols.basic import LineReceiver
+
+# Connect to:
+HOST = "127.0.0.1"
+PORT = 2002
+# Listen on:
+LISTEN_PORT = 9999
+LISTEN_ON = "127.0.0.1"
+
+
+class LineRecv(LineReceiver):
+    def __init__(self, notify):
+        self.delimiter = b"\n"  # \r"
+        self.notify = notify
+
+    def lineReceived(self, line):
+        log.msg("LR:", line)
+        self.notify.gotLine(line)
+
+
+class PlayerProtocol(protocol.Protocol):
+    def __init__(self):
+        self.user = None
+        self.dbinit = False
+        self.db = None
+        self.buffer = ""
+        self.linereader = LineRecv(self)
+        self.linereader.connectionMade()
+
+    def connectionMade(self):
+        log.msg("Client: connected to peer")
+        self.queue_twgs = self.factory.queue_twgs
+        self.queue_twgs.get().addCallback(self.serverDataReceived)
+
+    def gotLine(self, line):
+        log.msg(">>> [{0}]".format(line.decode("utf-8", "ignore")))
+
+    def serverDataReceived(self, chunk):
+        # rlogin looks like this:  \x00 password \x00 username \x00 terminal/speed \x00
+        # b'\x00up2lat3\x00bugz\x00ansi-bbs/115200\x00'
+        # We're looking for 4 \x00!
+        # TODO: Line processing, and line cleaning (remove ANSI color codes)
+        if chunk is False:
+            self.queue_twgs = None
+            log.msg("Client: disconnecting from peer")
+            self.factory.continueTrying = False
+            self.transport.loseConnection()
+        else:
+            self.buffer += chunk.decode("utf-8")
+            if self.user is None:
+                # Ok, process this
+                # self.buffer += chunk.decode('utf-8')
+                # We don't have the username yet
+                parts = self.buffer.split("\x00")
+                if len(parts) >= 5:
+                    # Got it!
+                    self.user = parts[1]
+                    log.msg("User: {0}".format(self.user))
+                    # Reset buffer -- remove everything before last \x00
+                    zpos = self.buffer.rindex("\x00")
+                    self.buffer = self.buffer[zpos + 1 :]
+                    self.buffer = ""
+                    # init sqlite db using the username
+            else:
+                # process the buffer
+                # Handle backspaces by deleting previous character.
+
+                # Send the received data into the linereader for "automatic" line processing.
+                self.linereader.dataReceived(chunk)
+
+                #
+                #
+                # Strip out ANSI color codes
+                # self.buffer = re.sub(r'\x1b[\d;?\d+m', '', self.buffer)
+                # Process lines ...
+
+            self.transport.write(chunk)
+            self.queue_twgs.get().addCallback(self.serverDataReceived)
+
+        # elif b"$" == chunk:
+        #     self.factory.svr_queue.put(b"HELLO.\r\n")
+        #     self.cli_queue.get().addCallback(self.serverDataReceived)
+        # elif self.cli_queue:
+        #     log.msg("Client: writing %d bytes to peer" % len(chunk))
+        #     log.msg(">>", repr(chunk))
+        #     self.transport.write(chunk)
+        #     self.cli_queue.get().addCallback(self.serverDataReceived)
+        # else:
+        #     self.factory.cli_queue.put(chunk)
+
+    def dataReceived(self, chunk):
+        # log.msg("Client: %d bytes received from peer" % len(chunk))
+        # clean, strip ANSI, etc.
+
+        # log.msg("<<", chunk.decode("utf-8", "ignore"))
+        log.msg("<<", repr(chunk))
+        self.factory.queue_client.put(chunk)
+
+    def connectionLost(self, why):
+        log.msg("connectionLost because: %s" % why)
+        # if self.cli_queue:
+        #     self.cli_queue = None
+        #     log.msg("Client: peer disconnect unexpectedly")
+        self.factory.queue_client.put(False)
+        self.queue_twgs = None
+        self.transport.loseConnection()
+
+
+class GlueFactory(protocol.ClientFactory):
+    maxDelay = 10
+    protocol = PlayerProtocol
+
+    def __init__(self, queue_client, queue_twgs):
+        self.queue_client = queue_client
+        self.queue_twgs = queue_twgs
+
+    def closeIt(self):
+        log.msg("closeIt")
+        self.queue_client.put(False)
+
+    def clientConnectionFailed(self, connector, why):
+        log.msg("connectionFailed: %s" % why)
+        self.queue_client.put(b"Sorry!  I'm Unable to connect to the game server.\r\n")
+        # syncterm gets cranky/locks up if we close this here.
+        # (Because it is still sending rlogin information?)
+        reactor.callLater(2, self.closeIt)
+
+
+# ProxyServer is created for each connection
+
+
+class TWGSServer(protocol.Protocol):
+    def connectionMade(self):
+        self.queue_twgs = defer.DeferredQueue()
+        self.queue_client = defer.DeferredQueue()
+        self.queue_client.get().addCallback(self.clientDataReceived)
+
+        factory = GlueFactory(self.queue_client, self.queue_twgs)
+        reactor.connectTCP(HOST, PORT, factory, 5)
+
+    def clientDataReceived(self, chunk):
+        if chunk is False:
+            self.transport.loseConnection()
+        else:
+            # log.msg("Server: writing %d bytes to original client" % len(chunk))
+            self.transport.write(chunk)
+            self.queue_client.get().addCallback(self.clientDataReceived)
+
+    def dataReceived(self, chunk):
+        # log.msg("Server: %d bytes received" % len(chunk))
+        self.queue_twgs.put(chunk)
+
+    def connectionLost(self, why):
+        log.msg("lost connection %s" % why)
+        self.queue_twgs.put(False)
+
+    def connectionFailed(self, why):
+        log.msg("connectionFailed: %s" % why)
+
+
+if __name__ == "__main__":
+    log.startLogging(sys.stdout)
+    factory = protocol.Factory()
+    factory.protocol = TWGSServer
+    reactor.listenTCP(LISTEN_PORT, factory, interface=LISTEN_ON)
+    reactor.run()

+ 327 - 0
examples/tcp-proxy2-rev0.py

@@ -0,0 +1,327 @@
+#!/usr/bin/env python3
+
+import sys
+import re
+
+from twisted.internet import defer
+from twisted.internet import protocol
+from twisted.internet import reactor
+from twisted.python import log
+from twisted.python.logfile import DailyLogFile
+
+# from twisted.enterprise import adbapi
+import pendulum
+from subprocess import check_output
+
+# This isn't the best configuration, but it's simple
+# and works.  Mostly.
+
+try:
+    from config_dev import *
+except ModuleNotFoundError:
+    from config import *
+
+# from config import *
+
+# Connect to:
+# HOST = "twgs"
+# PORT = 2002
+# Listen on:
+# LISTEN_PORT = 2002
+# LISTEN_ON = "0.0.0.0"
+
+version = check_output(
+    [
+        "git",
+        "describe",
+        "--long",
+        "--tags",
+        #        "--dirty",
+        "--always",
+        "--match",
+        "v[0-9]\.[0-9]\.[0-9]",
+    ],
+    universal_newlines=True,
+).strip()
+
+# Cleans all ANSI
+cleaner = re.compile(r"\x1b\[[0-9;]*[A-Zmh]")
+# Looks for ANSI (that should be considered to be a newline)
+makeNL = re.compile(r"\x1b\[[0-9;]*[J]")
+
+
+def treatAsNL(line):
+    """ Replace any ANSI codes that would be better understood as newlines. """
+    global makeNL
+    return makeNL.sub("\n", line)
+
+
+def cleanANSI(line):
+    """ Remove all ANSI codes. """
+    global cleaner
+    return cleaner.sub("", line)
+    # return re.sub(r'\x1b\[([0-9,A-Z]{1,2}(;[0-9]{1,2})?(;[0-9]{3})?)?[m|K]?', '', line)
+
+
+class Game(protocol.Protocol):
+    def __init__(self):
+        # user is rlogin username
+        self.user = None
+        # buffer is used to capture the rlogin username
+        self.buffer = ""
+
+    def connectionMade(self):
+        log.msg("Client: connected to peer")
+        self.queue_player = self.factory.queue_player
+        self.queue_game = self.factory.queue_game
+        self.setPlayerReceived()
+        # self.queue_twgs.get().addCallback(self.serverDataReceived)
+
+    def setPlayerReceived(self):
+        """ Get deferred from client queue, callback clientDataReceived. """
+        self.queue_player.get().addCallback(self, playerDataReceived)
+
+    def playerDataReceived(self, chunk):
+        # rlogin looks like this:  \x00 password \x00 username \x00 terminal/speed \x00
+        # b'\x00up2lat3\x00bugz\x00ansi-bbs/115200\x00'
+        # We're looking for 4 \x00!
+        # TODO: Line processing, and line cleaning (remove ANSI color codes)
+        if chunk is False:
+            self.queue_twgs = None
+            log.msg("Client: disconnecting from peer")
+            self.factory.continueTrying = False
+            self.transport.loseConnection()
+        else:
+            if self.user is None:
+                # Decode the rlogin data
+                self.buffer += chunk.decode("utf-8", "ignore")
+
+                # Ok, process this
+                # self.buffer += chunk.decode('utf-8')
+                # We don't have the username yet
+                parts = self.buffer.split("\x00")
+                if len(parts) >= 5:
+                    # Got it!
+                    self.user = parts[1]
+                    log.msg("User: {0}".format(self.user))
+                    # Reset buffer -- remove everything before last \x00
+                    zpos = self.buffer.rindex("\x00")
+                    self.buffer = self.buffer[zpos + 1 :]
+                    self.buffer = ""
+                    # init sqlite db using the username
+                    self.factory.getUser(self.user)
+
+            # Pass received data to the server
+            self.transport.write(chunk)
+            self.setPlayerReceived()
+            # self.queue_twgs.get().addCallback(self.serverDataReceived)
+
+    def dataReceived(self, chunk):
+        # log.msg("Client: %d bytes received from peer" % len(chunk))
+        # clean, strip ANSI, etc.
+
+        # log.msg("<<", chunk.decode("utf-8", "ignore"))
+        # log.msg("<<", repr(chunk))
+        # self.factory.queue_client.put(chunk)
+        # self.queue_game.put(chunk)
+        self.queue_player.put(chunk)
+
+    def connectionLost(self, why):
+        log.msg("Game connectionLost because: %s" % why)
+        self.queue_game.put(False)
+
+        # self.factory.queue_client.put(False)
+        # self.queue_twgs = None
+        self.transport.loseConnection()
+
+
+class GlueFactory(protocol.ClientFactory):
+    # class GlueFactory(protocol.Factory):
+    maxDelay = 10
+    protocol = Game
+
+    def __init__(self, twgs):
+        self.twgs = twgs
+        self.queue_player = twgs.queue_player
+        self.queue_game = twgs.queue_game
+
+    def closeIt(self):
+        log.msg("closeIt")
+        self.queue_player.put(False)
+
+    def getUser(self, user):
+        log.msg("getUser( %s )" % user)
+        self.twgs.logUser(user)
+
+    # This was needed when I replaced ClientFactory with Factory.
+    # def clientConnectionLost(self, connector, why):
+    #     log.msg("clientconnectionlost: %s" % why)
+    #     self.queue_client.put(False)
+
+    def clientConnectionFailed(self, connector, why):
+        log.msg("connection to game failed: %s" % why)
+        self.queue_player.put(b"Sorry!  I'm Unable to connect to the game server.\r\n")
+
+        # syncterm gets cranky/locks up if we close this here.
+        # (Because it is still sending rlogin information?)
+        reactor.callLater(2, self.closeIt)
+
+
+class Player(protocol.Protocol):
+    def __init__(self):
+        self.buffer = ""
+        self.fpRaw = None
+        self.fpLines = None
+        self.action = None
+        self.username = ""
+        self.game = ""
+        self.passon = True
+
+    def connectionMade(self):
+        """ connected, setup queues. """
+        self.queue_player = defer.DeferredQueue()
+        self.queue_game = defer.DeferredQueue()
+        self.setGameReceived()
+        # self.action = ProxyAction(self)
+
+        factory = GlueFactory(self)
+        # Make connection to the game server
+        reactor.connectTCP(HOST, PORT, factory, 5)
+
+    def setGameReceived(self):
+        """ Get deferred from client queue, callback clientDataReceived. """
+        self.queue_game.get().addCallback(self.gameDataReceived)
+
+    def logUser(self, user):
+        """ We have the username. """
+        now = pendulum.now()
+        self.username = user
+        filename = now.format("YYYY-MM-DD_HHmm") + "-" + user.lower()
+
+        # Are we saving RAW output?
+        if RAW:
+            self.fpRaw = open(filename + ".raw", "ab")
+        self.fpLines = open(filename + ".lines", "a")
+        print("Log created:", now.to_rss_string(), "\n", file=self.fpLines)
+
+    def setGame(self, game):
+        """ We have the game (A-P) they are playing. """
+        if self.game != game:
+            log.msg("USER {0} ENTERED {1}".format(self.username, self.game))
+            self.data = {}
+        self.game = game
+
+    def gotLine(self, line):
+        """ We got a line from the server.
+
+        This is ANSI filtered.  
+        Backspaces have removed the character from the line.
+        The line is unicode.  We don't need to decode it. ;)
+
+        """
+        log.msg(">>> [{0}]".format(line))
+        if self.fpLines is not None:
+            print(line, file=self.fpLines)
+
+        if "TWGS v2.20b" in line:
+            if "www.eisonline.com" in line:
+                # Must not be unicode
+
+                # Is there a way to NOT have this logged?
+                self.queue_client.put(
+                    (
+                        b"TWGS Proxy build "
+                        + version.encode()
+                        + b" is active. \x1b[1;34m~\x1b[0m to activate.\n\r\n\r",
+                    )
+                )
+        if "TradeWars Game Server" in line and "Copyright (C) EIS" in line:
+            # We are not in a game
+            self.game = ""
+        if "Selection (? for menu): " in line:
+            game = line[-1]
+            if game >= "A" and game < "Q":
+                self.setGame(game)
+        # If we're not passing it on to the user, we better be looking at it.
+        if self.action and not self.passon:
+            self.action.server(line)
+
+    def gameDataReceived(self, chunk):
+        """ Data received from the client/player. """
+        if chunk is False:
+            self.transport.loseConnection()
+        else:
+            if type(chunk) is tuple:
+                # Special case where we want to send something to the user
+                # but not have it logged.
+                self.transport.write(chunk[0])
+                self.setGameReceived()
+            else:
+                if self.fpRaw is not None:
+                    self.fpRaw.write(chunk)
+                self.buffer += chunk.decode("utf-8", "ignore")
+
+                # Process any backspaces in the buffer
+
+                while "\x08" in self.buffer:
+                    part = self.buffer.partition("\x08")
+                    self.buffer = part[0][:-1] + part[2]
+
+                # Treat some ANSI codes as a newline (for purposes of Lines)
+                self.buffer = treatAsNL(self.buffer)
+                # I think I need something else in here.  When something enters or leaves the sector
+                # The message isn't shown on it's own line.  I think they are using ANSI codes to
+                # clear the line.  (Which certainly would be faster!)
+
+                # Break the buffer into lines
+
+                while "\n" in self.buffer:
+                    part = self.buffer.partition("\n")
+                    line = part[0].replace("\r", "")
+                    # Clean ANSI codes from the line
+                    line = cleanANSI(line)
+                    self.gotLine(line)
+                    self.buffer = part[2]
+
+                # log.msg("Server: writing %d bytes to original client" % len(chunk))
+                if self.passon:
+                    self.transport.write(chunk)
+
+                self.setGameReceived()
+
+    def dataReceived(self, chunk):
+        if self.action and self.action.isActive():
+            # Do something completely different here
+            self.action.received(chunk)
+        else:
+            # Did player activate hotkey?
+            if chunk == b"~":
+                self.action.activate(self.buffer)
+            else:
+                self.queue_game.put(chunk)
+
+    def connectionLost(self, why):
+        log.msg("lost connection %s" % why)
+        self.queue_game.put(False)
+
+        # Close log files, if open
+        if self.fpRaw is not None:
+            self.fpRaw.close()
+            self.fpRaw = None
+        if self.fpLines is not None:
+            if self.buffer != "":
+                print(self.buffer, file=self.fpLines)
+            self.fpLines.close()
+            self.fpLines = None
+
+    def connectionFailed(self, why):
+        log.msg("connectionFailed: %s" % why)
+
+
+if __name__ == "__main__":
+    log.startLogging(DailyLogFile("proxy.log", "."))
+    log.msg("This is version: %s" % version)
+    factory = protocol.Factory()
+    factory.protocol = Player
+    reactor.listenTCP(LISTEN_PORT, factory, interface=LISTEN_ON)
+    reactor.run()

+ 0 - 0
tcp-proxy2.py → examples/tcp-proxy2.py


+ 0 - 0
tcp-proxy3.py → examples/tcp-proxy3.py


+ 0 - 0
try.py → examples/try.py


+ 0 - 0
twisted_basic_server.py → examples/twisted_basic_server.py