feat: CLI unificado mdns con browse/reverse/IPv6/cache-TTL
Cierra el gap hacia una implementacion mDNS/DNS-SD completa (RFC 6762/6763).
CLI nuevo `bin/mdns` con subcomandos:
- resolve : hostname -> IP (IPv4 stdlib rapido, IPv6 via zeroconf), cache TTL.
- reverse : IP -> hostname (PTR multicast, IPv4/IPv6).
- browse : descubrimiento DNS-SD (--list-types o --type _ssh._tcp.local.).
- publish : anuncia hostname + servicios (SSH/SMB/HTTP) con probing y goodbye.
- cache : list/clear del cache con TTL.
Biblioteca lib/mdns_tools/ (cada modulo < 100 lineas):
- resolve.py: stdlib-first para IPv4, zeroconf fallback para IPv6/miss.
- _stdlib_resolve.py: resolver A puro stdlib, acumula todas las A del host.
- _pick.py: elige la IP mas alcanzable ante multi-homing. Descarta rangos
virtuales (libvirt 192.168.122/24, docker 172.17/16, link-local) devolviendo
None para que el caller caiga a Tailscale en vez de a una IP muerta.
- browse.py / publish.py: DNS-SD sobre zeroconf.
- reverse.py: PTR stdlib. cache.py: TTL-aware JSON en ~/.cache/mdns.
Mejoras de calidad:
- IP_ADD_MEMBERSHIP en todos los sockets multicast.
- Retries con backoff (timeout dividido).
- Imports lazy en el CLI (resolve/cache no cargan zeroconf -> ~1s vs ~4s).
Wrappers:
- resolve (bash): orden mdns -> mdns-resolve stdlib -> Tailscale -> cache plano.
- mdns-publish.py: shim compat sobre `mdns publish`, lee ~/.config/mdns/services.json.
- install.sh: symlinks bin/ (updates via git pull), siembra services.json.
- ssh_config.example: HostKeyAlias documentado (alias comparte known_hosts con
el nombre canonico; evita fallos de verificacion tras reinstalar un host).
- bashrc.snippet: aliases mdns-types/ssh/smb/http, mdns-cache, whor.
Validado E2E en LAN: browse detecta 13 tipos; dell/movil resuelven a IP LAN
real; lenovo (solo publica virbr0 192.168.122.1) cae correctamente a Tailscale;
IPv6 de dell via zeroconf; reverse 192.168.1.17 -> dell-latitude3400.local;
publish visible desde otro browser. Modulos < 100 lineas salvo el dispatcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 12:06:53 +00:00
|
|
|
#!/data/data/com.termux/files/usr/bin/python3
|
|
|
|
|
"""mdns: unified CLI for mDNS resolve / browse / publish / reverse / cache.
|
|
|
|
|
|
|
|
|
|
Loads lib/mdns_tools/ from the repo root. Usable both from an install into
|
|
|
|
|
~/.local/bin (installer sets a symlink pointing at the repo checkout) and
|
|
|
|
|
directly from a clone.
|
|
|
|
|
"""
|
|
|
|
|
import argparse
|
|
|
|
|
import json
|
|
|
|
|
import os
|
|
|
|
|
import sys
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
_ROOT = Path(__file__).resolve().parent.parent
|
|
|
|
|
sys.path.insert(0, str(_ROOT / "lib"))
|
|
|
|
|
|
|
|
|
|
from mdns_tools import __version__ # noqa: E402
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cmd_resolve(a: argparse.Namespace) -> int:
|
|
|
|
|
from mdns_tools import resolve as _r, cache as _c
|
|
|
|
|
ip = _r.resolve(a.host, family=a.family, timeout=a.timeout,
|
|
|
|
|
retries=a.retries, use_cache=not a.no_cache)
|
|
|
|
|
if ip:
|
|
|
|
|
print(ip)
|
|
|
|
|
return 0
|
|
|
|
|
if a.stale:
|
|
|
|
|
ip = _c.get_stale(a.host if a.host.endswith(".local") else f"{a.host}.local",
|
|
|
|
|
family=a.family)
|
|
|
|
|
if ip:
|
|
|
|
|
print(ip)
|
|
|
|
|
return 0
|
|
|
|
|
print(f"error: could not resolve '{a.host}'", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cmd_reverse(a: argparse.Namespace) -> int:
|
|
|
|
|
from mdns_tools import reverse as _rev
|
|
|
|
|
name = _rev.reverse(a.ip, timeout=a.timeout, retries=a.retries)
|
|
|
|
|
if name:
|
|
|
|
|
print(name)
|
|
|
|
|
return 0
|
|
|
|
|
print(f"error: no PTR for {a.ip}", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cmd_browse(a: argparse.Namespace) -> int:
|
feat: monitor en vivo (--watch) y publish --address; interop validada
Añade las dos funciones que Avahi/Bonjour tenían y faltaban:
- `mdns browse --watch`: monitor DNS-SD en vivo (eventos +/=/-), equivalente
a `avahi-browse -a`. Sin --type descubre tipos dinámicamente via el
meta-servicio _services._dns-sd._udp.local. y adjunta un browser por tipo.
Nuevo modulo lib/mdns_tools/watch.py.
- `mdns publish --address <nombre> <ip>`: publica un A record puro
hostname->IP, equivalente a `avahi-publish -a`.
Robustez del publisher:
- Maneja NonUniqueNameException (probing RFC 6762): si el nombre ya esta
tomado, deja que zeroconf renombre en vez de crashear.
- `mdns publish` sin --config auto-carga ~/.config/mdns/services.json.
Interoperabilidad validada en LAN real (documentada en README):
- Termux -> Avahi: con `mdns publish` activo, un Linux con nss-mdns resolvio
`movil.local` y le hizo ping sin configuracion extra (getent + ping OK).
- Avahi -> Termux: `mdns browse` ve los servicios que publican equipos
Avahi/Bonjour (SMB, SSH, _workstation, Chromecast, AirPlay...).
Al usar zeroconf (RFC 6762/6763), Mac/Linux/Windows descubren y resuelven
lo publicado y viceversa, sin cliente especial.
README: seccion de interoperabilidad, tabla RFC ampliada, ejemplos --watch
y --address. bashrc.snippet: alias mdns-watch. publish.py recortado a <100.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 13:00:07 +00:00
|
|
|
if a.watch:
|
|
|
|
|
from mdns_tools import watch as _w
|
|
|
|
|
_w.watch(service_type=a.type, resolve=not a.no_resolve)
|
|
|
|
|
return 0
|
feat: CLI unificado mdns con browse/reverse/IPv6/cache-TTL
Cierra el gap hacia una implementacion mDNS/DNS-SD completa (RFC 6762/6763).
CLI nuevo `bin/mdns` con subcomandos:
- resolve : hostname -> IP (IPv4 stdlib rapido, IPv6 via zeroconf), cache TTL.
- reverse : IP -> hostname (PTR multicast, IPv4/IPv6).
- browse : descubrimiento DNS-SD (--list-types o --type _ssh._tcp.local.).
- publish : anuncia hostname + servicios (SSH/SMB/HTTP) con probing y goodbye.
- cache : list/clear del cache con TTL.
Biblioteca lib/mdns_tools/ (cada modulo < 100 lineas):
- resolve.py: stdlib-first para IPv4, zeroconf fallback para IPv6/miss.
- _stdlib_resolve.py: resolver A puro stdlib, acumula todas las A del host.
- _pick.py: elige la IP mas alcanzable ante multi-homing. Descarta rangos
virtuales (libvirt 192.168.122/24, docker 172.17/16, link-local) devolviendo
None para que el caller caiga a Tailscale en vez de a una IP muerta.
- browse.py / publish.py: DNS-SD sobre zeroconf.
- reverse.py: PTR stdlib. cache.py: TTL-aware JSON en ~/.cache/mdns.
Mejoras de calidad:
- IP_ADD_MEMBERSHIP en todos los sockets multicast.
- Retries con backoff (timeout dividido).
- Imports lazy en el CLI (resolve/cache no cargan zeroconf -> ~1s vs ~4s).
Wrappers:
- resolve (bash): orden mdns -> mdns-resolve stdlib -> Tailscale -> cache plano.
- mdns-publish.py: shim compat sobre `mdns publish`, lee ~/.config/mdns/services.json.
- install.sh: symlinks bin/ (updates via git pull), siembra services.json.
- ssh_config.example: HostKeyAlias documentado (alias comparte known_hosts con
el nombre canonico; evita fallos de verificacion tras reinstalar un host).
- bashrc.snippet: aliases mdns-types/ssh/smb/http, mdns-cache, whor.
Validado E2E en LAN: browse detecta 13 tipos; dell/movil resuelven a IP LAN
real; lenovo (solo publica virbr0 192.168.122.1) cae correctamente a Tailscale;
IPv6 de dell via zeroconf; reverse 192.168.1.17 -> dell-latitude3400.local;
publish visible desde otro browser. Modulos < 100 lineas salvo el dispatcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 12:06:53 +00:00
|
|
|
from mdns_tools import browse as _br # loads zeroconf
|
|
|
|
|
if a.list_types:
|
|
|
|
|
for t in _br.list_types(duration=a.duration):
|
|
|
|
|
print(t)
|
|
|
|
|
return 0
|
|
|
|
|
items = _br.browse(service_type=a.type, duration=a.duration)
|
|
|
|
|
if a.json:
|
|
|
|
|
print(json.dumps(items, indent=2))
|
|
|
|
|
return 0
|
|
|
|
|
for it in items:
|
|
|
|
|
addrs = ", ".join(it.get("addresses") or []) or "-"
|
|
|
|
|
print(f'{it["type"]:<30} {it.get("server","-"):<28} {addrs}:{it.get("port","-")}')
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cmd_publish(a: argparse.Namespace) -> int:
|
|
|
|
|
from mdns_tools import publish as _p # loads zeroconf
|
feat: monitor en vivo (--watch) y publish --address; interop validada
Añade las dos funciones que Avahi/Bonjour tenían y faltaban:
- `mdns browse --watch`: monitor DNS-SD en vivo (eventos +/=/-), equivalente
a `avahi-browse -a`. Sin --type descubre tipos dinámicamente via el
meta-servicio _services._dns-sd._udp.local. y adjunta un browser por tipo.
Nuevo modulo lib/mdns_tools/watch.py.
- `mdns publish --address <nombre> <ip>`: publica un A record puro
hostname->IP, equivalente a `avahi-publish -a`.
Robustez del publisher:
- Maneja NonUniqueNameException (probing RFC 6762): si el nombre ya esta
tomado, deja que zeroconf renombre en vez de crashear.
- `mdns publish` sin --config auto-carga ~/.config/mdns/services.json.
Interoperabilidad validada en LAN real (documentada en README):
- Termux -> Avahi: con `mdns publish` activo, un Linux con nss-mdns resolvio
`movil.local` y le hizo ping sin configuracion extra (getent + ping OK).
- Avahi -> Termux: `mdns browse` ve los servicios que publican equipos
Avahi/Bonjour (SMB, SSH, _workstation, Chromecast, AirPlay...).
Al usar zeroconf (RFC 6762/6763), Mac/Linux/Windows descubren y resuelven
lo publicado y viceversa, sin cliente especial.
README: seccion de interoperabilidad, tabla RFC ampliada, ejemplos --watch
y --address. bashrc.snippet: alias mdns-watch. publish.py recortado a <100.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 13:00:07 +00:00
|
|
|
if a.address:
|
|
|
|
|
_p.publish_address(a.address[0], a.address[1], verbose=not a.quiet)
|
|
|
|
|
return 0
|
feat: CLI unificado mdns con browse/reverse/IPv6/cache-TTL
Cierra el gap hacia una implementacion mDNS/DNS-SD completa (RFC 6762/6763).
CLI nuevo `bin/mdns` con subcomandos:
- resolve : hostname -> IP (IPv4 stdlib rapido, IPv6 via zeroconf), cache TTL.
- reverse : IP -> hostname (PTR multicast, IPv4/IPv6).
- browse : descubrimiento DNS-SD (--list-types o --type _ssh._tcp.local.).
- publish : anuncia hostname + servicios (SSH/SMB/HTTP) con probing y goodbye.
- cache : list/clear del cache con TTL.
Biblioteca lib/mdns_tools/ (cada modulo < 100 lineas):
- resolve.py: stdlib-first para IPv4, zeroconf fallback para IPv6/miss.
- _stdlib_resolve.py: resolver A puro stdlib, acumula todas las A del host.
- _pick.py: elige la IP mas alcanzable ante multi-homing. Descarta rangos
virtuales (libvirt 192.168.122/24, docker 172.17/16, link-local) devolviendo
None para que el caller caiga a Tailscale en vez de a una IP muerta.
- browse.py / publish.py: DNS-SD sobre zeroconf.
- reverse.py: PTR stdlib. cache.py: TTL-aware JSON en ~/.cache/mdns.
Mejoras de calidad:
- IP_ADD_MEMBERSHIP en todos los sockets multicast.
- Retries con backoff (timeout dividido).
- Imports lazy en el CLI (resolve/cache no cargan zeroconf -> ~1s vs ~4s).
Wrappers:
- resolve (bash): orden mdns -> mdns-resolve stdlib -> Tailscale -> cache plano.
- mdns-publish.py: shim compat sobre `mdns publish`, lee ~/.config/mdns/services.json.
- install.sh: symlinks bin/ (updates via git pull), siembra services.json.
- ssh_config.example: HostKeyAlias documentado (alias comparte known_hosts con
el nombre canonico; evita fallos de verificacion tras reinstalar un host).
- bashrc.snippet: aliases mdns-types/ssh/smb/http, mdns-cache, whor.
Validado E2E en LAN: browse detecta 13 tipos; dell/movil resuelven a IP LAN
real; lenovo (solo publica virbr0 192.168.122.1) cae correctamente a Tailscale;
IPv6 de dell via zeroconf; reverse 192.168.1.17 -> dell-latitude3400.local;
publish visible desde otro browser. Modulos < 100 lineas salvo el dispatcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 12:06:53 +00:00
|
|
|
_p.run(config_path=a.config, hostname=a.hostname, verbose=not a.quiet)
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _cmd_cache(a: argparse.Namespace) -> int:
|
|
|
|
|
from mdns_tools import cache as _c
|
|
|
|
|
if a.op == "list":
|
|
|
|
|
for k, v in _c.all_entries().items():
|
|
|
|
|
host, fam = k.split("|") if "|" in k else (k, "v4")
|
|
|
|
|
print(f'{host:<40} {fam} {v.get("ip"):<40} ttl={v.get("ttl")}')
|
|
|
|
|
elif a.op == "clear":
|
|
|
|
|
n = _c.clear(a.host)
|
|
|
|
|
print(f"removed {n} entries")
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main() -> int:
|
|
|
|
|
p = argparse.ArgumentParser(prog="mdns", description="mDNS / DNS-SD tools")
|
|
|
|
|
p.add_argument("--version", action="version", version=f"mdns {__version__}")
|
|
|
|
|
sub = p.add_subparsers(dest="cmd", required=True)
|
|
|
|
|
|
|
|
|
|
r = sub.add_parser("resolve", help="hostname -> IP")
|
|
|
|
|
r.add_argument("host")
|
|
|
|
|
r.add_argument("--family", choices=["v4", "v6"], default="v4")
|
|
|
|
|
r.add_argument("--timeout", type=float, default=1.5)
|
|
|
|
|
r.add_argument("--retries", type=int, default=2)
|
|
|
|
|
r.add_argument("--no-cache", action="store_true")
|
|
|
|
|
r.add_argument("--stale", action="store_true", help="accept expired cache as last resort")
|
|
|
|
|
r.set_defaults(func=_cmd_resolve)
|
|
|
|
|
|
|
|
|
|
rv = sub.add_parser("reverse", help="IP -> hostname (PTR)")
|
|
|
|
|
rv.add_argument("ip")
|
|
|
|
|
rv.add_argument("--timeout", type=float, default=1.5)
|
|
|
|
|
rv.add_argument("--retries", type=int, default=2)
|
|
|
|
|
rv.set_defaults(func=_cmd_reverse)
|
|
|
|
|
|
|
|
|
|
b = sub.add_parser("browse", help="discover services on the LAN")
|
|
|
|
|
b.add_argument("--type", help="e.g. _ssh._tcp.local. (default: all types)")
|
|
|
|
|
b.add_argument("--duration", type=float, default=3.0)
|
|
|
|
|
b.add_argument("--json", action="store_true")
|
|
|
|
|
b.add_argument("--list-types", action="store_true")
|
feat: monitor en vivo (--watch) y publish --address; interop validada
Añade las dos funciones que Avahi/Bonjour tenían y faltaban:
- `mdns browse --watch`: monitor DNS-SD en vivo (eventos +/=/-), equivalente
a `avahi-browse -a`. Sin --type descubre tipos dinámicamente via el
meta-servicio _services._dns-sd._udp.local. y adjunta un browser por tipo.
Nuevo modulo lib/mdns_tools/watch.py.
- `mdns publish --address <nombre> <ip>`: publica un A record puro
hostname->IP, equivalente a `avahi-publish -a`.
Robustez del publisher:
- Maneja NonUniqueNameException (probing RFC 6762): si el nombre ya esta
tomado, deja que zeroconf renombre en vez de crashear.
- `mdns publish` sin --config auto-carga ~/.config/mdns/services.json.
Interoperabilidad validada en LAN real (documentada en README):
- Termux -> Avahi: con `mdns publish` activo, un Linux con nss-mdns resolvio
`movil.local` y le hizo ping sin configuracion extra (getent + ping OK).
- Avahi -> Termux: `mdns browse` ve los servicios que publican equipos
Avahi/Bonjour (SMB, SSH, _workstation, Chromecast, AirPlay...).
Al usar zeroconf (RFC 6762/6763), Mac/Linux/Windows descubren y resuelven
lo publicado y viceversa, sin cliente especial.
README: seccion de interoperabilidad, tabla RFC ampliada, ejemplos --watch
y --address. bashrc.snippet: alias mdns-watch. publish.py recortado a <100.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 13:00:07 +00:00
|
|
|
b.add_argument("--watch", action="store_true",
|
|
|
|
|
help="live monitor (+/=/- events) until Ctrl+C")
|
|
|
|
|
b.add_argument("--no-resolve", action="store_true",
|
|
|
|
|
help="in --watch, don't resolve addresses")
|
feat: CLI unificado mdns con browse/reverse/IPv6/cache-TTL
Cierra el gap hacia una implementacion mDNS/DNS-SD completa (RFC 6762/6763).
CLI nuevo `bin/mdns` con subcomandos:
- resolve : hostname -> IP (IPv4 stdlib rapido, IPv6 via zeroconf), cache TTL.
- reverse : IP -> hostname (PTR multicast, IPv4/IPv6).
- browse : descubrimiento DNS-SD (--list-types o --type _ssh._tcp.local.).
- publish : anuncia hostname + servicios (SSH/SMB/HTTP) con probing y goodbye.
- cache : list/clear del cache con TTL.
Biblioteca lib/mdns_tools/ (cada modulo < 100 lineas):
- resolve.py: stdlib-first para IPv4, zeroconf fallback para IPv6/miss.
- _stdlib_resolve.py: resolver A puro stdlib, acumula todas las A del host.
- _pick.py: elige la IP mas alcanzable ante multi-homing. Descarta rangos
virtuales (libvirt 192.168.122/24, docker 172.17/16, link-local) devolviendo
None para que el caller caiga a Tailscale en vez de a una IP muerta.
- browse.py / publish.py: DNS-SD sobre zeroconf.
- reverse.py: PTR stdlib. cache.py: TTL-aware JSON en ~/.cache/mdns.
Mejoras de calidad:
- IP_ADD_MEMBERSHIP en todos los sockets multicast.
- Retries con backoff (timeout dividido).
- Imports lazy en el CLI (resolve/cache no cargan zeroconf -> ~1s vs ~4s).
Wrappers:
- resolve (bash): orden mdns -> mdns-resolve stdlib -> Tailscale -> cache plano.
- mdns-publish.py: shim compat sobre `mdns publish`, lee ~/.config/mdns/services.json.
- install.sh: symlinks bin/ (updates via git pull), siembra services.json.
- ssh_config.example: HostKeyAlias documentado (alias comparte known_hosts con
el nombre canonico; evita fallos de verificacion tras reinstalar un host).
- bashrc.snippet: aliases mdns-types/ssh/smb/http, mdns-cache, whor.
Validado E2E en LAN: browse detecta 13 tipos; dell/movil resuelven a IP LAN
real; lenovo (solo publica virbr0 192.168.122.1) cae correctamente a Tailscale;
IPv6 de dell via zeroconf; reverse 192.168.1.17 -> dell-latitude3400.local;
publish visible desde otro browser. Modulos < 100 lineas salvo el dispatcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 12:06:53 +00:00
|
|
|
b.set_defaults(func=_cmd_browse)
|
|
|
|
|
|
|
|
|
|
pu = sub.add_parser("publish", help="advertise this device + services")
|
feat: monitor en vivo (--watch) y publish --address; interop validada
Añade las dos funciones que Avahi/Bonjour tenían y faltaban:
- `mdns browse --watch`: monitor DNS-SD en vivo (eventos +/=/-), equivalente
a `avahi-browse -a`. Sin --type descubre tipos dinámicamente via el
meta-servicio _services._dns-sd._udp.local. y adjunta un browser por tipo.
Nuevo modulo lib/mdns_tools/watch.py.
- `mdns publish --address <nombre> <ip>`: publica un A record puro
hostname->IP, equivalente a `avahi-publish -a`.
Robustez del publisher:
- Maneja NonUniqueNameException (probing RFC 6762): si el nombre ya esta
tomado, deja que zeroconf renombre en vez de crashear.
- `mdns publish` sin --config auto-carga ~/.config/mdns/services.json.
Interoperabilidad validada en LAN real (documentada en README):
- Termux -> Avahi: con `mdns publish` activo, un Linux con nss-mdns resolvio
`movil.local` y le hizo ping sin configuracion extra (getent + ping OK).
- Avahi -> Termux: `mdns browse` ve los servicios que publican equipos
Avahi/Bonjour (SMB, SSH, _workstation, Chromecast, AirPlay...).
Al usar zeroconf (RFC 6762/6763), Mac/Linux/Windows descubren y resuelven
lo publicado y viceversa, sin cliente especial.
README: seccion de interoperabilidad, tabla RFC ampliada, ejemplos --watch
y --address. bashrc.snippet: alias mdns-watch. publish.py recortado a <100.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 13:00:07 +00:00
|
|
|
pu.add_argument("--config", help="path to services JSON")
|
feat: CLI unificado mdns con browse/reverse/IPv6/cache-TTL
Cierra el gap hacia una implementacion mDNS/DNS-SD completa (RFC 6762/6763).
CLI nuevo `bin/mdns` con subcomandos:
- resolve : hostname -> IP (IPv4 stdlib rapido, IPv6 via zeroconf), cache TTL.
- reverse : IP -> hostname (PTR multicast, IPv4/IPv6).
- browse : descubrimiento DNS-SD (--list-types o --type _ssh._tcp.local.).
- publish : anuncia hostname + servicios (SSH/SMB/HTTP) con probing y goodbye.
- cache : list/clear del cache con TTL.
Biblioteca lib/mdns_tools/ (cada modulo < 100 lineas):
- resolve.py: stdlib-first para IPv4, zeroconf fallback para IPv6/miss.
- _stdlib_resolve.py: resolver A puro stdlib, acumula todas las A del host.
- _pick.py: elige la IP mas alcanzable ante multi-homing. Descarta rangos
virtuales (libvirt 192.168.122/24, docker 172.17/16, link-local) devolviendo
None para que el caller caiga a Tailscale en vez de a una IP muerta.
- browse.py / publish.py: DNS-SD sobre zeroconf.
- reverse.py: PTR stdlib. cache.py: TTL-aware JSON en ~/.cache/mdns.
Mejoras de calidad:
- IP_ADD_MEMBERSHIP en todos los sockets multicast.
- Retries con backoff (timeout dividido).
- Imports lazy en el CLI (resolve/cache no cargan zeroconf -> ~1s vs ~4s).
Wrappers:
- resolve (bash): orden mdns -> mdns-resolve stdlib -> Tailscale -> cache plano.
- mdns-publish.py: shim compat sobre `mdns publish`, lee ~/.config/mdns/services.json.
- install.sh: symlinks bin/ (updates via git pull), siembra services.json.
- ssh_config.example: HostKeyAlias documentado (alias comparte known_hosts con
el nombre canonico; evita fallos de verificacion tras reinstalar un host).
- bashrc.snippet: aliases mdns-types/ssh/smb/http, mdns-cache, whor.
Validado E2E en LAN: browse detecta 13 tipos; dell/movil resuelven a IP LAN
real; lenovo (solo publica virbr0 192.168.122.1) cae correctamente a Tailscale;
IPv6 de dell via zeroconf; reverse 192.168.1.17 -> dell-latitude3400.local;
publish visible desde otro browser. Modulos < 100 lineas salvo el dispatcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 12:06:53 +00:00
|
|
|
pu.add_argument("--hostname")
|
feat: monitor en vivo (--watch) y publish --address; interop validada
Añade las dos funciones que Avahi/Bonjour tenían y faltaban:
- `mdns browse --watch`: monitor DNS-SD en vivo (eventos +/=/-), equivalente
a `avahi-browse -a`. Sin --type descubre tipos dinámicamente via el
meta-servicio _services._dns-sd._udp.local. y adjunta un browser por tipo.
Nuevo modulo lib/mdns_tools/watch.py.
- `mdns publish --address <nombre> <ip>`: publica un A record puro
hostname->IP, equivalente a `avahi-publish -a`.
Robustez del publisher:
- Maneja NonUniqueNameException (probing RFC 6762): si el nombre ya esta
tomado, deja que zeroconf renombre en vez de crashear.
- `mdns publish` sin --config auto-carga ~/.config/mdns/services.json.
Interoperabilidad validada en LAN real (documentada en README):
- Termux -> Avahi: con `mdns publish` activo, un Linux con nss-mdns resolvio
`movil.local` y le hizo ping sin configuracion extra (getent + ping OK).
- Avahi -> Termux: `mdns browse` ve los servicios que publican equipos
Avahi/Bonjour (SMB, SSH, _workstation, Chromecast, AirPlay...).
Al usar zeroconf (RFC 6762/6763), Mac/Linux/Windows descubren y resuelven
lo publicado y viceversa, sin cliente especial.
README: seccion de interoperabilidad, tabla RFC ampliada, ejemplos --watch
y --address. bashrc.snippet: alias mdns-watch. publish.py recortado a <100.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 13:00:07 +00:00
|
|
|
pu.add_argument("--address", nargs=2, metavar=("NAME", "IP"),
|
|
|
|
|
help="publish a bare hostname->IP (A record), like avahi-publish -a")
|
feat: CLI unificado mdns con browse/reverse/IPv6/cache-TTL
Cierra el gap hacia una implementacion mDNS/DNS-SD completa (RFC 6762/6763).
CLI nuevo `bin/mdns` con subcomandos:
- resolve : hostname -> IP (IPv4 stdlib rapido, IPv6 via zeroconf), cache TTL.
- reverse : IP -> hostname (PTR multicast, IPv4/IPv6).
- browse : descubrimiento DNS-SD (--list-types o --type _ssh._tcp.local.).
- publish : anuncia hostname + servicios (SSH/SMB/HTTP) con probing y goodbye.
- cache : list/clear del cache con TTL.
Biblioteca lib/mdns_tools/ (cada modulo < 100 lineas):
- resolve.py: stdlib-first para IPv4, zeroconf fallback para IPv6/miss.
- _stdlib_resolve.py: resolver A puro stdlib, acumula todas las A del host.
- _pick.py: elige la IP mas alcanzable ante multi-homing. Descarta rangos
virtuales (libvirt 192.168.122/24, docker 172.17/16, link-local) devolviendo
None para que el caller caiga a Tailscale en vez de a una IP muerta.
- browse.py / publish.py: DNS-SD sobre zeroconf.
- reverse.py: PTR stdlib. cache.py: TTL-aware JSON en ~/.cache/mdns.
Mejoras de calidad:
- IP_ADD_MEMBERSHIP en todos los sockets multicast.
- Retries con backoff (timeout dividido).
- Imports lazy en el CLI (resolve/cache no cargan zeroconf -> ~1s vs ~4s).
Wrappers:
- resolve (bash): orden mdns -> mdns-resolve stdlib -> Tailscale -> cache plano.
- mdns-publish.py: shim compat sobre `mdns publish`, lee ~/.config/mdns/services.json.
- install.sh: symlinks bin/ (updates via git pull), siembra services.json.
- ssh_config.example: HostKeyAlias documentado (alias comparte known_hosts con
el nombre canonico; evita fallos de verificacion tras reinstalar un host).
- bashrc.snippet: aliases mdns-types/ssh/smb/http, mdns-cache, whor.
Validado E2E en LAN: browse detecta 13 tipos; dell/movil resuelven a IP LAN
real; lenovo (solo publica virbr0 192.168.122.1) cae correctamente a Tailscale;
IPv6 de dell via zeroconf; reverse 192.168.1.17 -> dell-latitude3400.local;
publish visible desde otro browser. Modulos < 100 lineas salvo el dispatcher.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-20 12:06:53 +00:00
|
|
|
pu.add_argument("--quiet", action="store_true")
|
|
|
|
|
pu.set_defaults(func=_cmd_publish)
|
|
|
|
|
|
|
|
|
|
ca = sub.add_parser("cache", help="inspect/clear the resolver cache")
|
|
|
|
|
ca.add_argument("op", choices=["list", "clear"])
|
|
|
|
|
ca.add_argument("host", nargs="?")
|
|
|
|
|
ca.set_defaults(func=_cmd_cache)
|
|
|
|
|
|
|
|
|
|
args = p.parse_args()
|
|
|
|
|
return args.func(args)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
sys.exit(main())
|