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