hplip/hplip-mdns-retry-query.patch
Martin Wilck 2d6bd814e6 Accepting request 423113 from home:mwilck:branches:Printing
- Fixed device communication/detection problems with mdns/Bonjour
  see https://bugs.launchpad.net/hplip/+bug/1616861

In my home network, the HP printer/scanner (ENVY 5530) often fails with messages like this:

hp-toolbox[7119]: [7119]: error: Unable to communicate with device (code=12): hp:/net/ENVY_5530_series?zc=HP3464A9E628B4

I've tracked this down to two problems with the MDNS implementation in HPLIP:

 1. On a multi-homed host, HPLIP uses only one interface for MDNS multicast send and receive. This leads to failure if the printer is not on the "default" network. The code uses just INADDR_ANY in its multicast IP_MULTPATH_IF and IP_ADD_MEMBERSHIP setsockopt calls, meaning that the kernel has to figure out the interface to use. This fails if the system has no default route, or if the HP device is on a different network than the default route. The solution is to receive and send multicast on all interfaces.

 2. MDNS authorities (including HP printers) don't answer every query, especially the same query is repeated quickly. The latter happens when the HP tools start, causing the MDNS lookup procedure to fail and the "communication error" to be detected. This can be solved by retrying the query after a short wait (1-2s).

This build contains fixes for both problems.

OBS-URL: https://build.opensuse.org/request/show/423113
OBS-URL: https://build.opensuse.org/package/show/Printing/hplip?expand=0&rev=128
2016-08-26 14:55:31 +00:00

87 lines
3.2 KiB
Diff

diff --git a/protocol/discovery/mdns.c b/protocol/discovery/mdns.c
index 92e153a..75ef0ea 100644
--- a/protocol/discovery/mdns.c
+++ b/protocol/discovery/mdns.c
@@ -436,7 +436,7 @@ static int mdns_read_single_response(int udp_socket, char *recvbuffer, int recvb
FD_SET(udp_socket, &master);
maxfd = udp_socket;
tmo.tv_sec = 0;
- tmo.tv_usec = 300000;
+ tmo.tv_usec = 10000;
readfd = master;
ret = select(maxfd + 1, &readfd, NULL, NULL, &tmo);
@@ -549,6 +549,34 @@ static void mdns_rr_cleanup(DNS_RECORD *rr)
}
}
+static DNS_RECORD* send_and_receive(struct mdns_socket *mdns_sock,
+ const char *name,
+ int query_type, int read_mode)
+{
+ /* wait up to ~1s */
+ const useconds_t DELTA_T = 251000;
+ const int RETRIES = 8;
+ int retry = RETRIES;
+ DNS_RECORD *rr_list;
+
+ while (retry) {
+ if (mdns_send_query(mdns_sock, name, query_type) == MDNS_STATUS_OK) {
+ rr_list = mdns_read_responses(mdns_sock->socket, read_mode, name);
+ if (rr_list != NULL) {
+ DBG("send_and_receive: got response after %d retries\n",
+ RETRIES - retry);
+ return rr_list;
+ }
+ }
+ /* MDNS servers delay responses. If the server just responded
+ * to some query (maybe an earlier one we sent), we may need to wait. */
+ --retry;
+ usleep(DELTA_T);
+ }
+ BUG("send_and_receive: no response after %d retries\n", RETRIES);
+ return NULL;
+}
+
int mdns_probe_nw_scanners(char* uris_buf, int buf_size, int *count)
{
int n = 0, bytes_read = 0;
@@ -562,11 +590,7 @@ int mdns_probe_nw_scanners(char* uris_buf, int buf_size, int *count)
if (mdns_open_socket(&mdns_sock) != MDNS_STATUS_OK)
goto bugout;
- /* Send dns query */
- mdns_send_query(&mdns_sock, scanner_name, QTYPE_PTR);
-
- /* Read Responses */
- rr_list = mdns_read_responses(mdns_sock.socket, MODE_READ_ALL, scanner_name);
+ rr_list = send_and_receive(&mdns_sock, scanner_name, QTYPE_PTR, MODE_READ_ALL);
/* Update URIs buffer */
bytes_read = mdns_update_uris(rr_list, uris_buf, buf_size, count);
@@ -598,10 +622,8 @@ int mdns_lookup(char* hostname, unsigned char* ip)
/* Send dns query */
sprintf(fqdn, "%s.local", hostname);
- mdns_send_query(&mdns_sock, fqdn, QTYPE_A);
- /* Read Responses */
- rr_list = mdns_read_responses(mdns_sock.socket, MODE_READ_SINGLE, fqdn);
+ rr_list = send_and_receive(&mdns_sock, fqdn, QTYPE_A, MODE_READ_SINGLE);
/* Update IP Address buffer */
if(rr_list)
diff --git a/protocol/discovery/mdns.h b/protocol/discovery/mdns.h
index 34066fb..56d8847 100644
--- a/protocol/discovery/mdns.h
+++ b/protocol/discovery/mdns.h
@@ -102,5 +102,8 @@ 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, const char *question);
static unsigned char* mdns_readMDL(unsigned char *p, unsigned char *normalized_mdl, int len);
+static DNS_RECORD* send_and_receive(struct mdns_socket *mdns_sock, const char *name,
+ int query_type, int read_mode);
+static int get_ipv4_address(const char *iface, struct in_addr *addr);
#endif // _DISCOVERY_MDNS_H