Command handling in extra klassen ausgelagert

This commit is contained in:
trollhase 2013-08-23 14:11:14 +02:00
parent aa0a319234
commit 4f9b38cd78

View file

@ -3,28 +3,38 @@ import threading
import socket
from random import randint
import datetime
class Utils:
@staticmethod
def mask(rights):
m = ""
if rights&4 != 0:
m += "r"
else:
m += "-"
def mask(rights):
m = ""
if rights&4 != 0:
m += "r"
else:
m += "-"
if rights&2 != 0:
m += "w"
else:
m += "-"
if rights&2 != 0:
m += "w"
else:
m += "-"
if rights&1 != 0:
m += "x"
else:
m+= "-"
if rights&1 != 0:
m += "x"
else:
m+= "-"
return m
return m
@staticmethod
def _r( (u,g,o) ):
return ( (u&7) << 6 ) + ( (g&7) << 3 ) + (o&7)
def _r( (u,g,o) ):
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:
def __init__(self,name,owner="nobody",group="nobody",rights=(6,4,4),size=0, iun=1):
@ -33,11 +43,11 @@ class FSNode:
self.group = group
if type(rights) == tuple and len(rights) == 3:
self.rights = _r( rights )
self.rights = Utils._r( rights )
elif type(rights) == int:
self.rights = rights
else:
self.rights = _r( (6,4,4) )
self.rights = Utils._r( (6,4,4) )
self.size = size
@ -86,9 +96,9 @@ class FSNode:
m += "-"
rights = self.get_rights()
m += mask((rights >> 6)&7)
m += mask((rights >> 3)&7)
m += mask(rights&7)
m += Utils.mask((rights >> 6)&7)
m += Utils.mask((rights >> 3)&7)
m += Utils.mask(rights&7)
return m
@ -340,6 +350,10 @@ class SystemTypeMessage(FTPMessage):
def __init__(self, sys="UNIX",typ="L8"):
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
class NListHandler:
def __init__(self, directory, handler):
@ -381,148 +395,246 @@ class RetrieveHandler:
self.node.send_content(socket)
self.handler.send( EndFileRetrieve() )
# State classes
class State:
# CommandHandler classes
class CommandHandler:
cmd = None
def process(self, handler, message):
pass
class MainState(State):
class ForbiddenCommandHandler(CommandHandler):
def process(self, handler, message):
if message.cmd == "QUIT":
handler.debug( "Quitting" )
handler.send( QuitMessage() )
handler.running = False
if hasattr(self,'log'):
handler.log(self.log % message.parameter)
handler.send( OperationNotPermittedMessage() )
class UnknownCommandHandler(CommandHandler):
def process(self, handler, message):
handler.log("Unknown command %s" % message.cmd)
handler.send( InvalidCommandMessage(message.cmd) )
class QuitCommandHandler(CommandHandler):
cmd = "QUIT"
def process(self, handler, message):
handler.log("Client quits")
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.state = AuthorizingState()
handler.send( PasswordRequiredMessage(handler.user) )
class PassCommandHandler(CommandHandler):
cmd = "PASS"
def process(self, handler, message):
password = message.parameter
if handler.authorizer.authorize(handler.user, password, handler):
handler.log("User login %s successful using password %s" % (handler.user,password))
handler.state = AuthorizedState()
handler.send( AuthorizationSuccessfulMessage() )
else:
handler.send( InvalidCommandMessage(message.cmd) )
handler.log("User login %s failed using password %s" % (handler.user, password) )
handler.state = NonAuthorizedState()
handler.send( AuthorizationFailedMessage() )
class NonAuthorizedState(MainState):
class PasvCommandHandler(CommandHandler):
cmd = "PASV"
def process(self, handler, message):
if message.cmd == "USER":
handler.debug("Setting user to %s" % message.parameter)
handler.user = message.parameter
handler.state = AuthorizingState()
handler.send( PasswordRequiredMessage(handler.user) )
else:
MainState.process(self, handler, message)
addr = handler.createPassiveDataConnection()
handler.log("Create passive data connection")
handler.send( EnteringPassiveModeMessage( addr ) )
class AuthorizingState(MainState):
class PortCommandHandler(CommandHandler):
cmd = "PORT"
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
if handler.authorizer.authorize(handler.user, password, handler):
handler.debug("User login %s successful using password %s" % (handler.user,password))
handler.state = AuthorizedState()
handler.send( AuthorizationSuccessfulMessage() )
else:
handler.debug("User login %s failed using password %s" % (handler.user, password) )
handler.state = NonAuthorizedState()
handler.send( AuthorizationFailedMessage() )
else:
MainState.process(self, handler, message)
hp = message.parameter.split(",")
if len(hp) != 6:
handler.send( InvalidParametersMessage() )
return
class AuthorizedState(MainState):
if int(hp[0]) == 10 or int(hp[0]) == 127 or int(hp[0]) == 192: #Prevent local scanning
handler.log( "Attempt to port to local network." )
handler.send( InvalidPortCommandMessage() )
return
addr = ( ".".join(hp[:4]), int(hp[4])<<8+int(hp[5]) )
handler.log( "Creating active data connection to %s:%d" % addr )
handler.createActiveDataConnection(addr)
handler.send( CommandSuccessfulMessage("PORT") )
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):
if message.cmd == "PASV":
addr = handler.createPassiveDataConnection()
handler.debug("Created passive data connection")
handler.send( EnteringPassiveModeMessage( addr ) )
elif message.cmd == "DELE":
handler.debug( "Attempt to delete file %s" % message.parameter )
handler.send( OperationNotPermittedMessage() )
elif message.cmd == "RMD":
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(",")
if len(hp) != 6:
handler.send( InvalidParametersMessage() )
return
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.send( InvalidPortCommandMessage() )
return
addr = ( ".".join(hp[:4]), int(hp[4])<<8+int(hp[5]) )
handler.debug( "Creating active data connection to %s:%d" % addr )
handler.createActiveDataConnection(addr)
handler.send( CommandSuccessfulMessage("PORT") )
elif message.cmd == "LIST":
if handler.runDataConnection(ListHandler(handler.currentDirectory, handler)):
handler.debug("Listing current directory")
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() )
elif message.cmd == "NLST":
if handler.runDataConnection(NListHandler(handler.currentDirectory)):
handler.debug("Listing current directory (simple)")
else:
handler.send( UnableToBuildConnectionMessage() )
elif message.cmd == "CWD":
node = handler.get_node(message.parameter)
if node is None or not node.is_dir():
handler.debug("Error trying to switch into non-existent directory")
handler.send( ChangeDirectoryFailedMessage() )
else:
handler.currentDirectory = node
handler.debug("Changed working directory to "+node.get_absolute_path())
handler.send( ChangeDirectorySuccessfulMessage() )
elif message.cmd == "CDUP":
node = handler.currentDirectory.get_parent_directory()
if node is None or not node.is_dir():
handler.debug("Error trying to switch to non-existent parent directory")
else:
handler.debug("Changed working directory to "+node.get_absolute_path())
handler.currentDirectory = node
else:
handler.send( OperationNotPermittedMessage() )
class ListCommandHandler(CommandHandler):
cmd = "LIST"
def process(self, handler, message):
if handler.runDataConnection(ListHandler(handler.currentDirectory, handler)):
handler.log("Listing current directory")
else:
handler.send( UnableToBuildConnectionMessage() )
class SimpleListCommandHandler(CommandHandler):
cmd = "NLST"
def process(self, handler, message):
if handler.runDataConnection(NListHandler(handler.currentDirectory)):
handler.log("Listing current directory (simple)")
else:
handler.send( UnableToBuildConnectionMessage() )
class ChangeWorkingDirectoryCommandHandler(CommandHandler):
cmd = "CWD"
def process(self, handler, message):
node = handler.get_node(message.parameter)
if node is None or not node.is_dir():
handler.log("Error trying to switch into non-existent directory")
handler.send( ChangeDirectoryFailedMessage() )
else:
handler.currentDirectory = node
handler.log("Changed working directory to "+node.get_absolute_path())
handler.send( ChangeDirectorySuccessfulMessage() )
elif message.cmd == "PWD":
handler.debug( "Printing working directory" )
handler.send( WorkingDirectoryMessage(handler.currentDirectory.get_absolute_path()) )
elif message.cmd == "TYPE":
if message.parameter == "A":
handler.debug("Setting type to ASCII")
handler.send( TypeSetMessage(message.parameter) )
else:
handler.debug("Unrecognized type %s" % message.parameter)
handler.send( InvalidCommandMessage("TYPE") )
elif message.cmd == "NOOP":
handler.debug("No operation")
handler.send( CommandSuccessfulMessage("NOOP") )
elif message.cmd == "SIZE":
handler.debug("Requested size of %s" % message.parameter)
handler.send( CommandNotAllowedInAscii("SIZE") )
elif message.cmd == "ABOR":
handler.debug("Aborting data connection")
handler.send( AbortSuccessfulMessage() )
elif message.cmd == "SYST":
handler.debug("Requested system type")
handler.send( SystemTypeMessage() )
class ChangeDirectoryUpCommandHandler(CommandHandler):
cmd = "CDUP"
def process(self, handler, message):
node = handler.currentDirectory.get_parent_directory()
if node is None or not node.is_dir():
handler.log("Error trying to switch to non-existent parent directory")
else:
MainState.process(self, handler, message)
handler.log("Changed working directory to "+node.get_absolute_path())
handler.currentDirectory = node
handler.send( ChangeDirectorySuccessfulMessage() )
class PrintWorkingDirectoryCommandHandler(CommandHandler):
cmd = "PWD"
def process(self, handler, message):
handler.log( "Printing working directory" )
handler.send( WorkingDirectoryMessage(handler.currentDirectory.get_absolute_path()) )
class SetTypeCommandHandler(CommandHandler):
cmd = "TYPE"
def process(self, handler, message):
if message.parameter == "A":
handler.log("Setting type to ASCII")
handler.send( TypeSetMessage(message.parameter) )
else:
handler.log("Unrecognized type %s" % message.parameter)
handler.send( InvalidCommandMessage("TYPE") )
class NoOperationCommandHandler(CommandHandler):
cmd = "NOOP"
def process(self, handler, message):
handler.log("No operation")
handler.send( CommandSuccessfulMessage("NOOP") )
class SizeCommandHandler(CommandHandler):
cmd = "SIZE"
def process(self, handler, message):
handler.log("Requested size of %s" % message.parameter)
handler.send( CommandNotAllowedInAscii("SIZE") )
class AbortCommandHandler(CommandHandler):
cmd = "ABOR"
def process(self, handler, message):
handler.log("Aborting data connection")
handler.send( AbortSuccessfulMessage() )
class SystemTypeCommandHandler(CommandHandler):
cmd = "SYST"
def process(self, handler, message):
handler.log("Requested system type")
handler.send( SystemTypeMessage() )
# 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:
def __init__(self):
@ -542,8 +654,10 @@ class LineReader:
pos = self.buf.find("\n")
class FTPHandler(SocketServer.BaseRequestHandler):
def debug(self, text):
print ("[%s] " % self.client_address[0]) + text
logger = StdoutLogger()
def log(self, message):
self.logger.log(self,message)
def send(self, message):
self.request.send( str(message) )
@ -594,7 +708,7 @@ class FTPHandler(SocketServer.BaseRequestHandler):
self.state = NonAuthorizedState()
self.debug("Client connected");
self.log("Client connected");
self.send( WelcomeMessage() )
@ -613,7 +727,7 @@ class FTPHandler(SocketServer.BaseRequestHandler):
message = FTPMessage.parse(line)
self.state.process(self,message)
self.debug("Client disconnected")
self.log("Client disconnected")
handler = FTPHandler
handler.authorizer = AnonymousAuthorizer()