iftpd ist ein kleiner FTP-Server, der ohne Konfigurationsdatei auskommt
#!/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)