diff --git a/README.md b/README.md index d25054d..edc349d 100644 --- a/README.md +++ b/README.md @@ -3,4 +3,8 @@ mDNS server Minimalist mDNS server implementation with only core service annoucement and no conflict resolution. -To test our implementation we’re using zeroconf as a trusted client. \ No newline at end of file +Listen for `http` service questiond and answer with `toto` service. + +To test our implementation we’re using zeroconf as a trusted client. + +See the [blog post](https://alexrio.fr/blog/mdns-server) for references and details. diff --git a/main.py b/main.py index 6336a4b..e394300 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,9 @@ +""" +Simple and navie mDNS implementation. + +Low level socket management to listen to mDNS queries and respond to _http._tcp.local service queries with constant name. +""" + import socket from dnslib import DNSRecord, DNSHeader, QTYPE, A, SRV, TXT, RR, PTR import dns.message @@ -6,21 +12,30 @@ multicast_group = "224.0.0.251" multicast_port = 5353 interface_ip = "0.0.0.0" +SERVICE_TO_ANNOUNCE = "toto" + + def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32) # See man socket(7) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - #sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - + # sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) + sock.bind(("", multicast_port)) # sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(multicast_group) + socket.inet_aton(interface_ip)) - sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface_ip)) - sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(multicast_group) + socket.inet_aton(interface_ip)) + sock.setsockopt( + socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface_ip) + ) + sock.setsockopt( + socket.IPPROTO_IP, + socket.IP_ADD_MEMBERSHIP, + socket.inet_aton(multicast_group) + socket.inet_aton(interface_ip), + ) while True: received, sender = sock.recvfrom(1500) - print(f'Received packet of {len(received)} bytes from {sender}') + print(f"Received packet of {len(received)} bytes from {sender}") try: msg = dns.message.from_wire(received) except dns.exception.FormError: @@ -29,18 +44,36 @@ def main(): if msg.opcode() == dns.opcode.QUERY: for question in msg.question: - if str(question.name) == '_http._tcp.local.': + if str(question.name) == "_http._tcp.local.": print(f"question name {type(msg)}") unicast_response = question.rdclass == 0x8001 - response = DNSRecord(DNSHeader(id = 0, bitmap=0x8400)) - ptr = PTR("toto._http._tcp.local") + response = DNSRecord(DNSHeader(id=0, bitmap=0x8400)) + ptr = PTR(SERVICE_TO_ANNOUNCE + "._http._tcp.local") srv = SRV(target="toto.local", priority=10, port=80, weight=100) txt = TXT("path=/") a = A("192.168.1.21") - response.add_answer(RR('_http._tcp.local', QTYPE.PTR, rdata = ptr, ttl=120)) - response.add_ar(RR('toto._http._tcp.local', QTYPE.SRV, rdata = srv, ttl=120)) - response.add_ar(RR('toto._http._tcp.local', QTYPE.TXT, rdata = txt, ttl=120)) - response.add_ar(RR('toto.local', QTYPE.A, rdata = a, ttl=120)) + response.add_answer( + RR("_http._tcp.local", QTYPE.PTR, rdata=ptr, ttl=120) + ) + response.add_ar( + RR( + SERVICE_TO_ANNOUNCE + "._http._tcp.local", + QTYPE.SRV, + rdata=srv, + ttl=120, + ) + ) + response.add_ar( + RR( + SERVICE_TO_ANNOUNCE + "._http._tcp.local", + QTYPE.TXT, + rdata=txt, + ttl=120, + ) + ) + response.add_ar( + RR(SERVICE_TO_ANNOUNCE + ".local", QTYPE.A, rdata=a, ttl=120) + ) print(str(response)) if unicast_response: print("Unicast response") @@ -48,7 +81,8 @@ def main(): else: print("Multicast response") sock.sendto(response.pack(), (multicast_group, multicast_port)) - print(f'Send response to {sender}') + print(f"Send response to {sender}") + if __name__ == "__main__": main() diff --git a/pyproject.toml b/pyproject.toml index b82d5bd..e9a582d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "mdns" version = "0.1.0" -description = "Minimalist mdns server" +description = "Very minimalist mdns server" readme = "README.md" requires-python = ">=3.11" dependencies = [