format, add comments

This commit is contained in:
2025-12-22 13:57:56 +01:00
parent 4cf8499c32
commit b6a0322cdd
3 changed files with 53 additions and 15 deletions

View File

@@ -3,4 +3,8 @@ mDNS server
Minimalist mDNS server implementation with only core service annoucement and no conflict resolution. Minimalist mDNS server implementation with only core service annoucement and no conflict resolution.
Listen for `http` service questiond and answer with `toto` service.
To test our implementation were using zeroconf as a trusted client. To test our implementation were using zeroconf as a trusted client.
See the [blog post](https://alexrio.fr/blog/mdns-server) for references and details.

58
main.py
View File

@@ -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 import socket
from dnslib import DNSRecord, DNSHeader, QTYPE, A, SRV, TXT, RR, PTR from dnslib import DNSRecord, DNSHeader, QTYPE, A, SRV, TXT, RR, PTR
import dns.message import dns.message
@@ -6,21 +12,30 @@ multicast_group = "224.0.0.251"
multicast_port = 5353 multicast_port = 5353
interface_ip = "0.0.0.0" interface_ip = "0.0.0.0"
SERVICE_TO_ANNOUNCE = "toto"
def main(): def main():
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32) sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
# See man socket(7) # See man socket(7)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 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.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.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(
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(multicast_group) + socket.inet_aton(interface_ip)) 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: while True:
received, sender = sock.recvfrom(1500) 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: try:
msg = dns.message.from_wire(received) msg = dns.message.from_wire(received)
except dns.exception.FormError: except dns.exception.FormError:
@@ -29,18 +44,36 @@ def main():
if msg.opcode() == dns.opcode.QUERY: if msg.opcode() == dns.opcode.QUERY:
for question in msg.question: 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)}") print(f"question name {type(msg)}")
unicast_response = question.rdclass == 0x8001 unicast_response = question.rdclass == 0x8001
response = DNSRecord(DNSHeader(id = 0, bitmap=0x8400)) response = DNSRecord(DNSHeader(id=0, bitmap=0x8400))
ptr = PTR("toto._http._tcp.local") ptr = PTR(SERVICE_TO_ANNOUNCE + "._http._tcp.local")
srv = SRV(target="toto.local", priority=10, port=80, weight=100) srv = SRV(target="toto.local", priority=10, port=80, weight=100)
txt = TXT("path=/") txt = TXT("path=/")
a = A("192.168.1.21") a = A("192.168.1.21")
response.add_answer(RR('_http._tcp.local', QTYPE.PTR, rdata = ptr, ttl=120)) response.add_answer(
response.add_ar(RR('toto._http._tcp.local', QTYPE.SRV, rdata = srv, ttl=120)) RR("_http._tcp.local", QTYPE.PTR, rdata=ptr, 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_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)) print(str(response))
if unicast_response: if unicast_response:
print("Unicast response") print("Unicast response")
@@ -48,7 +81,8 @@ def main():
else: else:
print("Multicast response") print("Multicast response")
sock.sendto(response.pack(), (multicast_group, multicast_port)) sock.sendto(response.pack(), (multicast_group, multicast_port))
print(f'Send response to {sender}') print(f"Send response to {sender}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -1,7 +1,7 @@
[project] [project]
name = "mdns" name = "mdns"
version = "0.1.0" version = "0.1.0"
description = "Minimalist mdns server" description = "Very minimalist mdns server"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [