| 1 | from ftplib import FTP |
|---|
| 2 | |
|---|
| 3 | import os |
|---|
| 4 | import sys |
|---|
| 5 | import socket |
|---|
| 6 | import random |
|---|
| 7 | |
|---|
| 8 | class dFTP(FTP): |
|---|
| 9 | def __init__(self): |
|---|
| 10 | FTP.__init__(self) |
|---|
| 11 | self.nat_host = None |
|---|
| 12 | self.nat_port_range = None |
|---|
| 13 | |
|---|
| 14 | def makeport(self): |
|---|
| 15 | '''Create a new socket and send a PORT command for it.''' |
|---|
| 16 | msg = "getaddrinfo returns an empty list" |
|---|
| 17 | sock = None |
|---|
| 18 | for res in socket.getaddrinfo(None, 0, self.af, socket.SOCK_STREAM, 0, socket.AI_PASSIVE): |
|---|
| 19 | af, socktype, proto, canonname, sa = res |
|---|
| 20 | ports = list(filter(lambda port: ((port >> 4) & 0xf) != 0, range(self.nat_port_range[0], self.nat_port_range[1]))) |
|---|
| 21 | random.shuffle(ports) |
|---|
| 22 | for port in ports: |
|---|
| 23 | try: |
|---|
| 24 | sock = socket.socket(af, socktype, proto) |
|---|
| 25 | sa = tuple([sa[0], port]) |
|---|
| 26 | sock.bind(sa) |
|---|
| 27 | except socket.error as msg: |
|---|
| 28 | if sock: |
|---|
| 29 | sock.close() |
|---|
| 30 | sock = None |
|---|
| 31 | continue |
|---|
| 32 | break |
|---|
| 33 | if not sock: |
|---|
| 34 | raise socket.error(msg) |
|---|
| 35 | sock.listen(1) |
|---|
| 36 | port = sock.getsockname()[1] |
|---|
| 37 | host = socket.gethostbyname(self.nat_host) |
|---|
| 38 | if self.af == socket.AF_INET: |
|---|
| 39 | resp = self.sendport(host, port) |
|---|
| 40 | else: |
|---|
| 41 | resp = self.sendeprt(host, port) |
|---|
| 42 | return sock |
|---|
| 43 | |
|---|
| 44 | def retrbinary(self, cmd, callback, blocksize=8192, rest=None): |
|---|
| 45 | conn = self.transfercmd(cmd, rest) |
|---|
| 46 | while 1: |
|---|
| 47 | data = conn.recv(blocksize) |
|---|
| 48 | if not data: |
|---|
| 49 | break |
|---|
| 50 | callback(data) |
|---|
| 51 | conn.close() |
|---|
| 52 | return self.voidresp() |
|---|
| 53 | |
|---|
| 54 | def download(): |
|---|
| 55 | '''ftp via nat program. |
|---|
| 56 | Usage: ftpexe [options] |
|---|
| 57 | -v verbose |
|---|
| 58 | -H nat host |
|---|
| 59 | -R nat port-range |
|---|
| 60 | -s host:port |
|---|
| 61 | -u user |
|---|
| 62 | -p password |
|---|
| 63 | -f file |
|---|
| 64 | -b send TYPE I |
|---|
| 65 | ''' |
|---|
| 66 | |
|---|
| 67 | verbose = 0 |
|---|
| 68 | nat_host = None |
|---|
| 69 | nat_port_range = None |
|---|
| 70 | host = None |
|---|
| 71 | port = None |
|---|
| 72 | user = None |
|---|
| 73 | password = None |
|---|
| 74 | rfile = None |
|---|
| 75 | binary = False |
|---|
| 76 | |
|---|
| 77 | i = 1 |
|---|
| 78 | while i < len(sys.argv): |
|---|
| 79 | print("ARG %s" % (sys.argv[i])) |
|---|
| 80 | if sys.argv[i] == '-v': |
|---|
| 81 | verbose = verbose+1 |
|---|
| 82 | elif sys.argv[i] == '-H': |
|---|
| 83 | i=i+1 |
|---|
| 84 | nat_host = sys.argv[i] |
|---|
| 85 | elif sys.argv[i] == '-R': |
|---|
| 86 | i=i+1 |
|---|
| 87 | nat_port_range = sys.argv[i] |
|---|
| 88 | elif sys.argv[i] == '-s': |
|---|
| 89 | i=i+1 |
|---|
| 90 | host = sys.argv[i] |
|---|
| 91 | elif sys.argv[i] == '-u': |
|---|
| 92 | i=i+1 |
|---|
| 93 | user = sys.argv[i] |
|---|
| 94 | elif sys.argv[i] == '-f': |
|---|
| 95 | i=i+1 |
|---|
| 96 | rfile = sys.argv[i] |
|---|
| 97 | elif sys.argv[i] == '-b': |
|---|
| 98 | binary=True |
|---|
| 99 | elif sys.argv[i] == '-h': |
|---|
| 100 | print(download.__doc__) |
|---|
| 101 | sys.exit(0) |
|---|
| 102 | i=i+1 |
|---|
| 103 | |
|---|
| 104 | if not host or not nat_port_range or not nat_host or not rfile: |
|---|
| 105 | print(download.__doc__) |
|---|
| 106 | sys.exit(0) |
|---|
| 107 | |
|---|
| 108 | off = host.find(':') |
|---|
| 109 | if off != -1: |
|---|
| 110 | port = int(host[off+1:]) |
|---|
| 111 | host = host[:off] |
|---|
| 112 | else: |
|---|
| 113 | port = 21 |
|---|
| 114 | print("host %s %i" % (host, port)) |
|---|
| 115 | |
|---|
| 116 | if nat_port_range: |
|---|
| 117 | x = nat_port_range.find('-') |
|---|
| 118 | min_port = int(nat_port_range[:x]) |
|---|
| 119 | max_port = int(nat_port_range[x+1:]) |
|---|
| 120 | print("nat host %s port range %s to %s" % (nat_host, min_port, max_port)) |
|---|
| 121 | |
|---|
| 122 | if not user: |
|---|
| 123 | user = 'anonymous' |
|---|
| 124 | password = 'guest' |
|---|
| 125 | print("user %s pass %s" % (user, password)) |
|---|
| 126 | |
|---|
| 127 | ftp = dFTP() |
|---|
| 128 | ftp.set_debuglevel(verbose) |
|---|
| 129 | ftp.connect(host=host, port=port) |
|---|
| 130 | ftp.nat_host = nat_host |
|---|
| 131 | ftp.nat_port_range = [min_port, max_port] |
|---|
| 132 | |
|---|
| 133 | ftp.login(user, password) |
|---|
| 134 | lfile=open(rfile, 'wb') |
|---|
| 135 | ftp.set_pasv(not ftp.passiveserver) |
|---|
| 136 | if binary: |
|---|
| 137 | ftp.voidcmd('TYPE I') |
|---|
| 138 | ftp.retrbinary('RETR ' + rfile, \ |
|---|
| 139 | lfile.write, 1024) |
|---|
| 140 | ftp.quit() |
|---|
| 141 | |
|---|
| 142 | |
|---|
| 143 | if __name__ == '__main__': |
|---|
| 144 | download() |
|---|