hplip/hplip-mdns.patch

554 lines
18 KiB
Diff
Raw Normal View History

2016-08-26 16:55:31 +02:00
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 <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
+#include <net/if.h>
#include "mdns.h"
#include <unistd.h>
+#include <sys/ioctl.h>
/* 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