diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 05e11463..90b4c626 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -672,6 +672,8 @@ def add_network(ap): ap2.add_argument("--rp-loc", metavar="PATH", type=u, default="", help="if reverse-proxying on a location instead of a dedicated domain/subdomain, provide the base location here (eg. /foo/bar)") 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") + else: + ap2.add_argument("--freebind", action="store_true", help="allow listening on IPs which do not yet exist, for example if the network interfaces haven't finished going up. Only makes sense for IPs other than '0.0.0.0', '127.0.0.1', '::', and '::1'. May require running as root (unless net.ipv6.ip_nonlocal_bind)") ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes") ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0, help="debug: socket write delay in seconds") ap2.add_argument("--rsp-slp", metavar="SEC", type=float, default=0, help="debug: response delay in seconds") diff --git a/copyparty/multicast.py b/copyparty/multicast.py index a04e5d73..9baed3df 100644 --- a/copyparty/multicast.py +++ b/copyparty/multicast.py @@ -15,7 +15,7 @@ from ipaddress import ( ) from .__init__ import TYPE_CHECKING -from .util import MACOS, Netdev, min_ex, spack +from .util import MACOS, Netdev, find_prefix, min_ex, spack if TYPE_CHECKING: from .svchub import SvcHub @@ -110,9 +110,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 netdevs if x.startswith(y + "/")][0] for y in ips] + ips = find_prefix(ips, netdevs) on = self.on[:] off = self.off[:] diff --git a/copyparty/tcpsrv.py b/copyparty/tcpsrv.py index 5ad25ed7..826e56c7 100644 --- a/copyparty/tcpsrv.py +++ b/copyparty/tcpsrv.py @@ -29,6 +29,9 @@ if TYPE_CHECKING: if not hasattr(socket, "IPPROTO_IPV6"): setattr(socket, "IPPROTO_IPV6", 41) +if not hasattr(socket, "IP_FREEBIND"): + setattr(socket, "IP_FREEBIND", 15) + class TcpSrv(object): """ @@ -224,6 +227,9 @@ class TcpSrv(object): except: pass # will create another ipv4 socket instead + if not ANYWIN and self.args.freebind: + srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1) + try: srv.bind((ip, port)) self.srv.append(srv) diff --git a/copyparty/util.py b/copyparty/util.py index c816d9be..ad65992b 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -471,7 +471,7 @@ class NetMap(object): ) ips = [x for x in ips if x not in ("::1", "127.0.0.1")] - ips = [[x for x in netdevs if x.startswith(y + "/")][0] for y in ips] + ips = find_prefix(ips, netdevs) self.cache: dict[str, str] = {} self.b2sip: dict[bytes, str] = {} @@ -1714,6 +1714,15 @@ def ipnorm(ip: str) -> str: return ip +def find_prefix(ips: list[str], netdevs: dict[str, Netdev]) -> list[str]: + ret = [] + for ip in ips: + hit = next((x for x in netdevs if x.startswith(ip + "/")), None) + if hit: + ret.append(hit) + return ret + + def html_escape(s: str, quot: bool = False, crlf: bool = False) -> str: """html.escape but also newlines""" s = s.replace("&", "&").replace("<", "<").replace(">", ">")