2 * "$Id: dnssd.c 10379 2012-03-23 22:16:22Z mike $"
4 * DNS-SD discovery backend for CUPS.
6 * Copyright 2008-2012 by Apple Inc.
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/".
14 * This file is subject to the Apple OS-Developed Software exception.
18 * next_txt_record() - Get next TXT record from a cups_txt_records_t.
19 * parse_txt_record_pair() - Read key/value pair in cups_txt_records_t.
20 * main() - Browse for printers.
21 * browse_callback() - Browse devices.
22 * browse_local_callback() - Browse local devices.
23 * compare_devices() - Compare two devices.
24 * exec_backend() - Execute the backend that corresponds to the
25 * resolved service name.
26 * device_type() - Get DNS-SD type enumeration from string.
27 * get_device() - Create or update a device.
28 * query_callback() - Process query data.
29 * avahi_client_callback() - Avahi client callback function.
30 * avahi_query_callback() - Avahi query callback function.
31 * avahi_browse_callback() - Avahi browse callback function.
32 * find_device() - Find a device from its name and domain.
33 * sigterm_handler() - Handle termination signals...
34 * unquote() - Unquote a name string.
38 * Include necessary headers.
41 #include "backend-private.h"
42 #include <cups/array.h>
45 #endif /* HAVE_DNSSD */
47 # include <avahi-client/client.h>
48 # include <avahi-client/lookup.h>
49 # include <avahi-common/simple-watch.h>
50 # include <avahi-common/domain.h>
51 # include <avahi-common/error.h>
52 # include <avahi-common/malloc.h>
53 #define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX
54 #endif /* HAVE_AVAHI */
63 CUPS_DEVICE_PRINTER = 0, /* lpd://... */
64 CUPS_DEVICE_IPPS, /* ipps://... */
65 CUPS_DEVICE_IPP, /* ipp://... */
66 CUPS_DEVICE_FAX_IPP, /* ipp://... */
67 CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */
68 CUPS_DEVICE_RIOUSBPRINT /* riousbprint://... */
75 DNSServiceRef ref; /* Service reference for resolve */
76 #endif /* HAVE_DNSSD */
78 int resolved; /* Did we resolve the device? */
79 #endif /* HAVE_AVAHI */
80 char *name, /* Service name */
81 *domain, /* Domain name */
82 *fullName, /* Full name */
83 *make_and_model, /* Make and model from TXT record */
84 *device_id; /* 1284 device ID from TXT record */
85 cups_devtype_t type; /* Device registration type */
86 int priority, /* Priority associated with type */
87 cups_shared, /* CUPS shared printer? */
88 sent; /* Did we list the device? */
98 const uint8_t *datanext;
99 const uint8_t *dataend;
100 #else /* HAVE_AVAHI */
101 AvahiStringList *txt;
102 #endif /* HAVE_DNSSD */
103 } cups_txt_records_t;
110 static int job_canceled = 0;
111 /* Set to 1 on SIGTERM */
119 static void browse_callback(DNSServiceRef sdRef,
120 DNSServiceFlags flags,
121 uint32_t interfaceIndex,
122 DNSServiceErrorType errorCode,
123 const char *serviceName,
125 const char *replyDomain, void *context)
126 __attribute__((nonnull(1,5,6,7,8)));
127 static void browse_local_callback(DNSServiceRef sdRef,
128 DNSServiceFlags flags,
129 uint32_t interfaceIndex,
130 DNSServiceErrorType errorCode,
131 const char *serviceName,
133 const char *replyDomain,
135 __attribute__((nonnull(1,5,6,7,8)));
136 static void query_callback(DNSServiceRef sdRef,
137 DNSServiceFlags flags,
138 uint32_t interfaceIndex,
139 DNSServiceErrorType errorCode,
140 const char *fullName, uint16_t rrtype,
141 uint16_t rrclass, uint16_t rdlen,
142 const void *rdata, uint32_t ttl,
144 __attribute__((nonnull(1,5,9,11)));
145 #endif /* HAVE_DNSSD */
147 static void avahi_client_callback (AvahiClient *client,
148 AvahiClientState state,
150 static void avahi_browse_callback (AvahiServiceBrowser *browser,
151 AvahiIfIndex interface,
152 AvahiProtocol protocol,
153 AvahiBrowserEvent event,
154 const char *serviceName,
156 const char *replyDomain,
157 AvahiLookupResultFlags flags,
159 #endif /* HAVE_AVAHI */
161 static cups_device_t * find_device (cups_array_t *devices,
162 cups_txt_records_t *txt,
163 cups_device_t *dkey);
164 static int compare_devices(cups_device_t *a, cups_device_t *b);
165 static void exec_backend(char **argv);
166 static cups_device_t *get_device(cups_array_t *devices,
167 const char *serviceName,
169 const char *replyDomain);
170 static void sigterm_handler(int sig);
171 static void unquote(char *dst, const char *src, size_t dstsize)
172 __attribute__((nonnull(1,2)));
175 static AvahiSimplePoll *simple_poll = NULL;
176 static int avahi_got_callback;
177 #endif /* HAVE_AVAHI */
181 * 'next_txt_record()' - Get next TXT record from a cups_txt_records_t.
184 static cups_txt_records_t *
185 next_txt_record (cups_txt_records_t *txt)
188 txt->data = txt->datanext;
189 #else /* HAVE_AVAHI */
190 txt->txt = avahi_string_list_get_next (txt->txt);
191 if (txt->txt == NULL)
193 #endif /* HAVE_DNSSD */
200 * 'parse_txt_record_pair()' - Read key/value pair in cups_txt_records_t.
204 parse_txt_record_pair (cups_txt_records_t *txt)
208 uint8_t *data = txt->data;
212 * Read a key/value pair starting with an 8-bit length. Since the
213 * length is 8 bits and the size of the key/value buffers is 256, we
214 * don't need to check for overflow...
218 if (!datalen || (data + datalen) > txt->dataend)
220 txt->datanext = data + datalen;
222 for (ptr = txt->key; data < txt->datanext && *data != '='; data ++)
226 if (data < txt->datanext && *data == '=')
231 memcpy (txt->value, data, txt->datanext - data);
232 value[txt->datanext - data] = '\0';
236 #else /* HAVE_AVAHI */
239 avahi_string_list_get_pair (txt->txt, &key, &value, &len);
240 if (len > sizeof (txt->value) - 1)
241 len = sizeof (txt->value) - 1;
243 memcpy (txt->value, value, len);
244 txt->value[len] = '\0';
246 if (len > sizeof (txt->key) - 1)
247 len = sizeof (txt->key) - 1;
249 memcpy (txt->key, key, len);
250 txt->key[len] = '\0';
253 #endif /* HAVE_AVAHI */
260 * 'main()' - Browse for printers.
263 int /* O - Exit status */
264 main(int argc, /* I - Number of command-line args */
265 char *argv[]) /* I - Command-line arguments */
267 const char *name; /* Backend name */
268 cups_array_t *devices; /* Device array */
269 cups_device_t *device; /* Current device */
270 char uriName[1024]; /* Unquoted fullName for URI */
272 int fd; /* Main file descriptor */
273 fd_set input; /* Input set for select() */
274 struct timeval timeout; /* Timeout for select() */
275 DNSServiceRef main_ref, /* Main service reference */
276 fax_ipp_ref, /* IPP fax service reference */
277 ipp_ref, /* IPP service reference */
278 ipp_tls_ref, /* IPP w/TLS service reference */
279 ipps_ref, /* IPP service reference */
280 local_fax_ipp_ref, /* Local IPP fax service reference */
281 local_ipp_ref, /* Local IPP service reference */
282 local_ipp_tls_ref, /* Local IPP w/TLS service reference */
283 local_ipps_ref, /* Local IPP service reference */
284 local_printer_ref, /* Local LPD service reference */
285 pdl_datastream_ref, /* AppSocket service reference */
286 printer_ref, /* LPD service reference */
287 riousbprint_ref; /* Remote IO service reference */
288 #endif /* HAVE_DNSSD */
292 #endif /* HAVE_AVAHI */
293 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
294 struct sigaction action; /* Actions for POSIX signals */
295 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
299 * Don't buffer stderr, and catch SIGTERM...
302 setbuf(stderr, NULL);
305 #ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */
306 sigset(SIGTERM, sigterm_handler);
307 #elif defined(HAVE_SIGACTION)
308 memset(&action, 0, sizeof(action));
310 sigemptyset(&action.sa_mask);
311 action.sa_handler = sigterm_handler;
312 sigaction(SIGTERM, &action, NULL);
314 signal(SIGTERM, sigterm_handler);
315 #endif /* HAVE_SIGSET */
318 sigset(SIGPIPE, SIG_IGN);
319 #elif defined(HAVE_SIGACTION)
320 memset(&action, 0, sizeof(action));
321 action.sa_handler = SIG_IGN;
322 sigaction(SIGPIPE, &action, NULL);
324 signal(SIGPIPE, SIG_IGN);
325 #endif /* HAVE_SIGSET */
333 * Check command-line...
340 _cupsLangPrintf(stderr,
341 _("Usage: %s job-id user title copies options [file]"),
347 * Only do discovery when run as "dnssd"...
350 if ((name = strrchr(argv[0], '/')) != NULL)
355 if (strcmp(name, "dnssd"))
359 * Create an array to track devices...
362 devices = cupsArrayNew((cups_array_func_t)compare_devices, NULL);
365 * Browse for different kinds of printers...
369 if ((simple_poll = avahi_simple_poll_new ()) == NULL)
371 perror ("ERROR: Unable to create avahi simple poll object");
375 client = avahi_client_new (avahi_simple_poll_get (simple_poll),
376 0, avahi_client_callback, NULL, &error);
379 perror ("DEBUG: Unable to create avahi client");
383 avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
385 "_fax-ipp._tcp", NULL, 0,
386 avahi_browse_callback, devices);
387 avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
389 "_ipp._tcp", NULL, 0,
390 avahi_browse_callback, devices);
391 avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
393 "_ipp-tls._tcp", NULL, 0,
394 avahi_browse_callback, devices);
395 avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
397 "_pdl-datastream._tcp",
399 avahi_browse_callback,
401 avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
403 "_printer._tcp", NULL, 0,
404 avahi_browse_callback, devices);
405 avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
407 "_riousbprint._tcp", NULL, 0,
408 avahi_browse_callback, devices);
409 #endif /* HAVE_AVAHI */
411 if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError)
413 perror("ERROR: Unable to create service connection");
417 fd = DNSServiceRefSockFD(main_ref);
419 fax_ipp_ref = main_ref;
420 DNSServiceBrowse(&fax_ipp_ref, kDNSServiceFlagsShareConnection, 0,
421 "_fax-ipp._tcp", NULL, browse_callback, devices);
424 DNSServiceBrowse(&ipp_ref, kDNSServiceFlagsShareConnection, 0,
425 "_ipp._tcp", NULL, browse_callback, devices);
427 ipp_tls_ref = main_ref;
428 DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0,
429 "_ipp-tls._tcp", NULL, browse_callback, devices);
432 DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0,
433 "_ipps._tcp", NULL, browse_callback, devices);
435 local_fax_ipp_ref = main_ref;
436 DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection,
437 kDNSServiceInterfaceIndexLocalOnly,
438 "_fax-ipp._tcp", NULL, browse_local_callback, devices);
440 local_ipp_ref = main_ref;
441 DNSServiceBrowse(&local_ipp_ref, kDNSServiceFlagsShareConnection,
442 kDNSServiceInterfaceIndexLocalOnly,
443 "_ipp._tcp", NULL, browse_local_callback, devices);
445 local_ipp_tls_ref = main_ref;
446 DNSServiceBrowse(&local_ipp_tls_ref, kDNSServiceFlagsShareConnection,
447 kDNSServiceInterfaceIndexLocalOnly,
448 "_ipp-tls._tcp", NULL, browse_local_callback, devices);
450 local_ipps_ref = main_ref;
451 DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection,
452 kDNSServiceInterfaceIndexLocalOnly,
453 "_ipps._tcp", NULL, browse_local_callback, devices);
455 local_printer_ref = main_ref;
456 DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection,
457 kDNSServiceInterfaceIndexLocalOnly,
458 "_printer._tcp", NULL, browse_local_callback, devices);
460 pdl_datastream_ref = main_ref;
461 DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0,
462 "_pdl-datastream._tcp", NULL, browse_callback, devices);
464 printer_ref = main_ref;
465 DNSServiceBrowse(&printer_ref, kDNSServiceFlagsShareConnection, 0,
466 "_printer._tcp", NULL, browse_callback, devices);
468 riousbprint_ref = main_ref;
469 DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0,
470 "_riousbprint._tcp", NULL, browse_callback, devices);
471 #endif /* HAVE_DNSSD */
474 * Loop until we are killed...
477 while (!job_canceled)
486 timeout.tv_usec = 250000;
488 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
491 if (FD_ISSET(fd, &input))
494 * Process results of our browsing...
497 DNSServiceProcessResult(main_ref);
503 #else /* HAVE_AVAHI */
505 avahi_got_callback = 0;
506 r = avahi_simple_poll_iterate (simple_poll, 1);
507 if (r != 0 && r != EINTR)
510 * We've been told to exit the loop. Perhaps the connection to
517 if (avahi_got_callback)
519 #endif /* HAVE_DNSSD */
524 * Announce any devices we've found...
528 DNSServiceErrorType status; /* DNS query status */
529 #endif /* HAVE_DNSSD */
530 cups_device_t *best; /* Best matching device */
531 char device_uri[1024]; /* Device URI */
532 int count; /* Number of queries */
533 int sent; /* Number of sent */
535 for (device = (cups_device_t *)cupsArrayFirst(devices),
536 best = NULL, count = 0, sent = 0;
538 device = (cups_device_t *)cupsArrayNext(devices))
547 if (!device->ref && !device->sent)
550 * Found the device, now get the TXT record(s) for it...
555 device->ref = main_ref;
557 fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName);
559 status = DNSServiceQueryRecord(&(device->ref),
560 kDNSServiceFlagsShareConnection,
563 kDNSServiceClass_IN, query_callback,
565 if (status != kDNSServiceErr_NoError)
567 fputs("ERROR: Unable to query for TXT records!\n", stderr);
568 fprintf(stderr, "DEBUG: DNSServiceQueryRecord returned %d\n",
576 #endif /* HAVE_DNSSD */
578 if (!device->resolved)
581 #endif /* HAVE_AVAHI */
586 * Got the TXT records, now report the device...
589 DNSServiceRefDeallocate(device->ref);
591 #endif /* HAVE_DNSSD */
595 else if (_cups_strcasecmp(best->name, device->name) ||
596 _cups_strcasecmp(best->domain, device->domain))
598 unquote(uriName, best->fullName, sizeof(uriName));
600 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
601 "dnssd", NULL, uriName, 0,
602 best->cups_shared ? "/cups" : "/");
604 cupsBackendReport("network", device_uri, best->make_and_model,
605 best->name, best->device_id, NULL);
611 else if (best->priority > device->priority ||
612 (best->priority == device->priority &&
613 best->type < device->type))
631 unquote(uriName, best->fullName, sizeof(uriName));
633 httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri),
634 "dnssd", NULL, uriName, 0,
635 best->cups_shared ? "/cups" : "/");
637 cupsBackendReport("network", device_uri, best->make_and_model,
638 best->name, best->device_id, NULL);
643 if (sent == cupsArrayCount(devices))
648 return (CUPS_BACKEND_OK);
654 * 'browse_callback()' - Browse devices.
659 DNSServiceRef sdRef, /* I - Service reference */
660 DNSServiceFlags flags, /* I - Option flags */
661 uint32_t interfaceIndex, /* I - Interface number */
662 DNSServiceErrorType errorCode, /* I - Error, if any */
663 const char *serviceName, /* I - Name of service/device */
664 const char *regtype, /* I - Type of service */
665 const char *replyDomain, /* I - Service domain */
666 void *context) /* I - Devices array */
668 fprintf(stderr, "DEBUG2: browse_callback(sdRef=%p, flags=%x, "
669 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
670 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
671 sdRef, flags, interfaceIndex, errorCode,
672 serviceName ? serviceName : "(null)",
673 regtype ? regtype : "(null)",
674 replyDomain ? replyDomain : "(null)",
678 * Only process "add" data...
681 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
688 get_device((cups_array_t *)context, serviceName, regtype, replyDomain);
693 * 'browse_local_callback()' - Browse local devices.
697 browse_local_callback(
698 DNSServiceRef sdRef, /* I - Service reference */
699 DNSServiceFlags flags, /* I - Option flags */
700 uint32_t interfaceIndex, /* I - Interface number */
701 DNSServiceErrorType errorCode, /* I - Error, if any */
702 const char *serviceName, /* I - Name of service/device */
703 const char *regtype, /* I - Type of service */
704 const char *replyDomain, /* I - Service domain */
705 void *context) /* I - Devices array */
707 cups_device_t *device; /* Device */
710 fprintf(stderr, "DEBUG2: browse_local_callback(sdRef=%p, flags=%x, "
711 "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", "
712 "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n",
713 sdRef, flags, interfaceIndex, errorCode,
714 serviceName ? serviceName : "(null)",
715 regtype ? regtype : "(null)",
716 replyDomain ? replyDomain : "(null)",
720 * Only process "add" data...
723 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
730 device = get_device((cups_array_t *)context, serviceName, regtype,
734 * Hide locally-registered devices...
737 fprintf(stderr, "DEBUG: Hiding local printer \"%s\"...\n",
741 #endif /* HAVE_DNSSD */
745 * 'compare_devices()' - Compare two devices.
748 static int /* O - Result of comparison */
749 compare_devices(cups_device_t *a, /* I - First device */
750 cups_device_t *b) /* I - Second device */
752 return (strcmp(a->name, b->name));
757 * 'exec_backend()' - Execute the backend that corresponds to the
758 * resolved service name.
762 exec_backend(char **argv) /* I - Command-line arguments */
764 const char *resolved_uri, /* Resolved device URI */
765 *cups_serverbin; /* Location of programs */
766 char scheme[1024], /* Scheme from URI */
767 *ptr, /* Pointer into scheme */
768 filename[1024]; /* Backend filename */
772 * Resolve the device URI...
777 while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL)
779 _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer."));
782 if (getenv("CLASS") != NULL)
783 exit(CUPS_BACKEND_FAILED);
787 * Extract the scheme from the URI...
790 strlcpy(scheme, resolved_uri, sizeof(scheme));
791 if ((ptr = strchr(scheme, ':')) != NULL)
795 * Get the filename of the backend...
798 if ((cups_serverbin = getenv("CUPS_SERVERBIN")) == NULL)
799 cups_serverbin = CUPS_SERVERBIN;
801 snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme);
804 * Overwrite the device URI and run the new backend...
807 setenv("DEVICE_URI", resolved_uri, 1);
809 argv[0] = (char *)resolved_uri;
811 fprintf(stderr, "DEBUG: Executing backend \"%s\"...\n", filename);
813 execv(filename, argv);
815 fprintf(stderr, "ERROR: Unable to execute backend \"%s\": %s\n", filename,
817 exit(CUPS_BACKEND_STOP);
822 * 'device_type()' - Get DNS-SD type enumeration from string.
826 device_type (const char *regtype)
829 if (!strcmp(regtype, "_ipp._tcp"))
830 return (CUPS_DEVICE_IPP);
831 else if (!strcmp(regtype, "_ipps._tcp") ||
832 !strcmp(regtype, "_ipp-tls._tcp"))
833 return (CUPS_DEVICE_IPPS);
834 else if (!strcmp(regtype, "_fax-ipp._tcp"))
835 return (CUPS_DEVICE_FAX_IPP);
836 else if (!strcmp(regtype, "_printer._tcp"))
837 return (CUPS_DEVICE_PDL_DATASTREAM);
839 if (!strcmp(regtype, "_ipp._tcp."))
840 return (CUPS_DEVICE_IPP);
841 else if (!strcmp(regtype, "_ipps._tcp.") ||
842 !strcmp(regtype, "_ipp-tls._tcp."))
843 return (CUPS_DEVICE_IPPS);
844 else if (!strcmp(regtype, "_fax-ipp._tcp."))
845 return (CUPS_DEVICE_FAX_IPP);
846 else if (!strcmp(regtype, "_printer._tcp."))
847 return (CUPS_DEVICE_PRINTER);
848 else if (!strcmp(regtype, "_pdl-datastream._tcp."))
849 return (CUPS_DEVICE_PDL_DATASTREAM);
850 #endif /* HAVE_AVAHI */
852 return (CUPS_DEVICE_RIOUSBPRINT);
857 * 'get_device()' - Create or update a device.
860 static cups_device_t * /* O - Device */
861 get_device(cups_array_t *devices, /* I - Device array */
862 const char *serviceName, /* I - Name of service/device */
863 const char *regtype, /* I - Type of service */
864 const char *replyDomain) /* I - Service domain */
866 cups_device_t key, /* Search key */
867 *device; /* Device */
868 char fullName[kDNSServiceMaxDomainName];
869 /* Full name for query */
873 * See if this is a new device...
876 key.name = (char *)serviceName;
877 key.type = device_type (regtype);
879 for (device = cupsArrayFind(devices, &key);
881 device = cupsArrayNext(devices))
882 if (_cups_strcasecmp(device->name, key.name))
884 else if (device->type == key.type)
886 if (!_cups_strcasecmp(device->domain, "local.") &&
887 _cups_strcasecmp(device->domain, replyDomain))
890 * Update the .local listing to use the "global" domain name instead.
891 * The backend will try local lookups first, then the global domain name.
894 free(device->domain);
895 device->domain = strdup(replyDomain);
898 DNSServiceConstructFullName(fullName, device->name, regtype,
900 #else /* HAVE_AVAHI */
901 avahi_service_name_join (fullName, kDNSServiceMaxDomainName,
902 serviceName, regtype, replyDomain);
903 #endif /* HAVE_DNSSD */
905 free(device->fullName);
906 device->fullName = strdup(fullName);
913 * Yes, add the device...
916 fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype,
919 device = calloc(sizeof(cups_device_t), 1);
920 device->name = strdup(serviceName);
921 device->domain = strdup(replyDomain);
922 device->type = key.type;
923 device->priority = 50;
925 device->resolved = 0;
926 #endif /* HAVE_AVAHI */
928 cupsArrayAdd(devices, device);
931 * Set the "full name" of this service, which is used for queries...
935 DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain);
936 #else /* HAVE_AVAHI */
937 avahi_service_name_join (fullName, kDNSServiceMaxDomainName,
938 serviceName, regtype, replyDomain);
939 #endif /* HAVE_DNSSD */
941 device->fullName = strdup(fullName);
949 * 'query_callback()' - Process query data.
954 DNSServiceRef sdRef, /* I - Service reference */
955 DNSServiceFlags flags, /* I - Data flags */
956 uint32_t interfaceIndex, /* I - Interface */
957 DNSServiceErrorType errorCode, /* I - Error, if any */
958 const char *fullName, /* I - Full service name */
959 uint16_t rrtype, /* I - Record type */
960 uint16_t rrclass, /* I - Record class */
961 uint16_t rdlen, /* I - Length of record data */
962 const void *rdata, /* I - Record data */
963 uint32_t ttl, /* I - Time-to-live */
964 void *context) /* I - Devices array */
966 cups_array_t *devices; /* Device array */
967 char name[1024], /* Service name */
968 *ptr; /* Pointer into string */
969 cups_device_t dkey, /* Search key */
970 *device; /* Device */
971 cups_txt_records_t txt;
973 fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, "
974 "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", "
975 "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, "
977 sdRef, flags, interfaceIndex, errorCode,
978 fullName ? fullName : "(null)", rrtype, rrclass, rdlen, rdata, ttl,
982 * Only process "add" data...
985 if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd))
989 * Lookup the service in the devices array.
992 devices = (cups_array_t *)context;
995 unquote(name, fullName, sizeof(name));
997 if ((dkey.domain = strstr(name, "._tcp.")) != NULL)
1000 dkey.domain = (char *)"local.";
1002 if ((ptr = strstr(name, "._")) != NULL)
1005 dkey.type = device_type (fullName);
1008 txt.dataend = rdata + rdlen;
1009 device = find_device ((cups_array_t *) context, &txt, &dkey);
1011 fprintf(stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", fullName);
1013 #endif /* HAVE_DNSSD */
1018 * 'avahi_client_callback()' - Avahi client callback function.
1022 avahi_client_callback(AvahiClient *client,
1023 AvahiClientState state,
1027 * If the connection drops, quit.
1030 if (state == AVAHI_CLIENT_FAILURE)
1032 fprintf (stderr, "ERROR: Avahi connection failed\n");
1033 avahi_simple_poll_quit (simple_poll);
1039 * 'avahi_query_callback()' - Avahi query callback function.
1043 avahi_query_callback(AvahiServiceResolver *resolver,
1044 AvahiIfIndex interface,
1045 AvahiProtocol protocol,
1046 AvahiResolverEvent event,
1050 const char *host_name,
1051 const AvahiAddress *address,
1053 AvahiStringList *txt,
1054 AvahiLookupResultFlags flags,
1057 AvahiClient *client;
1062 cups_txt_records_t txtr;
1064 client = avahi_service_resolver_get_client (resolver);
1065 if (event != AVAHI_RESOLVER_FOUND)
1067 if (event == AVAHI_RESOLVER_FAILURE)
1069 fprintf (stderr, "ERROR: %s\n",
1070 avahi_strerror (avahi_client_errno (client)));
1073 avahi_service_resolver_free (resolver);
1078 * Set search key for device.
1082 unquote (uqname, name, sizeof (uqname));
1083 if ((ptr = strstr(name, "._")) != NULL)
1086 key.domain = (char *) domain;
1087 key.type = device_type (type);
1090 * Find the device and the the TXT information.
1094 device = find_device ((cups_array_t *) context, &txtr, &key);
1098 * Let the main loop know to announce the device.
1101 device->resolved = 1;
1102 avahi_got_callback = 1;
1105 fprintf (stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", name);
1107 avahi_service_resolver_free (resolver);
1112 * 'avahi_browse_callback()' - Avahi browse callback function.
1116 avahi_browse_callback(AvahiServiceBrowser *browser,
1117 AvahiIfIndex interface,
1118 AvahiProtocol protocol,
1119 AvahiBrowserEvent event,
1123 AvahiLookupResultFlags flags,
1126 AvahiClient *client = avahi_service_browser_get_client (browser);
1130 case AVAHI_BROWSER_FAILURE:
1131 fprintf (stderr, "ERROR: %s\n",
1132 avahi_strerror (avahi_client_errno (client)));
1133 avahi_simple_poll_quit (simple_poll);
1136 case AVAHI_BROWSER_NEW:
1138 * This object is new on the network.
1141 if (flags & AVAHI_LOOKUP_RESULT_LOCAL)
1144 * This comes from the local machine so ignore it.
1147 fprintf (stderr, "DEBUG: ignoring local service %s\n", name);
1152 * Create a device entry for it if it doesn't yet exist.
1155 get_device ((cups_array_t *)context, name, type, domain);
1158 * Now look for a TXT entry.
1161 if (avahi_service_resolver_new (client, interface, protocol,
1163 AVAHI_PROTO_UNSPEC, 0,
1164 avahi_query_callback, context) == NULL)
1166 fprintf (stderr, "ERROR: failed to resolve service %s: %s\n",
1167 name, avahi_strerror (avahi_client_errno (client)));
1173 case AVAHI_BROWSER_REMOVE:
1174 case AVAHI_BROWSER_ALL_FOR_NOW:
1175 case AVAHI_BROWSER_CACHE_EXHAUSTED:
1179 #endif /* HAVE_AVAHI */
1183 * 'find_device()' - Find a device from its name and domain.
1186 static cups_device_t *
1187 find_device (cups_array_t *devices,
1188 cups_txt_records_t *txt,
1189 cups_device_t *dkey)
1191 cups_device_t *device;
1194 for (device = cupsArrayFind(devices, dkey);
1196 device = cupsArrayNext(devices))
1198 if (_cups_strcasecmp(device->name, dkey->name) ||
1199 _cups_strcasecmp(device->domain, dkey->domain))
1204 else if (device->type == dkey->type)
1207 * Found it, pull out the priority and make and model from the TXT
1208 * record and save it...
1211 char make_and_model[512],
1212 /* Manufacturer and model */
1213 model[256], /* Model */
1214 device_id[2048]; /* 1284 device ID */
1216 device_id[0] = '\0';
1217 make_and_model[0] = '\0';
1219 strcpy(model, "Unknown");
1226 if (parse_txt_record_pair (txt))
1231 if (!strncasecmp(key, "usb_", 4))
1234 * Add USB device ID information...
1237 ptr = device_id + strlen(device_id);
1238 snprintf(ptr, sizeof(device_id) - (ptr - device_id), "%s:%s;",
1242 if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") ||
1243 !_cups_strcasecmp(key, "usb_MANUFACTURER"))
1244 strcpy(make_and_model, value);
1245 else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL"))
1246 strcpy(model, value);
1247 else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript"))
1249 if (value[0] == '(')
1252 * Strip parenthesis...
1255 if ((ptr = value + strlen(value) - 1) > value && *ptr == ')')
1258 strcpy(model, value + 1);
1261 strcpy(model, value);
1263 else if (!_cups_strcasecmp(key, "ty"))
1265 strcpy(model, value);
1267 if ((ptr = strchr(model, ',')) != NULL)
1270 else if (!_cups_strcasecmp(key, "priority"))
1271 device->priority = atoi(value);
1272 else if ((device->type == CUPS_DEVICE_IPP ||
1273 device->type == CUPS_DEVICE_IPPS ||
1274 device->type == CUPS_DEVICE_PRINTER) &&
1275 !_cups_strcasecmp(key, "printer-type"))
1278 * This is a CUPS printer!
1281 device->cups_shared = 1;
1283 if (device->type == CUPS_DEVICE_PRINTER)
1288 if (next_txt_record (txt) == NULL)
1292 if (device->device_id)
1293 free(device->device_id);
1295 if (!device_id[0] && strcmp(model, "Unknown"))
1297 if (make_and_model[0])
1298 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;",
1299 make_and_model, model);
1300 else if (!_cups_strncasecmp(model, "designjet ", 10))
1301 snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10);
1302 else if (!_cups_strncasecmp(model, "stylus ", 7))
1303 snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7);
1304 else if ((ptr = strchr(model, ' ')) != NULL)
1307 * Assume the first word is the make...
1310 memcpy(make_and_model, model, ptr - model);
1311 make_and_model[ptr - model] = '\0';
1313 snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s",
1314 make_and_model, ptr + 1);
1319 device->device_id = strdup(device_id);
1321 device->device_id = NULL;
1323 if (device->make_and_model)
1324 free(device->make_and_model);
1326 if (make_and_model[0])
1328 strlcat(make_and_model, " ", sizeof(make_and_model));
1329 strlcat(make_and_model, model, sizeof(make_and_model));
1331 device->make_and_model = strdup(make_and_model);
1334 device->make_and_model = strdup(model);
1343 * 'sigterm_handler()' - Handle termination signals...
1347 sigterm_handler(int sig) /* I - Signal number (unused) */
1352 exit(CUPS_BACKEND_OK);
1359 * 'unquote()' - Unquote a name string.
1363 unquote(char *dst, /* I - Destination buffer */
1364 const char *src, /* I - Source string */
1365 size_t dstsize) /* I - Size of destination buffer */
1367 char *dstend = dst + dstsize - 1; /* End of destination buffer */
1370 while (*src && dst < dstend)
1375 if (isdigit(src[0] & 255) && isdigit(src[1] & 255) &&
1376 isdigit(src[2] & 255))
1378 *dst++ = ((((src[0] - '0') * 10) + src[1] - '0') * 10) + src[2] - '0';
1393 * End of "$Id: dnssd.c 10379 2012-03-23 22:16:22Z mike $".