Command handling in extra klassen ausgelagert
This commit is contained in:
parent
aa0a319234
commit
4f9b38cd78
1 changed files with 267 additions and 153 deletions
294
fakeftp.py
294
fakeftp.py
|
@ -3,8 +3,9 @@ import threading
|
||||||
import socket
|
import socket
|
||||||
from random import randint
|
from random import randint
|
||||||
import datetime
|
import datetime
|
||||||
|
class Utils:
|
||||||
def mask(rights):
|
@staticmethod
|
||||||
|
def mask(rights):
|
||||||
m = ""
|
m = ""
|
||||||
if rights&4 != 0:
|
if rights&4 != 0:
|
||||||
m += "r"
|
m += "r"
|
||||||
|
@ -23,9 +24,18 @@ def mask(rights):
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
def _r( (u,g,o) ):
|
@staticmethod
|
||||||
|
def _r( (u,g,o) ):
|
||||||
return ( (u&7) << 6 ) + ( (g&7) << 3 ) + (o&7)
|
return ( (u&7) << 6 ) + ( (g&7) << 3 ) + (o&7)
|
||||||
|
|
||||||
|
class Logger:
|
||||||
|
def log(self, handler, message):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class StdoutLogger:
|
||||||
|
def log(self, handler, message):
|
||||||
|
print "[%s] %s" % (handler.client_address[0],message)
|
||||||
|
|
||||||
class FSNode:
|
class FSNode:
|
||||||
def __init__(self,name,owner="nobody",group="nobody",rights=(6,4,4),size=0, iun=1):
|
def __init__(self,name,owner="nobody",group="nobody",rights=(6,4,4),size=0, iun=1):
|
||||||
self.name = name
|
self.name = name
|
||||||
|
@ -33,11 +43,11 @@ class FSNode:
|
||||||
self.group = group
|
self.group = group
|
||||||
|
|
||||||
if type(rights) == tuple and len(rights) == 3:
|
if type(rights) == tuple and len(rights) == 3:
|
||||||
self.rights = _r( rights )
|
self.rights = Utils._r( rights )
|
||||||
elif type(rights) == int:
|
elif type(rights) == int:
|
||||||
self.rights = rights
|
self.rights = rights
|
||||||
else:
|
else:
|
||||||
self.rights = _r( (6,4,4) )
|
self.rights = Utils._r( (6,4,4) )
|
||||||
|
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
|
@ -86,9 +96,9 @@ class FSNode:
|
||||||
m += "-"
|
m += "-"
|
||||||
|
|
||||||
rights = self.get_rights()
|
rights = self.get_rights()
|
||||||
m += mask((rights >> 6)&7)
|
m += Utils.mask((rights >> 6)&7)
|
||||||
m += mask((rights >> 3)&7)
|
m += Utils.mask((rights >> 3)&7)
|
||||||
m += mask(rights&7)
|
m += Utils.mask(rights&7)
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
@ -340,6 +350,10 @@ class SystemTypeMessage(FTPMessage):
|
||||||
def __init__(self, sys="UNIX",typ="L8"):
|
def __init__(self, sys="UNIX",typ="L8"):
|
||||||
FTPMessage.__init__(self,"215","%s Type: %s" % ( sys.upper(), typ ))
|
FTPMessage.__init__(self,"215","%s Type: %s" % ( sys.upper(), typ ))
|
||||||
|
|
||||||
|
class FatalServerErrorMessage(FTPMessage):
|
||||||
|
def __init__(self):
|
||||||
|
FTPMessage.__init__(self, "421", "Fatal server error")
|
||||||
|
|
||||||
# Data Connection Handler
|
# Data Connection Handler
|
||||||
class NListHandler:
|
class NListHandler:
|
||||||
def __init__(self, directory, handler):
|
def __init__(self, directory, handler):
|
||||||
|
@ -381,148 +395,246 @@ class RetrieveHandler:
|
||||||
self.node.send_content(socket)
|
self.node.send_content(socket)
|
||||||
self.handler.send( EndFileRetrieve() )
|
self.handler.send( EndFileRetrieve() )
|
||||||
|
|
||||||
# State classes
|
# CommandHandler classes
|
||||||
class State:
|
class CommandHandler:
|
||||||
|
cmd = None
|
||||||
def process(self, handler, message):
|
def process(self, handler, message):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class MainState(State):
|
class ForbiddenCommandHandler(CommandHandler):
|
||||||
def process(self, handler, message):
|
def process(self, handler, message):
|
||||||
if message.cmd == "QUIT":
|
if hasattr(self,'log'):
|
||||||
handler.debug( "Quitting" )
|
handler.log(self.log % message.parameter)
|
||||||
handler.send( QuitMessage() )
|
handler.send( OperationNotPermittedMessage() )
|
||||||
handler.running = False
|
|
||||||
else:
|
class UnknownCommandHandler(CommandHandler):
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log("Unknown command %s" % message.cmd)
|
||||||
handler.send( InvalidCommandMessage(message.cmd) )
|
handler.send( InvalidCommandMessage(message.cmd) )
|
||||||
|
|
||||||
class NonAuthorizedState(MainState):
|
class QuitCommandHandler(CommandHandler):
|
||||||
|
cmd = "QUIT"
|
||||||
def process(self, handler, message):
|
def process(self, handler, message):
|
||||||
if message.cmd == "USER":
|
handler.log("Client quits")
|
||||||
handler.debug("Setting user to %s" % message.parameter)
|
handler.send( QuitMessage() )
|
||||||
|
handler.running = False
|
||||||
|
|
||||||
|
class UserCommandHandler(CommandHandler):
|
||||||
|
cmd = "USER"
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log("Setting user to %s" % message.parameter)
|
||||||
handler.user = message.parameter
|
handler.user = message.parameter
|
||||||
handler.state = AuthorizingState()
|
handler.state = AuthorizingState()
|
||||||
handler.send( PasswordRequiredMessage(handler.user) )
|
handler.send( PasswordRequiredMessage(handler.user) )
|
||||||
else:
|
|
||||||
MainState.process(self, handler, message)
|
|
||||||
|
|
||||||
class AuthorizingState(MainState):
|
class PassCommandHandler(CommandHandler):
|
||||||
|
cmd = "PASS"
|
||||||
def process(self, handler, message):
|
def process(self, handler, message):
|
||||||
if message.cmd == "USER":
|
|
||||||
handler.debug("Changing user to %s" % message.parameter)
|
|
||||||
handler.user = message.parameter
|
|
||||||
handler.send( PasswordRequiredMessage(handler.user) )
|
|
||||||
elif message.cmd == "PASS":
|
|
||||||
password = message.parameter
|
password = message.parameter
|
||||||
if handler.authorizer.authorize(handler.user, password, handler):
|
if handler.authorizer.authorize(handler.user, password, handler):
|
||||||
handler.debug("User login %s successful using password %s" % (handler.user,password))
|
handler.log("User login %s successful using password %s" % (handler.user,password))
|
||||||
handler.state = AuthorizedState()
|
handler.state = AuthorizedState()
|
||||||
handler.send( AuthorizationSuccessfulMessage() )
|
handler.send( AuthorizationSuccessfulMessage() )
|
||||||
else:
|
else:
|
||||||
handler.debug("User login %s failed using password %s" % (handler.user, password) )
|
handler.log("User login %s failed using password %s" % (handler.user, password) )
|
||||||
handler.state = NonAuthorizedState()
|
handler.state = NonAuthorizedState()
|
||||||
handler.send( AuthorizationFailedMessage() )
|
handler.send( AuthorizationFailedMessage() )
|
||||||
else:
|
|
||||||
MainState.process(self, handler, message)
|
|
||||||
|
|
||||||
class AuthorizedState(MainState):
|
class PasvCommandHandler(CommandHandler):
|
||||||
|
cmd = "PASV"
|
||||||
def process(self, handler, message):
|
def process(self, handler, message):
|
||||||
if message.cmd == "PASV":
|
|
||||||
addr = handler.createPassiveDataConnection()
|
addr = handler.createPassiveDataConnection()
|
||||||
handler.debug("Created passive data connection")
|
handler.log("Create passive data connection")
|
||||||
handler.send( EnteringPassiveModeMessage( addr ) )
|
handler.send( EnteringPassiveModeMessage( addr ) )
|
||||||
elif message.cmd == "DELE":
|
|
||||||
handler.debug( "Attempt to delete file %s" % message.parameter )
|
class PortCommandHandler(CommandHandler):
|
||||||
handler.send( OperationNotPermittedMessage() )
|
cmd = "PORT"
|
||||||
elif message.cmd == "RMD":
|
def process(self, handler, message):
|
||||||
handler.debug( "Attempt to delete directory %s" % message.parameter )
|
|
||||||
handler.send( OperationNotPermittedMessage() )
|
|
||||||
elif message.cmd == "RNFR":
|
|
||||||
handler.debug( "Attempt to rename %s" % message.parameter )
|
|
||||||
handler.send( OperationNotPermittedMessage() )
|
|
||||||
elif message.cmd == "RNTO":
|
|
||||||
handler.debug( "Attempt to rename to %s" % message.parameter )
|
|
||||||
handler.send( OperationNotPermittedMessage() )
|
|
||||||
elif message.cmd == "STOR":
|
|
||||||
handler.debug( "Attempt to store a file %s" % message.parameter )
|
|
||||||
handler.send( OperationNotPermittedMessage() )
|
|
||||||
elif message.cmd == "RETR":
|
|
||||||
node = handler.get_node(message.parameter)
|
|
||||||
if node.is_file():
|
|
||||||
if handler.runDataConnection( RetrieveHandler(node, handler) ):
|
|
||||||
handler.debug( "Retrieving file %s" % message.parameter )
|
|
||||||
else:
|
|
||||||
handler.send( UnableToBuildConnectionMessage() )
|
|
||||||
else:
|
|
||||||
handler.send( OperationNotPermittedMessage() )
|
|
||||||
elif message.cmd == "PORT":
|
|
||||||
hp = message.parameter.split(",")
|
hp = message.parameter.split(",")
|
||||||
if len(hp) != 6:
|
if len(hp) != 6:
|
||||||
handler.send( InvalidParametersMessage() )
|
handler.send( InvalidParametersMessage() )
|
||||||
return
|
return
|
||||||
|
|
||||||
if int(hp[0]) == 10 or int(hp[0]) == 127 or int(hp[0]) == 192: #Prevent local scanning
|
if int(hp[0]) == 10 or int(hp[0]) == 127 or int(hp[0]) == 192: #Prevent local scanning
|
||||||
handler.debug( "Attempt to port to local network." )
|
handler.log( "Attempt to port to local network." )
|
||||||
handler.send( InvalidPortCommandMessage() )
|
handler.send( InvalidPortCommandMessage() )
|
||||||
return
|
return
|
||||||
|
|
||||||
addr = ( ".".join(hp[:4]), int(hp[4])<<8+int(hp[5]) )
|
addr = ( ".".join(hp[:4]), int(hp[4])<<8+int(hp[5]) )
|
||||||
|
|
||||||
handler.debug( "Creating active data connection to %s:%d" % addr )
|
handler.log( "Creating active data connection to %s:%d" % addr )
|
||||||
|
|
||||||
handler.createActiveDataConnection(addr)
|
handler.createActiveDataConnection(addr)
|
||||||
|
|
||||||
handler.send( CommandSuccessfulMessage("PORT") )
|
handler.send( CommandSuccessfulMessage("PORT") )
|
||||||
elif message.cmd == "LIST":
|
|
||||||
|
class DeleteCommandHandler(ForbiddenCommandHandler):
|
||||||
|
cmd = "DELE"
|
||||||
|
log = "Attempt to delete file %s"
|
||||||
|
|
||||||
|
class RemoveDirCommandHandler(ForbiddenCommandHandler):
|
||||||
|
cmd = "RMD"
|
||||||
|
log = "Attempt to delete directory %s"
|
||||||
|
|
||||||
|
class RenameFromCommandHandler(ForbiddenCommandHandler):
|
||||||
|
cmd = "RNFR"
|
||||||
|
log = "Attempt to rename %s"
|
||||||
|
|
||||||
|
class RenameToCommandHandler(ForbiddenCommandHandler):
|
||||||
|
cmd = "RNTO"
|
||||||
|
log = "Attempt to rename to %s"
|
||||||
|
|
||||||
|
class StoreCommandHandler(ForbiddenCommandHandler):
|
||||||
|
cmd = "STOR"
|
||||||
|
log = "Attempt to store file %s"
|
||||||
|
|
||||||
|
class RetrieveCommandHandler(CommandHandler):
|
||||||
|
cmd = "RETR"
|
||||||
|
def process(self, handler, message):
|
||||||
|
node = handler.get_node(message.parameter)
|
||||||
|
if node.is_file():
|
||||||
|
if handler.runDataConnection( RetrieveHandler(node, handler) ):
|
||||||
|
handler.log( "Retrieving file %s" % message.parameter )
|
||||||
|
else:
|
||||||
|
handler.send( UnableToBuildConnectionMessage() )
|
||||||
|
else:
|
||||||
|
handler.send( OperationNotPermittedMessage() )
|
||||||
|
|
||||||
|
class ListCommandHandler(CommandHandler):
|
||||||
|
cmd = "LIST"
|
||||||
|
def process(self, handler, message):
|
||||||
if handler.runDataConnection(ListHandler(handler.currentDirectory, handler)):
|
if handler.runDataConnection(ListHandler(handler.currentDirectory, handler)):
|
||||||
handler.debug("Listing current directory")
|
handler.log("Listing current directory")
|
||||||
else:
|
else:
|
||||||
handler.send( UnableToBuildConnectionMessage() )
|
handler.send( UnableToBuildConnectionMessage() )
|
||||||
elif message.cmd == "NLST":
|
|
||||||
|
class SimpleListCommandHandler(CommandHandler):
|
||||||
|
cmd = "NLST"
|
||||||
|
def process(self, handler, message):
|
||||||
if handler.runDataConnection(NListHandler(handler.currentDirectory)):
|
if handler.runDataConnection(NListHandler(handler.currentDirectory)):
|
||||||
handler.debug("Listing current directory (simple)")
|
handler.log("Listing current directory (simple)")
|
||||||
else:
|
else:
|
||||||
handler.send( UnableToBuildConnectionMessage() )
|
handler.send( UnableToBuildConnectionMessage() )
|
||||||
elif message.cmd == "CWD":
|
|
||||||
|
class ChangeWorkingDirectoryCommandHandler(CommandHandler):
|
||||||
|
cmd = "CWD"
|
||||||
|
def process(self, handler, message):
|
||||||
node = handler.get_node(message.parameter)
|
node = handler.get_node(message.parameter)
|
||||||
if node is None or not node.is_dir():
|
if node is None or not node.is_dir():
|
||||||
handler.debug("Error trying to switch into non-existent directory")
|
handler.log("Error trying to switch into non-existent directory")
|
||||||
handler.send( ChangeDirectoryFailedMessage() )
|
handler.send( ChangeDirectoryFailedMessage() )
|
||||||
else:
|
else:
|
||||||
handler.currentDirectory = node
|
handler.currentDirectory = node
|
||||||
handler.debug("Changed working directory to "+node.get_absolute_path())
|
handler.log("Changed working directory to "+node.get_absolute_path())
|
||||||
handler.send( ChangeDirectorySuccessfulMessage() )
|
handler.send( ChangeDirectorySuccessfulMessage() )
|
||||||
elif message.cmd == "CDUP":
|
|
||||||
|
class ChangeDirectoryUpCommandHandler(CommandHandler):
|
||||||
|
cmd = "CDUP"
|
||||||
|
def process(self, handler, message):
|
||||||
node = handler.currentDirectory.get_parent_directory()
|
node = handler.currentDirectory.get_parent_directory()
|
||||||
if node is None or not node.is_dir():
|
if node is None or not node.is_dir():
|
||||||
handler.debug("Error trying to switch to non-existent parent directory")
|
handler.log("Error trying to switch to non-existent parent directory")
|
||||||
else:
|
else:
|
||||||
handler.debug("Changed working directory to "+node.get_absolute_path())
|
handler.log("Changed working directory to "+node.get_absolute_path())
|
||||||
handler.currentDirectory = node
|
handler.currentDirectory = node
|
||||||
handler.send( ChangeDirectorySuccessfulMessage() )
|
handler.send( ChangeDirectorySuccessfulMessage() )
|
||||||
elif message.cmd == "PWD":
|
|
||||||
handler.debug( "Printing working directory" )
|
class PrintWorkingDirectoryCommandHandler(CommandHandler):
|
||||||
|
cmd = "PWD"
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log( "Printing working directory" )
|
||||||
handler.send( WorkingDirectoryMessage(handler.currentDirectory.get_absolute_path()) )
|
handler.send( WorkingDirectoryMessage(handler.currentDirectory.get_absolute_path()) )
|
||||||
elif message.cmd == "TYPE":
|
|
||||||
|
class SetTypeCommandHandler(CommandHandler):
|
||||||
|
cmd = "TYPE"
|
||||||
|
def process(self, handler, message):
|
||||||
if message.parameter == "A":
|
if message.parameter == "A":
|
||||||
handler.debug("Setting type to ASCII")
|
handler.log("Setting type to ASCII")
|
||||||
handler.send( TypeSetMessage(message.parameter) )
|
handler.send( TypeSetMessage(message.parameter) )
|
||||||
else:
|
else:
|
||||||
handler.debug("Unrecognized type %s" % message.parameter)
|
handler.log("Unrecognized type %s" % message.parameter)
|
||||||
handler.send( InvalidCommandMessage("TYPE") )
|
handler.send( InvalidCommandMessage("TYPE") )
|
||||||
elif message.cmd == "NOOP":
|
|
||||||
handler.debug("No operation")
|
class NoOperationCommandHandler(CommandHandler):
|
||||||
|
cmd = "NOOP"
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log("No operation")
|
||||||
handler.send( CommandSuccessfulMessage("NOOP") )
|
handler.send( CommandSuccessfulMessage("NOOP") )
|
||||||
elif message.cmd == "SIZE":
|
|
||||||
handler.debug("Requested size of %s" % message.parameter)
|
class SizeCommandHandler(CommandHandler):
|
||||||
|
cmd = "SIZE"
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log("Requested size of %s" % message.parameter)
|
||||||
handler.send( CommandNotAllowedInAscii("SIZE") )
|
handler.send( CommandNotAllowedInAscii("SIZE") )
|
||||||
elif message.cmd == "ABOR":
|
|
||||||
handler.debug("Aborting data connection")
|
class AbortCommandHandler(CommandHandler):
|
||||||
|
cmd = "ABOR"
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log("Aborting data connection")
|
||||||
handler.send( AbortSuccessfulMessage() )
|
handler.send( AbortSuccessfulMessage() )
|
||||||
elif message.cmd == "SYST":
|
|
||||||
handler.debug("Requested system type")
|
class SystemTypeCommandHandler(CommandHandler):
|
||||||
|
cmd = "SYST"
|
||||||
|
def process(self, handler, message):
|
||||||
|
handler.log("Requested system type")
|
||||||
handler.send( SystemTypeMessage() )
|
handler.send( SystemTypeMessage() )
|
||||||
else:
|
|
||||||
MainState.process(self, handler, message)
|
# State classes
|
||||||
|
class State:
|
||||||
|
handlers = []
|
||||||
|
defaulthandler = UnknownCommandHandler()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_handlers(cls):
|
||||||
|
hlds = [] + cls.handlers
|
||||||
|
for b in cls.__bases__:
|
||||||
|
hlds += b.get_handlers()
|
||||||
|
return hlds
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_handler(cls, cmd):
|
||||||
|
for hld in cls.get_handlers():
|
||||||
|
if hld.cmd == cmd:
|
||||||
|
return hld
|
||||||
|
return cls.defaulthandler
|
||||||
|
|
||||||
|
def process(self, handler, message):
|
||||||
|
try:
|
||||||
|
self.get_handler(message.cmd).process(handler,message)
|
||||||
|
except Exception:
|
||||||
|
handler.send( FatalServerErrorMessage() )
|
||||||
|
handler.running = False
|
||||||
|
|
||||||
|
|
||||||
|
class BaseState(State):
|
||||||
|
handlers = [ QuitCommandHandler(), NoOperationCommandHandler() ]
|
||||||
|
|
||||||
|
class NonAuthorizedState(BaseState):
|
||||||
|
handlers = [ UserCommandHandler() ]
|
||||||
|
|
||||||
|
class AuthorizingState(NonAuthorizedState):
|
||||||
|
handlers = [ PassCommandHandler() ]
|
||||||
|
|
||||||
|
class AuthorizedState(BaseState):
|
||||||
|
handlers = [
|
||||||
|
PasvCommandHandler(),
|
||||||
|
PortCommandHandler(),
|
||||||
|
DeleteCommandHandler(),
|
||||||
|
RemoveDirCommandHandler(),
|
||||||
|
RenameToCommandHandler(),
|
||||||
|
RenameFromCommandHandler(),
|
||||||
|
StoreCommandHandler(),
|
||||||
|
RetrieveCommandHandler(),
|
||||||
|
ListCommandHandler(),
|
||||||
|
SimpleListCommandHandler(),
|
||||||
|
ChangeWorkingDirectoryCommandHandler(),
|
||||||
|
ChangeDirectoryUpCommandHandler(),
|
||||||
|
PrintWorkingDirectoryCommandHandler(),
|
||||||
|
SetTypeCommandHandler(),
|
||||||
|
SizeCommandHandler(),
|
||||||
|
AbortCommandHandler(),
|
||||||
|
SystemTypeCommandHandler()
|
||||||
|
]
|
||||||
|
|
||||||
class LineReader:
|
class LineReader:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -542,8 +654,10 @@ class LineReader:
|
||||||
pos = self.buf.find("\n")
|
pos = self.buf.find("\n")
|
||||||
|
|
||||||
class FTPHandler(SocketServer.BaseRequestHandler):
|
class FTPHandler(SocketServer.BaseRequestHandler):
|
||||||
def debug(self, text):
|
logger = StdoutLogger()
|
||||||
print ("[%s] " % self.client_address[0]) + text
|
|
||||||
|
def log(self, message):
|
||||||
|
self.logger.log(self,message)
|
||||||
|
|
||||||
def send(self, message):
|
def send(self, message):
|
||||||
self.request.send( str(message) )
|
self.request.send( str(message) )
|
||||||
|
@ -594,7 +708,7 @@ class FTPHandler(SocketServer.BaseRequestHandler):
|
||||||
|
|
||||||
self.state = NonAuthorizedState()
|
self.state = NonAuthorizedState()
|
||||||
|
|
||||||
self.debug("Client connected");
|
self.log("Client connected");
|
||||||
|
|
||||||
self.send( WelcomeMessage() )
|
self.send( WelcomeMessage() )
|
||||||
|
|
||||||
|
@ -613,7 +727,7 @@ class FTPHandler(SocketServer.BaseRequestHandler):
|
||||||
message = FTPMessage.parse(line)
|
message = FTPMessage.parse(line)
|
||||||
self.state.process(self,message)
|
self.state.process(self,message)
|
||||||
|
|
||||||
self.debug("Client disconnected")
|
self.log("Client disconnected")
|
||||||
|
|
||||||
handler = FTPHandler
|
handler = FTPHandler
|
||||||
handler.authorizer = AnonymousAuthorizer()
|
handler.authorizer = AnonymousAuthorizer()
|
||||||
|
|
Loading…
Reference in a new issue