|
@@ -0,0 +1,92 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+
|
|
|
+import hashlib
|
|
|
+import os
|
|
|
+import pyftpdlib.authorizers
|
|
|
+import pyftpdlib.handlers
|
|
|
+import pyftpdlib.servers
|
|
|
+
|
|
|
+
|
|
|
+class SHA256Authorizer(pyftpdlib.authorizers.DummyAuthorizer):
|
|
|
+
|
|
|
+ def validate_authentication(self, username, password, handler):
|
|
|
+ # pyftpdlib/authorizers.py", line 110, in add_user
|
|
|
+ # dic = {'pwd': str(password),
|
|
|
+ # so we can not compare against .digest()
|
|
|
+ password_hash = hashlib.sha256(password.encode()).hexdigest()
|
|
|
+ if self.user_table[username]['pwd'] != password_hash.lower():
|
|
|
+ raise pyftpdlib.authorizers.AuthenticationFailed()
|
|
|
+
|
|
|
+
|
|
|
+def serve(root_dir_path, username, password_sha256_hexdigest, control_port, passive_port):
|
|
|
+ assert os.path.isdir(root_dir_path), root_dir_path
|
|
|
+ authorizer = SHA256Authorizer()
|
|
|
+ authorizer.add_user(
|
|
|
+ username,
|
|
|
+ password_sha256_hexdigest.lower(),
|
|
|
+ homedir=root_dir_path,
|
|
|
+ # https://pyftpdlib.readthedocs.io/en/latest/api.html#pyftpdlib.authorizers.DummyAuthorizer.add_user
|
|
|
+ # e: change dir
|
|
|
+ # m: mkdir
|
|
|
+ # w: write
|
|
|
+ perm='emw',
|
|
|
+ msg_login='renshi ni hen gaoxing',
|
|
|
+ msg_quit='zaijian',
|
|
|
+ )
|
|
|
+ handler = pyftpdlib.handlers.FTPHandler
|
|
|
+ handler.authorizer = authorizer
|
|
|
+ handler.banner = 'ni hao'
|
|
|
+ # handler.masquerade_address = '192.168.1.1'
|
|
|
+ handler.passive_ports = (passive_port,)
|
|
|
+ server = pyftpdlib.servers.FTPServer((None, control_port), handler)
|
|
|
+ # apparently requires +1 for unknown reasons
|
|
|
+ server.max_cons = 1 + 1
|
|
|
+ server.serve_forever()
|
|
|
+
|
|
|
+
|
|
|
+def _init_argparser():
|
|
|
+ import argparse
|
|
|
+ argparser = argparse.ArgumentParser()
|
|
|
+ argparser.add_argument(
|
|
|
+ '--root', '--root-dir',
|
|
|
+ metavar='path',
|
|
|
+ dest='root_dir_path',
|
|
|
+ required=True,
|
|
|
+ )
|
|
|
+ argparser.add_argument(
|
|
|
+ '--user', '--username',
|
|
|
+ metavar='username',
|
|
|
+ dest='username',
|
|
|
+ required=True,
|
|
|
+ )
|
|
|
+ argparser.add_argument(
|
|
|
+ '--pwd-hash', '--password-hash',
|
|
|
+ metavar='sha256_hexdigest',
|
|
|
+ dest='password_sha256_hexdigest',
|
|
|
+ required=True,
|
|
|
+ )
|
|
|
+ argparser.add_argument(
|
|
|
+ '--ctrl-port', '--control-port',
|
|
|
+ metavar='port',
|
|
|
+ dest='control_port',
|
|
|
+ default=2121,
|
|
|
+ )
|
|
|
+ argparser.add_argument(
|
|
|
+ '--pasv-port', '--passive-port',
|
|
|
+ metavar='port',
|
|
|
+ dest='passive_port',
|
|
|
+ default=62121,
|
|
|
+ )
|
|
|
+ return argparser
|
|
|
+
|
|
|
+
|
|
|
+def main(argv):
|
|
|
+ argparser = _init_argparser()
|
|
|
+ args = argparser.parse_args(argv[1:])
|
|
|
+ serve(**vars(args))
|
|
|
+ return 0
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ import sys
|
|
|
+ sys.exit(main(sys.argv))
|