#!/usr/bin/env python3 """Пассивная mDNS-проба модуля кондиционера: A-запрос .local на 224.0.0.251:5353 и :10276 (нестандартный порт Ayla из APK).""" import socket, struct, sys, time DSN = "AC000W00REDACTED" HOST = DSN + ".local" def qname(name): out = b"" for lbl in name.split("."): out += bytes([len(lbl)]) + lbl.encode() return out + b"\x00" def query_packet(txid): # standard query, RD=1, one question: A, class IN header = struct.pack(">HHHHHH", txid, 0x0100, 1, 0, 0, 0) return header + qname(HOST) + struct.pack(">HH", 1, 1) def parse_name(buf, off): parts = [] while True: l = buf[off] if l == 0: off += 1 break if l & 0xC0 == 0xC0: ptr = struct.unpack(">H", buf[off:off+2])[0] & 0x3FFF parts.append(parse_name(buf, ptr)[0]) off += 2 break parts.append(buf[off+1:off+1+l].decode("latin1")) off += 1 + l return ".".join(parts), off def parse_answers(buf): try: qd = struct.unpack(">H", buf[4:6])[0] an = struct.unpack(">H", buf[6:8])[0] off = 12 for _ in range(qd): _, off = parse_name(buf, off) off += 4 found = [] for _ in range(an): name, off = parse_name(buf, off) rtype, rclass, ttl, rdlen = struct.unpack(">HHIH", buf[off:off+10]) off += 10 rdata = buf[off:off+rdlen] if rtype == 1 and rdlen == 4: ip = ".".join(str(b) for b in rdata) found.append((name, ip, rclass, ttl)) off += rdlen return found except Exception as e: return [("parse-error", str(e), 0, 0)] def probe(port, txid, wait=4.0): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind(("", 0)) pkt = query_packet(txid) for _ in range(3): s.sendto(pkt, ("224.0.0.251", port)) time.sleep(0.5) s.settimeout(wait) try: while True: data, addr = s.recvfrom(4096) print(f" [:{port}] from {addr}: {len(data)} bytes") for rec in parse_answers(data): print(f" {rec}") except socket.timeout: pass finally: s.close() if __name__ == "__main__": print("mDNS A?", HOST) print("-- querying :5353") probe(5353, 0x1234) print("-- querying :10276") probe(10276, 0x1235)