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&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