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 - - diff --git a/protocol/discovery/mdns.c b/protocol/discovery/mdns.c index 3324d2a..92e153a 100644 --- a/protocol/discovery/mdns.c +++ b/protocol/discovery/mdns.c @@ -1,3 +1,4 @@ + /***************************************************************************** mdns.c - mDNS related calls @@ -29,14 +30,17 @@ #endif #include +#include #include #include #include #include #include #include +#include #include "mdns.h" #include +#include /* Convert "www.google.com" to "3www6google3com". */ static int mdns_convert_name_to_dns(const char *name, int name_size, char *dns_name) @@ -71,14 +75,50 @@ static int mdns_convert_name_to_dns(const char *name, int name_size, char *dns_n return x; /* return length DOES include null termination */ } +#define MREQN_INIT(_mr, index) do { \ + (&(_mr))->imr_multiaddr.s_addr = inet_addr("224.0.0.251"); \ + (&(_mr))->imr_address.s_addr = htonl(INADDR_ANY); \ + (&(_mr))->imr_ifindex = (index); \ + } while(0) + +struct mdns_socket { + int socket; + struct if_nameindex *idx; + struct if_nameindex **good; +}; +#define MDNS_SOCKET_INIT { .socket = -1, .idx = NULL, .good = NULL, } + +static int get_ipv4_address(const char *iface, struct in_addr *addr) +{ + int s, r; + struct ifreq ifr; + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, iface, IFNAMSIZ-1); + + s = socket(AF_INET, SOCK_DGRAM, 0); + r = ioctl(s, SIOCGIFADDR, &ifr); + close(s); + + if (r == -1) { + DBG("error in SIOCGIFADDR for %s: %m\n", iface); + return MDNS_STATUS_ERROR; + } + + if (addr != NULL) + memcpy(addr, &((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr, + sizeof(*addr)); + return MDNS_STATUS_OK; +} -static int mdns_open_socket(int *psocket) +static int mdns_open_socket(struct mdns_socket *mdns_sock) { int stat = MDNS_STATUS_ERROR; - int udp_socket = -1, yes = 1; + int udp_socket = -1, yes = 1, ifaces; char loop = 0, ttl = 255; struct sockaddr_in recv_addr , addr; - struct ip_mreq mreq; + struct ip_mreqn mreqn; + struct if_nameindex *idx; DBG("mdns_open_socket entry.\n"); @@ -120,24 +160,86 @@ static int mdns_open_socket(int *psocket) goto bugout; } - /* Join the .local multicast group */ - mreq.imr_multiaddr.s_addr = inet_addr("224.0.0.251"); - mreq.imr_interface.s_addr = htonl(INADDR_ANY); - if (setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(struct ip_mreq)) == -1) { - BUG("unable to add to multicast group: %m\n"); - close(udp_socket); + mdns_sock->idx = if_nameindex(); + if (mdns_sock->idx == NULL) { + BUG("if_nameindex failed: %m\n"); + goto bugout; + } + + for (idx = mdns_sock->idx, ifaces = 0; + idx && (idx->if_index != 0 || idx->if_name != NULL); idx++) { + ifaces ++; + } + + mdns_sock->good = calloc(ifaces, sizeof(struct if_nameindex*)); + if (mdns_sock->good == NULL) + goto bugout; + + for (idx = mdns_sock->idx, ifaces = 0; + idx && (idx->if_index != 0 || idx->if_name != NULL); idx++) { + + /* Skip lo and interfaces without IPv4 address */ + if (!strcmp(idx->if_name, "lo")) + continue; + if (get_ipv4_address(idx->if_name, NULL) == MDNS_STATUS_ERROR) + continue; + + /* Join the multicast group on each local interface */ + MREQN_INIT(mreqn, idx->if_index); + if (setsockopt(udp_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, + sizeof(struct ip_mreqn)) == -1) { + BUG("unable to add to multicast group for %s: %m\n", idx->if_name); + } else { + mdns_sock->good[ifaces++] = idx; + DBG("added multicast group on interface %s\n", idx->if_name); + } + } + + if (ifaces == 0) { + BUG("no interfaces for multicast found\n"); goto bugout; } - *psocket = udp_socket; - DBG("pSocket = [%d]: %m\n", *psocket); + mdns_sock->socket = udp_socket; + DBG("Socket = [%d]: %m\n", mdns_sock->socket); stat = MDNS_STATUS_OK; bugout: + if (stat != MDNS_STATUS_OK) + mdns_close_socket(mdns_sock); return stat; } -static void mdns_create_query_packet(char* fqdn, int query_type, char* querybuf, int *length) +static void mdns_close_socket(struct mdns_socket *mdns_sock) +{ + int i; + struct if_nameindex *idx; + struct ip_mreqn mreqn; + if (mdns_sock->socket != -1) { + for (i = 0; mdns_sock->good[i]; i++) { + idx = mdns_sock->good[i]; + MREQN_INIT(mreqn, idx->if_index); + if (setsockopt(mdns_sock->socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, + &mreqn, sizeof(struct ip_mreqn)) == -1) { + BUG("unable to drop multicast group for %s: %m\n", idx->if_name); + }; + } + close(mdns_sock->socket); + mdns_sock->socket = -1; + } + + if (mdns_sock->idx != NULL) { + if_freenameindex(mdns_sock->idx); + mdns_sock->idx = NULL; + } + + if (mdns_sock->good != NULL) { + free(mdns_sock->good); + mdns_sock->good = NULL; + } +} + +static void mdns_create_query_packet(const char* fqdn, int query_type, char* querybuf, int *length) { int n = 0; char header[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -157,14 +259,14 @@ static void mdns_create_query_packet(char* fqdn, int query_type, char* querybuf, *length = n; } -static int mdns_send_query(int udp_socket, char *fqdn, int query_type) +static int mdns_send_query(struct mdns_socket *mdns_sock, const char *fqdn, int query_type) { char querybuf[256] = {0,}; - int length = 0; + int length = 0, i, success; int stat = MDNS_STATUS_OK; struct sockaddr_in send_addr; - DBG("mdns_send_query entry. send socket=%d len=%d\n", udp_socket, length); + DBG("mdns_send_query entry. send socket=%d len=%d\n", mdns_sock->socket, length); mdns_create_query_packet(fqdn, query_type, querybuf, &length); @@ -172,8 +274,26 @@ static int mdns_send_query(int udp_socket, char *fqdn, int query_type) send_addr.sin_family = AF_INET; send_addr.sin_addr.s_addr = inet_addr("224.0.0.251"); send_addr.sin_port = htons(5353); - if (sendto(udp_socket, querybuf, length, 0, (struct sockaddr *) &send_addr, sizeof(send_addr)) < 0) - stat = MDNS_STATUS_ERROR; + + for (i = 0, success = 0; mdns_sock->good[i]; i++) { + struct if_nameindex *idx; + struct ip_mreqn mreqn; + idx = mdns_sock->good[i]; + MREQN_INIT(mreqn, idx->if_index); + if (setsockopt(mdns_sock->socket, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn)) + == -1) { + DBG("failed to set IP_MULTICAST_IF to %s\n", idx->if_name); + continue; + } + if (sendto(mdns_sock->socket, querybuf, length, 0, + (struct sockaddr *) &send_addr, sizeof(send_addr))< 0) { + DBG("failed to send on %s\n", idx->if_name); + } else + success++; + } + + if (success == 0) + stat = MDNS_STATUS_ERROR; DBG("mdns_send_query returning with status(%d)...\n", stat); return stat; @@ -252,7 +372,7 @@ static void mdns_read_header(char *Response, DNS_PKT_HEADER *h) } -static void mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr) +static int mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr) { unsigned char *p = Response; unsigned short type = 0, data_len = 0; @@ -263,6 +383,11 @@ static void mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr) mdns_read_header(Response, &h); p += MDNS_HEADER_SIZE; + if (h.answers + h.additionals <= 0) { + DBG("mdns_parse_respponse: no answers"); + return MDNS_STATUS_ERROR; + } + for (i = 0; i < h.questions; i++) { p += mdns_readName(Response, p, rr->name); @@ -295,6 +420,7 @@ static void mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr) } DBG("mdns_parse_respponse returning MDL = %s, IP = %s\n",rr->mdl, rr->ip); + return MDNS_STATUS_OK; } static int mdns_read_single_response(int udp_socket, char *recvbuffer, int recvbufsize) @@ -328,7 +454,7 @@ static int mdns_read_single_response(int udp_socket, char *recvbuffer, int recvb return ret; } -static DNS_RECORD *mdns_read_responses(int udp_socket, int mode) +static DNS_RECORD *mdns_read_responses(int udp_socket, int mode, const char *question) { int retries = 3, ret = 0; char recvbuffer[MAX_MDNS_RESPONSE_LEN] = { 0, }; @@ -351,17 +477,22 @@ static DNS_RECORD *mdns_read_responses(int udp_socket, int mode) temp = (DNS_RECORD *)malloc(sizeof(DNS_RECORD)); if(temp) { - temp->next = NULL; - if(head == NULL) - rr = head = temp; - else - { - rr->next = temp; - rr = rr->next; - } - - memset(rr, 0, sizeof(DNS_RECORD)); - mdns_parse_respponse(recvbuffer, rr); + memset(temp, 0, sizeof(DNS_RECORD)); + if (mdns_parse_respponse(recvbuffer, temp) == MDNS_STATUS_OK && + (mode == MODE_READ_ALL || question == NULL || + !strncmp(question, temp->name, sizeof(temp->name)-1))) { + if(head == NULL) + rr = head = temp; + else + { + rr->next = temp; + rr = rr->next; + } + } else { + DBG("Parse error or wrong MDNS name"); + free(temp); + continue; + } if(mode == MODE_READ_SINGLE) break; @@ -421,28 +552,28 @@ static void mdns_rr_cleanup(DNS_RECORD *rr) int mdns_probe_nw_scanners(char* uris_buf, int buf_size, int *count) { int n = 0, bytes_read = 0; - int udp_socket = 0; + struct mdns_socket mdns_sock = MDNS_SOCKET_INIT; int stat = MDNS_STATUS_ERROR; DNS_RECORD *rr_list = NULL; + const char scanner_name[] = "_scanner._tcp.local"; DBG("mdns_probe_nw_scanners entry.\n"); /* Open UDP socket */ - if (mdns_open_socket(&udp_socket) != MDNS_STATUS_OK) + if (mdns_open_socket(&mdns_sock) != MDNS_STATUS_OK) goto bugout; /* Send dns query */ - mdns_send_query(udp_socket, "_scanner._tcp.local", QTYPE_PTR); + mdns_send_query(&mdns_sock, scanner_name, QTYPE_PTR); /* Read Responses */ - rr_list = mdns_read_responses(udp_socket, MODE_READ_ALL); + rr_list = mdns_read_responses(mdns_sock.socket, MODE_READ_ALL, scanner_name); /* Update URIs buffer */ bytes_read = mdns_update_uris(rr_list, uris_buf, buf_size, count); DBG("mdns_probe_nw_scanners returned with bytes_read = [%d].\n",bytes_read); bugout: - if (udp_socket >= 0) - close(udp_socket); + mdns_close_socket(&mdns_sock); mdns_rr_cleanup(rr_list); @@ -455,22 +586,22 @@ bugout: */ int mdns_lookup(char* hostname, unsigned char* ip) { - int udp_socket = 0; + struct mdns_socket mdns_sock = MDNS_SOCKET_INIT; int stat = MDNS_STATUS_ERROR; char fqdn[MAX_NAME_LENGTH] = {0}; DNS_RECORD *rr_list = NULL; DBG("mdns_probe_nw_scanners entry.\n"); /* Open UDP socket */ - if (mdns_open_socket(&udp_socket) != MDNS_STATUS_OK) + if (mdns_open_socket(&mdns_sock) != MDNS_STATUS_OK) goto bugout; /* Send dns query */ sprintf(fqdn, "%s.local", hostname); - mdns_send_query(udp_socket, fqdn, QTYPE_A); + mdns_send_query(&mdns_sock, fqdn, QTYPE_A); /* Read Responses */ - rr_list = mdns_read_responses(udp_socket, MODE_READ_SINGLE); + rr_list = mdns_read_responses(mdns_sock.socket, MODE_READ_SINGLE, fqdn); /* Update IP Address buffer */ if(rr_list) @@ -481,8 +612,7 @@ int mdns_lookup(char* hostname, unsigned char* ip) } bugout: - if (udp_socket >= 0) - close(udp_socket); + mdns_close_socket(&mdns_sock); mdns_rr_cleanup(rr_list); return stat; diff --git a/protocol/discovery/mdns.h b/protocol/discovery/mdns.h index 8fccc82..34066fb 100644 --- a/protocol/discovery/mdns.h +++ b/protocol/discovery/mdns.h @@ -86,19 +86,21 @@ typedef struct _DNS_PKT_HEADER int mdns_probe_nw_scanners(char* buf, int buf_size, int *count); int mdns_lookup(char* hostname, unsigned char* ip); +struct mdns_socket; /*Helper Function Prototypes*/ static int mdns_convert_name_to_dns(const char *name, int name_size, char *dns_name); static int mdns_read_single_response(int udp_socket, char *recvbuffer, int recvbufsize); -static int mdns_open_socket(int *psocket); -static int mdns_send_query(int udp_socket, char *fqdn, int query_type); +static int mdns_open_socket(struct mdns_socket *mdns_sock); +static void mdns_close_socket(struct mdns_socket *mdns_sock); +static int mdns_send_query(struct mdns_socket *mdns_sock, const char *fqdn, int query_type); static int mdns_readName(unsigned char* start, unsigned char *p, char *buf); static int mdns_update_uris(DNS_RECORD *rr, char* uris_buf, int buf_size, int *count); -static void mdns_create_query_packet(char* fqdn, int query_type, char* dnsquery, int *length); +static void mdns_create_query_packet(const char* fqdn, int query_type, char* dnsquery, int *length); static void mdns_read_header(char *Response, DNS_PKT_HEADER *h); -static void mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr); +static int mdns_parse_respponse(unsigned char *Response, DNS_RECORD *rr); static void mdns_rr_cleanup(DNS_RECORD *rr); -static DNS_RECORD *mdns_read_responses(int udp_socket, int mode); +static DNS_RECORD *mdns_read_responses(int udp_socket, int mode, const char *question); static unsigned char* mdns_readMDL(unsigned char *p, unsigned char *normalized_mdl, int len); #endif // _DISCOVERY_MDNS_H