801 lines
20 KiB
Python
801 lines
20 KiB
Python
import SocketServer
|
|
import threading
|
|
import socket
|
|
from random import randint
|
|
import datetime
|
|
|
|
SocketServer.ThreadingTCPServer.address_family = socket.AF_INET
|
|
|
|
class Utils:
|
|
@staticmethod
|
|
def mask(rights):
|
|
m = ""
|
|
if rights&4 != 0:
|
|
m += "r"
|
|
else:
|
|
m += "-"
|
|
|
|
if rights&2 != 0:
|
|
m += "w"
|
|
else:
|
|
m += "-"
|
|
|
|
if rights&1 != 0:
|
|
m += "x"
|
|
else:
|
|
m+= "-"
|
|
|
|
return m
|
|
|
|
@staticmethod
|
|
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):
|
|
self.name = name
|
|
self.owner = owner
|
|
self.group = group
|
|
|
|
if type(rights) == tuple and len(rights) == 3:
|
|
self.rights = Utils._r( rights )
|
|
elif type(rights) == int:
|
|
self.rights = rights
|
|
else:
|
|
self.rights = Utils._r( (6,4,4) )
|
|
|
|
self.size = size
|
|
|
|
self.parent = None
|
|
self.iun = iun
|
|
|
|
def is_dir(self):
|
|
return False
|
|
|
|
def is_file(self):
|
|
return False
|
|
|
|
def get_name(self):
|
|
return self.name
|
|
|
|
def get_size(self):
|
|
return self.size
|
|
|
|
def get_rights(self):
|
|
return self.rights
|
|
|
|
def get_last_change(self):
|
|
return datetime.date(2013,8,22)
|
|
|
|
#Thanks to a mumble session with lod :3
|
|
def get_boner(self):
|
|
return "large"
|
|
|
|
def get_parent_directory(self):
|
|
return self.parent
|
|
|
|
def get_owner(self):
|
|
return self.owner
|
|
|
|
def get_group(self):
|
|
return self.group
|
|
|
|
def get_imaginary_unicorn_number(self):
|
|
return self.iun
|
|
|
|
def get_mask(self):
|
|
m = ""
|
|
if self.is_dir():
|
|
m += "d"
|
|
else:
|
|
m += "-"
|
|
|
|
rights = self.get_rights()
|
|
m += Utils.mask((rights >> 6)&7)
|
|
m += Utils.mask((rights >> 3)&7)
|
|
m += Utils.mask(rights&7)
|
|
|
|
return m
|
|
|
|
def get_absolute_path(self):
|
|
if self.parent is None:
|
|
if self.is_dir():
|
|
return "/"
|
|
else:
|
|
return self.get_name()
|
|
else:
|
|
p = self.parent.get_absolute_path()
|
|
if p[-1] != "/":
|
|
p+= "/"
|
|
p+=self.get_name()
|
|
return p
|
|
|
|
class Directory(FSNode):
|
|
def __init__(self,name,owner="nobody",group="nobody",rights=(6,4,4),size=4096,files=[]):
|
|
FSNode.__init__(self, name=name, owner=owner, group=group,rights=rights, size=size, iun=randint(2,10))
|
|
|
|
self.files = files
|
|
|
|
for f in self.files:
|
|
f.parent = self
|
|
|
|
def is_dir(self):
|
|
return True
|
|
|
|
def get_node(self,name):
|
|
for f in self.files:
|
|
if f.name == name:
|
|
return f
|
|
return None
|
|
|
|
class File(FSNode):
|
|
def __init__(self,name,owner="nobody",group="nobody",rights=384,size=4096, content=None):
|
|
FSNode.__init__(self, name=name, owner=owner, group=group,rights=rights, size=size, iun=1)
|
|
if content is None:
|
|
content = TextFileContent()
|
|
self.content = content
|
|
|
|
def is_file(self):
|
|
return True
|
|
|
|
def send_content(self,socket):
|
|
self.content.send_content(socket)
|
|
|
|
class FileContent:
|
|
def send_content(self, socket):
|
|
pass
|
|
|
|
class TextFileContent(FileContent):
|
|
def __init__(self, text=""):
|
|
self.text = text
|
|
|
|
def send_content(self, socket):
|
|
socket.send(self.text)
|
|
|
|
class FileFileContent(FileContent):
|
|
def __init__(self, filepath):
|
|
self.filepath = filepath
|
|
|
|
def send_content(self, socket):
|
|
fh = file(self.filepath,"r")
|
|
buf = fh.read(1024)
|
|
while len(buf) > 0:
|
|
socket.send(buf)
|
|
buf = fh.read(1024)
|
|
|
|
class HoneypotFileContent(FileContent):
|
|
def send_content(self, socket):
|
|
for i in range(4000):
|
|
socket.send('HONEYPOT');
|
|
|
|
class URandomFileContent(FileFileContent):
|
|
def __init__(self):
|
|
FileFileContent.__init__(self, "/dev/urandom")
|
|
|
|
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))
|
|
self.connection = None
|
|
threading.Thread(target=self.waitForConnection).start()
|
|
|
|
def waitForConnection(self):
|
|
self.socket.listen(1)
|
|
self.connection, _ = self.socket.accept()
|
|
|
|
def run(self, dchandler):
|
|
while self.connection is None:
|
|
pass
|
|
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, addr):
|
|
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.socket.connect( addr )
|
|
|
|
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,(ip,port)):
|
|
addr = ip.split(".")
|
|
addr.append( str( (port >> 8) & 0xFF ) )
|
|
addr.append( str( port & 0xFF ) )
|
|
|
|
FTPMessage.__init__(self,"227","Entering Passive Mode (%s)." % (",".join(addr) ) )
|
|
|
|
class BeginDirectoryListingMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"150","Here comes the directory listing.")
|
|
|
|
class EndDirectoryListingMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"226","Directory send OK.")
|
|
|
|
class WorkingDirectoryMessage(FTPMessage):
|
|
def __init__(self, workingDirectory):
|
|
FTPMessage.__init__(self,"257","\"%s\" is the current directory" % workingDirectory)
|
|
|
|
class ChangeDirectorySuccessfulMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"250","command successful")
|
|
|
|
class ChangeDirectoryFailedMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"550","No such directory")
|
|
|
|
class InvalidParametersMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"504","Command not implemented for that parameter")
|
|
|
|
class InvalidPortCommandMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"500","Illegal port command")
|
|
|
|
class CommandSuccessfulMessage(FTPMessage):
|
|
def __init__(self,cmd):
|
|
FTPMessage.__init__(self,"200","%s command successful" % cmd)
|
|
|
|
class OperationNotPermittedMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"550","Operation not permitted")
|
|
|
|
class TypeSetMessage(FTPMessage):
|
|
def __init__(self,val):
|
|
FTPMessage.__init__(self,"200","Type set to %s" % val)
|
|
|
|
class BeginFileRetrieve(FTPMessage):
|
|
def __init__(self,node):
|
|
FTPMessage.__init__(self,"150","Opening ASCII mode data connection for %s (%d bytes)" % (node.get_name(),node.get_size()))
|
|
|
|
class EndFileRetrieve(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"226","Transfer complete")
|
|
|
|
class UnableToBuildConnectionMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"425","Unable to build data connection")
|
|
|
|
class CommandNotAllowedInAscii(FTPMessage):
|
|
def __init__(self,cmd):
|
|
FTPMessage.__init__(self,"550","%s not allowed in ASCII mode")
|
|
|
|
class AbortSuccessfulMessage(FTPMessage):
|
|
def __init__(self):
|
|
FTPMessage.__init__(self,"226","Abort successful")
|
|
|
|
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):
|
|
self.directory = directory
|
|
self.handler = handler
|
|
|
|
def handle(self, socket):
|
|
for f in self.directory.files:
|
|
socket.send(f.get_name()+"\n")
|
|
|
|
class ListHandler:
|
|
def __init__(self, directory, handler):
|
|
self.directory = directory
|
|
self.handler = handler
|
|
|
|
def handle(self, socket):
|
|
self.handler.send( BeginDirectoryListingMessage() )
|
|
for f in self.directory.files:
|
|
socket.send( "%10s %3d %-8s %-8s %8d %3s %2d %5d %s\n" % (
|
|
f.get_mask(),
|
|
f.get_imaginary_unicorn_number(),
|
|
f.get_owner(),
|
|
f.get_group(),
|
|
f.get_size(),
|
|
f.get_last_change().strftime("%b"),
|
|
f.get_last_change().day,
|
|
f.get_last_change().year,
|
|
f.get_name()
|
|
) )
|
|
self.handler.send( EndDirectoryListingMessage() )
|
|
|
|
class RetrieveHandler:
|
|
def __init__(self, node, handler):
|
|
self.node = node
|
|
self.handler = handler
|
|
|
|
def handle(self, socket):
|
|
self.handler.send( BeginFileRetrieve(self.node) )
|
|
self.node.send_content(socket)
|
|
self.handler.send( EndFileRetrieve() )
|
|
|
|
# CommandHandler classes
|
|
class CommandHandler:
|
|
cmd = None
|
|
def process(self, handler, message):
|
|
pass
|
|
|
|
class ForbiddenCommandHandler(CommandHandler):
|
|
def process(self, handler, message):
|
|
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.log("User login %s failed using password %s" % (handler.user, password) )
|
|
handler.state = NonAuthorizedState()
|
|
handler.send( AuthorizationFailedMessage() )
|
|
|
|
class PasvCommandHandler(CommandHandler):
|
|
cmd = "PASV"
|
|
def process(self, handler, message):
|
|
addr = handler.createPassiveDataConnection()
|
|
handler.log("Create passive data connection")
|
|
handler.send( EnteringPassiveModeMessage( addr ) )
|
|
|
|
class PortCommandHandler(CommandHandler):
|
|
cmd = "PORT"
|
|
def process(self, handler, message):
|
|
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.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):
|
|
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)):
|
|
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() )
|
|
|
|
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:
|
|
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" or message.parameter == "I":
|
|
handler.log("Setting type to " + message.parameter)
|
|
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):
|
|
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[:-1]
|
|
yield line
|
|
self.buf = self.buf[pos+1:]
|
|
pos = self.buf.find("\n")
|
|
|
|
class FTPHandler(SocketServer.BaseRequestHandler):
|
|
logger = StdoutLogger()
|
|
rootDirectory = Directory("")
|
|
authorizer = FailAuthorizer()
|
|
|
|
def log(self, message):
|
|
self.logger.log(self,message)
|
|
|
|
def send(self, message):
|
|
self.request.send( str(message) )
|
|
|
|
def createPassiveDataConnection(self):
|
|
port = randint(60000,63000)
|
|
self.connection = PassiveDataConnection(port)
|
|
myaddr = self.request.getsockname()
|
|
return (myaddr[0],port)
|
|
|
|
def createActiveDataConnection(self,addr):
|
|
self.connection = ActiveDataConnection(addr)
|
|
|
|
def get_node(self,path):
|
|
if path == "/":
|
|
return self.rootDirectory
|
|
|
|
if path[0] == "/":
|
|
cnode = self.rootDirectory
|
|
path = path[1:]
|
|
else:
|
|
cnode = self.currentDirectory
|
|
|
|
if path[-1] == "/":
|
|
path = path[:-1]
|
|
|
|
parts = path.split("/")
|
|
for part in parts:
|
|
if cnode == None or not cnode.is_dir():
|
|
return None
|
|
|
|
cnode = cnode.get_node(part)
|
|
|
|
return cnode
|
|
|
|
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.currentDirectory = self.rootDirectory
|
|
|
|
self.connection = None
|
|
|
|
self.state = NonAuthorizedState()
|
|
|
|
self.log("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.log("Client disconnected")
|
|
|
|
#Sample configuration:
|
|
|
|
handler = FTPHandler
|
|
handler.authorizer = AnonymousAuthorizer()
|
|
|
|
recdir = Directory("recursive",files=[
|
|
File("is")
|
|
])
|
|
recdir.files.append(recdir)
|
|
|
|
handler.rootDirectory = Directory("",files=[
|
|
Directory("etc", files=[
|
|
File("passwd",content=URandomFileContent()),
|
|
File("shadow",content=URandomFileContent()),
|
|
recdir
|
|
]),
|
|
Directory("usr", files=[
|
|
Directory("share"),
|
|
Directory("bin", files=[
|
|
File("sh")
|
|
])
|
|
]),
|
|
Directory("home", files=[
|
|
Directory("john",owner="john"),
|
|
Directory("jane",owner="jane"),
|
|
Directory("peter",owner="peter")
|
|
]),
|
|
Directory("root", files=[
|
|
Directory("ssh",files=[
|
|
File("authorized_keys",content=URandomFileContent())
|
|
]),
|
|
File("db.dump", content=URandomFileContent())
|
|
]),
|
|
Directory("dev"),
|
|
Directory("mnt"),
|
|
Directory("var", files=[
|
|
Directory("www")
|
|
]),
|
|
Directory("bin"),
|
|
Directory("tmp"),
|
|
Directory("lost+found"),
|
|
Directory("boot"),
|
|
Directory("lib"),
|
|
Directory("lib32"),
|
|
Directory("proc"),
|
|
Directory("src"),
|
|
File("vmlinuz",size=4000000),
|
|
Directory("selinux"),
|
|
Directory("opt"),
|
|
File("initrd.img",size=4000000)
|
|
])
|
|
|
|
port = 21
|
|
while True:
|
|
try:
|
|
server = SocketServer.ThreadingTCPServer(("0.0.0.0",port),handler)
|
|
print "Listening on port %d" % port
|
|
server.serve_forever()
|
|
break
|
|
except Exception:
|
|
if port < 1500:
|
|
port += 1
|
|
else:
|
|
print "Failed to find a port"
|
|
break
|