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