2026-07-20 01:53:44 +00:00
|
|
|
|
# mdns-termux
|
|
|
|
|
|
|
|
|
|
|
|
**mDNS + Tailscale hostname resolution for Termux (Android).**
|
|
|
|
|
|
|
|
|
|
|
|
Termux runs on Android's Bionic libc, which does **not** resolve `.local` names
|
|
|
|
|
|
and lacks `getent`. This repo ships small, dependency-light helpers that fix
|
|
|
|
|
|
that — without root, and without a custom system resolver.
|
|
|
|
|
|
|
|
|
|
|
|
Works on any network: LAN via multicast, off-LAN via Tailscale MagicDNS.
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## What's in here
|
|
|
|
|
|
|
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
|
|
|
|
**Unified CLI:** `mdns` with five subcommands (`resolve`, `browse`, `publish`,
|
|
|
|
|
|
`reverse`, `cache`). Backed by `zeroconf` for RFC-complete behavior — TTL
|
|
|
|
|
|
cache, IPv4 + IPv6, service discovery, probing on publish, goodbye packets
|
|
|
|
|
|
on exit.
|
|
|
|
|
|
|
2026-07-20 01:53:44 +00:00
|
|
|
|
| Script | Role |
|
|
|
|
|
|
|--------|------|
|
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
|
|
|
|
| `mdns` | Unified CLI (zeroconf-backed). Preferred entry point. |
|
|
|
|
|
|
| `mdns-resolve` | Standalone stdlib fallback: works with **zero** dependencies. |
|
|
|
|
|
|
| `mdns-publish.py` | Compat shim over `mdns publish` for existing aliases. |
|
2026-07-20 01:53:44 +00:00
|
|
|
|
| `ssh-mdns-proxy` | SSH `ProxyCommand`: mDNS → Tailscale DNS → cached IP. |
|
|
|
|
|
|
| `ssh-fallback` | SSH `ProxyCommand`: Tailscale → LAN, for flaky Tailscale. |
|
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
|
|
|
|
| `resolve` | Bash wrapper: **`mdns` → stdlib → Tailscale → cache**, usable in any command. |
|
|
|
|
|
|
|
|
|
|
|
|
Python library under `lib/mdns_tools/` (each module < 100 lines):
|
|
|
|
|
|
`resolve.py`, `browse.py`, `publish.py`, `reverse.py`, `cache.py`,
|
|
|
|
|
|
`_stdlib_resolve.py` (dep-free A-record resolver).
|
2026-07-20 01:53:44 +00:00
|
|
|
|
|
|
|
|
|
|
## Install
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
git clone https://devops.ingeniumcodex.com/andresgarcia0313/mdns-termux.git
|
|
|
|
|
|
cd mdns-termux
|
|
|
|
|
|
bash install.sh # or --dry-run first
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Then follow the printed reminders (append `bashrc.snippet`, set your
|
|
|
|
|
|
`TAILSCALE_DOMAIN`, optionally merge `ssh_config.example`).
|
|
|
|
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
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
|
|
|
|
### `mdns` CLI
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mdns resolve <host> [--family v4|v6] [--timeout 1.5] [--stale] [--no-cache]
|
|
|
|
|
|
mdns reverse <ip> # PTR: IP -> hostname
|
|
|
|
|
|
mdns browse [--type _svc._tcp.local.] [--json] [--list-types]
|
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
|
|
|
|
mdns browse --watch [--type ...] [--no-resolve] # live monitor (avahi-browse -a)
|
|
|
|
|
|
mdns publish [--config PATH] [--hostname NAME] # services from JSON
|
|
|
|
|
|
mdns publish --address <name> <ip> # bare A record (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
|
|
|
|
mdns cache list | clear [host]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
mdns browse --list-types # every service type on the LAN
|
|
|
|
|
|
mdns browse --type _ssh._tcp.local. # every SSH host on the LAN
|
|
|
|
|
|
mdns resolve lenovo-ideapad # -> 192.168.1.106 (LAN, fast stdlib path)
|
|
|
|
|
|
mdns resolve lenovo-ideapad --family v6 # -> fe80::... (via zeroconf)
|
|
|
|
|
|
mdns reverse 192.168.1.17 # -> dell-latitude3400.local
|
|
|
|
|
|
mdns publish # advertise services from ~/.config/mdns/services.json
|
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
|
|
|
|
mdns publish --address printer 192.168.1.50 # publish printer.local -> that IP
|
|
|
|
|
|
mdns browse --watch # live +/=/- events, all types
|
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
|
|
|
|
mdns cache list # inspect TTL-aware cache
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### `resolve` (bash) — for any command
|
2026-07-20 01:53:44 +00:00
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
resolve lenovo # -> 100.69.236.16
|
|
|
|
|
|
ping $(resolve hp62a)
|
|
|
|
|
|
curl "http://$(resolve dell):8080"
|
|
|
|
|
|
kubectl --server="https://$(resolve master):6443" get nodes
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Environment knobs:
|
|
|
|
|
|
|
|
|
|
|
|
- `TAILSCALE_DOMAIN` — defaults to `tailb0bb74.ts.net`. Set to your own tailnet.
|
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
|
|
|
|
- `MDNS_TIMEOUT` — seconds waiting for multicast (default `2`).
|
|
|
|
|
|
- `MDNS_CACHE_PATH` — override cache location (default `~/.cache/mdns/hosts.json`).
|
2026-07-20 01:53:44 +00:00
|
|
|
|
|
|
|
|
|
|
### Shell helpers (from `config/bashrc.snippet`)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
alias r='resolve'
|
|
|
|
|
|
pingr <host> # ping -c 3 to resolved IP
|
|
|
|
|
|
curlr <host> [flags] # curl http://<resolved-ip>
|
|
|
|
|
|
sshr <host> [cmd...] # ssh via resolved IP with correct HostKeyAlias
|
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
|
|
|
|
whor <ip> # PTR reverse: IP -> hostname
|
|
|
|
|
|
mdns-types # list all service types advertised on LAN
|
|
|
|
|
|
mdns-ssh / mdns-smb / mdns-http # browse specific service types
|
|
|
|
|
|
mdns-cache / mdns-forget # inspect / clear the resolver cache
|
2026-07-20 01:53:44 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### SSH by hostname (via `ProxyCommand`)
|
|
|
|
|
|
|
|
|
|
|
|
Drop `config/ssh_config.example` into `~/.ssh/config`. Then:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
ssh lenovo # resolves via mDNS then Tailscale, connects
|
|
|
|
|
|
ssh lenovo.local # forces mDNS-first path
|
|
|
|
|
|
```
|
|
|
|
|
|
|
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
|
|
|
|
### Publishing this device on the LAN
|
|
|
|
|
|
|
|
|
|
|
|
Edit `~/.config/mdns/services.json` (the installer seeds a template), then:
|
2026-07-20 01:53:44 +00:00
|
|
|
|
|
|
|
|
|
|
```bash
|
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
|
|
|
|
mdns publish # foreground
|
|
|
|
|
|
nohup mdns publish >>~/.cache/mdns.log 2>&1 & disown # background
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The publisher registers `<hostname>.local` (`_workstation._tcp`) plus every
|
|
|
|
|
|
service listed in the config. On Ctrl+C it sends proper mDNS goodbye packets.
|
|
|
|
|
|
Example `services.json`:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"hostname": "movil",
|
|
|
|
|
|
"services": [
|
|
|
|
|
|
{"type": "_ssh._tcp", "port": 8022, "name": "Termux SSH"},
|
|
|
|
|
|
{"type": "_smb._tcp", "port": 4450, "name": "Termux SMB"}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
2026-07-20 01:53:44 +00:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Behavior by network
|
|
|
|
|
|
|
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
|
|
|
|
| Scenario | Path | Typical latency |
|
|
|
|
|
|
|--------------------|------------------------------------------------------|-----------------|
|
|
|
|
|
|
| On home LAN | stdlib multicast → zeroconf (IPv6 or miss) | < 1 s (IPv4) |
|
|
|
|
|
|
| Mobile data / away | multicast times out → Tailscale MagicDNS | < 2 s |
|
|
|
|
|
|
| No Tailscale | all fail → TTL-aware cache → legacy flat cache | instant |
|
|
|
|
|
|
|
|
|
|
|
|
Cache honors real mDNS TTL (default 30 – 3600 s) and stores IPv4 + IPv6
|
|
|
|
|
|
separately. `mdns resolve --stale <host>` accepts expired entries as a
|
|
|
|
|
|
last-resort fallback.
|
|
|
|
|
|
|
|
|
|
|
|
## RFC coverage (what's implemented)
|
|
|
|
|
|
|
|
|
|
|
|
| Feature | Status | Notes |
|
|
|
|
|
|
|----------------------------------|:------:|------------------------------------------|
|
|
|
|
|
|
| A / AAAA hostname resolution | ✓ | stdlib for A, zeroconf for AAAA |
|
|
|
|
|
|
| PTR reverse lookup | ✓ | stdlib multicast, IPv4 + IPv6 |
|
|
|
|
|
|
| DNS-SD service browsing | ✓ | `mdns browse`, all types or one |
|
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
|
|
|
|
| Live service monitor | ✓ | `mdns browse --watch` (like avahi-browse)|
|
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
|
|
|
|
| Publisher (hostname + services) | ✓ | via `zeroconf`, with probing + goodbye |
|
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
|
|
|
|
| Bare hostname->IP publish | ✓ | `mdns publish --address` (avahi-publish -a)|
|
|
|
|
|
|
| Name-conflict handling | ✓ | RFC 6762 probe + auto-rename |
|
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
|
|
|
|
| Multicast group membership | ✓ | `IP_ADD_MEMBERSHIP` on all sockets |
|
|
|
|
|
|
| Retries with backoff | ✓ | 2 attempts, split timeout |
|
|
|
|
|
|
| TTL-honoring cache | ✓ | JSON at `~/.cache/mdns/hosts.json` |
|
|
|
|
|
|
| Legacy unicast queries | — | Not implemented (rarely needed) |
|
|
|
|
|
|
| Known-answer suppression | — | zeroconf handles internally |
|
2026-07-20 01:53:44 +00:00
|
|
|
|
|
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
|
|
|
|
## Interoperability (Avahi / Bonjour)
|
|
|
|
|
|
|
|
|
|
|
|
Because publishing goes through `zeroconf` (a full RFC 6762 / 6763
|
|
|
|
|
|
implementation), anything you advertise is visible to standard mDNS stacks —
|
|
|
|
|
|
**no special client needed on the other end**.
|
|
|
|
|
|
|
|
|
|
|
|
Verified on a real LAN:
|
|
|
|
|
|
|
|
|
|
|
|
- **Termux → Avahi (Linux):** with `mdns publish` running on the phone, a Linux
|
|
|
|
|
|
box with `nss-mdns` resolved `movil.local` and pinged it, with no extra config:
|
|
|
|
|
|
```
|
|
|
|
|
|
$ getent hosts movil.local
|
|
|
|
|
|
192.168.1.112 movil.local
|
|
|
|
|
|
$ ping movil.local # 64 bytes ... time=3.84 ms
|
|
|
|
|
|
```
|
|
|
|
|
|
- **Avahi → Termux:** `mdns browse` on the phone sees services published by
|
|
|
|
|
|
Avahi/Bonjour hosts (SMB, SSH, `_workstation`, Chromecast, AirPlay, …).
|
|
|
|
|
|
|
|
|
|
|
|
So a Mac (Bonjour), a Linux box (Avahi), or a Windows machine (Bonjour Print
|
|
|
|
|
|
Services) will all discover and resolve what this tool publishes, and vice
|
|
|
|
|
|
versa.
|
|
|
|
|
|
|
2026-07-20 01:53:44 +00:00
|
|
|
|
## Gotchas found the hard way
|
|
|
|
|
|
|
|
|
|
|
|
- **`getent` doesn't exist on Termux.** Scripts that shell out to it silently
|
|
|
|
|
|
fail. This repo uses `python -c "import socket; socket.gethostbyname(...)"`
|
|
|
|
|
|
as the portable fallback on Bionic.
|
|
|
|
|
|
- **Shebang matters.** `#!/data/data/com.termux/files/usr/bin/bash` is required
|
|
|
|
|
|
for `declare -A`. A stray backslash (`#\!`) makes the kernel fall through to
|
|
|
|
|
|
`sh`, which rejects bash-only syntax with `Syntax error: "(" unexpected`.
|
|
|
|
|
|
- **mDNS off-LAN is a no-op.** Multicast (224.0.0.251) does not cross networks.
|
|
|
|
|
|
Over mobile data, only Tailscale resolves.
|
|
|
|
|
|
- **`zeroconf` is not `pkg`-managed.** After restoring Termux from a `tar`
|
|
|
|
|
|
backup, re-run `pip install zeroconf` or `mdns-publish.py` will silently die
|
|
|
|
|
|
with `ModuleNotFoundError`.
|
|
|
|
|
|
- **Silent failures are the enemy.** The `.bashrc` snippet deliberately logs
|
|
|
|
|
|
to `~/.cache/mdns.log` instead of `/dev/null`. Keep it that way.
|
|
|
|
|
|
|
|
|
|
|
|
## Tailscale on Termux
|
|
|
|
|
|
|
|
|
|
|
|
Tailscale runs as the **Android app**, not as a Termux CLI — `tailscale up`
|
|
|
|
|
|
inside a shell won't work. The app provides MagicDNS for
|
|
|
|
|
|
`*.<your-tailnet>.ts.net`, which Bionic-libc resolves fine (it's just a
|
|
|
|
|
|
regular DNS lookup).
|
|
|
|
|
|
|
|
|
|
|
|
## Related
|
|
|
|
|
|
|
|
|
|
|
|
- [claude-code-termux](https://devops.ingeniumcodex.com/andresgarcia0313/claude-code-termux)
|
|
|
|
|
|
— Claude Code CLI setup for Termux. Uses this repo for SSH/host resolution.
|
|
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
|
|
MIT — see `LICENSE`.
|