-
-
Save muttiopenbts/81eec43f0204558f43e782e61fb8edaa to your computer and use it in GitHub Desktop.
simple ftp server by python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# coding: utf-8 | |
import os,socket,threading,time | |
import sys | |
#import traceback | |
allow_delete = False | |
local_ip = sys.argv[1] | |
local_port = 21 | |
currdir=os.path.abspath('/tmp') | |
# Change behaviour of server. Better to abstract all file io in future | |
security_mode = {'fakefile':1} | |
class FTPserverThread(threading.Thread): | |
def __init__(self,(conn,addr)): | |
self.conn=conn | |
self.addr=addr | |
self.basewd=currdir | |
self.cwd=self.basewd | |
self.rest=False | |
self.pasv_mode=False | |
threading.Thread.__init__(self) | |
self.security_mode = security_mode | |
if self.security_mode['fakefile']: | |
self.fakefilename = os.path.join(self.cwd,'fakefile.txt') | |
self.init_fakefile() | |
def run(self): | |
self.conn.send('220 Welcome!\r\n') | |
while True: | |
cmd=self.conn.recv(256) | |
if not cmd: break | |
else: | |
print 'Recieved:',cmd | |
try: | |
func=getattr(self,cmd[:4].strip().upper()) | |
func(cmd) | |
except Exception,e: | |
print 'ERROR:',e | |
#traceback.print_exc() | |
self.conn.send('500 Sorry.\r\n') | |
def SYST(self,cmd): | |
self.conn.send('215 UNIX Type: L8\r\n') | |
def OPTS(self,cmd): | |
if cmd[5:-2].upper()=='UTF8 ON': | |
self.conn.send('200 OK.\r\n') | |
else: | |
self.conn.send('451 Sorry.\r\n') | |
def USER(self,cmd): | |
self.conn.send('331 OK.\r\n') | |
def PASS(self,cmd): | |
self.conn.send('230 OK.\r\n') | |
#self.conn.send('530 Incorrect.\r\n') | |
def QUIT(self,cmd): | |
self.conn.send('221 Goodbye.\r\n') | |
def NOOP(self,cmd): | |
self.conn.send('200 OK.\r\n') | |
def TYPE(self,cmd): | |
self.mode=cmd[5] | |
self.conn.send('200 Binary mode.\r\n') | |
def CDUP(self,cmd): | |
if not self.security_mode['fakefile']: | |
if not os.path.samefile(self.cwd,self.basewd): | |
#learn from stackoverflow | |
self.cwd=os.path.abspath(os.path.join(self.cwd,'..')) | |
self.conn.send('200 OK.\r\n') | |
def PWD(self,cmd): | |
if not self.security_mode['fakefile']: | |
cwd=os.path.relpath(self.cwd,self.basewd) | |
if cwd=='.': | |
cwd='/' | |
else: | |
cwd='/'+cwd | |
cwd = self.cwd | |
self.conn.send('257 \"%s\"\r\n' % cwd) | |
def CWD(self,cmd): | |
if not self.security_mode['fakefile']: | |
chwd=cmd[4:-2] | |
if chwd=='/': | |
self.cwd=self.basewd | |
elif chwd[0]=='/': | |
self.cwd=os.path.join(self.basewd,chwd[1:]) | |
else: | |
self.cwd=os.path.join(self.cwd,chwd) | |
self.conn.send('250 OK.\r\n') | |
def PORT(self,cmd): | |
if self.pasv_mode: | |
self.servsock.close() | |
self.pasv_mode = False | |
l=cmd[5:].split(',') | |
self.dataAddr='.'.join(l[:4]) | |
self.dataPort=(int(l[4])<<8)+int(l[5]) | |
self.conn.send('200 Get port.\r\n') | |
def PASV(self,cmd): # from http://goo.gl/3if2U | |
self.pasv_mode = True | |
self.servsock = socket.socket(socket.AF_INET,socket.SOCK_STREAM) | |
self.servsock.bind((local_ip,0)) | |
self.servsock.listen(1) | |
ip, port = self.servsock.getsockname() | |
print 'open', ip, port | |
self.conn.send('227 Entering Passive Mode (%s,%u,%u).\r\n' % | |
(','.join(ip.split('.')), port>>8&0xFF, port&0xFF)) | |
def start_datasock(self): | |
if self.pasv_mode: | |
self.datasock, addr = self.servsock.accept() | |
print 'connect:', addr | |
else: | |
self.datasock=socket.socket(socket.AF_INET,socket.SOCK_STREAM) | |
self.datasock.connect((self.dataAddr,self.dataPort)) | |
def stop_datasock(self): | |
self.datasock.close() | |
if self.pasv_mode: | |
self.servsock.close() | |
def LIST(self,cmd): | |
self.conn.send('150 Here comes the directory listing.\r\n') | |
print 'list:', self.cwd | |
self.start_datasock() | |
for t in os.listdir(self.cwd): | |
k=self.toListItem(os.path.join(self.cwd,t)) | |
self.datasock.send(k+'\r\n') | |
self.stop_datasock() | |
self.conn.send('226 Directory send OK.\r\n') | |
def toListItem(self,fn): | |
st=os.stat(fn) | |
fullmode='rwxrwxrwx' | |
mode='' | |
for i in range(9): | |
mode+=((st.st_mode>>(8-i))&1) and fullmode[i] or '-' | |
d=(os.path.isdir(fn)) and 'd' or '-' | |
ftime=time.strftime(' %b %d %H:%M ', time.gmtime(st.st_mtime)) | |
return d+mode+' 1 user group '+str(st.st_size)+ftime+os.path.basename(fn) | |
def MKD(self,cmd): | |
if not self.security_mode['fakefile']: | |
dn=os.path.join(self.cwd,cmd[4:-2]) | |
os.mkdir(dn) | |
self.conn.send('257 Directory created.\r\n') | |
def RMD(self,cmd): | |
if not self.security_mode['fakefile']: | |
dn=os.path.join(self.cwd,cmd[4:-2]) | |
if allow_delete: | |
os.rmdir(dn) | |
self.conn.send('250 Directory deleted.\r\n') | |
else: | |
self.conn.send('450 Not allowed.\r\n') | |
def DELE(self,cmd): | |
if not self.security_mode['fakefile']: | |
fn=os.path.join(self.cwd,cmd[5:-2]) | |
if allow_delete: | |
os.remove(fn) | |
self.conn.send('250 File deleted.\r\n') | |
else: | |
self.conn.send('450 Not allowed.\r\n') | |
def RNFR(self,cmd): | |
if not self.security_mode['fakefile']: | |
self.rnfn=os.path.join(self.cwd,cmd[5:-2]) | |
self.conn.send('350 Ready.\r\n') | |
def RNTO(self,cmd): | |
if not self.security_mode['fakefile']: | |
fn=os.path.join(self.cwd,cmd[5:-2]) | |
os.rename(self.rnfn,fn) | |
self.conn.send('250 File renamed.\r\n') | |
def REST(self,cmd): | |
if not self.security_mode['fakefile']: | |
self.pos=int(cmd[5:-2]) | |
self.rest=True | |
self.conn.send('250 File position reseted.\r\n') | |
# Will enable after testing | |
def __SIZE(self,cmd): | |
if not self.security_mode['fakefile']: | |
statinfo = os.stat(self.fakefilename) | |
self.conn.send('2213 {}\r\n'.format(statinfo.st_size)) | |
def init_fakefile(self): | |
filename = self.fakefilename | |
# Delete fake file | |
os.remove(filename) | |
print("File Removed!") | |
# Create fake File | |
fakefile = open(filename,"w+") | |
fakefile.write('Happy hacking!') | |
fakefile.close() | |
def RETR(self,cmd): | |
if self.security_mode['fakefile']: | |
fn = self.fakefilename | |
else: | |
fn=os.path.join(self.cwd,cmd[5:-2]) | |
#fn=os.path.join(self.cwd,cmd[5:-2]).lstrip('/') | |
print 'Downloading:',fn | |
if self.mode=='I': | |
fi=open(fn,'rb') | |
else: | |
fi=open(fn,'r') | |
self.conn.send('150 Opening data connection.\r\n') | |
if self.rest: | |
fi.seek(self.pos) | |
self.rest=False | |
data= fi.read(1024) | |
self.start_datasock() | |
while data: | |
self.datasock.send(data) | |
data=fi.read(1024) | |
fi.close() | |
self.stop_datasock() | |
self.conn.send('226 Transfer complete.\r\n') | |
def STOR(self,cmd): | |
if not self.security_mode['fakefile']: | |
fn=os.path.join(self.cwd,cmd[5:-2]) | |
print 'Uploading:',fn | |
if self.mode=='I': | |
fo=open(fn,'wb') | |
else: | |
fo=open(fn,'w') | |
self.conn.send('150 Opening data connection.\r\n') | |
self.start_datasock() | |
while True: | |
data=self.datasock.recv(1024) | |
if not data: break | |
if not self.security_mode['fakefile']: | |
fo.write(data) | |
fo.close() | |
self.stop_datasock() | |
self.conn.send('226 Transfer complete.\r\n') | |
class FTPserver(threading.Thread): | |
def __init__(self): | |
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | |
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | |
self.sock.bind((local_ip,local_port)) | |
threading.Thread.__init__(self) | |
def run(self): | |
self.sock.listen(5) | |
while True: | |
th=FTPserverThread(self.sock.accept()) | |
th.daemon=True | |
th.start() | |
def stop(self): | |
self.sock.close() | |
if __name__=='__main__': | |
ftp=FTPserver() | |
ftp.daemon=True | |
ftp.start() | |
print 'On', local_ip, ':', local_port | |
raw_input('Enter to end...\n') | |
ftp.stop() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment