]> git.ipfire.org Git - thirdparty/cups.git/blob - backend/dnssd.c
7074f4294abc4613bcf9b798f64eebca3b1254b4
[thirdparty/cups.git] / backend / dnssd.c
1 /*
2 * DNS-SD discovery backend for CUPS.
3 *
4 * Copyright © 2008-2018 by Apple Inc.
5 *
6 * Licensed under Apache License v2.0. See the file "LICENSE" for more
7 * information.
8 */
9
10 /*
11 * Include necessary headers.
12 */
13
14 #include "backend-private.h"
15 #include <cups/array.h>
16 #ifdef HAVE_DNSSD
17 # include <dns_sd.h>
18 #endif /* HAVE_DNSSD */
19 #ifdef HAVE_AVAHI
20 # include <avahi-client/client.h>
21 # include <avahi-client/lookup.h>
22 # include <avahi-common/simple-watch.h>
23 # include <avahi-common/domain.h>
24 # include <avahi-common/error.h>
25 # include <avahi-common/malloc.h>
26 #define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
27 #endif /* HAVE_AVAHI */
28
29
30 /*
31 * Device structure...
32 */
33
34 typedef enum
35 {
36 CUPS_DEVICE_PRINTER = 0, /* lpd://... */
37 CUPS_DEVICE_IPPS, /* ipps://... */
38 CUPS_DEVICE_IPP, /* ipp://... */
39 CUPS_DEVICE_FAX_IPP, /* ipp://... */
40 CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */
41 CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */
42 } cups_devtype_t;
43
44
45 typedef struct
46 {
47 #ifdef HAVE_DNSSD
48 DNSServiceRef ref; /* Service reference for query */
49 #endif /* HAVE_DNSSD */
50 #ifdef HAVE_AVAHI
51 AvahiRecordBrowser *ref; /* Browser for query */
52 #endif /* HAVE_AVAHI */
53 char *name, /* Service name */
54 *domain, /* Domain name */
55 *fullName, /* Full name */
56 *make_and_model, /* Make and model from TXT record */
57 *device_id, /* 1284 device ID from TXT record */
58 *uuid; /* UUID from TXT record */
59 cups_devtype_t type; /* Device registration type */
60 int priority, /* Priority associated with type */
61 cups_shared, /* CUPS shared printer? */
62 sent; /* Did we list the device? */
63 } cups_device_t;
64
65
66 /*
67 * Local globals...
68 */
69
70 static int job_canceled = 0;
71 /* Set to 1 on SIGTERM */
72 #ifdef HAVE_AVAHI
73 static AvahiSimplePoll *simple_poll = NULL;
74 /* Poll information */
75 static int got_data = 0; /* Got data from poll? */
76 static int browsers = 0; /* Number of running browsers */
77 #endif /* HAVE_AVAHI */
78
79
80 /*
81 * Local functions...
82 */
83
84 #ifdef HAVE_DNSSD
85 static void browse_callback(DNSServiceRef sdRef,
86 DNSServiceFlags flags,
87 uint32_t interfaceIndex,
88 DNSServiceErrorType errorCode,
89 const char *serviceName,
90 const char *regtype,
91 const char *replyDomain, void *context)
92 __attribute__((nonnull(1,5,6,7,8)));
93 static void browse_local_callback(DNSServiceRef sdRef,
94 DNSServiceFlags flags,
95 uint32_t interfaceIndex,
96 DNSServiceErrorType errorCode,
97 const char *serviceName,
98 const char *regtype,
99 const char *replyDomain,
100 void *context)
101 __attribute__((nonnull(1,5,6,7,8)));
102 #endif /* HAVE_DNSSD */
103 #ifdef HAVE_AVAHI
104 static void browse_callback(AvahiServiceBrowser *browser,
105 AvahiIfIndex interface,
106 AvahiProtocol protocol,
107 AvahiBrowserEvent event,
108 const char *serviceName,
109 const char *regtype,
110 const char *replyDomain,
111 AvahiLookupResultFlags flags,
112 void *context);
113 static void client_callback(AvahiClient *client,
114 AvahiClientState state,
115 void *context);
116 #endif /* HAVE_AVAHI */
117
118 static int compare_devices(cups_device_t *a, cups_device_t *b);
119 static void exec_backend(char **argv) __attribute__((noreturn));
120 static cups_device_t *get_device(cups_array_t *devices,
121 const char *serviceName,
122 const char *regtype,
123 const char *replyDomain)
124 __attribute__((nonnull(1,2,3,4)));
125 #ifdef HAVE_DNSSD
126 static void query_callback(DNSServiceRef sdRef,
127 DNSServiceFlags flags,
128 uint32_t interfaceIndex,
129 DNSServiceErrorType errorCode,
130 const char *fullName, uint16_t rrtype,
131 uint16_t rrclass, uint16_t rdlen,
132 const void *rdata, uint32_t ttl,
133 void *context)
134 __attribute__((nonnull(1,5,9,11)));
135 #elif defined(HAVE_AVAHI)
136 static int poll_callback(struct pollfd *pollfds,
137 unsigned int num_pollfds, int timeout,
138 void *context);
139 static void query_callback(AvahiRecordBrowser *browser,
140 AvahiIfIndex interface,
141 AvahiProtocol protocol,
142 AvahiBrowserEvent event,
143 const char *name, uint16_t rrclass,
144 uint16_t rrtype, const void *rdata,
145 size_t rdlen,
146 AvahiLookupResultFlags flags,
147 void *context);
148 #endif /* HAVE_DNSSD */
149 static void sigterm_handler(int sig);
150 static void unquote(char *dst, const char *src, size_t dstsize)
151 __attribute__((nonnull(1,2)));
152
153
154 /*
155 * 'main()' - Browse for printers.
156 */
157
158 int /* O - Exit status */
159 main(int argc, /* I - Number of command-line args */
160 char *argv[]) /* I - Command-line arguments */
161 {
162 const char *name; /* Backend name */
163 cups_array_t *devices; /* Device array */
164 cups_device_t *device; /* Current device */
165 char uriName[1024]; /* Unquoted fullName for URI */
166 #ifdef HAVE_DNSSD
167 int fd; /* Main file descriptor */
168 fd_set input; /* Input set for select() */
169 struct timeval timeout; /* Timeout for select() */
170 DNSServiceRef main_ref, /* Main service reference */
171 fax_ipp_ref, /* IPP fax service reference */
172 ipp_ref, /* IPP service reference */
173 ipp_tls_ref, /* IPP w/TLS service reference */
174 ipps_ref, /* IPP service reference */
175 local_fax_ipp_ref, /* Local IPP fax service reference */
176 local_ipp_ref, /* Local IPP service reference */
177 local_ipp_tls_ref, /* Local IPP w/TLS service reference */
178 local_ipps_ref, /* Local IPP service reference */
179 local_printer_ref, /* Local LPD service reference */
180 pdl_datastream_ref, /* AppSocket service reference */
181 printer_ref, /* LPD service reference */
182 riousbprint_ref; /* Remote IO service reference */
183 #endif /* HAVE_DNSSD */
184 #ifdef HAVE_AVAHI
185 AvahiClient *client; /* Client information */
186 int error; /* Error code, if any */
187 #endif /* HAVE_AVAHI */
188 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
189 struct sigaction action; /* Actions for POSIX signals */
190 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
191
192
193 /*
194 * Don't buffer stderr, and catch SIGTERM...
195 */
196
197 setbuf(stderr, NULL);
198
199 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
200 sigset(SIGTERM, sigterm_handler);
201 #elif defined(HAVE_SIGACTION)
202 memset(&action, 0, sizeof(action));
203
204 sigemptyset(&action.sa_mask);
205 action.sa_handler = sigterm_handler;
206 sigaction(SIGTERM, &action, NULL);
207 #else
208 signal(SIGTERM, sigterm_handler);
209 #endif /* HAVE_SIGSET */
210
211 /*
212 * Check command-line...
213 */
214
215 if (argc >= 6)
216 exec_backend(argv);
217 else if (argc != 1)
218 {
219 _cupsLangPrintf(stderr,
220 _("Usage: %s job-id user title copies options [file]"),
221 argv[0]);
222 return (1);
223 }
224
225 /*
226 * Only do discovery when run as "dnssd"...
227 */
228
229 if ((name = strrchr(argv[0], '/')) != NULL)
230 name ++;
231 else
232 name = argv[0];
233
234 if (strcmp(name, "dnssd"))
235 return (0);
236
237 /*
238 * Create an array to track devices...
239 */
240
241 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
242
243 /*
244 * Browse for different kinds of printers...
245 */
246
247 #ifdef HAVE_DNSSD
248 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
249 {
250 perror("ERROR: Unable to create service connection");
251 return (1);
252 }
253
254 fd = DNSServiceRefSockFD(main_ref);
255
256 fax_ipp_ref = main_ref;
257 DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
258 "_fax-ipp._tcp", NULL, browse_callback, devices);
259
260 ipp_ref = main_ref;
261 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
262 "_ipp._tcp", NULL, browse_callback, devices);
263
264 ipp_tls_ref = main_ref;
265 DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
266 "_ipp-tls._tcp", NULL, browse_callback, devices);
267
268 ipps_ref = main_ref;
269 DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
270 "_ipps._tcp", NULL, browse_callback, devices);
271
272 local_fax_ipp_ref = main_ref;
273 DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
274 kDNSServiceInterfaceIndexLocalOnly,
275 "_fax-ipp._tcp", NULL, browse_local_callback, devices);
276
277 local_ipp_ref = main_ref;
278 DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
279 kDNSServiceInterfaceIndexLocalOnly,
280 "_ipp._tcp", NULL, browse_local_callback, devices);
281
282 local_ipp_tls_ref = main_ref;
283 DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
284 kDNSServiceInterfaceIndexLocalOnly,
285 "_ipp-tls._tcp", NULL, browse_local_callback, devices);
286
287 local_ipps_ref = main_ref;
288 DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
289 kDNSServiceInterfaceIndexLocalOnly,
290 "_ipps._tcp", NULL, browse_local_callback, devices);
291
292 local_printer_ref = main_ref;
293 DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
294 kDNSServiceInterfaceIndexLocalOnly,
295 "_printer._tcp", NULL, browse_local_callback, devices);
296
297 pdl_datastream_ref = main_ref;
298 DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
299 "_pdl-datastream._tcp", NULL, browse_callback, devices);
300
301 printer_ref = main_ref;
302 DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
303 "_printer._tcp", NULL, browse_callback, devices);
304
305 riousbprint_ref = main_ref;
306 DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
307 "_riousbprint._tcp", NULL, browse_callback, devices);
308 #endif /* HAVE_DNSSD */
309
310 #ifdef HAVE_AVAHI
311 if ((simple_poll = avahi_simple_poll_new()) == NULL)
312 {
313 fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr);
314 return (0);
315 }
316
317 avahi_simple_poll_set_func(simple_poll, poll_callback, NULL);
318
319 client = avahi_client_new(avahi_simple_poll_get(simple_poll),
320 0, client_callback, simple_poll, &error);
321 if (!client)
322 {
323 fputs("DEBUG: Unable to create Avahi client.\n", stderr);
324 return (0);
325 }
326
327 browsers = 6;
328 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
329 AVAHI_PROTO_UNSPEC,
330 "_fax-ipp._tcp", NULL, 0,
331 browse_callback, devices);
332 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
333 AVAHI_PROTO_UNSPEC,
334 "_ipp._tcp", NULL, 0,
335 browse_callback, devices);
336 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
337 AVAHI_PROTO_UNSPEC,
338 "_ipp-tls._tcp", NULL, 0,
339 browse_callback, devices);
340 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
341 AVAHI_PROTO_UNSPEC,
342 "_ipps._tcp", NULL, 0,
343 browse_callback, devices);
344 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
345 AVAHI_PROTO_UNSPEC,
346 "_pdl-datastream._tcp",
347 NULL, 0,
348 browse_callback,
349 devices);
350 avahi_service_browser_new(client, AVAHI_IF_UNSPEC,
351 AVAHI_PROTO_UNSPEC,
352 "_printer._tcp", NULL, 0,
353 browse_callback, devices);
354 #endif /* HAVE_AVAHI */
355
356 /*
357 * Loop until we are killed...
358 */
359
360 while (!job_canceled)
361 {
362 int announce = 0; /* Announce printers? */
363
364 #ifdef HAVE_DNSSD
365 FD_ZERO(&input);
366 FD_SET(fd, &input);
367
368 timeout.tv_sec = 0;
369 timeout.tv_usec = 500000;
370
371 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
372 continue;
373
374 if (FD_ISSET(fd, &input))
375 {
376 /*
377 * Process results of our browsing...
378 */
379
380 DNSServiceProcessResult(main_ref);
381 }
382 else
383 announce = 1;
384
385 #elif defined(HAVE_AVAHI)
386 got_data = 0;
387
388 if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0)
389 {
390 /*
391 * We've been told to exit the loop. Perhaps the connection to
392 * Avahi failed.
393 */
394
395 break;
396 }
397
398 if (!got_data)
399 announce = 1;
400 #endif /* HAVE_DNSSD */
401
402 /* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/
403
404 if (announce)
405 {
406 /*
407 * Announce any devices we've found...
408 */
409
410 #ifdef HAVE_DNSSD
411 DNSServiceErrorType status; /* DNS query status */
412 #endif /* HAVE_DNSSD */
413 cups_device_t *best; /* Best matching device */
414 char device_uri[1024]; /* Device URI */
415 int count; /* Number of queries */
416 int sent; /* Number of sent */
417
418 for (device = (cups_device_t *)cupsArrayFirst(devices),
419 best = NULL, count = 0, sent = 0;
420 device;
421 device = (cups_device_t *)cupsArrayNext(devices))
422 {
423 if (device->sent)
424 sent ++;
425
426 if (device->ref)
427 count ++;
428
429 if (!device->ref && !device->sent)
430 {
431 /*
432 * Found the device, now get the TXT record(s) for it...
433 */
434
435 if (count < 50)
436 {
437 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
438
439 #ifdef HAVE_DNSSD
440 device->ref = main_ref;
441
442 status = DNSServiceQueryRecord(&(device->ref),
443 kDNSServiceFlagsShareConnection,
444 0, device->fullName,
445 kDNSServiceType_TXT,
446 kDNSServiceClass_IN, query_callback,
447 device);
448 if (status != kDNSServiceErr_NoError)
449 fprintf(stderr,
450 "ERROR: Unable to query \"%s\" for TXT records: %d\n",
451 device->fullName, status);
452 /* Users never see this */
453 else
454 count ++;
455
456 #else
457 if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC,
458 AVAHI_PROTO_UNSPEC,
459 device->fullName,
460 AVAHI_DNS_CLASS_IN,
461 AVAHI_DNS_TYPE_TXT,
462 0,
463 query_callback,
464 device)) == NULL)
465 fprintf(stderr,
466 "ERROR: Unable to query \"%s\" for TXT records: %s\n",
467 device->fullName,
468 avahi_strerror(avahi_client_errno(client)));
469 /* Users never see this */
470 else
471 count ++;
472 #endif /* HAVE_AVAHI */
473 }
474 }
475 else if (!device->sent)
476 {
477 #ifdef HAVE_DNSSD
478 /*
479 * Got the TXT records, now report the device...
480 */
481
482 DNSServiceRefDeallocate(device->ref);
483 #else
484 avahi_record_browser_free(device->ref);
485 #endif /* HAVE_DNSSD */
486
487 device->ref = NULL;
488
489 if (!best)
490 best = device;
491 else if (_cups_strcasecmp(best->name, device->name) ||
492 _cups_strcasecmp(best->domain, device->domain))
493 {
494 unquote(uriName, best->fullName, sizeof(uriName));
495
496 if (best->uuid)
497 httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
498 sizeof(device_uri), "dnssd", NULL, uriName, 0,
499 best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
500 best->uuid);
501 else
502 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
503 sizeof(device_uri), "dnssd", NULL, uriName, 0,
504 best->cups_shared ? "/cups" : "/");
505
506 cupsBackendReport("network", device_uri, best->make_and_model,
507 best->name, best->device_id, NULL);
508 best->sent = 1;
509 best = device;
510
511 sent ++;
512 }
513 else if (best->priority > device->priority ||
514 (best->priority == device->priority &&
515 best->type < device->type))
516 {
517 best->sent = 1;
518 best = device;
519
520 sent ++;
521 }
522 else
523 {
524 device->sent = 1;
525
526 sent ++;
527 }
528 }
529 }
530
531 if (best)
532 {
533 unquote(uriName, best->fullName, sizeof(uriName));
534
535 if (best->uuid)
536 httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri,
537 sizeof(device_uri), "dnssd", NULL, uriName, 0,
538 best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s",
539 best->uuid);
540 else
541 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri,
542 sizeof(device_uri), "dnssd", NULL, uriName, 0,
543 best->cups_shared ? "/cups" : "/");
544
545 cupsBackendReport("network", device_uri, best->make_and_model,
546 best->name, best->device_id, NULL);
547 best->sent = 1;
548 sent ++;
549 }
550
551 fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count);
552
553 #ifdef HAVE_AVAHI
554 if (sent == cupsArrayCount(devices) && browsers == 0)
555 #else
556 if (sent == cupsArrayCount(devices))
557 #endif /* HAVE_AVAHI */
558 break;
559 }
560 }
561
562 return (CUPS_BACKEND_OK);
563 }
564
565
566 #ifdef HAVE_DNSSD
567 /*
568 * 'browse_callback()' - Browse devices.
569 */
570
571 static void
572 browse_callback(
573 DNSServiceRef sdRef, /* I - Service reference */
574 DNSServiceFlags flags, /* I - Option flags */
575 uint32_t interfaceIndex, /* I - Interface number */
576 DNSServiceErrorType errorCode, /* I - Error, if any */
577 const char *serviceName, /* I - Name of service/device */
578 const char *regtype, /* I - Type of service */
579 const char *replyDomain, /* I - Service domain */
580 void *context) /* I - Devices array */
581 {
582 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
583 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
584 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
585 sdRef, flags, interfaceIndex, errorCode,
586 serviceName, regtype, replyDomain, context);
587
588 /*
589 * Only process "add" data...
590 */
591
592 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
593 return;
594
595 /*
596 * Get the device...
597 */
598
599 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
600 }
601
602
603 /*
604 * 'browse_local_callback()' - Browse local devices.
605 */
606
607 static void
608 browse_local_callback(
609 DNSServiceRef sdRef, /* I - Service reference */
610 DNSServiceFlags flags, /* I - Option flags */
611 uint32_t interfaceIndex, /* I - Interface number */
612 DNSServiceErrorType errorCode, /* I - Error, if any */
613 const char *serviceName, /* I - Name of service/device */
614 const char *regtype, /* I - Type of service */
615 const char *replyDomain, /* I - Service domain */
616 void *context) /* I - Devices array */
617 {
618 cups_device_t *device; /* Device */
619
620
621 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
622 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
623 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
624 sdRef, flags, interfaceIndex, errorCode,
625 serviceName, regtype, replyDomain, context);
626
627 /*
628 * Only process "add" data...
629 */
630
631 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
632 return;
633
634 /*
635 * Get the device...
636 */
637
638 device = get_device((cups_array_t *)context, serviceName, regtype,
639 replyDomain);
640
641 /*
642 * Hide locally-registered devices...
643 */
644
645 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
646 device->fullName);
647 device->sent = 1;
648 }
649 #endif /* HAVE_DNSSD */
650
651
652 #ifdef HAVE_AVAHI
653 /*
654 * 'browse_callback()' - Browse devices.
655 */
656
657 static void
658 browse_callback(
659 AvahiServiceBrowser *browser, /* I - Browser */
660 AvahiIfIndex interface, /* I - Interface index (unused) */
661 AvahiProtocol protocol, /* I - Network protocol (unused) */
662 AvahiBrowserEvent event, /* I - What happened */
663 const char *name, /* I - Service name */
664 const char *type, /* I - Registration type */
665 const char *domain, /* I - Domain */
666 AvahiLookupResultFlags flags, /* I - Flags */
667 void *context) /* I - Devices array */
668 {
669 AvahiClient *client = avahi_service_browser_get_client(browser);
670 /* Client information */
671
672
673 (void)interface;
674 (void)protocol;
675 (void)context;
676
677 switch (event)
678 {
679 case AVAHI_BROWSER_FAILURE:
680 fprintf(stderr, "DEBUG: browse_callback: %s\n",
681 avahi_strerror(avahi_client_errno(client)));
682 avahi_simple_poll_quit(simple_poll);
683 break;
684
685 case AVAHI_BROWSER_NEW:
686 /*
687 * This object is new on the network.
688 */
689
690 if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
691 {
692 /*
693 * This comes from the local machine so ignore it.
694 */
695
696 fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name);
697 }
698 else
699 {
700 /*
701 * Create a device entry for it if it doesn't yet exist.
702 */
703
704 get_device((cups_array_t *)context, name, type, domain);
705 }
706 break;
707
708 case AVAHI_BROWSER_REMOVE:
709 case AVAHI_BROWSER_CACHE_EXHAUSTED:
710 break;
711
712 case AVAHI_BROWSER_ALL_FOR_NOW:
713 browsers--;
714 break;
715 }
716 }
717
718
719 /*
720 * 'client_callback()' - Avahi client callback function.
721 */
722
723 static void
724 client_callback(
725 AvahiClient *client, /* I - Client information (unused) */
726 AvahiClientState state, /* I - Current state */
727 void *context) /* I - User data (unused) */
728 {
729 (void)client;
730 (void)context;
731
732 /*
733 * If the connection drops, quit.
734 */
735
736 if (state == AVAHI_CLIENT_FAILURE)
737 {
738 fputs("DEBUG: Avahi connection failed.\n", stderr);
739 avahi_simple_poll_quit(simple_poll);
740 }
741 }
742 #endif /* HAVE_AVAHI */
743
744
745 /*
746 * 'compare_devices()' - Compare two devices.
747 */
748
749 static int /* O - Result of comparison */
750 compare_devices(cups_device_t *a, /* I - First device */
751 cups_device_t *b) /* I - Second device */
752 {
753 return (strcmp(a->name, b->name));
754 }
755
756
757 /*
758 * 'exec_backend()' - Execute the backend that corresponds to the
759 * resolved service name.
760 */
761
762 static void
763 exec_backend(char **argv) /* I - Command-line arguments */
764 {
765 const char *resolved_uri, /* Resolved device URI */
766 *cups_serverbin; /* Location of programs */
767 char scheme[1024], /* Scheme from URI */
768 *ptr, /* Pointer into scheme */
769 filename[1024]; /* Backend filename */
770
771
772 /*
773 * Resolve the device URI...
774 */
775
776 job_canceled = -1;
777
778 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
779 {
780 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
781 sleep(10);
782
783 if (getenv("CLASS") != NULL)
784 exit(CUPS_BACKEND_FAILED);
785 }
786
787 /*
788 * Extract the scheme from the URI...
789 */
790
791 strlcpy(scheme, resolved_uri, sizeof(scheme));
792 if ((ptr = strchr(scheme, ':')) != NULL)
793 *ptr = '\0';
794
795 /*
796 * Get the filename of the backend...
797 */
798
799 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
800 cups_serverbin = CUPS_SERVERBIN;
801
802 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
803
804 /*
805 * Overwrite the device URI and run the new backend...
806 */
807
808 setenv("DEVICE_URI", resolved_uri, 1);
809
810 argv[0] = (char *)resolved_uri;
811
812 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
813
814 execv(filename, argv);
815
816 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
817 strerror(errno));
818 exit(CUPS_BACKEND_STOP);
819 }
820
821
822 /*
823 * 'device_type()' - Get DNS-SD type enumeration from string.
824 */
825
826 static cups_devtype_t /* O - Device type */
827 device_type(const char *regtype) /* I - Service registration type */
828 {
829 #ifdef HAVE_AVAHI
830 if (!strcmp(regtype, "_ipp._tcp"))
831 return (CUPS_DEVICE_IPP);
832 else if (!strcmp(regtype, "_ipps._tcp") ||
833 !strcmp(regtype, "_ipp-tls._tcp"))
834 return (CUPS_DEVICE_IPPS);
835 else if (!strcmp(regtype, "_fax-ipp._tcp"))
836 return (CUPS_DEVICE_FAX_IPP);
837 else if (!strcmp(regtype, "_printer._tcp"))
838 return (CUPS_DEVICE_PDL_DATASTREAM);
839 #else
840 if (!strcmp(regtype, "_ipp._tcp."))
841 return (CUPS_DEVICE_IPP);
842 else if (!strcmp(regtype, "_ipps._tcp.") ||
843 !strcmp(regtype, "_ipp-tls._tcp."))
844 return (CUPS_DEVICE_IPPS);
845 else if (!strcmp(regtype, "_fax-ipp._tcp."))
846 return (CUPS_DEVICE_FAX_IPP);
847 else if (!strcmp(regtype, "_printer._tcp."))
848 return (CUPS_DEVICE_PRINTER);
849 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
850 return (CUPS_DEVICE_PDL_DATASTREAM);
851 #endif /* HAVE_AVAHI */
852
853 return (CUPS_DEVICE_RIOUSBPRINT);
854 }
855
856
857 /*
858 * 'get_device()' - Create or update a device.
859 */
860
861 static cups_device_t * /* O - Device */
862 get_device(cups_array_t *devices, /* I - Device array */
863 const char *serviceName, /* I - Name of service/device */
864 const char *regtype, /* I - Type of service */
865 const char *replyDomain) /* I - Service domain */
866 {
867 cups_device_t key, /* Search key */
868 *device; /* Device */
869 char fullName[kDNSServiceMaxDomainName];
870 /* Full name for query */
871
872
873 /*
874 * See if this is a new device...
875 */
876
877 key.name = (char *)serviceName;
878 key.type = device_type(regtype);
879
880 for (device = cupsArrayFind(devices, &key);
881 device;
882 device = cupsArrayNext(devices))
883 if (_cups_strcasecmp(device->name, key.name))
884 break;
885 else if (device->type == key.type)
886 {
887 if (!_cups_strcasecmp(device->domain, "local.") &&
888 _cups_strcasecmp(device->domain, replyDomain))
889 {
890 /*
891 * Update the .local listing to use the "global" domain name instead.
892 * The backend will try local lookups first, then the global domain name.
893 */
894
895 free(device->domain);
896 device->domain = strdup(replyDomain);
897
898 #ifdef HAVE_DNSSD
899 DNSServiceConstructFullName(fullName, device->name, regtype,
900 replyDomain);
901 #else /* HAVE_AVAHI */
902 avahi_service_name_join(fullName, kDNSServiceMaxDomainName,
903 serviceName, regtype, replyDomain);
904 #endif /* HAVE_DNSSD */
905
906 free(device->fullName);
907 device->fullName = strdup(fullName);
908 }
909
910 return (device);
911 }
912
913 /*
914 * Yes, add the device...
915 */
916
917 device = calloc(sizeof(cups_device_t), 1);
918 device->name = strdup(serviceName);
919 device->domain = strdup(replyDomain);
920 device->type = key.type;
921 device->priority = 50;
922
923 cupsArrayAdd(devices, device);
924
925 /*
926 * Set the "full name" of this service, which is used for queries...
927 */
928
929 #ifdef HAVE_DNSSD
930 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
931 #else /* HAVE_AVAHI */
932 avahi_service_name_join(fullName, kDNSServiceMaxDomainName, serviceName, regtype, replyDomain);
933 #endif /* HAVE_DNSSD */
934
935 device->fullName = strdup(fullName);
936
937 return (device);
938 }
939
940
941 #ifdef HAVE_AVAHI
942 /*
943 * 'poll_callback()' - Wait for input on the specified file descriptors.
944 *
945 * Note: This function is needed because avahi_simple_poll_iterate is broken
946 * and always uses a timeout of 0 (!) milliseconds.
947 * (https://github.com/lathiat/avahi/issues/127)
948 */
949
950 static int /* O - Number of file descriptors matching */
951 poll_callback(
952 struct pollfd *pollfds, /* I - File descriptors */
953 unsigned int num_pollfds, /* I - Number of file descriptors */
954 int timeout, /* I - Timeout in milliseconds (unused) */
955 void *context) /* I - User data (unused) */
956 {
957 int val; /* Return value */
958
959
960 (void)timeout;
961 (void)context;
962
963 val = poll(pollfds, num_pollfds, 500);
964
965 if (val < 0)
966 fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno));
967 else if (val > 0)
968 got_data = 1;
969
970 return (val);
971 }
972 #endif /* HAVE_AVAHI */
973
974
975 #if defined(HAVE_DNSSD) || defined(HAVE_AVAHI)
976 # ifdef HAVE_DNSSD
977 /*
978 * 'query_callback()' - Process query data.
979 */
980
981 static void
982 query_callback(
983 DNSServiceRef sdRef, /* I - Service reference */
984 DNSServiceFlags flags, /* I - Data flags */
985 uint32_t interfaceIndex, /* I - Interface */
986 DNSServiceErrorType errorCode, /* I - Error, if any */
987 const char *fullName, /* I - Full service name */
988 uint16_t rrtype, /* I - Record type */
989 uint16_t rrclass, /* I - Record class */
990 uint16_t rdlen, /* I - Length of record data */
991 const void *rdata, /* I - Record data */
992 uint32_t ttl, /* I - Time-to-live */
993 void *context) /* I - Device */
994 {
995 # else
996 /*
997 * 'query_callback()' - Process query data.
998 */
999
1000 static void
1001 query_callback(
1002 AvahiRecordBrowser *browser, /* I - Record browser */
1003 AvahiIfIndex interfaceIndex,
1004 /* I - Interface index (unused) */
1005 AvahiProtocol protocol, /* I - Network protocol (unused) */
1006 AvahiBrowserEvent event, /* I - What happened? */
1007 const char *fullName, /* I - Service name */
1008 uint16_t rrclass, /* I - Record class */
1009 uint16_t rrtype, /* I - Record type */
1010 const void *rdata, /* I - TXT record */
1011 size_t rdlen, /* I - Length of TXT record */
1012 AvahiLookupResultFlags flags, /* I - Flags */
1013 void *context) /* I - Device */
1014 {
1015 AvahiClient *client = avahi_record_browser_get_client(browser);
1016 /* Client information */
1017 # endif /* HAVE_DNSSD */
1018 char *ptr; /* Pointer into string */
1019 cups_device_t *device = (cups_device_t *)context;
1020 /* Device */
1021 const uint8_t *data, /* Pointer into data */
1022 *datanext, /* Next key/value pair */
1023 *dataend; /* End of entire TXT record */
1024 uint8_t datalen; /* Length of current key/value pair */
1025 char key[256], /* Key string */
1026 value[256], /* Value string */
1027 make_and_model[512], /* Manufacturer and model */
1028 model[256], /* Model */
1029 pdl[256], /* PDL */
1030 device_id[2048]; /* 1284 device ID */
1031
1032
1033 # ifdef HAVE_DNSSD
1034 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
1035 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
1036 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
1037 "context=%p)\n",
1038 sdRef, flags, interfaceIndex, errorCode, fullName, rrtype, rrclass, rdlen, rdata, ttl, context);
1039
1040 /*
1041 * Only process "add" data...
1042 */
1043
1044 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
1045 return;
1046
1047 # else
1048 fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, "
1049 "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, "
1050 "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n",
1051 browser, interfaceIndex, protocol, event, fullName, rrclass, rrtype, rdata, (unsigned)rdlen, flags, context);
1052
1053 /*
1054 * Only process "add" data...
1055 */
1056
1057 if (event != AVAHI_BROWSER_NEW)
1058 {
1059 if (event == AVAHI_BROWSER_FAILURE)
1060 fprintf(stderr, "ERROR: %s\n",
1061 avahi_strerror(avahi_client_errno(client)));
1062
1063 return;
1064 }
1065 # endif /* HAVE_DNSSD */
1066
1067 /*
1068 * Pull out the priority and make and model from the TXT
1069 * record and save it...
1070 */
1071
1072 device_id[0] = '\0';
1073 make_and_model[0] = '\0';
1074 pdl[0] = '\0';
1075
1076 strlcpy(model, "Unknown", sizeof(model));
1077
1078 for (data = rdata, dataend = data + rdlen;
1079 data < dataend;
1080 data = datanext)
1081 {
1082 /*
1083 * Read a key/value pair starting with an 8-bit length. Since the
1084 * length is 8 bits and the size of the key/value buffers is 256, we
1085 * don't need to check for overflow...
1086 */
1087
1088 datalen = *data++;
1089
1090 if (!datalen || (data + datalen) > dataend)
1091 break;
1092
1093 datanext = data + datalen;
1094
1095 for (ptr = key; data < datanext && *data != '='; data ++)
1096 *ptr++ = (char)*data;
1097 *ptr = '\0';
1098
1099 if (data < datanext && *data == '=')
1100 {
1101 data ++;
1102
1103 if (data < datanext)
1104 memcpy(value, data, (size_t)(datanext - data));
1105 value[datanext - data] = '\0';
1106
1107 fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n",
1108 key, value);
1109 }
1110 else
1111 {
1112 fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n",
1113 key);
1114 continue;
1115 }
1116
1117 if (!_cups_strncasecmp(key, "usb_", 4))
1118 {
1119 /*
1120 * Add USB device ID information...
1121 */
1122
1123 ptr = device_id + strlen(device_id);
1124 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value);
1125 }
1126
1127 if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
1128 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
1129 strlcpy(make_and_model, value, sizeof(make_and_model));
1130 else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
1131 strlcpy(model, value, sizeof(model));
1132 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1133 {
1134 if (value[0] == '(')
1135 {
1136 /*
1137 * Strip parenthesis...
1138 */
1139
1140 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1141 *ptr = '\0';
1142
1143 strlcpy(model, value + 1, sizeof(model));
1144 }
1145 else
1146 strlcpy(model, value, sizeof(model));
1147 }
1148 else if (!_cups_strcasecmp(key, "ty"))
1149 {
1150 strlcpy(model, value, sizeof(model));
1151
1152 if ((ptr = strchr(model, ',')) != NULL)
1153 *ptr = '\0';
1154 }
1155 else if (!_cups_strcasecmp(key, "pdl"))
1156 strlcpy(pdl, value, sizeof(pdl));
1157 else if (!_cups_strcasecmp(key, "priority"))
1158 device->priority = atoi(value);
1159 else if ((device->type == CUPS_DEVICE_IPP ||
1160 device->type == CUPS_DEVICE_IPPS ||
1161 device->type == CUPS_DEVICE_PRINTER) &&
1162 !_cups_strcasecmp(key, "printer-type"))
1163 {
1164 /*
1165 * This is a CUPS printer!
1166 */
1167
1168 device->cups_shared = 1;
1169
1170 if (device->type == CUPS_DEVICE_PRINTER)
1171 device->sent = 1;
1172 }
1173 else if (!_cups_strcasecmp(key, "UUID"))
1174 device->uuid = strdup(value);
1175 }
1176
1177 if (device->device_id)
1178 free(device->device_id);
1179
1180 if (!device_id[0] && strcmp(model, "Unknown"))
1181 {
1182 if (make_and_model[0])
1183 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1184 make_and_model, model);
1185 else if (!_cups_strncasecmp(model, "designjet ", 10))
1186 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s;", model + 10);
1187 else if (!_cups_strncasecmp(model, "stylus ", 7))
1188 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s;", model + 7);
1189 else if ((ptr = strchr(model, ' ')) != NULL)
1190 {
1191 /*
1192 * Assume the first word is the make...
1193 */
1194
1195 memcpy(make_and_model, model, (size_t)(ptr - model));
1196 make_and_model[ptr - model] = '\0';
1197
1198 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1199 make_and_model, ptr + 1);
1200 }
1201 }
1202
1203 if (device_id[0] &&
1204 !strstr(device_id, "CMD:") &&
1205 !strstr(device_id, "COMMAND SET:") &&
1206 (strstr(pdl, "application/pdf") ||
1207 strstr(pdl, "application/postscript") ||
1208 strstr(pdl, "application/vnd.hp-PCL") ||
1209 strstr(pdl, "image/")))
1210 {
1211 value[0] = '\0';
1212 if (strstr(pdl, "application/pdf"))
1213 strlcat(value, ",PDF", sizeof(value));
1214 if (strstr(pdl, "application/postscript"))
1215 strlcat(value, ",PS", sizeof(value));
1216 if (strstr(pdl, "application/vnd.hp-PCL"))
1217 strlcat(value, ",PCL", sizeof(value));
1218 for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/"))
1219 {
1220 char *valptr = value + strlen(value);
1221 /* Pointer into value */
1222
1223 if (valptr < (value + sizeof(value) - 1))
1224 *valptr++ = ',';
1225
1226 ptr += 6;
1227 while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.')
1228 {
1229 if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1))
1230 *valptr++ = (char)toupper(*ptr++ & 255);
1231 else
1232 break;
1233 }
1234
1235 *valptr = '\0';
1236 }
1237
1238 ptr = device_id + strlen(device_id);
1239 snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "CMD:%s;", value + 1);
1240 }
1241
1242 if (device_id[0])
1243 device->device_id = strdup(device_id);
1244 else
1245 device->device_id = NULL;
1246
1247 if (device->make_and_model)
1248 free(device->make_and_model);
1249
1250 if (make_and_model[0])
1251 {
1252 strlcat(make_and_model, " ", sizeof(make_and_model));
1253 strlcat(make_and_model, model, sizeof(make_and_model));
1254
1255 if (!_cups_strncasecmp(make_and_model, "EPSON EPSON ", 12))
1256 _cups_strcpy(make_and_model, make_and_model + 6);
1257 else if (!_cups_strncasecmp(make_and_model, "HP HP ", 6))
1258 _cups_strcpy(make_and_model, make_and_model + 3);
1259 else if (!_cups_strncasecmp(make_and_model, "Lexmark International Lexmark ", 30))
1260 _cups_strcpy(make_and_model, make_and_model + 22);
1261
1262 device->make_and_model = strdup(make_and_model);
1263 }
1264 else
1265 device->make_and_model = strdup(model);
1266 }
1267 #endif /* HAVE_DNSSD || HAVE_AVAHI */
1268
1269
1270 /*
1271 * 'sigterm_handler()' - Handle termination signals.
1272 */
1273
1274 static void
1275 sigterm_handler(int sig) /* I - Signal number (unused) */
1276 {
1277 (void)sig;
1278
1279 if (job_canceled)
1280 _exit(CUPS_BACKEND_OK);
1281 else
1282 job_canceled = 1;
1283 }
1284
1285
1286 /*
1287 * 'unquote()' - Unquote a name string.
1288 */
1289
1290 static void
1291 unquote(char *dst, /* I - Destination buffer */
1292 const char *src, /* I - Source string */
1293 size_t dstsize) /* I - Size of destination buffer */
1294 {
1295 char *dstend = dst + dstsize - 1; /* End of destination buffer */
1296
1297
1298 while (*src && dst < dstend)
1299 {
1300 if (*src == '\\')
1301 {
1302 src ++;
1303 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1304 isdigit(src[2] & 255))
1305 {
1306 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1307 src += 3;
1308 }
1309 else
1310 *dst++ = *src++;
1311 }
1312 else
1313 *dst++ = *src ++;
1314 }
1315
1316 *dst = '\0';
1317 }