From 252b5a88b18376c59fc903f9b491a228546ffb49 Mon Sep 17 00:00:00 2001 From: ed Date: Fri, 9 Dec 2022 19:11:26 +0000 Subject: [PATCH] use linklocal on NICs without routable IPs --- README.md | 4 ++++ copyparty/__main__.py | 2 +- copyparty/mdns.py | 9 ++++++--- copyparty/multicast.py | 25 +++++++++++++++++++++++-- copyparty/ssdp.py | 2 +- copyparty/tcpsrv.py | 20 +++++++++++++++----- copyparty/web/splash.css | 4 +++- 7 files changed, 53 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index d5859b56..864113d1 100644 --- a/README.md +++ b/README.md @@ -724,6 +724,10 @@ uses [ssdp](https://en.wikipedia.org/wiki/Simple_Service_Discovery_Protocol) to doubleclicking the icon opens the "connect" page which explains how to mount copyparty as a local filesystem +if copyparty does not appear in windows explorer, use `--zsv` to see why: + +* maybe the discovery multicast was sent from an IP which does not intersect with the server subnets + ## qr-code diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 10efe73a..a4857ac4 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -655,7 +655,7 @@ def run_argparse( ap2 = ap.add_argument_group('network options') ap2.add_argument("-i", metavar="IP", type=u, default="::", help="ip to bind (comma-sep.), default: all IPv4 and IPv6") ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)") - ap2.add_argument("--ll", action="store_true", help="enable link-local IPv6 (supported by ie11 and firefox (not chrome)) -- breaks some mdns clients") + ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 even if the NIC has routable IPs (breaks some mdns clients)") ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to keep; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd), [\033[32m2\033[0m]=cloudflare, [\033[32m3\033[0m]=nginx, [\033[32m-1\033[0m]=closest proxy") if ANYWIN: ap2.add_argument("--reuseaddr", action="store_true", help="set reuseaddr on listening sockets on windows; allows rapid restart of copyparty at the expense of being able to accidentally start multiple instances") diff --git a/copyparty/mdns.py b/copyparty/mdns.py index 07f45add..1bc330a3 100644 --- a/copyparty/mdns.py +++ b/copyparty/mdns.py @@ -313,15 +313,18 @@ class MDNS(MCast): self.running = False if not panic: for srv in self.srv.values(): - srv.sck.sendto(srv.bp_bye, (srv.grp, 5353)) + try: + srv.sck.sendto(srv.bp_bye, (srv.grp, 5353)) + except: + pass self.srv = {} def eat(self, buf: bytes, addr: tuple[str, int], sck: socket.socket) -> None: cip = addr[0] v6 = ":" in cip - if cip.startswith("169.254") or ( - v6 and not cip.startswith("fe80") and not self.args.ll + if (cip.startswith("169.254") and not self.ll_ok) or ( + v6 and not cip.startswith("fe80") ): return diff --git a/copyparty/multicast.py b/copyparty/multicast.py index 55676888..a04e5d73 100644 --- a/copyparty/multicast.py +++ b/copyparty/multicast.py @@ -82,6 +82,7 @@ class MCast(object): self.srv: dict[socket.socket, MC_Sck] = {} # listening sockets self.sips: set[str] = set() # all listening ips (including failed attempts) + self.ll_ok: set[str] = set() # fallback linklocal IPv4 and IPv6 addresses self.b2srv: dict[bytes, MC_Sck] = {} # binary-ip -> server socket self.b4: list[bytes] = [] # sorted list of binary-ips self.b6: list[bytes] = [] # sorted list of binary-ips @@ -183,11 +184,21 @@ class MCast(object): srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False) # gvfs breaks if a linklocal ip appears in a dns reply + ll = { + k: v + for k, v in srv.ips.items() + if k.startswith("169.254") or k.startswith("fe80") + } + rt = {k: v for k, v in srv.ips.items() if k not in ll} + + if self.args.ll or not rt: + self.ll_ok.update(list(ll)) + if not self.args.ll: - srv.ips = {k: v for k, v in srv.ips.items() if not k.startswith("fe80")} + srv.ips = rt or ll if not srv.ips: - self.log("no routable IPs on {}; skipping [{}]".format(netdev, ip), 3) + self.log("no IPs on {}; skipping [{}]".format(netdev, ip), 3) continue try: @@ -337,6 +348,16 @@ class MCast(object): # just give it something ret = list(self.srv.values())[0] + if not ret and cip.startswith("169.254"): + # idk how to map LL IPv4 msgs to nics; + # just pick one and hope for the best + lls = ( + x + for x in self.srv.values() + if next((y for y in x.ips if y in self.ll_ok), None) + ) + ret = next(lls, None) + if ret: t = "new client on {} ({}): {}" self.log(t.format(ret.name, ret.net, cip), 6) diff --git a/copyparty/ssdp.py b/copyparty/ssdp.py index 42310f1d..11ee8436 100644 --- a/copyparty/ssdp.py +++ b/copyparty/ssdp.py @@ -148,7 +148,7 @@ class SSDPd(MCast): def eat(self, buf: bytes, addr: tuple[str, int]) -> None: cip = addr[0] - if cip.startswith("169.254"): + if cip.startswith("169.254") and not self.ll_ok: return if buf in self.rxc.c: diff --git a/copyparty/tcpsrv.py b/copyparty/tcpsrv.py index b4e1128d..f813eb0b 100644 --- a/copyparty/tcpsrv.py +++ b/copyparty/tcpsrv.py @@ -121,6 +121,20 @@ class TcpSrv(object): else: self.netdevs = {} + # keep IPv6 LL-only nics + ll_ok: set[str] = set() + for ip, nd in self.netdevs.items(): + if not ip.startswith("fe80"): + continue + + just_ll = True + for ip2, nd2 in self.netdevs.items(): + if nd == nd2 and ":" in ip2 and not ip2.startswith("fe80"): + just_ll = False + + if just_ll or self.args.ll: + ll_ok.add(ip.split("/")[0]) + qr1: dict[str, list[int]] = {} qr2: dict[str, list[int]] = {} msgs = [] @@ -128,7 +142,7 @@ class TcpSrv(object): title_vars = [x[1:] for x in self.args.wintitle.split(" ") if x.startswith("$")] t = "available @ {}://{}:{}/ (\033[33m{}\033[0m)" for ip, desc in sorted(eps.items(), key=lambda x: x[1]): - if ip.startswith("fe80") and not self.args.ll: + if ip.startswith("fe80") and ip not in ll_ok: continue for port in sorted(self.args.p): @@ -279,10 +293,6 @@ class TcpSrv(object): for nip in nic.ips: ipa = nip.ip[0] if ":" in str(nip.ip) else nip.ip sip = "{}/{}".format(ipa, nip.network_prefix) - if sip.startswith("169.254"): - # browsers dont impl linklocal - continue - nd = Netdev(sip, nic.index or 0, nic.nice_name, "") eps[sip] = nd try: diff --git a/copyparty/web/splash.css b/copyparty/web/splash.css index a9563afe..446e146f 100644 --- a/copyparty/web/splash.css +++ b/copyparty/web/splash.css @@ -16,7 +16,8 @@ html { h1 { border-bottom: 1px solid #ccc; margin: 2em 0 .4em 0; - padding: 0 0 .2em 0; + padding: 0; + line-height: 1em; font-weight: normal; } li { @@ -26,6 +27,7 @@ a { color: #047; background: #fff; text-decoration: none; + white-space: nowrap; border-bottom: 1px solid #8ab; border-radius: .2em; padding: .2em .6em;