claude-code-termux/bin/mdns-resolve

104 lines
3.0 KiB
Python
Executable File

#!/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()