@@ -287,20 +287,22 @@ def _from_ifconfig(self) -> None:
287287def is_phone_reachable_gateway_url (url : str ) -> bool :
288288 """True when a phone on LAN/VPN can open this gateway base URL.
289289
290- Rejects empty hosts, ``localhost``, IPv4/IPv6 loopback, link-local,
291- unspecified, and multicast. Non-IP hostnames (MagicDNS, custom DNS) pass.
290+ Rejects empty hosts, ``localhost`` / ``localhost.*``, abbreviated IPv4
291+ loopback (``127.1``), IPv4/IPv6 loopback, link-local, unspecified, and
292+ multicast. Other non-IP hostnames (MagicDNS, custom DNS) pass without DNS
293+ lookup.
292294 """
293295 host = urlparse (url ).hostname
294296 if not host :
295297 return False
296- if host .lower () == "localhost" :
298+ lowered = host .lower ()
299+ if lowered == "localhost" or lowered .startswith ("localhost." ):
297300 return False
298- try :
299- ip_address = ipaddress .ip_address (host )
300- except ValueError :
301+ ip_address = _parse_gateway_host_ip (host )
302+ if ip_address is None :
301303 return True
302304 if isinstance (ip_address , ipaddress .IPv4Address ):
303- return _Ipv4Policy .is_reachable (host )
305+ return _Ipv4Policy .is_reachable (str ( ip_address ) )
304306 blocked = (
305307 ip_address .is_loopback
306308 or ip_address .is_link_local
@@ -310,6 +312,21 @@ def is_phone_reachable_gateway_url(url: str) -> bool:
310312 return not blocked
311313
312314
315+ def _parse_gateway_host_ip (host : str ) -> ipaddress .IPv4Address | ipaddress .IPv6Address | None :
316+ """Parse a URL host as an IP, including short-form IPv4 that ``ip_address`` rejects."""
317+ try :
318+ return ipaddress .ip_address (host )
319+ except ValueError :
320+ return _parse_short_ipv4_host (host )
321+
322+
323+ def _parse_short_ipv4_host (host : str ) -> ipaddress .IPv4Address | None :
324+ try :
325+ return ipaddress .IPv4Address (socket .inet_aton (host ))
326+ except OSError :
327+ return None
328+
329+
313330def unreachable_pairing_override () -> tuple [str , str ] | None :
314331 """Return ``(env_key, raw_value)`` when a pairing override is set but unusable.
315332
@@ -350,6 +367,8 @@ def default_url(cls, port: int, *, saved_pairing_url: str | None = None) -> str
350367
351368 @classmethod
352369 def _keep_saved (cls , port : int , saved_pairing_url : str ) -> bool :
370+ if not is_phone_reachable_gateway_url (saved_pairing_url ):
371+ return False
353372 if not is_ambient_lan_address (saved_pairing_url ):
354373 return True
355374 return saved_pairing_url in cls .discover (port )
0 commit comments