1 /* AirScan (a.k.a. eSCL) backend for SANE
2  *
3  * Copyright (C) 2019 and up by Alexander Pevzner (pzz@apevzner.com)
4  * See LICENSE for license terms and conditions
5  *
6  * Web Services Dynamic Discovery (WS-Discovery)
7  */
8 
9 #define _GNU_SOURCE
10 
11 #include "airscan.h"
12 
13 #include <errno.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <unistd.h>
17 
18 #include <arpa/inet.h>
19 #include <netinet/in.h>
20 #include <net/if.h>
21 
22 #ifdef BSD
23 #   include <net/if_dl.h>
24 #endif
25 
26 #include <sys/socket.h>
27 
28 /* Protocol times, in milliseconds
29  */
30 #define WSDD_RETRANSMIT_MIN     100     /* Min retransmit time */
31 #define WSDD_RETRANSMIT_MAX     250     /* Max retransmit time */
32 #define WSDD_DISCOVERY_TIME     2500    /* Overall discovery time */
33 
34 /* This delay is taken, if we have discovered, say, device's IPv6
35  * addresses and have a strong suspicion that device has not yet
36  * discovered IPv4 addresses as well
37  */
38 #define WSDD_PUBLISH_DELAY      1000
39 
40 /* WS-Discovery stable endpoint path
41  */
42 #define WSDD_STABLE_ENDPOINT    \
43         "/StableWSDiscoveryEndpoint/schemas-xmlsoap-org_ws_2005_04_discovery"
44 
45 /* wsdd_resolver represents a per-interface WSDD resolver
46  */
47 typedef struct {
48     int          fd;           /* File descriptor */
49     int          ifindex;      /* Interface index */
50     bool         ipv6;         /* We are on IPv6 */
51     eloop_fdpoll *fdpoll;      /* Socket fdpoll */
52     eloop_timer  *timer;       /* Retransmit timer */
53     uint32_t     total_time;   /* Total elapsed time */
54     ip_straddr   str_ifaddr;   /* Interface address */
55     ip_straddr   str_sockaddr; /* Per-interface socket address */
56     bool         initscan;     /* Initial scan in progress */
57 } wsdd_resolver;
58 
59 /* wsdd_finding represents zeroconf_finding for WSD
60  * device discovery
61  */
62 typedef struct {
63     zeroconf_finding  finding;        /* Base class */
64     const char        *address;       /* Device "address" in WS-SD sense */
65     ll_head           xaddrs;         /* List of wsdd_xaddr */
66     http_client       *http_client;   /* HTTP client */
67     ll_node           list_node;      /* In wsdd_finding_list */
68     eloop_timer       *publish_timer; /* WSDD_PUBLISH_DELAY timer */
69     bool              published;      /* This finding is published */
70 } wsdd_finding;
71 
72 /* wsdd_xaddr represents device transport address
73  */
74 typedef struct {
75     http_uri   *uri;      /* Device URI */
76     ll_node    list_node; /* In wsdd_finding::xaddrs */
77 } wsdd_xaddr;
78 
79 /* WSDD_ACTION represents WSDD message action
80  */
81 typedef enum {
82     WSDD_ACTION_UNKNOWN,
83     WSDD_ACTION_HELLO,
84     WSDD_ACTION_BYE,
85     WSDD_ACTION_PROBEMATCHES
86 } WSDD_ACTION;
87 
88 /* wsdd_message represents a parsed WSDD message
89  */
90 typedef struct {
91     WSDD_ACTION  action;     /* Message action */
92     const char   *address;   /* Endpoint reference */
93     ll_head      xaddrs;     /* List of wsdd_xaddr */
94     bool         is_scanner; /* Device is scanner */
95     bool         is_printer; /* Device is printer */
96 } wsdd_message;
97 
98 /* Forward declarations
99  */
100 static void
101 wsdd_message_free(wsdd_message *msg);
102 
103 static void
104 wsdd_resolver_send_probe (wsdd_resolver *resolver);
105 
106 static wsdd_resolver*
107 wsdd_netif_resolver_by_ifindex (int ifindex);
108 
109 /* Static variables
110  */
111 static log_ctx             *wsdd_log;
112 static netif_notifier      *wsdd_netif_notifier;
113 static netif_addr          *wsdd_netif_addr_list;
114 static int                 wsdd_mcsock_ipv4 = -1;
115 static int                 wsdd_mcsock_ipv6 = -1;
116 static eloop_fdpoll        *wsdd_fdpoll_ipv4;
117 static eloop_fdpoll        *wsdd_fdpoll_ipv6;
118 static char                wsdd_buf[65536];
119 static struct sockaddr_in  wsdd_mcast_ipv4;
120 static struct sockaddr_in6 wsdd_mcast_ipv6;
121 static ll_head             wsdd_finding_list;
122 static int                 wsdd_initscan_count;
123 static http_client         *wsdd_http_client;
124 static ip_addrset          *wsdd_addrs_probing;
125 
126 /* WS-DD Probe template
127  */
128 static const char *wsdd_probe_template =
129     "<?xml version=\"1.0\"?>"
130     "<s:Envelope xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:d=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:wsdp=\"http://schemas.xmlsoap.org/ws/2006/02/devprof\">"
131     "<s:Header>"
132       "<a:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action>"
133       "<a:MessageID>%s</a:MessageID>"
134       "<a:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To>"
135     "</s:Header>"
136     "<s:Body>"
137       "<d:Probe>"
138         "<d:Types>wsdp:Device</d:Types>"
139       "</d:Probe>"
140     "</s:Body>"
141     "</s:Envelope>";
142 
143 /* WS-DD Get (metadata) template
144  */
145 static const char *wsdd_get_metadata_template =
146     "<?xml version=\"1.0\"?>"
147     "<s:Envelope xmlns:a=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" xmlns:s=\"http://www.w3.org/2003/05/soap-envelope\">"
148       "<s:Header>"
149         "<a:Action>http://schemas.xmlsoap.org/ws/2004/09/transfer/Get</a:Action>"
150         "<a:MessageID>%s</a:MessageID>"
151         "<a:To>%s</a:To>"
152         "<a:ReplyTo>"
153           "<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>"
154         "</a:ReplyTo>"
155       "</s:Header>"
156       "<s:Body/>"
157     "</s:Envelope>";
158 
159 /* XML namespace translation
160  */
161 static const xml_ns wsdd_ns_rules[] = {
162     {"s",       "http*://schemas.xmlsoap.org/soap/envelope"}, /* SOAP 1.1 */
163     {"s",       "http*://www.w3.org/2003/05/soap-envelope"},  /* SOAP 1.2 */
164     {"d",       "http*://schemas.xmlsoap.org/ws/2005/04/discovery"},
165     {"a",       "http*://schemas.xmlsoap.org/ws/2004/08/addressing"},
166     {"devprof", "http*://schemas.xmlsoap.org/ws/2006/02/devprof"},
167     {"mex",     "http*://schemas.xmlsoap.org/ws/2004/09/mex"},
168     {"pnpx",    "http*://schemas.microsoft.com/windows/pnpx/2005/10"},
169     {NULL, NULL}
170 };
171 
172 /******************** wsdd_xaddr operations ********************/
173 /* Create new wsdd_xaddr. Newly created wsdd_xaddr takes uri ownership
174  */
175 static wsdd_xaddr*
wsdd_xaddr_new(http_uri * uri)176 wsdd_xaddr_new (http_uri *uri)
177 {
178     wsdd_xaddr *xaddr = mem_new(wsdd_xaddr, 1);
179     xaddr->uri = uri;
180     return xaddr;
181 }
182 
183 /* Destroy wsdd_xaddr
184  */
185 static void
wsdd_xaddr_free(wsdd_xaddr * xaddr)186 wsdd_xaddr_free (wsdd_xaddr *xaddr)
187 {
188     http_uri_free(xaddr->uri);
189     mem_free(xaddr);
190 }
191 
192 /* Add wsdd_xaddr to the list.
193  * Takes ownership on URI.
194  */
195 static void
wsdd_xaddr_list_add(ll_head * list,http_uri * uri)196 wsdd_xaddr_list_add (ll_head *list, http_uri *uri)
197 {
198     wsdd_xaddr *xaddr;
199     ll_node    *node;
200 
201     /* Check for duplicates */
202     for (LL_FOR_EACH(node, list)) {
203         xaddr = OUTER_STRUCT(node, wsdd_xaddr, list_node);
204         if (http_uri_equal(xaddr->uri, uri)) {
205             http_uri_free(uri);
206             return;
207         }
208     }
209 
210     /* Add new xaddr */
211     xaddr = wsdd_xaddr_new(uri);
212     ll_push_end(list, &xaddr->list_node);
213 }
214 
215 /* Purge list of wsdd_xaddr
216  */
217 static void
wsdd_xaddr_list_purge(ll_head * list)218 wsdd_xaddr_list_purge (ll_head *list)
219 {
220     ll_node    *node;
221 
222     while ((node = ll_pop_beg(list)) != NULL) {
223         wsdd_xaddr *xaddr = OUTER_STRUCT(node, wsdd_xaddr, list_node);
224         wsdd_xaddr_free(xaddr);
225     }
226 }
227 
228 /******************** wsdd_initscan_count operations ********************/
229 /* Increment wsdd_initscan_count
230  */
231 static void
wsdd_initscan_count_inc(void)232 wsdd_initscan_count_inc (void)
233 {
234     wsdd_initscan_count ++;
235 }
236 
237 /* Decrement wsdd_initscan_count
238  */
239 static void
wsdd_initscan_count_dec(void)240 wsdd_initscan_count_dec (void)
241 {
242     log_assert(wsdd_log, wsdd_initscan_count > 0);
243     wsdd_initscan_count --;
244     if (wsdd_initscan_count == 0) {
245         zeroconf_finding_done(ZEROCONF_WSD);
246     }
247 }
248 
249 /******************** wsdd_finding operations ********************/
250 /* Create new wsdd_finding
251  */
252 static wsdd_finding*
wsdd_finding_new(int ifindex,const char * address)253 wsdd_finding_new (int ifindex, const char *address)
254 {
255     wsdd_finding *wsdd = mem_new(wsdd_finding, 1);
256 
257     wsdd->finding.method = ZEROCONF_WSD;
258     wsdd->finding.uuid = uuid_parse(address);
259     if (!uuid_valid(wsdd->finding.uuid)) {
260         wsdd->finding.uuid = uuid_hash(address);
261     }
262     wsdd->finding.addrs = ip_addrset_new();
263     wsdd->finding.ifindex = ifindex;
264 
265     wsdd->address = str_dup(address);
266     ll_init(&wsdd->xaddrs);
267     wsdd->http_client = http_client_new(wsdd_log, wsdd);
268 
269     return wsdd;
270 }
271 
272 /* Destroy wsdd_finding
273  */
274 static void
wsdd_finding_free(wsdd_finding * wsdd)275 wsdd_finding_free (wsdd_finding *wsdd)
276 {
277     if (wsdd->published) {
278         zeroconf_finding_withdraw(&wsdd->finding);
279     }
280 
281     http_client_cancel(wsdd->http_client);
282     http_client_free(wsdd->http_client);
283 
284     if (wsdd->publish_timer != NULL) {
285         eloop_timer_cancel(wsdd->publish_timer);
286     }
287 
288     zeroconf_endpoint_list_free(wsdd->finding.endpoints);
289     mem_free((char*) wsdd->address);
290     wsdd_xaddr_list_purge(&wsdd->xaddrs);
291     ip_addrset_free(wsdd->finding.addrs);
292     mem_free((char*) wsdd->finding.model);
293     mem_free((char*) wsdd->finding.name);
294     mem_free(wsdd);
295 }
296 
297 /* Publish wsdd_finding
298  */
299 static void
wsdd_finding_publish(wsdd_finding * wsdd)300 wsdd_finding_publish (wsdd_finding *wsdd)
301 {
302     if (wsdd->published) {
303         return;
304     }
305 
306     wsdd->published = true;
307     wsdd->finding.endpoints = zeroconf_endpoint_list_sort_dedup(
308             wsdd->finding.endpoints);
309 
310     if (wsdd->publish_timer != NULL) {
311         log_debug(wsdd_log, "\"%s\": publish-delay timer canceled",
312                 wsdd->finding.model);
313 
314         eloop_timer_cancel(wsdd->publish_timer);
315         wsdd->publish_timer = NULL;
316     }
317 
318     zeroconf_finding_publish(&wsdd->finding);
319 }
320 
321 /* WSDD_PUBLISH_DELAY timer callback
322  */
323 static void
wsdd_finding_publish_delay_timer_callback(void * data)324 wsdd_finding_publish_delay_timer_callback (void *data)
325 {
326     wsdd_finding *wsdd = data;
327 
328     wsdd->publish_timer = NULL;
329     log_debug(wsdd_log, "\"%s\": publish-delay timer expired",
330             wsdd->finding.model);
331 
332     wsdd_finding_publish(wsdd);
333 }
334 
335 /* Publish wsdd_finding with optional delay
336  */
337 static void
wsdd_finding_publish_delay(wsdd_finding * wsdd)338 wsdd_finding_publish_delay (wsdd_finding *wsdd)
339 {
340     bool delay = false;
341 
342     if (wsdd->published) {
343         return;
344     }
345 
346     /* Continue discovery, if interface has IPv4/IPv6 address,
347      * and we have not yet discovered address of the same address
348      * family of device
349      *
350      * Some devices doesn't return their IPv4 endpoints, if
351      * metadata is queried via IPv6, and visa versa. This is
352      * possible that we will finish discovery of the particular
353      * address family, before we'll ever know that the device
354      * may have address of another address family, so part
355      * of addresses will be never discovered (see #44 for details).
356      *
357      * To prevent this situation, we continue discovery with
358      * some reasonable delay, if network interface has IPv4/IPv6
359      * address, but device is not yet.
360      */
361 
362     if (netif_has_non_link_local_addr(AF_INET, wsdd->finding.ifindex) &&
363         !zeroconf_endpoint_list_has_non_link_local_addr(AF_INET,
364             wsdd->finding.endpoints)) {
365         log_debug(wsdd_log,
366                 "\"%s\": IPv4 address expected but not yet discovered",
367                 wsdd->finding.model);
368         delay = true;
369     }
370 
371     if (netif_has_non_link_local_addr(AF_INET6, wsdd->finding.ifindex) &&
372         !zeroconf_endpoint_list_has_non_link_local_addr(AF_INET6,
373             wsdd->finding.endpoints)) {
374         log_debug(wsdd_log,
375                 "\"%s\": IPv6 address expected but not yet discovered",
376                 wsdd->finding.model);
377         delay = true;
378     }
379 
380     if (delay) {
381         if (wsdd->publish_timer == NULL) {
382             wsdd->publish_timer = eloop_timer_new(WSDD_PUBLISH_DELAY,
383                 wsdd_finding_publish_delay_timer_callback, wsdd);
384         }
385 
386         return;
387     }
388 
389     wsdd_finding_publish(wsdd);
390 }
391 
392 /* Get existent finding or add a new one
393  */
394 static wsdd_finding*
wsdd_finding_get(int ifindex,const char * address)395 wsdd_finding_get (int ifindex, const char *address)
396 {
397     ll_node      *node;
398     wsdd_finding *wsdd;
399 
400     /* Check for duplicates */
401     for (LL_FOR_EACH(node, &wsdd_finding_list)) {
402         wsdd = OUTER_STRUCT(node, wsdd_finding, list_node);
403         if (wsdd->finding.ifindex == ifindex &&
404             !strcmp(wsdd->address, address)) {
405             return wsdd;
406         }
407     }
408 
409     /* Add new finding */
410     wsdd = wsdd_finding_new(ifindex, address);
411     ll_push_end(&wsdd_finding_list, &wsdd->list_node);
412 
413     return wsdd;
414 }
415 
416 /* Lookup wsdd_finding by IP address
417  */
418 static wsdd_finding*
wsdd_finding_by_address(ip_addr addr)419 wsdd_finding_by_address (ip_addr addr)
420 {
421     ll_node               *node, *node2;
422     wsdd_finding          *wsdd;
423     wsdd_xaddr            *xaddr;
424     const struct sockaddr *sockaddr;
425 
426     /* Check for duplicates */
427     for (LL_FOR_EACH(node, &wsdd_finding_list)) {
428         wsdd = OUTER_STRUCT(node, wsdd_finding, list_node);
429 
430         for (LL_FOR_EACH(node2, &wsdd->xaddrs)) {
431             xaddr = OUTER_STRUCT(node2, wsdd_xaddr, list_node);
432             sockaddr = http_uri_addr(xaddr->uri);
433 
434             if (sockaddr != NULL) {
435                 ip_addr addr2 = ip_addr_from_sockaddr(sockaddr);
436                 if (ip_addr_equal(addr, addr2)) {
437                     return wsdd;
438                 }
439             }
440         }
441     }
442 
443     return NULL;
444 }
445 
446 /* Check if finding already has particular xaddr
447  */
448 static bool
wsdd_finding_has_xaddr(wsdd_finding * wsdd,const wsdd_xaddr * xaddr)449 wsdd_finding_has_xaddr (wsdd_finding *wsdd, const wsdd_xaddr *xaddr)
450 {
451     ll_node    *node;
452     wsdd_xaddr *xaddr2;
453 
454     for (LL_FOR_EACH(node, &wsdd->xaddrs)) {
455         xaddr2 = OUTER_STRUCT(node, wsdd_xaddr, list_node);
456 
457         if (http_uri_equal(xaddr->uri, xaddr2->uri)) {
458             return true;
459         }
460     }
461 
462     return false;
463 }
464 
465 /* Delete wsdd_finding from the wsdd_finding_list
466  */
467 static void
wsdd_finding_del(const char * address)468 wsdd_finding_del (const char *address)
469 {
470     ll_node   *node;
471 
472     /* Lookup finding in the list */
473     for (LL_FOR_EACH(node, &wsdd_finding_list)) {
474         wsdd_finding *wsdd = OUTER_STRUCT(node, wsdd_finding, list_node);
475         if (!strcmp(wsdd->address, address)) {
476             ll_del(&wsdd->list_node);
477             wsdd_finding_free(wsdd);
478             return;
479         }
480     }
481 }
482 
483 /* Delete all findings from the wsdd_finding_list
484  */
485 static void
wsdd_finding_list_purge(void)486 wsdd_finding_list_purge (void)
487 {
488     ll_node   *node;
489 
490     while ((node = ll_first(&wsdd_finding_list)) != NULL) {
491         wsdd_finding *wsdd = OUTER_STRUCT(node, wsdd_finding, list_node);
492         ll_del(&wsdd->list_node);
493         wsdd_finding_free(wsdd);
494     }
495 }
496 
497 /* Parse endpoint addresses from the devprof:Hosted section of the
498  * device metadata:
499  *   <devprof:Hosted>
500  *     <a:EndpointReference>
501  *       <a:Address>http://192.168.1.102:5358/WSDScanner</a:Address>
502  *     </addressing:EndpointReference>
503  *     <devprof:Types>scan:ScannerServiceType</devprof:Types>
504  *     <devprof:ServiceId>uri:4509a320-00a0-008f-00b6-002507510eca/WSDScanner</devprof:ServiceId>
505  *     <pnpx:CompatibleId>http://schemas.microsoft.com/windows/2006/08/wdp/scan/ScannerServiceType</pnpx:CompatibleId>
506  *     <pnpx:HardwareId>VEN_0103&amp;DEV_069D</pnpx:HardwareId>
507  *   </devprof:Hosted>
508  *
509  * It ignores all endpoints except ScannerServiceType, extracts endpoint
510  * URLs and prepends them to the wsdd->finding.endpoints
511  *
512  * Returns true if some endpoints were extracted, false otherwise
513  */
514 static bool
wsdd_finding_parse_endpoints(wsdd_finding * wsdd,xml_rd * xml)515 wsdd_finding_parse_endpoints (wsdd_finding *wsdd, xml_rd *xml)
516 {
517     unsigned int      level = xml_rd_depth(xml);
518     size_t            prefixlen = strlen(xml_rd_node_path(xml));
519     bool              is_scanner = false;
520     zeroconf_endpoint *endpoints = NULL;
521     bool              ok = false;
522 
523     while (!xml_rd_end(xml)) {
524         const char *path = xml_rd_node_path(xml) + prefixlen;
525         const char *val;
526 
527         if (!strcmp(path, "/devprof:Types")) {
528             val = xml_rd_node_value(xml);
529             if (strstr(val, "ScannerServiceType") != NULL) {
530                 is_scanner = true;
531             }
532         } else if (!strcmp(path, "/a:EndpointReference/a:Address")) {
533             http_uri          *uri;
534             zeroconf_endpoint *ep;
535 
536             val = xml_rd_node_value(xml);
537             uri = http_uri_new(val, true);
538             if (uri != NULL) {
539                 http_uri_fix_ipv6_zone(uri, wsdd->finding.ifindex);
540                 ep = zeroconf_endpoint_new(ID_PROTO_WSD, uri);
541                 ep->next = endpoints;
542                 endpoints = ep;
543             }
544         }
545 
546         xml_rd_deep_next(xml, level);
547     }
548 
549     if (!is_scanner) {
550         zeroconf_endpoint_list_free(endpoints);
551         return false;
552     }
553 
554     ok = endpoints != NULL;
555 
556     while (endpoints != NULL) {
557         zeroconf_endpoint     *ep = endpoints;
558         const struct sockaddr *addr = http_uri_addr(ep->uri);
559 
560         if (addr != NULL) {
561             ip_addrset_add(wsdd->finding.addrs, ip_addr_from_sockaddr(addr));
562         }
563         endpoints = endpoints->next;
564         ep->next = wsdd->finding.endpoints;
565         wsdd->finding.endpoints = ep;
566     }
567 
568     return ok;
569 }
570 
571 /* Get metadata callback
572  */
573 static void
wsdd_finding_get_metadata_callback(void * ptr,http_query * q)574 wsdd_finding_get_metadata_callback (void *ptr, http_query *q)
575 {
576     error        err;
577     xml_rd       *xml = NULL;
578     http_data    *data;
579     wsdd_finding *wsdd = ptr;
580     char         *model = NULL, *manufacturer = NULL;
581     bool         ok = false;
582 
583     (void) ptr;
584 
585     /* Check query status */
586     err = http_query_error(q);
587     if (err != NULL) {
588         log_trace(wsdd_log, "metadata query: %s", ESTRING(err));
589         goto DONE;
590     }
591 
592     /* Parse XML */
593     data = http_query_get_response_data(q);
594     if (data->size == 0) {
595         log_trace(wsdd_log, "metadata query: no data");
596         goto DONE;
597     }
598 
599     err = xml_rd_begin(&xml, data->bytes, data->size, wsdd_ns_rules);
600     if (err != NULL) {
601         log_trace(wsdd_log, "metadata query: %s", ESTRING(err));
602         goto DONE;
603     }
604 
605     /* Decode XML */
606     while (!xml_rd_end(xml)) {
607         const char *path = xml_rd_node_path(xml);
608 
609         if (!strcmp(path, "s:Envelope/s:Body/mex:Metadata/mex:MetadataSection"
610                 "/devprof:Relationship/devprof:Hosted")) {
611             ok = wsdd_finding_parse_endpoints(wsdd, xml) || ok;
612         } else if (!strcmp(path, "s:Envelope/s:Body/mex:Metadata/mex:MetadataSection"
613                 "/devprof:ThisModel/devprof:Manufacturer")) {
614             if (manufacturer == NULL) {
615                 manufacturer = str_dup(xml_rd_node_value(xml));
616             }
617         } else if (!strcmp(path, "s:Envelope/s:Body/mex:Metadata/mex:MetadataSection"
618                 "/devprof:ThisModel/devprof:ModelName")) {
619             if (model == NULL) {
620                 model = str_dup(xml_rd_node_value(xml));
621             }
622         }
623 
624         xml_rd_deep_next(xml, 0);
625     }
626 
627     if (wsdd->finding.model == NULL) {
628         if (model != NULL && manufacturer != NULL &&
629             str_has_prefix(model, manufacturer)) {
630             mem_free(manufacturer);
631             manufacturer = NULL;
632         }
633 
634         if (model != NULL && manufacturer != NULL) {
635             wsdd->finding.model = str_printf("%s %s", manufacturer, model);
636         } else if (model != NULL) {
637             wsdd->finding.model = model;
638             model = NULL;
639         } else if (manufacturer != NULL) {
640             wsdd->finding.model = manufacturer;
641             manufacturer = NULL;
642         } else {
643             wsdd->finding.model = str_dup(wsdd->address);
644         }
645     }
646 
647     /* Cancel cancel all unnecessary metadata requests.
648      *
649      * Note, we consider request is unnecessary if:
650      *   * it has the same address family
651      *   * it belongs to the same network interface
652      */
653     if (ok) {
654         http_client_cancel_af_uintptr(wsdd->http_client,
655             http_uri_af(http_query_uri(q)), http_query_get_uintptr(q));
656     }
657 
658     /* Cleanup and exit */
659 DONE:
660     xml_rd_finish(&xml);
661     mem_free(model);
662     mem_free(manufacturer);
663 
664     if (http_client_has_pending(wsdd->http_client) == 0) {
665         wsdd_finding_publish_delay(wsdd);
666     }
667 }
668 
669 /* Query device metadata
670  */
671 static void
wsdd_finding_get_metadata(wsdd_finding * wsdd,int ifindex,wsdd_xaddr * xaddr)672 wsdd_finding_get_metadata (wsdd_finding *wsdd, int ifindex, wsdd_xaddr *xaddr)
673 {
674     uuid       u = uuid_rand();
675     http_query *q;
676 
677     log_trace(wsdd_log, "querying metadata from %s", http_uri_str(xaddr->uri));
678 
679     sprintf(wsdd_buf, wsdd_get_metadata_template, u.text, wsdd->address);
680     q = http_query_new(wsdd->http_client, http_uri_clone(xaddr->uri),
681         "POST", str_dup(wsdd_buf), "application/soap+xml; charset=utf-8");
682 
683     http_query_set_uintptr(q, ifindex);
684     http_query_submit(q, wsdd_finding_get_metadata_callback);
685 }
686 
687 /******************** wsdd_message operations ********************/
688 /* Parse transport addresses. Universal function
689  * for Hello/Bye/ProbeMatch message
690  */
691 static void
wsdd_message_parse_endpoint(wsdd_message * msg,xml_rd * xml)692 wsdd_message_parse_endpoint (wsdd_message *msg, xml_rd *xml)
693 {
694     unsigned int level = xml_rd_depth(xml);
695     char         *xaddrs_text = NULL;
696     size_t       prefixlen = strlen(xml_rd_node_path(xml));
697 
698     while (!xml_rd_end(xml)) {
699         const char *path = xml_rd_node_path(xml) + prefixlen;
700         const char *val;
701 
702         if (!strcmp(path, "/d:Types")) {
703             val = xml_rd_node_value(xml);
704             msg->is_scanner = !!strstr(val, "ScanDeviceType");
705             msg->is_printer = !!strstr(val, "PrintDeviceType");
706         } else if (!strcmp(path, "/d:XAddrs")) {
707             mem_free(xaddrs_text);
708             xaddrs_text = str_dup(xml_rd_node_value(xml));
709         } else if (!strcmp(path, "/a:EndpointReference/a:Address")) {
710             mem_free((char*) msg->address);
711             msg->address = str_dup(xml_rd_node_value(xml));
712         }
713 
714         xml_rd_deep_next(xml, level);
715     }
716 
717     if (xaddrs_text != NULL) {
718         char              *tok, *saveptr;
719         static const char *delim = "\t\n\v\f\r \x85\xA0";
720 
721         for (tok = strtok_r(xaddrs_text, delim, &saveptr); tok != NULL;
722              tok = strtok_r(NULL, delim, &saveptr)) {
723 
724             http_uri   *uri = http_uri_new(tok, true);
725 
726             if (uri != NULL) {
727                 wsdd_xaddr_list_add(&msg->xaddrs, uri);
728             }
729         }
730     }
731 
732     mem_free(xaddrs_text);
733 }
734 
735 /* Parse WSDD message
736  */
737 static wsdd_message*
wsdd_message_parse(const char * xml_text,size_t xml_len)738 wsdd_message_parse (const char *xml_text, size_t xml_len)
739 {
740     wsdd_message *msg = mem_new(wsdd_message, 1);
741     xml_rd       *xml;
742     error        err;
743 
744     ll_init(&msg->xaddrs);
745 
746     err = xml_rd_begin(&xml, xml_text, xml_len, wsdd_ns_rules);
747     if (err != NULL) {
748         goto DONE;
749     }
750 
751     while (!xml_rd_end(xml)) {
752         const char *path = xml_rd_node_path(xml);
753         const char *val;
754 
755         if (!strcmp(path, "s:Envelope/s:Header/a:Action")) {
756             val = xml_rd_node_value(xml);
757             if (strstr(val, "Hello")) {
758                 msg->action = WSDD_ACTION_HELLO;
759             } else if (strstr(val, "Bye")) {
760                 msg->action = WSDD_ACTION_BYE;
761             } else if (strstr(val, "ProbeMatches")) {
762                 msg->action = WSDD_ACTION_PROBEMATCHES;
763             }
764         } else if (!strcmp(path, "s:Envelope/s:Body/d:Hello") ||
765                    !strcmp(path, "s:Envelope/s:Body/d:Bye") ||
766                    !strcmp(path, "s:Envelope/s:Body/d:ProbeMatches/d:ProbeMatch")) {
767             wsdd_message_parse_endpoint(msg, xml);
768         }
769         xml_rd_deep_next(xml, 0);
770     }
771 
772 DONE:
773     xml_rd_finish(&xml);
774     if (err != NULL ||
775         msg->action == WSDD_ACTION_UNKNOWN ||
776         msg->address == NULL ||
777         (msg->action == WSDD_ACTION_HELLO && ll_empty(&msg->xaddrs)) ||
778         (msg->action == WSDD_ACTION_PROBEMATCHES && ll_empty(&msg->xaddrs))) {
779         wsdd_message_free(msg);
780         msg = NULL;
781     }
782 
783     return msg;
784 }
785 
786 /* Free wsdd_message
787  */
788 static void
wsdd_message_free(wsdd_message * msg)789 wsdd_message_free (wsdd_message *msg)
790 {
791     if (msg != NULL) {
792         mem_free((char*) msg->address);
793         wsdd_xaddr_list_purge(&msg->xaddrs);
794         mem_free(msg);
795     }
796 }
797 
798 /* Get message action name, for debugging
799  */
800 static const char*
wsdd_message_action_name(const wsdd_message * msg)801 wsdd_message_action_name (const wsdd_message *msg)
802 {
803     switch (msg->action) {
804     case WSDD_ACTION_UNKNOWN:
805         break;
806 
807     case WSDD_ACTION_HELLO:        return "Hello";
808     case WSDD_ACTION_BYE:          return "Bye";
809     case WSDD_ACTION_PROBEMATCHES: return "ProbeMatches";
810     }
811 
812     return "UNKNOWN";
813 }
814 
815 /******************** Advanced socket options ********************/
816 /* Setup IP_PKTINFO/IP_RECVIF reception for IPv6 sockets
817  */
818 static int
wsdd_sock_enable_pktinfo_ip6(int fd)819 wsdd_sock_enable_pktinfo_ip6 (int fd)
820 {
821     static int yes = 1;
822     int        rc;
823 
824     rc = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &yes, sizeof(yes));
825     if (rc < 0) {
826         log_debug(wsdd_log, "setsockopt(AF_INET6, IPV6_RECVPKTINFO): %s",
827                 strerror(errno));
828     }
829 
830     return rc;
831 }
832 
833 /* Setup IP_PKTINFO/IP_RECVIF reception for IPv4 sockets
834  */
835 static int
wsdd_sock_enable_pktinfo_ip4(int fd)836 wsdd_sock_enable_pktinfo_ip4 (int fd)
837 {
838     static int yes = 1;
839     int        rc;
840 
841 #ifdef IP_PKTINFO
842     /* Linux version */
843     rc = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &yes, sizeof(yes));
844 #elif defined(IP_RECVIF)
845     /* OpenBSD */
846     rc = setsockopt(fd, IPPROTO_IP, IP_RECVIF, &yes, sizeof(yes));
847 #else
848 #  error FIX ME
849 #endif
850 
851     if (rc < 0) {
852         log_debug(wsdd_log, "setsockopt(AF_INET,IP_PKTINFO/IP_RECVIF): %s",
853                   strerror(errno));
854     }
855 
856     return rc;
857 }
858 
859 /******************** wsdd_resolver operations ********************/
860 /* Dispatch received WSDD message
861  */
862 static void
wsdd_resolver_message_dispatch(wsdd_resolver * resolver,wsdd_message * msg,const char * from)863 wsdd_resolver_message_dispatch (wsdd_resolver *resolver,
864         wsdd_message *msg, const char *from)
865 {
866     wsdd_finding *wsdd;
867     wsdd_xaddr   *xaddr;
868     ll_node      *node;
869 
870     /* Fixup ipv6 zones */
871     for (LL_FOR_EACH(node, &msg->xaddrs)) {
872         xaddr = OUTER_STRUCT(node, wsdd_xaddr, list_node);
873         http_uri_fix_ipv6_zone(xaddr->uri, resolver->ifindex);
874     }
875 
876     /* Write trace messages */
877     log_trace(wsdd_log, "%s message received from %s:",
878         wsdd_message_action_name(msg), from);
879     log_trace(wsdd_log, "  address:    %s", msg->address);
880     log_trace(wsdd_log, "  is_scanner: %s", msg->is_scanner ? "yes" : "no");
881     log_trace(wsdd_log, "  is_printer: %s", msg->is_printer ? "yes" : "no");
882     for (LL_FOR_EACH(node, &msg->xaddrs)) {
883         xaddr = OUTER_STRUCT(node, wsdd_xaddr, list_node);
884         log_trace(wsdd_log, "  xaddr:      %s", http_uri_str(xaddr->uri));
885     }
886 
887     /* Handle the message */
888     switch (msg->action) {
889     case WSDD_ACTION_HELLO:
890     case WSDD_ACTION_PROBEMATCHES:
891         /* Ignore devices that are neither scanner not printer */
892         if (!(msg->is_scanner || msg->is_printer)) {
893             log_trace(wsdd_log,
894                 "skipped: device is neither scanner not printer");
895             goto DONE;
896         }
897 
898         /* Add a finding or get existent one */
899         wsdd = wsdd_finding_get(resolver->ifindex, msg->address);
900 
901         /* Import newly discovered xaddrs and initiate metadata
902          * query
903          */
904         while ((node = ll_pop_beg(&msg->xaddrs)) != NULL) {
905             xaddr = OUTER_STRUCT(node, wsdd_xaddr, list_node);
906 
907             if (wsdd_finding_has_xaddr(wsdd, xaddr)) {
908                 wsdd_xaddr_free(xaddr);
909                 continue;
910             }
911 
912             ll_push_end(&wsdd->xaddrs, &xaddr->list_node);
913             if (msg->is_scanner) {
914                 wsdd_finding_get_metadata(wsdd, resolver->ifindex, xaddr);
915             }
916         }
917 
918         /* If there is no pending metadata queries, it may mean
919          * one of the following:
920          *   1) device is not scanner, metadata won't be requested
921          *   2) there is no xaddrs (which is very unlikely, but
922          *      just in case...)
923          *   3) device already known and all metadata queries
924          *      already finished
925          *
926          * At this case we can publish device now
927          */
928         if (http_client_has_pending(wsdd->http_client) == 0) {
929             if (msg->is_scanner) {
930                 wsdd_finding_publish_delay(wsdd);
931             } else {
932                 wsdd_finding_publish(wsdd);
933             }
934         }
935         break;
936 
937     case WSDD_ACTION_BYE:
938         wsdd_finding_del(msg->address);
939         break;
940 
941     default:
942         break;
943     }
944 
945     /* Cleanup and exit */
946 DONE:
947     wsdd_message_free(msg);
948     log_trace(wsdd_log, "");
949 }
950 
951 
952 /* Resolver read callback
953  */
954 static void
wsdd_resolver_read_callback(int fd,void * data,ELOOP_FDPOLL_MASK mask)955 wsdd_resolver_read_callback (int fd, void *data, ELOOP_FDPOLL_MASK mask)
956 {
957     struct sockaddr_storage from, to;
958     socklen_t               tolen = sizeof(to);
959     ip_straddr              str_from, str_to;
960     int                     rc;
961     wsdd_message            *msg;
962     struct iovec            vec = {wsdd_buf, sizeof(wsdd_buf)};
963     uint8_t                 aux[8192];
964     struct cmsghdr          *cmsg;
965     int                     ifindex = 0;
966     wsdd_resolver           *resolver;
967     struct msghdr           msghdr = {
968         .msg_name = &from,
969         .msg_namelen = sizeof(from),
970         .msg_iov = &vec,
971         .msg_iovlen = 1,
972         .msg_control = aux,
973         .msg_controllen = sizeof(aux)
974     };
975 
976     (void) mask;
977     (void) data;
978 
979     /* Receive a packet */
980     rc = recvmsg(fd, &msghdr, 0);
981     if (rc <= 0) {
982         return;
983     }
984 
985     /* Fetch interface index from auxiliary data */
986     for (cmsg = CMSG_FIRSTHDR(&msghdr); cmsg != NULL;
987          cmsg = CMSG_NXTHDR(&msghdr, cmsg)) {
988         if (cmsg->cmsg_level == IPPROTO_IPV6 &&
989             cmsg->cmsg_type == IPV6_PKTINFO) {
990             struct in6_pktinfo *pkt = (struct in6_pktinfo*) CMSG_DATA(cmsg);
991             ifindex = pkt->ipi6_ifindex;
992         }
993 #ifdef IP_PKTINFO
994         /* Linux version */
995         else if (cmsg->cmsg_level == IPPROTO_IP &&
996             cmsg->cmsg_type == IP_PKTINFO) {
997             struct in_pktinfo *pkt = (struct in_pktinfo*) CMSG_DATA(cmsg);
998             ifindex = pkt->ipi_ifindex;
999         }
1000 #elif defined(IP_RECVIF)
1001         /* OpenBSD */
1002         else if (cmsg->cmsg_level == IPPROTO_IP &&
1003             cmsg->cmsg_type == IP_RECVIF) {
1004             struct sockaddr_dl *pkt = (struct sockaddr_dl *) CMSG_DATA(cmsg);
1005             ifindex = pkt->sdl_index;
1006         }
1007 #else
1008 #   error FIX ME
1009 #endif
1010     }
1011 
1012     str_from = ip_straddr_from_sockaddr((struct sockaddr*) &from, true);
1013     (void) getsockname(fd, (struct sockaddr*) &to, &tolen);
1014     str_to = ip_straddr_from_sockaddr((struct sockaddr*) &to, true);
1015 
1016     log_trace(wsdd_log, "%d bytes received: %s->%s", rc,
1017         str_from.text, str_to.text);
1018     log_trace_data(wsdd_log, "application/xml", wsdd_buf, rc);
1019 
1020     /* Lookup resolver by interface index */
1021     resolver = wsdd_netif_resolver_by_ifindex(ifindex);
1022     if (resolver == NULL) {
1023         return;
1024     }
1025 
1026     /* Parse and dispatch the message */
1027     msg = wsdd_message_parse(wsdd_buf, rc);
1028     if (msg != NULL) {
1029         wsdd_resolver_message_dispatch(resolver, msg, "UDP");
1030     }
1031 }
1032 
1033 /* Retransmit timer callback
1034  */
1035 static void
wsdd_resolver_timer_callback(void * data)1036 wsdd_resolver_timer_callback (void *data)
1037 {
1038     wsdd_resolver *resolver = data;
1039     resolver->timer = NULL;
1040 
1041     if (resolver->total_time >= WSDD_DISCOVERY_TIME) {
1042         eloop_fdpoll_free(resolver->fdpoll);
1043         close(resolver->fd);
1044         resolver->fdpoll = NULL;
1045         resolver->fd = -1;
1046         log_debug(wsdd_log, "%s: done discovery", resolver->str_ifaddr.text);
1047 
1048         if (resolver->initscan) {
1049             resolver->initscan = false;
1050             wsdd_initscan_count_dec();
1051         }
1052     } else {
1053         wsdd_resolver_send_probe(resolver);
1054     };
1055 }
1056 
1057 /* Set retransmit timer
1058  */
1059 static void
wsdd_resolver_timer_set(wsdd_resolver * resolver)1060 wsdd_resolver_timer_set (wsdd_resolver *resolver)
1061 {
1062     uint32_t t;
1063 
1064     log_assert(wsdd_log, resolver->timer == NULL);
1065 
1066     if (resolver->total_time + WSDD_RETRANSMIT_MAX >= WSDD_DISCOVERY_TIME) {
1067         t = WSDD_DISCOVERY_TIME - resolver->total_time;
1068     } else {
1069         t = math_rand_range(WSDD_RETRANSMIT_MIN, WSDD_RETRANSMIT_MAX);
1070     }
1071 
1072     resolver->total_time += t;
1073     resolver->timer = eloop_timer_new(t,
1074             wsdd_resolver_timer_callback, resolver);
1075 }
1076 
1077 /* Send probe
1078  */
1079 static void
wsdd_resolver_send_probe(wsdd_resolver * resolver)1080 wsdd_resolver_send_probe (wsdd_resolver *resolver)
1081 {
1082     uuid            u = uuid_rand();
1083     int             n = sprintf(wsdd_buf, wsdd_probe_template, u.text);
1084     int             rc;
1085     struct sockaddr *addr;
1086     socklen_t       addrlen;
1087     ip_straddr      straddr;
1088 
1089     if (resolver->ipv6) {
1090         addr = (struct sockaddr*) &wsdd_mcast_ipv6;
1091         addrlen = sizeof(wsdd_mcast_ipv6);
1092     } else {
1093         addr = (struct sockaddr*) &wsdd_mcast_ipv4;
1094         addrlen = sizeof(wsdd_mcast_ipv4);
1095     }
1096 
1097     straddr = ip_straddr_from_sockaddr(addr, true);
1098     log_trace(wsdd_log, "probe sent: %s->%s",
1099         resolver->str_sockaddr.text, straddr.text);
1100     log_trace_data(wsdd_log, "application/xml", wsdd_buf, n);
1101 
1102     rc = sendto(resolver->fd, wsdd_buf, n, 0, addr, addrlen);
1103 
1104     if (rc < 0) {
1105         log_debug(wsdd_log, "send_probe: %s", strerror(errno));
1106     }
1107 
1108     wsdd_resolver_timer_set(resolver);
1109 }
1110 
1111 /* Create wsdd_resolver
1112  */
1113 static wsdd_resolver*
wsdd_resolver_new(const netif_addr * addr,bool initscan)1114 wsdd_resolver_new (const netif_addr *addr, bool initscan)
1115 {
1116     wsdd_resolver *resolver = mem_new(wsdd_resolver, 1);
1117     int           af = addr->ipv6 ? AF_INET6 : AF_INET;
1118     const char    *af_name = addr->ipv6 ? "AF_INET6" : "AF_INET";
1119     int           rc;
1120     static int    no = 0;
1121     uint16_t      port;
1122 
1123     /* Build resolver structure */
1124     resolver->ifindex = addr->ifindex;
1125 
1126     /* Open a socket */
1127     resolver->ipv6 = addr->ipv6;
1128     resolver->fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
1129     if (resolver->fd < 0) {
1130         log_debug(wsdd_log, "socket(%s): %s", af_name, strerror(errno));
1131         goto FAIL;
1132     }
1133 
1134     /* Set socket options */
1135     if (addr->ipv6) {
1136         rc = setsockopt(resolver->fd, IPPROTO_IPV6, IPV6_MULTICAST_IF,
1137                 &addr->ifindex, sizeof(addr->ifindex));
1138 
1139         if (rc < 0) {
1140             log_debug(wsdd_log, "setsockopt(AF_INET6,IPV6_MULTICAST_IF): %s",
1141                 strerror(errno));
1142             goto FAIL;
1143         }
1144 
1145         rc = wsdd_sock_enable_pktinfo_ip6(resolver->fd);
1146         if (rc < 0) {
1147             goto FAIL;
1148         }
1149 
1150         /* Note: error is not a problem here */
1151         (void) setsockopt(resolver->fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
1152                 &no, sizeof(no));
1153     } else {
1154         rc = setsockopt(resolver->fd, IPPROTO_IP, IP_MULTICAST_IF,
1155                 &addr->ip.v4, sizeof(addr->ip.v4));
1156 
1157         if (rc < 0) {
1158             log_debug(wsdd_log, "setsockopt(AF_INET,IP_MULTICAST_IF): %s",
1159                     strerror(errno));
1160             goto FAIL;
1161         }
1162 
1163 
1164         rc = wsdd_sock_enable_pktinfo_ip4(resolver->fd);
1165         if (rc < 0) {
1166             goto FAIL;
1167         }
1168 
1169         /* Note: error is not a problem here */
1170         (void) setsockopt(resolver->fd, IPPROTO_IP, IP_MULTICAST_LOOP,
1171                 &no, sizeof(no));
1172     }
1173 
1174     /* Bind the socket */
1175     if (addr->ipv6) {
1176         struct sockaddr_in6 a;
1177         socklen_t           alen = sizeof(a);
1178 
1179         memset(&a, 0, sizeof(a));
1180         a.sin6_family = AF_INET6;
1181         a.sin6_addr = addr->ip.v6;
1182         a.sin6_scope_id = addr->ifindex;
1183         resolver->str_ifaddr = ip_straddr_from_ip(AF_INET6, &addr->ip);
1184 
1185         rc = bind(resolver->fd, (struct sockaddr*) &a, sizeof(a));
1186 
1187         (void) getsockname(resolver->fd, (struct sockaddr*) &a, &alen);
1188         port = a.sin6_port;
1189         resolver->str_sockaddr = ip_straddr_from_sockaddr(
1190                 (struct sockaddr*) &a, true);
1191     } else {
1192         struct sockaddr_in a;
1193         socklen_t          alen = sizeof(a);
1194 
1195         memset(&a, 0, sizeof(a));
1196         a.sin_family = AF_INET;
1197         a.sin_addr = addr->ip.v4;
1198         resolver->str_ifaddr = ip_straddr_from_ip(AF_INET, &addr->ip);
1199         resolver->str_sockaddr = ip_straddr_from_sockaddr(
1200                 (struct sockaddr*) &a, true);
1201 
1202         rc = bind(resolver->fd, (struct sockaddr*) &a, sizeof(a));
1203 
1204         (void) getsockname(resolver->fd, (struct sockaddr*) &a, &alen);
1205         port = a.sin_port;
1206         resolver->str_sockaddr = ip_straddr_from_sockaddr(
1207                 (struct sockaddr*) &a, true);
1208     }
1209 
1210     log_debug(wsdd_log, "%s: started discovery, UDP port=%d",
1211         resolver->str_ifaddr.text, ntohs(port));
1212 
1213     if (rc < 0) {
1214         log_debug(wsdd_log, "bind(%s): %s", resolver->str_sockaddr.text,
1215                 strerror(errno));
1216         goto FAIL;
1217     }
1218 
1219     /* Setup fdpoll */
1220     resolver->fdpoll = eloop_fdpoll_new(resolver->fd,
1221         wsdd_resolver_read_callback, NULL);
1222     eloop_fdpoll_set_mask(resolver->fdpoll, ELOOP_FDPOLL_READ);
1223 
1224     wsdd_resolver_send_probe(resolver);
1225 
1226     /* Update wsdd_initscan_count */
1227     resolver->initscan = initscan;
1228     if (resolver->initscan) {
1229         wsdd_initscan_count_inc();
1230     }
1231 
1232     return resolver;
1233 
1234     /* Error: cleanup and exit */
1235 FAIL:
1236     if (resolver->fd >= 0) {
1237         close(resolver->fd);
1238         resolver->fd = -1;
1239     }
1240     return resolver;
1241 }
1242 
1243 /* Destroy wsdd_resolver
1244  */
1245 static void
wsdd_resolver_free(wsdd_resolver * resolver)1246 wsdd_resolver_free (wsdd_resolver *resolver)
1247 {
1248     if (resolver->initscan) {
1249         wsdd_initscan_count_dec();
1250     }
1251 
1252     if (resolver->fdpoll != NULL) {
1253         eloop_fdpoll_free(resolver->fdpoll);
1254         close(resolver->fd);
1255     }
1256 
1257     if (resolver->timer != NULL) {
1258         eloop_timer_cancel(resolver->timer);
1259     }
1260 
1261     mem_free(resolver);
1262 }
1263 
1264 /******************** Miscellaneous events ********************/
1265 /* Called by zeroconf to notify wsdd about initial scan timer expiration
1266  */
1267 void
wsdd_initscan_timer_expired(void)1268 wsdd_initscan_timer_expired (void)
1269 {
1270     ll_node      *node;
1271     wsdd_finding *wsdd;
1272 
1273     /* Publish all incomplete but useful findings, if any
1274      *
1275      * Without it, if metadata query takes too long time (for example,
1276      * device has 2 IP addresses, one is unreachable from PC) it
1277      * effectively blocks the device from being discovered
1278      */
1279     for (LL_FOR_EACH(node, &wsdd_finding_list)) {
1280         wsdd = OUTER_STRUCT(node, wsdd_finding, list_node);
1281         if (!wsdd->published && wsdd->finding.endpoints != NULL) {
1282             http_client_cancel(wsdd->http_client);
1283             wsdd_finding_publish(wsdd);
1284         }
1285     }
1286 }
1287 
1288 /******************** WS-Discovery directed probes ********************/
1289 /* WS-Discovery send directed probe callback
1290  */
1291 static void
wsdd_send_directed_probe_callback(void * ptr,http_query * q)1292 wsdd_send_directed_probe_callback (void *ptr, http_query *q)
1293 {
1294     error                 err;
1295     const struct sockaddr *sockaddr = http_uri_addr(http_query_uri(q));
1296     int                   ifindex;
1297     http_data             *data;
1298     wsdd_resolver         *resolver;
1299     wsdd_message          *msg;
1300 
1301     (void) ptr;
1302 
1303     /* Drop query address from list of pending probes */
1304     if (sockaddr != NULL) {
1305         ip_addrset_del(wsdd_addrs_probing, ip_addr_from_sockaddr(sockaddr));
1306     }
1307 
1308     err = http_query_error(q);
1309     if (err != NULL) {
1310         log_debug(wsdd_log, "directed probe: HTTP %s", ESTRING(err));
1311         return;
1312     }
1313 
1314     /* Find appropriate resolver */
1315     ifindex = (int) http_query_get_uintptr(q);
1316     resolver = wsdd_netif_resolver_by_ifindex(ifindex);
1317     if (resolver == NULL) {
1318         log_debug(wsdd_log,
1319             "directed probe: resolver not found for interface %d", ifindex);
1320         return;
1321     }
1322 
1323     /* Parse and dispatch the message */
1324     data = http_query_get_response_data(q);
1325     msg = wsdd_message_parse(data->bytes, data->size);
1326     if (msg != NULL) {
1327         wsdd_resolver_message_dispatch(resolver, msg, "HTTP");
1328     }
1329 }
1330 
1331 /* Send WD-Discovery directed probe
1332  *
1333  * WS-Discovery defines two mechanisms for sending Probes:
1334  *   * probes can be send using UDP milticasts
1335  *   * probes can be send directly via HTTP POST to the following URL:
1336  *     http://addr//StableWSDiscoveryEndpoint/schemas-xmlsoap-org_ws_2005_04_discovery
1337  *
1338  * The second mechanism is called "Directed discovery Probe message", and
1339  * information about it is exceptionally hard to discover.
1340  *
1341  * BTW, this is why this protocol is called Web Services Discovery: you
1342  * need to browse the entire web to discover a bit of useful information
1343  *
1344  * This function is called from DNS-SD module when new device is found,
1345  * and sends directed probe to its HTTP stable discovery endpoint
1346  *
1347  * To avoid device overload with discovery requests, this function
1348  * sends only one request a time per address and doesn't send
1349  * requests to already known devices.
1350  */
1351 void
wsdd_send_directed_probe(int ifindex,int af,const void * addr)1352 wsdd_send_directed_probe (int ifindex, int af, const void *addr)
1353 {
1354     char          ifname[IF_NAMESIZE] = "?";
1355     ip_straddr    straddr = ip_straddr_from_ip(af, addr);
1356     char          uri_buf[1024];
1357     http_uri      *uri;
1358     uuid          u;
1359     http_query    *q;
1360     ip_addr       ipa = ip_addr_make(ifindex, af, addr);
1361 
1362     /* Do nothing, if discovery is disabled */
1363     if (!conf.discovery || conf.wsdd_mode == WSDD_OFF) {
1364         return;
1365     }
1366 
1367     /* Write log messages */
1368     if_indextoname(ifindex, ifname);
1369     log_debug(wsdd_log, "directed probe: trying if=%s, addr=%s",
1370         ifname, straddr.text);
1371 
1372     /* Skip loopback address, we will not find anything interesting there */
1373     if (ip_is_loopback(af, addr)) {
1374         log_debug(wsdd_log, "directed probe: skipping loopback address");
1375         return;
1376     }
1377 
1378     /* Already probing? */
1379     if (ip_addrset_lookup(wsdd_addrs_probing, ipa)) {
1380         log_debug(wsdd_log, "directed probe: already in progress, skipping");
1381         return;
1382     }
1383 
1384     /* Already contacted? */
1385     if (wsdd_finding_by_address(ipa) != NULL) {
1386         log_debug(wsdd_log, "directed probe: device already contacted, skipping");
1387         return;
1388     }
1389 
1390     ip_addrset_add_unsafe(wsdd_addrs_probing, ipa);
1391 
1392     /* Build request URI */
1393     if (af == AF_INET) {
1394         sprintf(uri_buf, "http://%s", straddr.text);
1395     } else if (!ip_is_linklocal(af, addr)) {
1396         sprintf(uri_buf, "http://[%s]", straddr.text);
1397     } else {
1398         /* Percent character in the IPv6 address literal
1399          * needs to be properly escaped, so it becomes %25
1400          * See RFC6874 for details
1401          */
1402         sprintf(uri_buf, "http://[%s%%25%d]", straddr.text, ifindex);
1403     }
1404 
1405     strcat(uri_buf, WSDD_STABLE_ENDPOINT);
1406     uri = http_uri_new(uri_buf, true);
1407     log_assert(wsdd_log, uri != NULL);
1408 
1409     /* Build probe request */
1410     u = uuid_rand();
1411     sprintf(wsdd_buf, wsdd_probe_template, u.text);
1412 
1413     /* Send probe request */
1414     q = http_query_new(wsdd_http_client, uri,
1415         "POST", str_dup(wsdd_buf), "application/soap+xml; charset=utf-8");
1416     http_query_set_uintptr(q, ifindex);
1417     http_query_submit(q, wsdd_send_directed_probe_callback);
1418 }
1419 
1420 /******************** Management of multicast sockets ********************/
1421 /* Open IPv4 or IPv6 multicast socket
1422  */
1423 static int
wsdd_mcsock_open(bool ipv6)1424 wsdd_mcsock_open (bool ipv6)
1425 {
1426     int        af = ipv6 ? AF_INET6 : AF_INET;
1427     int        fd, rc;
1428     const char *af_name = ipv6 ? "AF_INET6" : "AF_INET";
1429     static int yes = 1;
1430     ip_straddr straddr;
1431 
1432     /* Open a socket */
1433     fd = socket(af, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
1434     if (fd < 0) {
1435         log_debug(wsdd_log, "socket(%s): %s", af_name, strerror(errno));
1436         return fd;
1437     }
1438 
1439     /* Set socket options */
1440     rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
1441     if (rc < 0) {
1442         log_debug(wsdd_log, "setsockopt(%s, SO_REUSEADDR): %s",
1443                 af_name, strerror(errno));
1444         goto FAIL;
1445     }
1446 
1447     if (ipv6) {
1448         rc = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes));
1449         if (rc < 0) {
1450             log_debug(wsdd_log, "setsockopt(%s, IPV6_V6ONLY): %s",
1451                     af_name, strerror(errno));
1452             goto FAIL;
1453         }
1454 
1455         rc = wsdd_sock_enable_pktinfo_ip6(fd);
1456         if (rc < 0) {
1457             goto FAIL;
1458         }
1459     } else {
1460         rc = wsdd_sock_enable_pktinfo_ip4(fd);
1461         if (rc < 0) {
1462             goto FAIL;
1463         }
1464     }
1465 
1466     /* Bind socket to WSDD multicast port; group membership
1467      * will be added later on per-interface-address basis
1468      */
1469     if (ipv6) {
1470         struct sockaddr_in6 addr;
1471         memset(&addr, 0, sizeof(addr));
1472         addr.sin6_family = AF_INET6;
1473         addr.sin6_port = wsdd_mcast_ipv6.sin6_port;
1474         straddr = ip_straddr_from_sockaddr((struct sockaddr*) &addr, true);
1475         rc = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
1476     } else {
1477         struct sockaddr_in addr;
1478         memset(&addr, 0, sizeof(addr));
1479         addr.sin_family = AF_INET;
1480         addr.sin_port = wsdd_mcast_ipv4.sin_port;
1481         straddr = ip_straddr_from_sockaddr((struct sockaddr*) &addr, true);
1482         rc = bind(fd, (struct sockaddr*) &addr, sizeof(addr));
1483     }
1484     if (rc < 0) {
1485         log_debug(wsdd_log,
1486                 "bind(%s): %s", straddr.text, strerror(errno));
1487         goto FAIL;
1488     }
1489 
1490     return fd;
1491 
1492     /* Error: cleanup and exit */
1493 FAIL:
1494     rc = errno;
1495     close(fd);
1496     errno = rc;
1497 
1498     return -1;
1499 }
1500 
1501 /* Add or drop multicast group membership, on
1502  * per-interface-address basis
1503  */
1504 static void
wsdd_mcast_update_membership(int fd,netif_addr * addr,bool add)1505 wsdd_mcast_update_membership (int fd, netif_addr *addr, bool add)
1506 {
1507     int rc, opt;
1508 
1509     if (addr->ipv6) {
1510         struct ipv6_mreq mreq6;
1511 
1512         memset(&mreq6, 0, sizeof(mreq6));
1513 	mreq6.ipv6mr_multiaddr = wsdd_mcast_ipv6.sin6_addr;
1514 	mreq6.ipv6mr_interface = addr->ifindex;
1515 
1516         opt = add ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP;
1517         rc = setsockopt(fd, IPPROTO_IPV6, opt, &mreq6, sizeof(mreq6));
1518 
1519         if (rc < 0) {
1520             log_debug(wsdd_log, "setsockopt(AF_INET6,%s): %s",
1521                     add ? "IPV6_ADD_MEMBERSHIP" : "IPV6_DROP_MEMBERSHIP",
1522                     strerror(errno));
1523         }
1524     } else {
1525 #ifdef OS_HAVE_IP_MREQN
1526         struct ip_mreqn  mreq4;
1527 #else
1528         struct ip_mreq  mreq4;
1529 #endif
1530 
1531         memset(&mreq4, 0, sizeof(mreq4));
1532         mreq4.imr_multiaddr = wsdd_mcast_ipv4.sin_addr;
1533 #ifdef OS_HAVE_IP_MREQN
1534         mreq4.imr_address = addr->ip.v4;
1535         mreq4.imr_ifindex = addr->ifindex;
1536 #else
1537         mreq4.imr_interface = addr->ip.v4;
1538 #endif
1539 
1540         opt = add ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP;
1541         rc = setsockopt(fd, IPPROTO_IP, opt, &mreq4, sizeof(mreq4));
1542 
1543         if (rc < 0) {
1544             log_debug(wsdd_log, "setsockopt(AF_INET,%s): %s",
1545                     add ? "IP_ADD_MEMBERSHIP" : "IP_DROP_MEMBERSHIP",
1546                     strerror(errno));
1547         }
1548     }
1549 }
1550 
1551 /******************** Monitoring of network interfaces ********************/
1552 /* Dump list of network interfaces addresses
1553  */
1554 static void
wsdd_netif_dump_addresses(const char * prefix,netif_addr * list)1555 wsdd_netif_dump_addresses (const char *prefix, netif_addr *list)
1556 {
1557     char suffix[32] = "";
1558 
1559     while (list != NULL) {
1560         if (list->ipv6 && ip_is_linklocal(AF_INET6, &list->ip)) {
1561             sprintf(suffix, "%%%d", list->ifindex);
1562         }
1563         log_debug(wsdd_log, "%s%s%s", prefix, list->straddr, suffix);
1564         list = list->next;
1565     }
1566 }
1567 
1568 /* Lookup wsdd_resolver by interface index
1569  */
1570 static wsdd_resolver*
wsdd_netif_resolver_by_ifindex(int ifindex)1571 wsdd_netif_resolver_by_ifindex (int ifindex)
1572 {
1573     netif_addr *addr;
1574 
1575     for (addr = wsdd_netif_addr_list; addr != NULL; addr = addr->next) {
1576         if (addr->ifindex == ifindex) {
1577             return addr->data;
1578         }
1579     }
1580 
1581     return NULL;
1582 }
1583 
1584 /* Update network interfaces addresses
1585  */
1586 static void
wsdd_netif_update_addresses(bool initscan)1587 wsdd_netif_update_addresses (bool initscan)
1588 {
1589     netif_addr *addr_list = netif_addr_list_get();
1590     netif_addr *addr;
1591     netif_diff diff = netif_diff_compute(wsdd_netif_addr_list, addr_list);
1592 
1593     log_debug(wsdd_log, "netif addresses update:");
1594     wsdd_netif_dump_addresses(" + ", diff.added);
1595     wsdd_netif_dump_addresses(" - ", diff.removed);
1596 
1597     /* Update multicast group membership, and start/stop
1598      * per-interface-address resolvers */
1599     for (addr = diff.removed; addr != NULL; addr = addr->next) {
1600         int fd = addr->ipv6 ? wsdd_mcsock_ipv6 : wsdd_mcsock_ipv4;
1601         wsdd_mcast_update_membership(fd, addr, false);
1602         wsdd_resolver_free(addr->data);
1603     }
1604 
1605     for (addr = diff.added; addr != NULL; addr = addr->next) {
1606         int fd = addr->ipv6 ? wsdd_mcsock_ipv6 : wsdd_mcsock_ipv4;
1607         wsdd_mcast_update_membership(fd, addr, true);
1608         addr->data = wsdd_resolver_new(addr, initscan);
1609     }
1610 
1611     /* Update wsdd_netif_addr_list */
1612     wsdd_netif_addr_list = netif_addr_list_merge(diff.preserved, diff.added);
1613     netif_addr_list_free(diff.removed);
1614 }
1615 
1616 /* Network interfaces address change notification
1617  */
1618 static void
wsdd_netif_notifier_callback(void * data)1619 wsdd_netif_notifier_callback (void *data)
1620 {
1621     (void) data;
1622 
1623     log_debug(wsdd_log, "netif event");
1624     wsdd_netif_update_addresses(false);
1625 }
1626 
1627 /******************** Initialization and cleanup ********************/
1628 /* eloop start/stop callback
1629  */
1630 static void
wsdd_start_stop_callback(bool start)1631 wsdd_start_stop_callback (bool start)
1632 {
1633     if (start) {
1634         /* Setup WS-Discovery stable endpoint handling */
1635         wsdd_addrs_probing = ip_addrset_new();
1636         wsdd_http_client = http_client_new(wsdd_log, NULL);
1637 
1638         /* Setup WSDD multicast reception */
1639         if (wsdd_mcsock_ipv4 >= 0) {
1640             wsdd_fdpoll_ipv4 = eloop_fdpoll_new(wsdd_mcsock_ipv4,
1641                 wsdd_resolver_read_callback, NULL);
1642             eloop_fdpoll_set_mask(wsdd_fdpoll_ipv4, ELOOP_FDPOLL_READ);
1643         }
1644 
1645         if (wsdd_mcsock_ipv6 >= 0) {
1646             wsdd_fdpoll_ipv6 = eloop_fdpoll_new(wsdd_mcsock_ipv6,
1647                 wsdd_resolver_read_callback, NULL);
1648             eloop_fdpoll_set_mask(wsdd_fdpoll_ipv6, ELOOP_FDPOLL_READ);
1649         }
1650 
1651         /* Update netif addresses. Initscan counter is incremented and
1652          *
1653          * decremented to ensure that initial scan completion notification
1654          * will be raised even if there are no network interfaces.
1655          */
1656         wsdd_initscan_count_inc();
1657         wsdd_netif_update_addresses(true);
1658         wsdd_initscan_count_dec();
1659     } else {
1660         /* Cleanup WS-Discovery stable endpoint handling */
1661         ip_addrset_free(wsdd_addrs_probing);
1662         http_client_cancel(wsdd_http_client);
1663         http_client_free(wsdd_http_client);
1664 
1665         wsdd_addrs_probing = NULL;
1666         wsdd_http_client = NULL;
1667 
1668         /* Stop multicast reception */
1669         if (wsdd_fdpoll_ipv4 != NULL) {
1670             eloop_fdpoll_free(wsdd_fdpoll_ipv4);
1671             wsdd_fdpoll_ipv4 = NULL;
1672         }
1673         if (wsdd_fdpoll_ipv6 != NULL) {
1674             eloop_fdpoll_free(wsdd_fdpoll_ipv6);
1675             wsdd_fdpoll_ipv6 = NULL;
1676         }
1677 
1678         /* Cleanup resources */
1679         wsdd_finding_list_purge();
1680     }
1681 }
1682 
1683 /* Initialize WS-Discovery
1684  */
1685 SANE_Status
wsdd_init(void)1686 wsdd_init (void)
1687 {
1688     /* Initialize logging */
1689     wsdd_log = log_ctx_new("WSDD", zeroconf_log);
1690 
1691     /* Initialize wsdd_finding_list */
1692     ll_init(&wsdd_finding_list);
1693 
1694     /* All for now, if WS-Discovery is disabled */
1695     if (!conf.discovery || conf.wsdd_mode == WSDD_OFF) {
1696         log_debug(wsdd_log, "devices discovery disabled");
1697         zeroconf_finding_done(ZEROCONF_WSD);
1698         return SANE_STATUS_GOOD;
1699     }
1700 
1701     /* Create IPv4/IPv6 multicast addresses */
1702     wsdd_mcast_ipv4.sin_family = AF_INET;
1703     inet_pton(AF_INET, "239.255.255.250", &wsdd_mcast_ipv4.sin_addr);
1704     wsdd_mcast_ipv4.sin_port = htons(3702);
1705 
1706     wsdd_mcast_ipv6.sin6_family = AF_INET6;
1707     inet_pton(AF_INET6, "ff02::c", &wsdd_mcast_ipv6.sin6_addr);
1708     wsdd_mcast_ipv6.sin6_port = htons(3702);
1709 
1710     /* Open multicast sockets */
1711     wsdd_mcsock_ipv4 = wsdd_mcsock_open(false);
1712     if (wsdd_mcsock_ipv4 < 0) {
1713         goto FAIL;
1714     }
1715 
1716     wsdd_mcsock_ipv6 = wsdd_mcsock_open(true);
1717     if (wsdd_mcsock_ipv6 < 0 && errno != EAFNOSUPPORT) {
1718         goto FAIL;
1719     }
1720 
1721     /* Create netif notifier */
1722     wsdd_netif_notifier = netif_notifier_create(
1723         wsdd_netif_notifier_callback, NULL);
1724     if (wsdd_netif_notifier == NULL) {
1725         goto FAIL;
1726     }
1727 
1728     /* Register start/stop callback */
1729     eloop_add_start_stop_callback(wsdd_start_stop_callback);
1730 
1731     return SANE_STATUS_GOOD;
1732 
1733     /* Error: cleanup and exit */
1734 FAIL:
1735     wsdd_cleanup();
1736     return SANE_STATUS_IO_ERROR;
1737 }
1738 
1739 /* Cleanup WS-Discovery
1740  */
1741 void
wsdd_cleanup(void)1742 wsdd_cleanup (void)
1743 {
1744     netif_addr *addr;
1745 
1746     if (wsdd_log == NULL) {
1747         return; /* WSDD not initialized */
1748     }
1749 
1750     if (wsdd_netif_notifier != NULL) {
1751         netif_notifier_free(wsdd_netif_notifier);
1752         wsdd_netif_notifier = NULL;
1753     }
1754 
1755     for (addr = wsdd_netif_addr_list; addr != NULL; addr = addr->next) {
1756         wsdd_resolver_free(addr->data);
1757     }
1758 
1759     netif_addr_list_free(wsdd_netif_addr_list);
1760     wsdd_netif_addr_list = NULL;
1761 
1762     if (wsdd_mcsock_ipv4 >= 0) {
1763         close(wsdd_mcsock_ipv4);
1764         wsdd_mcsock_ipv4 = -1;
1765     }
1766 
1767     if (wsdd_mcsock_ipv6 >= 0) {
1768         close(wsdd_mcsock_ipv6);
1769         wsdd_mcsock_ipv6 = -1;
1770     }
1771 
1772     log_assert(wsdd_log, ll_empty(&wsdd_finding_list));
1773 
1774     log_ctx_free(wsdd_log);
1775     wsdd_log = NULL;
1776 }
1777 
1778 /* vim:ts=8:sw=4:et
1779  */
1780