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