#!/data/data/com.termux/files/usr/bin/python3 """ Resuelve hostname.local via mDNS multicast. Uso: mdns-resolve Retorna: IP address o exit 1 """ import sys import socket import struct import select def build_mdns_query(hostname: str) -> bytes: """Construye query DNS para hostname.""" # Header: ID=0, Flags=0, QDCOUNT=1, ANCOUNT=0, NSCOUNT=0, ARCOUNT=0 header = struct.pack('>HHHHHH', 0, 0, 1, 0, 0, 0) # Question section question = b'' for part in hostname.rstrip('.').split('.'): question += bytes([len(part)]) + part.encode() question += b'\x00' # End of name question += struct.pack('>HH', 1, 1) # Type A, Class IN return header + question def parse_mdns_response(data: bytes, hostname: str) -> str | None: """Parsea respuesta mDNS y extrae IP.""" if len(data) < 12: return None # Skip header, find answers qdcount = struct.unpack('>H', data[4:6])[0] ancount = struct.unpack('>H', data[6:8])[0] if ancount == 0: return None # Skip questions offset = 12 for _ in range(qdcount): while offset < len(data) and data[offset] != 0: if data[offset] & 0xc0 == 0xc0: # Compression offset += 2 break offset += data[offset] + 1 else: offset += 1 offset += 4 # Type + Class # Parse answers for _ in range(ancount): # Skip name (handle compression) 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 # Type A (1) = IPv4 address if rtype == 1 and rdlength == 4 and offset + 4 <= len(data): ip = socket.inet_ntoa(data[offset:offset+4]) return ip offset += rdlength return None def resolve_mdns(hostname: str, timeout: float = 2.0) -> str | None: """Resuelve hostname via mDNS multicast.""" if not hostname.endswith('.local'): hostname += '.local' MDNS_ADDR = '224.0.0.251' MDNS_PORT = 5353 try: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.setblocking(False) # Enviar query query = build_mdns_query(hostname) sock.sendto(query, (MDNS_ADDR, MDNS_PORT)) # Esperar respuesta end_time = __import__('time').time() + timeout while __import__('time').time() < end_time: ready, _, _ = select.select([sock], [], [], 0.1) if ready: try: data, addr = sock.recvfrom(4096) ip = parse_mdns_response(data, hostname) if ip: sock.close() return ip except: pass sock.close() except Exception as e: pass return None def main(): if len(sys.argv) < 2: print("Uso: mdns-resolve ", 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) sys.exit(0) else: sys.exit(1) if __name__ == "__main__": main()