From 186cbf939f49d5f1fed7c019d82a01d17166f442 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 2 Oct 2026 03:29:12 +0000 Subject: [PATCH] sftp: add fastloader; startup on an 800mhz armv5 exceeded the 30sec timeout --- copyparty/__main__.py | 1 + copyparty/sftpd.py | 23 ++++++++++++++++++++++ copyparty/svchub.py | 45 +++++++++++++++++-------------------------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 39460bd47..58ab90c9b 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1699,6 +1699,7 @@ def add_sftp(ap): ap2.add_argument("--sftp-pw", action="store_true", help="allow password-authentication with sftp (not just ssh-keys)") ap2.add_argument("--sftp-anon", metavar="TXT", type=u, default="", help="allow anonymous/unauthenticated connections with \033[33mTXT\033[0m as username") ap2.add_argument("--sftp-hostk", metavar="FP", type=u, default=E.cfg, help="path to folder with hostkeys, for example 'ssh_host_rsa_key'; missing keys will be generated") + ap2.add_argument("--sftp-fastldr", action="store_true", help="speedhack for 'loading hostkeys' on old CPUs; only use with known-good hostkeys") ap2.add_argument("--sftp-banner", metavar="T", type=u, default="", help="bannertext to send when someone connects; can be @filepath") ap2.add_argument("--sftp-ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); specify [\033[32many\033[0m] to disable inheriting \033[33m--ipa\033[0m / \033[33m--ipar\033[0m. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]") ap2.add_argument("--sftp-hs-t", metavar="SEC", type=int, default=15, help="connection handshake timeout in seconds") diff --git a/copyparty/sftpd.py b/copyparty/sftpd.py index 050533d43..40c53d138 100644 --- a/copyparty/sftpd.py +++ b/copyparty/sftpd.py @@ -55,6 +55,23 @@ if True: # pylint: disable=using-constant-test SATTR = paramiko.sftp_attr.SFTPAttributes +try: + from paramiko.rsakey import serialization + + _load_der_pk = serialization.load_der_private_key + + def fastloader(*a, **ka): + ka0 = ka.copy() + try: + ka["unsafe_skip_rsa_key_validation"] = True + return _load_der_pk(*a, **ka) + except: + return _load_der_pk(*a, **ka0) + +except: + pass + + class SSH_Srv(paramiko.ServerInterface): def __init__(self, hub: "SvcHub", addr: Any): self.hub = hub @@ -774,6 +791,10 @@ class Sftpd(object): self.log("cannot start sftp-server; no compatible IPs in -i", 1) return + if args.sftp_fastldr: + serialization.load_der_private_key = fastloader # type: ignore + else: + self.log("loading hostkeys...") self.hostkeys = [] hostkeytypes = ( ("ed25519", "Ed25519Key", {}), # best @@ -801,6 +822,8 @@ class Sftpd(object): self.hostkeys.append(pkey) if args.sftpv: self.log("loaded hostkey %r" % (pkey,)) + if args.sftp_fastldr: + serialization.load_der_private_key = _load_der_pk # type: ignore ips = list(ODict.fromkeys(ips)) # dedup diff --git a/copyparty/svchub.py b/copyparty/svchub.py index b68d2fa48..836e0eca2 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -174,6 +174,7 @@ class SvcHub(object): self.is_dut = False # running in unittest; always False self.stopping = False self.stopped = False + self.init_mutex = threading.Lock() self.reload_mutex = threading.Lock() self.retcode = 0 self.httpsrv_up = 0 @@ -535,9 +536,6 @@ class SvcHub(object): self.tftpd: Optional[Tftpd] = None - if args.sftp or args.ftp or args.ftps or args.tftp: - Daemon(self.start_ftpd, "start_tftpd") - if args.smb: # impacket.dcerpc is noisy about listen timeouts sto = socket.getdefaulttimeout() @@ -861,29 +859,19 @@ class SvcHub(object): cur.close() db.close() - def start_ftpd(self) -> None: - time.sleep(30) - - if hasattr(self, "sftpd") and not self.sftpd: - self.restart_sftpd() - - if hasattr(self, "ftpd") and not self.ftpd: - self.restart_ftpd() - - if hasattr(self, "tftpd") and not self.tftpd: - self.restart_tftpd() - def restart_sftpd(self) -> None: if not hasattr(self, "sftpd"): return from .sftpd import Sftpd - if self.sftpd: - return # todo + with self.init_mutex: + if self.sftpd: + return # todo + + self.sftpd = Sftpd(self) + self.sftpd.run() - self.sftpd = Sftpd(self) - self.sftpd.run() self.log("root", "started SFTPd") def restart_ftpd(self) -> None: @@ -892,13 +880,15 @@ class SvcHub(object): from .ftpd import Ftpd - if self.ftpd: - return # todo + with self.init_mutex: + if self.ftpd: + return # todo - if not os.path.exists(self.args.cert): - ensure_cert(self.log, self.args) + if not os.path.exists(self.args.cert): + ensure_cert(self.log, self.args) + + self.ftpd = Ftpd(self) - self.ftpd = Ftpd(self) self.log("root", "started FTPd") def restart_tftpd(self) -> None: @@ -907,10 +897,11 @@ class SvcHub(object): from .tftpd import Tftpd - if self.tftpd: - return # todo + with self.init_mutex: + if self.tftpd: + return # todo - self.tftpd = Tftpd(self) + self.tftpd = Tftpd(self) def thr_httpsrv_up(self) -> None: time.sleep(1 if self.args.ign_ebind_all else 5)