From 3623ea85d4cbb8b985bbc05b3803c0c9d449acf9 Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 24 Aug 2016 15:03:09 +0200 Subject: [PATCH 03/12] mdns.py: send/receive MDNS on all interfaces This patch makes the same changes to the python MDNS implementation as the previous patches to the C implementation. Signed-off-by: Martin Wilck --- base/mdns.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/base/mdns.py b/base/mdns.py index 03bdb92..11d08b8 100644 --- a/base/mdns.py +++ b/base/mdns.py @@ -35,6 +35,17 @@ from .g import * from . import utils from .sixext import BytesIO, to_bytes_utf8, to_bytes_latin, to_string_latin +if hasattr(socket, "if_nameindex"): + if_nameindex = socket.if_nameindex +else: + def _if_nameindex(): + """"Poor man's if_nameindex for Python 2.""" + import os + sysdir = "/sys/class/net" + return sorted([ (int(open("%s/%s/ifindex" % (sysdir, iface), "r").read(), 0), iface) + for iface in os.listdir(sysdir) ]) + if_nameindex = _if_nameindex + MAX_ANSWERS_PER_PACKET = 24 QTYPE_A = 1 @@ -45,6 +56,8 @@ QTYPE_PTR = 12 QCLASS_IN = 1 +MCAST_ADDR='224.0.0.251' + # Caller needs to ensure, data should be in string format. def read_utf8(offset, data, l): return offset+l, data[offset:offset+l] @@ -188,11 +201,6 @@ def createSocketsWithsetOption(ttl=4): s=None try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) - x = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - x.connect(('1.2.3.4', 56)) - intf = x.getsockname()[0] - x.close() - s.setblocking(0) ttl = struct.pack('B', ttl) except socket.error: @@ -209,14 +217,42 @@ def createSocketsWithsetOption(ttl=4): try: s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, ttl) - s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(intf) + socket.inet_aton('0.0.0.0')) s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP ,1) except Exception as e: log.error("Unable to setup multicast socket for mDNS: %s" % e) if s: s.close() return None - return s + + ifaces = [] + for idx, name in if_nameindex(): + mreqn = struct.pack("=4sii", socket.inet_aton(MCAST_ADDR), + socket.ntohl(socket.INADDR_ANY), idx) + try: + s.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, mreqn) + except Exception as e: + log.debug("Failed to join multicast group on interface %s: %s" % + (name, e)) + else: + log.debug("Joined multicast group on interface %s" % name) + ifaces.append((idx, name)) + + if len(ifaces) == 0: + log.error("failed to join multicast group on any interface") + s.close() + return None + + return (s, ifaces) + +def closeSocket(s): + for idx, name in if_nameindex(): + mreqn = struct.pack("=4sii", socket.inet_aton(MCAST_ADDR), + socket.ntohl(socket.INADDR_ANY), idx) + try: + s.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, mreqn) + except Exception: + pass + s.close() def updateReceivedData(data, answers): update_spinner() @@ -299,13 +335,22 @@ def updateReceivedData(data, answers): break return y, answers +def send_packets(s, answers, name, mcast_addr, mcast_port): + for p in create_outgoing_packets(answers): + log.debug("Outgoing on %s: (%d)" % (name, len(p))) + log.log_data(p, width=16) + try: + s.sendto(p, 0, (mcast_addr, mcast_port)) + except socket.error as e: + log.debug("Unable to send broadcast DNS packet on %s: %s" % (name, e)) + raise def detectNetworkDevices(ttl=4, timeout=10): - mcast_addr, mcast_port ='224.0.0.251', 5353 + mcast_addr, mcast_port =MCAST_ADDR, 5353 found_devices = {} answers = [] - s = createSocketsWithsetOption(ttl) + s, ifaces = createSocketsWithsetOption(ttl) if not s: return {} @@ -321,14 +366,24 @@ def detectNetworkDevices(ttl=4, timeout=10): break if now >= next: - try: - for p in create_outgoing_packets(answers): - log.debug("Outgoing: (%d)" % len(p)) - log.log_data(p, width=16) - s.sendto(p, 0, (mcast_addr, mcast_port)) + good = [] + for idx, name in ifaces: + mreqn = struct.pack("=4sii", socket.inet_aton(mcast_addr), + socket.ntohl(socket.INADDR_ANY), idx) + try: + s.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, mreqn) + except socket.error as e: + log.debug("failed to set IP_MULTICAST_IF on %s" % name) + continue + try: + send_packets(s, answers, name, mcast_addr, mcast_port) + except socket.error: + continue + else: + good.append((idx, name)) - except socket.error as e: - log.error("Unable to send broadcast DNS packet: %s" % e) + if len(good) == 0: + log.error("Failed to send MDNS packet on any interface") next += delay delay *= 2 @@ -347,7 +402,5 @@ def detectNetworkDevices(ttl=4, timeout=10): found_devices[y['ip']] = y log.debug("Found %d devices" % len(found_devices)) - s.close() + closeSocket(s) return found_devices - - -- 2.9.2