mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
mdns ipv6 fixes; now works on ie11/safari, not linux:
* subscribe/announce on LL only * add NSEC records if 4/6-only
This commit is contained in:
parent
37c1cab726
commit
8829f56d4c
|
@ -19,6 +19,7 @@ from .stolen.dnslib import (
|
|||
QTYPE,
|
||||
A,
|
||||
AAAA,
|
||||
NSEC,
|
||||
SRV,
|
||||
PTR,
|
||||
TXT,
|
||||
|
@ -41,11 +42,12 @@ class MDNS_Sck(MC_Sck):
|
|||
self,
|
||||
sck: socket.socket,
|
||||
idx: int,
|
||||
name: str,
|
||||
grp: str,
|
||||
ip: str,
|
||||
net: Union[IPv4Network, IPv6Network],
|
||||
):
|
||||
super(MDNS_Sck, self).__init__(sck, idx, grp, ip, net)
|
||||
super(MDNS_Sck, self).__init__(sck, idx, name, grp, ip, net)
|
||||
|
||||
self.bp_probe = b""
|
||||
self.bp_ip = b""
|
||||
|
@ -143,6 +145,16 @@ class MDNS(MCast):
|
|||
sreply = DNSRecord(DNSHeader(0, 0x8400))
|
||||
bye = DNSRecord(DNSHeader(0, 0x8400))
|
||||
|
||||
have4 = have6 = False
|
||||
for s2 in self.srv.values():
|
||||
if srv.idx != s2.idx:
|
||||
continue
|
||||
|
||||
if s2.v6:
|
||||
have6 = True
|
||||
else:
|
||||
have4 = True
|
||||
|
||||
for ip in srv.ips:
|
||||
if ":" in ip:
|
||||
qt = QTYPE.AAAA
|
||||
|
@ -162,6 +174,12 @@ class MDNS(MCast):
|
|||
sreply.add_answer(r120)
|
||||
bye.add_answer(r0)
|
||||
|
||||
if not have4 or not have6:
|
||||
ns = NSEC(self.hn, ["AAAA" if have4 else "A"])
|
||||
r = RR(self.hn, QTYPE.NSEC, DC.F_IN, 120, ns)
|
||||
areply.add_ar(r)
|
||||
sreply.add_ar(r)
|
||||
|
||||
for sclass, props in self.svcs.items():
|
||||
sname = props["name"]
|
||||
sport = props["port"]
|
||||
|
@ -255,13 +273,13 @@ class MDNS(MCast):
|
|||
rx: list[socket.socket] = rdy[0] # type: ignore
|
||||
self.rx4.cln()
|
||||
self.rx6.cln()
|
||||
for srv in rx:
|
||||
buf, addr = srv.recvfrom(4096)
|
||||
for sck in rx:
|
||||
buf, addr = sck.recvfrom(4096)
|
||||
try:
|
||||
self.eat(buf, addr)
|
||||
self.eat(buf, addr, sck)
|
||||
except:
|
||||
t = "{} \033[33m|{}| {}\n{}".format(
|
||||
addr, len(buf), repr(buf)[2:-1], min_ex()
|
||||
t = "{} {} \033[33m|{}| {}\n{}".format(
|
||||
self.srv[sck].name, addr, len(buf), repr(buf)[2:-1], min_ex()
|
||||
)
|
||||
self.log(t, 6)
|
||||
|
||||
|
@ -279,9 +297,11 @@ class MDNS(MCast):
|
|||
for srv in self.srv.values():
|
||||
srv.sck.sendto(srv.bp_bye, (srv.grp, 5353))
|
||||
|
||||
def eat(self, buf: bytes, addr: tuple[str, int]):
|
||||
self.srv = {}
|
||||
|
||||
def eat(self, buf: bytes, addr: tuple[str, int], sck: socket.socket):
|
||||
cip = addr[0]
|
||||
if cip.startswith("fe80") or cip.startswith("169.254"):
|
||||
if cip.startswith("169.254"):
|
||||
return
|
||||
|
||||
v6 = ":" in cip
|
||||
|
@ -290,14 +310,15 @@ class MDNS(MCast):
|
|||
return
|
||||
|
||||
cache.add(buf)
|
||||
srv: Optional[MDNS_Sck] = self.map_client(cip) # type: ignore
|
||||
srv: Optional[MDNS_Sck] = self.srv[sck] if v6 else self.map_client(cip) # type: ignore
|
||||
if not srv:
|
||||
return
|
||||
|
||||
now = time.time()
|
||||
|
||||
if self.args.zmv:
|
||||
self.log("[{}] \033[36m{} \033[0m|{}|".format(srv.ip, cip, len(buf)), "90")
|
||||
t = "{} [{}] \033[36m{} \033[0m|{}|"
|
||||
self.log(t.format(srv.name, srv.ip, cip, len(buf)), "90")
|
||||
|
||||
p = DNSRecord.parse(buf)
|
||||
if self.args.zmvv:
|
||||
|
@ -336,8 +357,8 @@ class MDNS(MCast):
|
|||
else:
|
||||
t += "Emergency stop; hostname '{}' got stolen"
|
||||
|
||||
t += "! Use --name to set another hostname.\n\nName taken by {}\n\nYour IPs: {}\n"
|
||||
self.log(t.format(self.args.name, cips, list(self.sips)), 1)
|
||||
t += " on {}! Use --name to set another hostname.\n\nName taken by {}\n\nYour IPs: {}\n"
|
||||
self.log(t.format(self.args.name, srv.name, cips, list(self.sips)), 1)
|
||||
self.stop(True)
|
||||
return
|
||||
|
||||
|
@ -348,11 +369,17 @@ class MDNS(MCast):
|
|||
|
||||
# gvfs keeps repeating itself
|
||||
found = False
|
||||
unicast = False
|
||||
for r in p.rr:
|
||||
rname = U(r.rname).lower()
|
||||
if rname == self.hn and r.ttl > 60:
|
||||
found = True
|
||||
break
|
||||
if rname == self.hn:
|
||||
if r.ttl > 60:
|
||||
found = True
|
||||
if r.rclass == DC.F_IN:
|
||||
unicast = True
|
||||
|
||||
if unicast:
|
||||
srv.sck.sendto(srv.bp_ip, (cip, 5353))
|
||||
|
||||
if not found:
|
||||
self.q[cip] = (0, srv, srv.bp_ip)
|
||||
|
|
|
@ -26,12 +26,14 @@ class MC_Sck(object):
|
|||
self,
|
||||
sck: socket.socket,
|
||||
idx: int,
|
||||
name: str,
|
||||
grp: str,
|
||||
ip: str,
|
||||
net: Union[IPv4Network, IPv6Network],
|
||||
):
|
||||
self.sck = sck
|
||||
self.idx = idx
|
||||
self.name = name
|
||||
self.grp = grp
|
||||
self.mreq = b""
|
||||
self.ip = ip
|
||||
|
@ -55,7 +57,7 @@ class MCast(object):
|
|||
self.port = port
|
||||
|
||||
self.srv: dict[socket.socket, MC_Sck] = {} # listening sockets
|
||||
self.sips: set[str] = set() # all listening ips
|
||||
self.sips: set[str] = set() # all listening ips (including failed attempts)
|
||||
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
|
||||
|
@ -82,6 +84,7 @@ class MCast(object):
|
|||
|
||||
ips = [x for x in ips if x not in ("::1", "127.0.0.1")]
|
||||
|
||||
# ip -> ip/prefix
|
||||
ips = [
|
||||
[x for x in self.hub.tcpsrv.netdevs if x.startswith(y + "/")][0]
|
||||
for y in ips
|
||||
|
@ -93,6 +96,10 @@ class MCast(object):
|
|||
if not self.grp6:
|
||||
ips = [x for x in ips if ":" not in x]
|
||||
|
||||
# discard non-linklocal ipv6
|
||||
all_selected = ips[:]
|
||||
ips = [x for x in ips if ":" not in x or x.startswith("fe80")]
|
||||
|
||||
if not ips:
|
||||
raise Exception("no server IP matches the mdns config")
|
||||
|
||||
|
@ -117,16 +124,39 @@ class MCast(object):
|
|||
except:
|
||||
pass
|
||||
|
||||
# most ipv6 clients expect multicast on linklocal ip only;
|
||||
# add a/aaaa records for the other nic IPs
|
||||
other_ips: set[str] = set()
|
||||
if v6 and netdev not in ("?", ""):
|
||||
for oip, onic in self.hub.tcpsrv.netdevs.items():
|
||||
if (
|
||||
onic.split(",")[0] == netdev
|
||||
and oip in all_selected
|
||||
and ":" in oip
|
||||
):
|
||||
other_ips.add(oip)
|
||||
|
||||
net = ipaddress.ip_network(ip, False)
|
||||
ip = ip.split("/")[0]
|
||||
srv = self.Srv(sck, idx, self.grp6 if ":" in ip else self.grp4, ip, net)
|
||||
srv = self.Srv(
|
||||
sck, idx, netdev, self.grp6 if ":" in ip else self.grp4, ip, net
|
||||
)
|
||||
for oth_ip in other_ips:
|
||||
srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False)
|
||||
|
||||
# gvfs breaks if a linklocal ip appears in a dns reply
|
||||
srv.ips = {k: v for k, v in srv.ips.items() if not k.startswith("fe80")}
|
||||
if not srv.ips:
|
||||
self.log("no routable IPs on {}; skipping [{}]".format(netdev, ip), 3)
|
||||
continue
|
||||
|
||||
try:
|
||||
self.setup_socket(srv)
|
||||
self.srv[sck] = srv
|
||||
bound.append(ip)
|
||||
except:
|
||||
self.log("announce failed on [{}]:\n{}".format(ip, min_ex()))
|
||||
t = "announce failed on {} [{}]:\n{}"
|
||||
self.log(t.format(netdev, ip, min_ex()), 3)
|
||||
|
||||
if self.args.zm_msub:
|
||||
for s1 in self.srv.values():
|
||||
|
@ -145,18 +175,22 @@ class MCast(object):
|
|||
if net1 == net2 and ip1 != ip2:
|
||||
s1.ips[ip2] = net2
|
||||
|
||||
self.sips = set([x.ip for x in self.srv.values()])
|
||||
self.sips = set([x.split("/")[0] for x in all_selected])
|
||||
for srv in self.srv.values():
|
||||
assert srv.ip in self.sips
|
||||
|
||||
return bound
|
||||
|
||||
def setup_socket(self, srv: MC_Sck) -> None:
|
||||
sck = srv.sck
|
||||
if srv.v6:
|
||||
if self.args.zmv:
|
||||
self.log("v6({}) idx({})".format(srv.ip, srv.idx), 6)
|
||||
self.log("v6({}) idx({}) {}".format(srv.ip, srv.idx, srv.ips), 6)
|
||||
|
||||
bip = socket.inet_pton(socket.AF_INET6, srv.ip)
|
||||
self.b2srv[bip] = srv
|
||||
self.b6.append(bip)
|
||||
for ip in srv.ips:
|
||||
bip = socket.inet_pton(socket.AF_INET6, ip)
|
||||
self.b2srv[bip] = srv
|
||||
self.b6.append(bip)
|
||||
|
||||
sck.bind((self.grp6 if srv.idx else "", self.port, 0, srv.idx))
|
||||
bgrp = socket.inet_pton(socket.AF_INET6, self.grp6)
|
||||
|
|
|
@ -764,7 +764,7 @@ class NSEC(RD):
|
|||
label = property(get_label, set_label)
|
||||
|
||||
def pack(self, buffer):
|
||||
buffer.encode_name_nocompress(self.label)
|
||||
buffer.encode_name(self.label)
|
||||
buffer.append(encode_type_bitmap(self.rrlist))
|
||||
|
||||
def __repr__(self):
|
||||
|
|
|
@ -397,7 +397,7 @@ 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("fe80") or sip.startswith("169.254"):
|
||||
if sip.startswith("169.254"):
|
||||
# browsers dont impl linklocal
|
||||
continue
|
||||
|
||||
|
|
|
@ -16,6 +16,10 @@ https://github.com/paulc/dnslib/
|
|||
C: 2010-2017 Paul Chakravarti
|
||||
L: BSD 2-Clause
|
||||
|
||||
https://github.com/pydron/ifaddr/
|
||||
C: 2014 Stefan C. Mueller
|
||||
L: BSD-2-Clause
|
||||
|
||||
https://github.com/giampaolo/pyftpdlib/
|
||||
C: 2007 Giampaolo Rodola
|
||||
L: MIT
|
||||
|
|
|
@ -19,6 +19,7 @@ copyparty/httpconn.py,
|
|||
copyparty/httpsrv.py,
|
||||
copyparty/ico.py,
|
||||
copyparty/mdns.py,
|
||||
copyparty/multicast.py,
|
||||
copyparty/mtag.py,
|
||||
copyparty/res,
|
||||
copyparty/res/COPYING.txt,
|
||||
|
|
Loading…
Reference in a new issue