import SocketServer import threading import socket from random import randint class DataConnection: def run(self, dchandler): pass class PassiveDataConnection(DataConnection): def __init__(self, port): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.bind(("",port)) threading.Thread(target=self.waitForConnection).start() def waitForConnection(self): self.socket.listen(1) self.connection, _ = self.socket.accept() def run(self, dchandler): threading.Thread(target=self.runHandler, args=(dchandler,) ).start() def runHandler(self, dchandler): dchandler.handle(self.connection) self.connection.close() class ActiveDataConnection(DataConnection): def __init__(self, host, port): self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.socket.connect( (host,port) ) def run(self, dchandler): threading.Thread(target=self.runHandler, args=(dchandler,)).start() def runHandler(self, dchandler): dchandler.handle(self.socket) self.socket.close() class FailAuthorizer: '''Default authorizer. Denys all logins''' def authorize(self, username, password, handler): return False class AnonymousAuthorizer: def authorize(self, username, password, handler): if username in ["ftp","anonymous"]: return True else: return False class FTPMessage: '''Parses and creates FTP Messages''' def __init__(self,cmd,parameter): self.cmd = cmd self.parameter = parameter def generate(self): return "%s %s\r\n" % (self.cmd.upper(), self.parameter) def __str__(self): return self.generate() @staticmethod def parse(line): pos = line.find(" ") if pos == -1: cmd = line param = None else: cmd = line[:pos] param = line[pos+1:] return FTPMessage(cmd.upper(),param) class QuitMessage(FTPMessage): '''Message sent to client when client wants to disconnect''' def __init__(self): FTPMessage.__init__(self,"221","Goodbye.") class InvalidCommandMessage(FTPMessage): '''Message sent to client when server received message it didn't understand''' def __init__(self,cmd): FTPMessage.__init__(self,"500","%s not understood" % cmd) class PasswordRequiredMessage(FTPMessage): '''Message sent to client when it specified a username''' def __init__(self,user): FTPMessage.__init__(self,"331","Password required for %s" % user) class AuthorizationSuccessfulMessage(FTPMessage): '''Message sent to client when authorization was successful''' def __init__(self): FTPMessage.__init__(self,"230","Welcome user.") class AuthorizationFailedMessage(FTPMessage): '''Message sent to client when authorization failed''' def __init__(self): FTPMessage.__init__(self,"530","Login incorrect.") class WelcomeMessage(FTPMessage): '''Message sent to client after client has connected''' def __init__(self): FTPMessage.__init__(self,"220","FTP Server ready") class EnteringPassiveModeMessage(FTPMessage): def __init__(self,addrtuple): FTPMessage.__init__(self,"227","Entering Passive Mode %s." % addrtuple) # Data Connection Handler class HeloHandler: def handle(self, socket): socket.send("Hello World") # State classes class State: def process(self, handler, message): pass class MainState(State): def process(self, handler, message): if message.cmd == "QUIT": handler.send( QuitMessage() ) handler.running = False else: handler.send( InvalidCommandMessage(message.cmd) ) class NonAuthorizedState(MainState): def process(self, handler, message): if message.cmd == "USER": handler.user = message.parameter handler.state = AuthorizingState() handler.send( PasswordRequiredMessage(handler.user) ) else: MainState.process(self, handler, message) class AuthorizingState(MainState): def process(self, handler, message): if message.cmd == "PASS": password = message.parameter if handler.authorizer.authorize(handler.user, password, handler): handler.debug("User login %s successful" % handler.user) handler.state = AuthorizedState() handler.send( AuthorizationSuccessfulMessage() ) else: handler.debug("User login %s failed" % handler.user) handler.state = NonAuthorizedState() handler.send( AuthorizationFailedMessage() ) else: MainState.process(self, handler, message) class AuthorizedState(MainState): def addrtuple(self, ip, port): b = ip.split(".") b.append( str( (port >> 8) & 0xFF ) ) b.append( str( port & 0xFF ) ) return "(" + ",".join(b)+ ")" def process(self, handler, message): if message.cmd == "PASV": port = handler.createPassiveDataConnection() handler.debug("Created passive data connection") handler.send( EnteringPassiveModeMessage( self.addrtuple("127.0.0.1",port) ) ) elif message.cmd == "HELO": handler.debug("Running Helo on data connection") if handler.runDataConnection(HeloHandler()): handler.debug("successful") else: handler.debug("failed") else: MainState.process(self, handler, message) class LineReader: def __init__(self): self.buf = "" def push(self,data): self.buf += data def get_lines(self): pos = self.buf.find("\n") while pos != -1: line = self.buf[:pos] if line[-1] == "\r": line = line[:-2] yield line self.buf = self.buf[pos+1:] pos = self.buf.find("\n") class FTPHandler(SocketServer.BaseRequestHandler): def debug(self, text): print ("[%s] " % self.client_address[0]) + text def send(self, message): self.request.send( str(message) ) def createPassiveDataConnection(self): port = randint(60000,63000) self.connection = PassiveDataConnection(port) return port def createActiveDataConnection(self,port): self.connection = ActiveDataConnection(self.client_address[0],port) def runDataConnection(self, dchandler): if self.connection is None: return False else: self.connection.run(dchandler) self.connection = None return True def handle(self): self.connection = None self.state = NonAuthorizedState() self.debug("Client connected"); self.send( WelcomeMessage() ) linereader = LineReader() self.running = True while self.running: data = self.request.recv(1024) if len(data) == 0: break linereader.push(data) for line in linereader.get_lines(): message = FTPMessage.parse(line) self.state.process(self,message) self.debug("Client disconnected") handler = FTPHandler handler.authorizer = AnonymousAuthorizer() server = SocketServer.ThreadingTCPServer(("",1234),handler) server.serve_forever()