Pberndt V4

Direkt zum Inhalt springen


Quellcode iftpd.py

Beschreibung

iftpd ist ein kleiner FTP-Server, der ohne Konfigurationsdatei auskommt

Sourcecode

#!/usr/bin/python
import SocketServer
import sys
import socket
import os
import threading
import getopt
import time

_pasv_ports = {}
_portmap_lock = threading.Lock()
def acquirePort():
    _portmap_lock.acquire()
    usePort = 0
    for port, active in _pasv_ports.items():
        if active == False:
            usePort = port
            break
    if usePort in _pasv_ports:
        _pasv_ports[usePort] = True
    _portmap_lock.release()
    return usePort


class FTPHandler(SocketServer.StreamRequestHandler):
    def output(self, *text):
        now = time.localtime()
        text = " ".join(map(str, text))
        print "[%04d/%02d/%02d %02d:%02d %s] %s" % (
            now.tm_year,
            now.tm_mon,
            now.tm_mday,
            now.tm_hour,
            now.tm_min,
            ":".join(map(str, self.client_address)),
            text)
    def build_data(self):
        if self.connect_to == "PASV":
            srv = socket.socket()
            srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            srv.bind(('', self.pasv_port))
            srv.listen(1)
            data = srv.accept()[0]
            srv.shutdown(socket.SHUT_RDWR)
            srv.close()
            del srv
            return data
        else:
            data = socket.socket()
            data.connect(self.connect_to)
            return data
    data_socket = False
    def finish(self):
        time.sleep(1)
        if self.pasv_port != 0:
            _portmap_lock.acquire()
            _pasv_ports[self.pasv_port] = False
            _portmap_lock.release()
    def handle(self):
        self.output("Connection from %s" % self.connection.getpeername()[0])
        self.wfile.write("220 Hello out there\r\n")
        self.pasv_port = acquirePort()
        self.output("Using port %d for passive ftp " % self.pasv_port)
        self.path = "/"
        self.rest = 0
        self.type = "I"
        self.renameFrom = None
        self.login = False
        while True:
            line = self.rfile.readline().strip().split()
            if debug_mode:
                self.output("Received command: ", line)
                try:
                    arg = " ".join(line[1:]).replace('"', '')
                    pwd = os.getcwd()
                    if arg[0] == "/":
                        newpath = os.path.abspath(pwd + "/" + arg)
                    else:
                        newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                    self.output(" Interpreted as file ", newpath)
                except:
                    pass
            if not line:
                break
            if line == "":
                self.wfile.write("530 Args?\r\n")
                continue
            # Allow pre login
            if line[0] == "SYST":
                self.wfile.write("215 UNIX Type: L8\r\n")
                continue
            elif line[0] == "FEAT":
                self.wfile.write("211-Features:\r\nUTF8\r\nPASV\r\n211 End\r\n")
                continue
            elif line[0] == "NOOP":
                self.wfile.write("200 Pong\r\n")
                continue
            # Login
            if require_login and self.login != 2:
                if line[0] == "USER" and line[1] == require_login[0] and self.login == False:
                    self.login = 1
                    self.wfile.write("331 Ok\r\n")
                    continue
                if line[0] == "USER":
                    self.wfile.write("331 Ok\r\n")
                    continue
                elif line[0] == "PASS" and len(line) > 1 and line[1] == require_login[1] and self.login == 1:
                    self.login = 2
                    self.wfile.write("200 Ok\r\n")
                    continue
                self.wfile.write("530 Bah. Wrong. Authenticate yourself!\r\n")
                continue
            # Post login
            if line[0] == "USER" or line[0] == "PASS":
                self.wfile.write("230 Ill accept whatever you say\r\n")
            elif line[0] == "PORT":
                data = line[1].split(',')
                if len(data) != 6:
                    self.wfile.write("500 Oops\r\n")
                else:
                    ip = ".".join(data[0:4])
                    port = (int(data[4]) << 8) + int(data[5])
                    self.connect_to = (ip, port)
                    self.wfile.write("200 Ok Ill connect to %s:%d\r\n" % (ip, port))
                    self.data_socket = socket.socket()
                    try:
                        self.data_socket.connect(self.connect_to)
                    except:
                        self.data_socket = None
                        self.wfile.write("500 Failed to connect to you\r\n")
            elif line[0] == "PASV":
                if self.pasv_port == 0:
                    self.wfile.write("425 Out of resources, sorry\r\n")
                    continue
                srv = socket.socket()
                try:
                    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
                    srv.bind(('', self.pasv_port))
                    srv.listen(1)
                    srv.settimeout(60)
                except:
                    del srv
                    self.wfile.write("425 Out of resources, sorry\r\n")
                    continue
                try:
                    self.wfile.write("227 Entering passive mode (%s,%d,%d)\r\n" % (",".join(my_ip.split(".")),
                        (self.pasv_port & 0xFF00) >> 8, (self.pasv_port & 0x00FF)))
                    data = srv.accept()[0]
                    srv.shutdown(socket.SHUT_RDWR)
                    srv.close()
                    del srv
                except:
                    try:
                        srv.shutdown(socket.SHUT_RDWR)
                        srv.close()
                        del srv
                    except:
                        pass
                    self.wfile.write("425 You failed, sorry\r\n")
                    continue
                self.data_socket = data
            elif line[0] == "QUIT":
                self.wfile.write("200 Bye\r\n")
                self.wfile.close()
                break
            elif line[0] == "RNFR":
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                elif not (os.access(newpath, os.F_OK) and os.path.isfile(newpath)):
                    self.wfile.write("550 Not found\r\n")
                    continue
                else:
                    self.renameFrom = newpath
                    self.wfile.write("200 Ok\r\n")
            elif line[0] == "RNTO":
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue
                if not self.renameFrom:
                    self.wfile.write("500 Specify RNFR first\r\n")
                    continue
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                else:
                    self.output("Renamed ", self.renameFrom, " to ", newpath)
                    os.rename(self.renameFrom, newpath)
                    self.renameFrom = None
                    self.wfile.write("250 Ok\r\n")
            elif line[0] == "DELE":
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                else:
                    os.unlink(newpath)
                    self.wfile.write("250 Ok\r\n")
                    self.output("Removed file ", newpath)
            elif line[0] == "SITE":
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue
                if line[1] != "CHMOD":
                    self.wfile.write("500 I dont know anything but chmod\r\n")
                    continue
                mode = line[2]
                arg = " ".join(line[3:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                elif not (os.access(newpath, os.F_OK) and os.path.isfile(newpath)):
                    self.wfile.write("550 Not found\r\n")
                    continue
                else:
                    os.chmod(newpath, int(mode, 8))
                    self.wfile.write("200 Ok\r\n")
            elif line[0] == "CWD":
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                elif not (os.access(newpath, os.F_OK) and os.path.isdir(newpath)):
                    self.wfile.write("550 Not found\r\n")
                    continue
                else:
                    self.path = newpath[len(pwd):]
                    self.wfile.write("200 Ok\r\n")
            elif line[0] == "CDUP":
                pwd = os.getcwd()
                newpath = os.path.abspath(pwd + "/" + self.path + "/..")
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                else:
                    self.path = newpath[len(pwd):]
                    self.wfile.write("200 Ok\r\n")
            elif line[0] == "PWD":
                self.wfile.write("257 \"%s\"\r\n" % self.path)
            elif line[0] == "TYPE":
                self.type = line[1]
                self.wfile.write("200 Ok\r\n")
            elif line[0] == "REST":
                self.rest = abs(int(line[1]))
                self.wfile.write("350 Position accepted")
            elif line[0] == "RMD":
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                    continue
                else:
                    try:
                        os.rmdir(newpath)
                        self.output("Removed directory ", newpath)
                        self.wfile.write("200 Ok\r\n")
                    except:
                        self.wfile.write("400 Failure\r\n")
            elif line[0] == "MKD":
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                    continue
                else:
                    try:
                        os.mkdir(newpath)
                        self.wfile.write("257 Ok\r\n")
                        self.output("Made dir ", newpath)
                    except:
                        self.wfile.write("400 Failure\r\n")
            elif line[0] == "STOR" or line[0] == "APPE":
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                    continue
                if not allow_store:
                    self.wfile.write("550 Naa. Not here.\r\n")
                    continue

                if not self.data_socket:
                    self.wfile.write("425 PASV or PORT first\r\n")
                    continue
                writeto = open(newpath, "w" if line[0] == "STOR" else "a")
                self.wfile.write("150 Here we go\r\n")
                self.output("Receiving ", newpath)
               
                while True:
                    data = self.data_socket.recv(10240)
                    if self.type == "A":
                        if len(data) > 0 and data[-1] == "r": data = data + self.data_socket.recv(1)
                        data = data.replace("\r\n", "\n")
                    if data == "":
                        break
                    writeto.write(data)
                self.output("Done")

                writeto.close()               
                self.data_socket.close()
                self.data_socket = False
                self.wfile.write("226 Ok.\r\n")
            elif line[0] == "RETR":
                arg = " ".join(line[1:]).replace('"', '')
                pwd = os.getcwd()
                if arg[0] == "/":
                    newpath = os.path.abspath(pwd + "/" + arg)
                else:
                    newpath = os.path.abspath(pwd + "/" + self.path + "/" + arg)
                if newpath[0:len(pwd)] != pwd:
                    self.wfile.write("553 You just tried to leave root. I cant let you do that\r\n")
                    continue
                elif not (os.access(newpath, os.F_OK) and os.path.isfile(newpath)):
                    self.wfile.write("550 Not found\r\n")
                    continue
                self.output("Sending ", newpath)

                if not self.data_socket:
                    self.wfile.write("425 PASV or PORT first\r\n")
                    continue
                self.wfile.write("150 Here we go\r\n")

                fcont = open(newpath)
                fcont.seek(self.rest)
                self.rest = 0
                while True:
                    content = fcont.read(10240)
                    if self.type == "A":
                        content = content.replace("\n", "\r\n")
                    if content == "": break
                    self.data_socket.send(content)
                fcont.close()
                self.data_socket.close()
                self.data_socket = False
                self.wfile.write("226 Ok what now?\r\n")
            elif line[0] == "LIST":
                if not self.data_socket:
                    self.wfile.write("425 PASV or PORT first\r\n")
                    continue
                self.wfile.write("150 Here we go\r\n")
                args = ""
                if len(line) > 1 and line[1] == "-a":
                    args = "a"
                self.data_socket.send(os.popen("ls -ln%s --time-style='+%%b %%d %%Y' .%r" %
                    (args, self.path)).read().replace("\n", "\r\n"))
                self.data_socket.close()
                self.wfile.write("226 Ok what now?\r\n")
                self.data_socket = False
            else:
                self.wfile.write("500 I dont know about that as Im dumb (You said: %s)\r\n" % line[0])

                   

my_ip = socket.gethostbyname(socket.gethostname())
allow_store = False
baseport = 12000
trange = 5
add = 0
debug_mode = False
require_login = False

try:
    (options, rest) = getopt.getopt(sys.argv[1:], "sp:r:h:dl:")
except:
    print "iftpd - Instant FTPD"
    print "Copyright (c) 2009, Phillip Berndt"
    print
    print "Creates a FTP daemon in ./"
    print
    print "Syntax: iftpd -s -p <port> -r <range> -h <host>"
    print
    print " -s            Allow users to modify data"
    print " -p            Listen on port <port>          (Default: 12000)"
    print " -r            Use <port> - <port> + <range>"
    print "                for passive mode FTP          (Default: 5)"
    print " -h            Send <host> for passive mode"
    print "                connection host"
    print " -d            Debug mode"
    print " -l            Require <user>:<pass> login    (Default: Allow all)"
    print
    sys.exit(1)

for option, arg in options:
    if option == "-s":
        print "WARNING: Activating store mode"
        allow_store = True
    if option == "-p":
        baseport = int(arg)
    if option == "-r":
        trange = int(arg)
    if option == "-h":
        my_ip = socket.gethostbyname(arg)
    if option == "-d":
        debug_mode = True
    if option == "-l":
        require_login = arg.split(":", 2)

while True:
    try:
        server = SocketServer.ThreadingTCPServer(('', baseport + add), FTPHandler, True)
        print "Serving on port %d" % (baseport + add)
    except:
        add = add + 1
        if add > 20:
            print "Failed to find a port :/"
            sys.exit(1)
        continue
    break
_pasv_ports = dict(zip(range(baseport + add + 1, baseport + add + trange + 1), [ False ] * trange))
for port in _pasv_ports.keys():
    srv = socket.socket()
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    try:
        srv.bind(('', port))
        srv.close()
        del srv
    except socket.error:
        del _pasv_ports[port]
        del srv
print "Using ports %d to %d for passive mode (%d are useable)" % (baseport + 1 + add, baseport + 1 + add + trange, len(_pasv_ports))
print
try:
    server.serve_forever()
except KeyboardInterrupt:
    sys.exit(0)

Download

Dateiname
iftpd.py
Größe
14.16kb