init: extraer herramientas mDNS de claude-code-termux
Scripts (bin/): - mdns-resolve: resolver .local por multicast UDP (stdlib Python). - mdns-publish.py: publicar este dispositivo como movil.local (zeroconf). - ssh-mdns-proxy: ProxyCommand SSH con orden mDNS -> Tailscale -> cache. - ssh-fallback: ProxyCommand con fallback Tailscale -> LAN. - resolve: resolver genérico para cualquier comando (ping, curl, kubectl...). Config (config/): - bashrc.snippet: aliases r/pingr/curlr/sshr + auto-arranque publisher. - ssh_config.example: entradas ProxyCommand para .local y hostnames cortos. Docs y setup: - README.md standalone con guía de uso, comportamiento por red y gotchas (getent ausente en Bionic, shebang bash-only, mDNS no cruza redes, zeroconf no sobrevive tar-backups, no silenciar logs). - install.sh independiente con --dry-run. Origen: extraído de andresgarcia0313/claude-code-termux para reutilizarse sin acoplar a Claude Code. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
0b6642ec84
|
|
@ -0,0 +1,7 @@
|
|||
*.log
|
||||
*.tmp
|
||||
*.bak
|
||||
*.bak-*
|
||||
__pycache__/
|
||||
*.pyc
|
||||
.venv/
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Andrés García
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# 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
|
||||
|
||||
| Script | Role |
|
||||
|--------|------|
|
||||
| `mdns-resolve` | Resolve `<host>.local` via raw multicast UDP (pure Python, stdlib only). |
|
||||
| `mdns-publish.py` | Publish this device on the LAN as `movil.local` using `zeroconf`. |
|
||||
| `ssh-mdns-proxy` | SSH `ProxyCommand`: mDNS → Tailscale DNS → cached IP. |
|
||||
| `ssh-fallback` | SSH `ProxyCommand`: Tailscale → LAN, for flaky Tailscale. |
|
||||
| `resolve` | Generic resolver for **any** command (ping, curl, kubectl, nc…). Same order. |
|
||||
|
||||
## 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
|
||||
|
||||
### Generic resolver — works in any command
|
||||
|
||||
```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.
|
||||
- `MDNS_TIMEOUT` — seconds waiting for multicast (default `1`, keeps off-LAN snappy).
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### Publishing this device as `movil.local`
|
||||
|
||||
```bash
|
||||
python ~/.local/bin/mdns-publish.py # foreground
|
||||
nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown
|
||||
```
|
||||
|
||||
## Behavior by network
|
||||
|
||||
| Scenario | Path | Typical latency |
|
||||
|--------------------|--------------------------------------------|-----------------|
|
||||
| On home LAN | mDNS multicast (1 s cap) → else Tailscale | < 100 ms |
|
||||
| Mobile data / away | mDNS fails → Tailscale MagicDNS | < 200 ms |
|
||||
| No Tailscale | Both fail → cached last-known IP | instant |
|
||||
|
||||
## 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`.
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#!/data/data/com.termux/files/usr/bin/python
|
||||
"""Publica servicios via mDNS/Zeroconf: SSH + Samba"""
|
||||
import socket
|
||||
import signal
|
||||
import sys
|
||||
from zeroconf import ServiceInfo, Zeroconf, IPVersion
|
||||
|
||||
HOSTNAME = 'movil'
|
||||
|
||||
SERVICES = [
|
||||
{
|
||||
'type': '_ssh._tcp.local.',
|
||||
'port': 8022,
|
||||
'props': {'description': 'Termux SSH'},
|
||||
},
|
||||
{
|
||||
'type': '_smb._tcp.local.',
|
||||
'port': 4450,
|
||||
'props': {'description': 'Termux Samba', 'path': '/'},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def get_local_ip():
|
||||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(('8.8.8.8', 80))
|
||||
ip = s.getsockname()[0]
|
||||
s.close()
|
||||
return ip
|
||||
except Exception:
|
||||
return '127.0.0.1'
|
||||
|
||||
|
||||
def main():
|
||||
ip = get_local_ip()
|
||||
addr = socket.inet_aton(ip)
|
||||
zc = Zeroconf(ip_version=IPVersion.V4Only)
|
||||
infos = []
|
||||
|
||||
for svc in SERVICES:
|
||||
info = ServiceInfo(
|
||||
svc['type'],
|
||||
f'{HOSTNAME}.{svc["type"]}',
|
||||
addresses=[addr],
|
||||
port=svc['port'],
|
||||
properties=svc['props'],
|
||||
server=f'{HOSTNAME}.local.',
|
||||
)
|
||||
zc.register_service(info)
|
||||
infos.append(info)
|
||||
print(f'Publicando {HOSTNAME}.local {svc["type"]} -> {ip}:{svc["port"]}')
|
||||
|
||||
def cleanup(sig, frame):
|
||||
print('Deteniendo mDNS...')
|
||||
for info in infos:
|
||||
zc.unregister_service(info)
|
||||
zc.close()
|
||||
sys.exit(0)
|
||||
|
||||
signal.signal(signal.SIGINT, cleanup)
|
||||
signal.signal(signal.SIGTERM, cleanup)
|
||||
|
||||
try:
|
||||
signal.pause()
|
||||
except AttributeError:
|
||||
import time
|
||||
while True:
|
||||
time.sleep(3600)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Resolve hostname.local via mDNS multicast (pure Python, no dependencies).
|
||||
Usage: mdns-resolve <hostname.local> [timeout_seconds]
|
||||
Returns: IP address on stdout, or exit 1 on failure.
|
||||
"""
|
||||
import sys
|
||||
import socket
|
||||
import struct
|
||||
import select
|
||||
import time
|
||||
|
||||
|
||||
def build_mdns_query(hostname: str) -> bytes:
|
||||
header = struct.pack('>HHHHHH', 0, 0, 1, 0, 0, 0)
|
||||
question = b''
|
||||
for part in hostname.rstrip('.').split('.'):
|
||||
question += bytes([len(part)]) + part.encode()
|
||||
question += b'\x00'
|
||||
question += struct.pack('>HH', 1, 1) # Type A, Class IN
|
||||
return header + question
|
||||
|
||||
|
||||
def parse_mdns_response(data: bytes) -> str | None:
|
||||
if len(data) < 12:
|
||||
return None
|
||||
qdcount = struct.unpack('>H', data[4:6])[0]
|
||||
ancount = struct.unpack('>H', data[6:8])[0]
|
||||
if ancount == 0:
|
||||
return None
|
||||
|
||||
offset = 12
|
||||
for _ in range(qdcount):
|
||||
while offset < len(data) and data[offset] != 0:
|
||||
if data[offset] & 0xc0 == 0xc0:
|
||||
offset += 2
|
||||
break
|
||||
offset += data[offset] + 1
|
||||
else:
|
||||
offset += 1
|
||||
offset += 4
|
||||
|
||||
for _ in range(ancount):
|
||||
while offset < len(data):
|
||||
if data[offset] & 0xc0 == 0xc0:
|
||||
offset += 2
|
||||
break
|
||||
elif data[offset] == 0:
|
||||
offset += 1
|
||||
break
|
||||
else:
|
||||
offset += data[offset] + 1
|
||||
if offset + 10 > len(data):
|
||||
break
|
||||
rtype, rclass, ttl, rdlength = struct.unpack('>HHIH', data[offset:offset+10])
|
||||
offset += 10
|
||||
if rtype == 1 and rdlength == 4 and offset + 4 <= len(data):
|
||||
return socket.inet_ntoa(data[offset:offset+4])
|
||||
offset += rdlength
|
||||
return None
|
||||
|
||||
|
||||
def resolve_mdns(hostname: str, timeout: float = 2.0) -> str | None:
|
||||
if not hostname.endswith('.local'):
|
||||
hostname += '.local'
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.setblocking(False)
|
||||
sock.sendto(build_mdns_query(hostname), ('224.0.0.251', 5353))
|
||||
end_time = time.time() + timeout
|
||||
while time.time() < end_time:
|
||||
ready, _, _ = select.select([sock], [], [], 0.1)
|
||||
if ready:
|
||||
try:
|
||||
data, _ = sock.recvfrom(4096)
|
||||
ip = parse_mdns_response(data)
|
||||
if ip:
|
||||
sock.close()
|
||||
return ip
|
||||
except Exception:
|
||||
pass
|
||||
sock.close()
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print("Usage: mdns-resolve <hostname.local> [timeout]", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
hostname = sys.argv[1]
|
||||
timeout = float(sys.argv[2]) if len(sys.argv) > 2 else 2.0
|
||||
ip = resolve_mdns(hostname, timeout)
|
||||
if ip:
|
||||
print(ip)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
# resolve: hostname -> IP con fallback mDNS (LAN) -> Tailscale DNS -> cache
|
||||
# Uso: resolve <hostname>[.local|.tailnet]
|
||||
# Ej: resolve lenovo # -> 100.69.236.16
|
||||
# ping $(resolve hp62a)
|
||||
# curl "http://$(resolve dell):8080"
|
||||
set -e
|
||||
HOST="${1:-}"
|
||||
[[ -z "$HOST" ]] && { echo "Uso: resolve <hostname>" >&2; exit 1; }
|
||||
|
||||
CACHE="$HOME/.ssh/resolve-cache"
|
||||
TAILSCALE_DOMAIN="${TAILSCALE_DOMAIN:-tailb0bb74.ts.net}"
|
||||
MDNS_TIMEOUT="${MDNS_TIMEOUT:-1}"
|
||||
|
||||
QUERY="${HOST%.local}"
|
||||
QUERY="${QUERY%.${TAILSCALE_DOMAIN}}"
|
||||
|
||||
declare -A ALIASES=(
|
||||
[dell]="dell-latitude3400"
|
||||
[lenovo]="lenovo-ideapad"
|
||||
)
|
||||
[[ -n "${ALIASES[$QUERY]:-}" ]] && QUERY="${ALIASES[$QUERY]}"
|
||||
|
||||
# 1. mDNS multicast (LAN)
|
||||
IP=$("$HOME/.local/bin/mdns-resolve" "${QUERY}.local" "$MDNS_TIMEOUT" 2>/dev/null || true)
|
||||
|
||||
# 2. Tailscale MagicDNS (remoto)
|
||||
if [[ -z "$IP" ]]; then
|
||||
IP=$(python -c "import socket; print(socket.gethostbyname('${QUERY}.${TAILSCALE_DOMAIN}'))" 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# 3. Cache
|
||||
if [[ -z "$IP" && -f "$CACHE" ]]; then
|
||||
IP=$(grep "^${QUERY} " "$CACHE" 2>/dev/null | awk '{print $2}')
|
||||
fi
|
||||
|
||||
if [[ -z "$IP" ]]; then
|
||||
echo "Error: no se resuelve '${HOST}'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$CACHE")"
|
||||
grep -v "^${QUERY} " "$CACHE" > "${CACHE}.tmp" 2>/dev/null || true
|
||||
echo "${QUERY} ${IP}" >> "${CACHE}.tmp"
|
||||
mv "${CACHE}.tmp" "$CACHE"
|
||||
|
||||
echo "$IP"
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
#
|
||||
# ssh-fallback: Proxy para SSH con fallback automático
|
||||
# Usado como ProxyCommand en ~/.ssh/config
|
||||
#
|
||||
# Uso: ssh-fallback <tailscale_host> <lan_ip> <port>
|
||||
|
||||
TAILSCALE_HOST="$1"
|
||||
LAN_IP="$2"
|
||||
PORT="${3:-22}"
|
||||
|
||||
# Intentar Tailscale primero (1 segundo timeout)
|
||||
if ping -c 1 -W 1 "$TAILSCALE_HOST" &>/dev/null 2>&1; then
|
||||
exec nc -w 5 "$TAILSCALE_HOST" "$PORT"
|
||||
fi
|
||||
|
||||
# Fallback a IP LAN
|
||||
if ping -c 1 -W 1 "$LAN_IP" &>/dev/null 2>&1; then
|
||||
exec nc -w 5 "$LAN_IP" "$PORT"
|
||||
fi
|
||||
|
||||
# Último intento: Tailscale sin verificar
|
||||
exec nc -w 10 "$TAILSCALE_HOST" "$PORT"
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#!/usr/bin/env bash
|
||||
# ssh-mdns-proxy: Resolve .local via mDNS with fallbacks, then connect
|
||||
#
|
||||
# Resolution order: mDNS → Tailscale DNS → cached IP
|
||||
# Usage: ssh-mdns-proxy <hostname> [port]
|
||||
#
|
||||
# Configure your Tailscale domain:
|
||||
TAILSCALE_DOMAIN="${TAILSCALE_DOMAIN:-tailb0bb74.ts.net}"
|
||||
|
||||
HOST="$1"
|
||||
PORT="${2:-22}"
|
||||
CACHE="$HOME/.ssh/mdns-cache"
|
||||
QUERY="${HOST%.local}"
|
||||
|
||||
# Alias mapping (short name -> mDNS hostname)
|
||||
declare -A ALIASES=(
|
||||
[dell]="dell-latitude3400"
|
||||
[lenovo]="lenovo-ideapad"
|
||||
)
|
||||
[[ -n "${ALIASES[$QUERY]}" ]] && QUERY="${ALIASES[$QUERY]}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
|
||||
# 1. mDNS resolution
|
||||
IP=$("$SCRIPT_DIR/mdns-resolve" "${QUERY}.local" 2 2>/dev/null)
|
||||
|
||||
# 2. Fallback: Tailscale DNS (Termux carece de getent, usar python si esta disponible)
|
||||
if [[ -z "$IP" ]]; then
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
IP=$(getent hosts "${QUERY}.${TAILSCALE_DOMAIN}" 2>/dev/null | awk '{print $1}')
|
||||
else
|
||||
IP=$(python -c "import socket; print(socket.gethostbyname('${QUERY}.${TAILSCALE_DOMAIN}'))" 2>/dev/null)
|
||||
fi
|
||||
fi
|
||||
|
||||
# 3. Fallback: cached IP
|
||||
if [[ -z "$IP" && -f "$CACHE" ]]; then
|
||||
IP=$(grep "^${QUERY} " "$CACHE" 2>/dev/null | awk '{print $2}')
|
||||
fi
|
||||
|
||||
if [[ -z "$IP" ]]; then
|
||||
echo "Error: cannot resolve ${HOST}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Save to cache
|
||||
mkdir -p "$(dirname "$CACHE")"
|
||||
grep -v "^${QUERY} " "$CACHE" > "${CACHE}.tmp" 2>/dev/null || true
|
||||
echo "${QUERY} ${IP}" >> "${CACHE}.tmp"
|
||||
mv "${CACHE}.tmp" "$CACHE"
|
||||
|
||||
exec nc "$IP" "$PORT"
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
# === mdns-termux - .bashrc additions ===
|
||||
# Append these lines to your ~/.bashrc
|
||||
|
||||
# Ensure ~/.local/bin is on PATH
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
|
||||
# Your Tailscale tailnet (change to yours). Used as fallback for resolve/sshr.
|
||||
export TAILSCALE_DOMAIN="tailXXXXXX.ts.net"
|
||||
|
||||
# --- mDNS publisher (this device as movil.local on the LAN) ---
|
||||
alias mdns-status='pgrep -af mdns-publish || echo "mDNS publisher is not running"'
|
||||
alias mdns-restart='pkill -f mdns-publish; sleep 1; nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown && echo "mDNS restarted"'
|
||||
alias mdns-stop='pkill -f mdns-publish && echo "mDNS stopped"'
|
||||
|
||||
# --- Generic host resolver (resolve: mDNS -> Tailscale -> cache) ---
|
||||
# Works in any command: ping $(resolve lenovo) / curl "http://$(resolve hp62a)"
|
||||
alias r='resolve'
|
||||
pingr() { ping -c 3 "$(resolve "$1")"; }
|
||||
curlr() { local h="$1"; shift; curl "$@" "http://$(resolve "$h")"; }
|
||||
# HostKeyAlias reuses the known_hosts entry for the Tailscale FQDN so SSH-by-IP
|
||||
# does not fail host-key verification.
|
||||
sshr() {
|
||||
local h="$1"; shift
|
||||
local canonical="$h"
|
||||
# Add your own short-name -> canonical mappings here:
|
||||
case "$h" in dell) canonical="dell-latitude3400";; lenovo) canonical="lenovo-ideapad";; esac
|
||||
ssh -o "HostKeyAlias=${canonical}.${TAILSCALE_DOMAIN}" \
|
||||
"YOUR_USER@$(resolve "$h")" "$@"
|
||||
}
|
||||
|
||||
# --- Auto-start publisher on shell login (optional) ---
|
||||
# Uncomment if you want movil.local advertised whenever you open a Termux shell.
|
||||
# Failures land in ~/.cache/mdns.log — do NOT redirect to /dev/null.
|
||||
#
|
||||
# mkdir -p ~/.cache
|
||||
# pgrep -f "mdns-publish" >/dev/null 2>&1 || \
|
||||
# nohup python ~/.local/bin/mdns-publish.py >>~/.cache/mdns.log 2>&1 & disown
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# SSH Config for Termux with mDNS/Tailscale resolution
|
||||
# Copy relevant sections to ~/.ssh/config
|
||||
|
||||
Host *
|
||||
IdentityFile ~/.ssh/id_ed25519
|
||||
IdentitiesOnly yes
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 3
|
||||
ConnectTimeout 10
|
||||
|
||||
# === mDNS (.local) - Dynamic resolution ===
|
||||
Host *.local
|
||||
User <your-user>
|
||||
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||
|
||||
# === Short hostnames -> mDNS first, then Tailscale ===
|
||||
Host <host-1>
|
||||
User <your-user>
|
||||
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||
|
||||
Host <host-2>
|
||||
User <your-user>
|
||||
ProxyCommand ~/.local/bin/ssh-mdns-proxy %h %p
|
||||
|
||||
# === ProxyJump (hosts without Tailscale, via jump host) ===
|
||||
# Host <lan-only-host>
|
||||
# HostName <lan-only-host>.local
|
||||
# User <user>
|
||||
# ProxyJump <jump-host>
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
#!/data/data/com.termux/files/usr/bin/bash
|
||||
# install.sh - Install mdns-termux scripts to ~/.local/bin
|
||||
# Usage: bash install.sh [--dry-run]
|
||||
set -euo pipefail
|
||||
|
||||
DRY_RUN=0
|
||||
[[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
BIN_DIR="$HOME/.local/bin"
|
||||
|
||||
run() {
|
||||
if [[ $DRY_RUN -eq 1 ]]; then
|
||||
echo "[dry-run] $*"
|
||||
else
|
||||
eval "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
step() { echo ""; echo ">>> Step $1: $2"; }
|
||||
|
||||
# 1. Verify Termux
|
||||
step 1 "Verifying Termux environment..."
|
||||
if [[ -z "${PREFIX:-}" || "$PREFIX" != *"com.termux"* ]]; then
|
||||
echo "ERROR: This installer targets Termux. \$PREFIX not set as expected." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " Termux detected: $PREFIX"
|
||||
|
||||
# 2. Base packages
|
||||
step 2 "Installing required packages (pkg + pip)..."
|
||||
run "pkg install -y python proot"
|
||||
run "pip install --upgrade zeroconf"
|
||||
|
||||
# 3. Copy scripts
|
||||
step 3 "Installing scripts to $BIN_DIR..."
|
||||
run "mkdir -p '$BIN_DIR'"
|
||||
for script in mdns-resolve mdns-publish.py ssh-mdns-proxy ssh-fallback resolve; do
|
||||
run "cp '$SCRIPT_DIR/bin/$script' '$BIN_DIR/$script'"
|
||||
run "chmod +x '$BIN_DIR/$script'"
|
||||
done
|
||||
|
||||
# 4. Reminders
|
||||
step 4 "Manual steps remaining:"
|
||||
cat <<EOF
|
||||
|
||||
a) Append aliases to ~/.bashrc:
|
||||
cat $SCRIPT_DIR/config/bashrc.snippet >> ~/.bashrc
|
||||
Then edit TAILSCALE_DOMAIN inside your ~/.bashrc.
|
||||
|
||||
b) (Optional) Merge SSH config for .local + short-name resolution:
|
||||
cat $SCRIPT_DIR/config/ssh_config.example >> ~/.ssh/config
|
||||
Adjust User and hostnames.
|
||||
|
||||
c) Test:
|
||||
resolve <hostname>
|
||||
pingr <hostname>
|
||||
|
||||
EOF
|
||||
echo "Done."
|
||||
Loading…
Reference in New Issue