X-Git-Url: http://git.ipfire.org/?a=blobdiff_plain;f=backend%2Fdnssd.c;h=83f450959d8732f44cc8bf119c2e4be7ac3d9468;hb=a215cf8413f393c6751f6341b9ce7159e5ffd315;hp=97c0d60715834d88ce0f7ffab2d6e5aee1364411;hpb=bf3816c7edd01247b850a12e52856819190b66fd;p=thirdparty%2Fcups.git diff --git a/backend/dnssd.c b/backend/dnssd.c index 97c0d6071..83f450959 100644 --- a/backend/dnssd.c +++ b/backend/dnssd.c @@ -1,27 +1,17 @@ /* - * "$Id$" + * "$Id: dnssd.c 12660 2015-05-22 19:09:57Z msweet $" * - * DNS-SD discovery backend for the Common UNIX Printing System (CUPS). + * DNS-SD discovery backend for CUPS. * - * Copyright 2008-2009 by Apple Inc. + * Copyright 2008-2015 by Apple Inc. * - * These coded instructions, statements, and computer programs are the - * property of Apple Inc. and are protected by Federal copyright - * law. Distribution and use rights are outlined in the file "LICENSE.txt" - * "LICENSE" which should have been included with this file. If this - * file is missing or damaged, see the license at "http://www.cups.org/". + * These coded instructions, statements, and computer programs are the + * property of Apple Inc. and are protected by Federal copyright + * law. Distribution and use rights are outlined in the file "LICENSE.txt" + * "LICENSE" which should have been included with this file. If this + * file is missing or damaged, see the license at "http://www.cups.org/". * - * This file is subject to the Apple OS-Developed Software exception. - * - * Contents: - * - * main() - Browse for printers. - * browse_callback() - Browse devices. - * browse_local_callback() - Browse local devices. - * compare_devices() - Compare two devices. - * get_device() - Create or update a device. - * query_callback() - Process query data. - * unquote() - Unquote a name string. + * This file is subject to the Apple OS-Developed Software exception. */ /* @@ -30,7 +20,18 @@ #include "backend-private.h" #include -#include +#ifdef HAVE_DNSSD +# include +#endif /* HAVE_DNSSD */ +#ifdef HAVE_AVAHI +# include +# include +# include +# include +# include +# include +#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX +#endif /* HAVE_AVAHI */ /* @@ -40,6 +41,7 @@ typedef enum { CUPS_DEVICE_PRINTER = 0, /* lpd://... */ + CUPS_DEVICE_IPPS, /* ipps://... */ CUPS_DEVICE_IPP, /* ipp://... */ CUPS_DEVICE_FAX_IPP, /* ipp://... */ CUPS_DEVICE_PDL_DATASTREAM, /* socket://... */ @@ -49,11 +51,18 @@ typedef enum typedef struct { - DNSServiceRef ref; /* Service reference for resolve */ +#ifdef HAVE_DNSSD + DNSServiceRef ref; /* Service reference for query */ +#endif /* HAVE_DNSSD */ +#ifdef HAVE_AVAHI + AvahiRecordBrowser *ref; /* Browser for query */ +#endif /* HAVE_AVAHI */ char *name, /* Service name */ *domain, /* Domain name */ *fullName, /* Full name */ - *make_and_model; /* Make and model from TXT record */ + *make_and_model, /* Make and model from TXT record */ + *device_id, /* 1284 device ID from TXT record */ + *uuid; /* UUID from TXT record */ cups_devtype_t type; /* Device registration type */ int priority, /* Priority associated with type */ cups_shared, /* CUPS shared printer? */ @@ -61,17 +70,33 @@ typedef struct } cups_device_t; +/* + * Local globals... + */ + +static int job_canceled = 0; + /* Set to 1 on SIGTERM */ +#ifdef HAVE_AVAHI +static AvahiSimplePoll *simple_poll = NULL; + /* Poll information */ +static int got_data = 0; /* Got data from poll? */ +static int browsers = 0; /* Number of running browsers */ +#endif /* HAVE_AVAHI */ + + /* * Local functions... */ +#ifdef HAVE_DNSSD static void browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName, const char *regtype, - const char *replyDomain, void *context); + const char *replyDomain, void *context) + __attribute__((nonnull(1,5,6,7,8))); static void browse_local_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -79,13 +104,32 @@ static void browse_local_callback(DNSServiceRef sdRef, const char *serviceName, const char *regtype, const char *replyDomain, - void *context); + void *context) + __attribute__((nonnull(1,5,6,7,8))); +#endif /* HAVE_DNSSD */ +#ifdef HAVE_AVAHI +static void browse_callback(AvahiServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *serviceName, + const char *regtype, + const char *replyDomain, + AvahiLookupResultFlags flags, + void *context); +static void client_callback(AvahiClient *client, + AvahiClientState state, + void *context); +#endif /* HAVE_AVAHI */ + static int compare_devices(cups_device_t *a, cups_device_t *b); -static void exec_backend(char **argv); +static void exec_backend(char **argv) __attribute__((noreturn)); static cups_device_t *get_device(cups_array_t *devices, const char *serviceName, const char *regtype, - const char *replyDomain); + const char *replyDomain) + __attribute__((nonnull(1,2,3,4))); +#ifdef HAVE_DNSSD static void query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -93,8 +137,25 @@ static void query_callback(DNSServiceRef sdRef, const char *fullName, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, + void *context) + __attribute__((nonnull(1,5,9,11))); +#elif defined(HAVE_AVAHI) +static int poll_callback(struct pollfd *pollfds, + unsigned int num_pollfds, int timeout, + void *context); +static void query_callback(AvahiRecordBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, uint16_t rrclass, + uint16_t rrtype, const void *rdata, + size_t rdlen, + AvahiLookupResultFlags flags, void *context); -static void unquote(char *dst, const char *src, size_t dstsize); +#endif /* HAVE_DNSSD */ +static void sigterm_handler(int sig); +static void unquote(char *dst, const char *src, size_t dstsize) + __attribute__((nonnull(1,2))); /* @@ -106,37 +167,65 @@ main(int argc, /* I - Number of command-line args */ char *argv[]) /* I - Command-line arguments */ { const char *name; /* Backend name */ + cups_array_t *devices; /* Device array */ + cups_device_t *device; /* Current device */ + char uriName[1024]; /* Unquoted fullName for URI */ +#ifdef HAVE_DNSSD + int fd; /* Main file descriptor */ + fd_set input; /* Input set for select() */ + struct timeval timeout; /* Timeout for select() */ DNSServiceRef main_ref, /* Main service reference */ fax_ipp_ref, /* IPP fax service reference */ ipp_ref, /* IPP service reference */ ipp_tls_ref, /* IPP w/TLS service reference */ + ipps_ref, /* IPP service reference */ local_fax_ipp_ref, /* Local IPP fax service reference */ local_ipp_ref, /* Local IPP service reference */ local_ipp_tls_ref, /* Local IPP w/TLS service reference */ + local_ipps_ref, /* Local IPP service reference */ local_printer_ref, /* Local LPD service reference */ pdl_datastream_ref, /* AppSocket service reference */ printer_ref, /* LPD service reference */ riousbprint_ref; /* Remote IO service reference */ - int fd; /* Main file descriptor */ - fd_set input; /* Input set for select() */ - struct timeval timeout; /* Timeout for select() */ - cups_array_t *devices; /* Device array */ - cups_device_t *device; /* Current device */ - char uriName[1024]; /* Unquoted fullName for URI */ +#endif /* HAVE_DNSSD */ +#ifdef HAVE_AVAHI + AvahiClient *client; /* Client information */ + int error; /* Error code, if any */ +#endif /* HAVE_AVAHI */ +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) + struct sigaction action; /* Actions for POSIX signals */ +#endif /* HAVE_SIGACTION && !HAVE_SIGSET */ /* - * Check command-line... + * Don't buffer stderr, and catch SIGTERM... */ setbuf(stderr, NULL); +#ifdef HAVE_SIGSET /* Use System V signals over POSIX to avoid bugs */ + sigset(SIGTERM, sigterm_handler); +#elif defined(HAVE_SIGACTION) + memset(&action, 0, sizeof(action)); + + sigemptyset(&action.sa_mask); + action.sa_handler = sigterm_handler; + sigaction(SIGTERM, &action, NULL); +#else + signal(SIGTERM, sigterm_handler); +#endif /* HAVE_SIGSET */ + + /* + * Check command-line... + */ + if (argc >= 6) exec_backend(argv); else if (argc != 1) { - fprintf(stderr, "Usage: %s job user title copies options [filename(s)]\n", - argv[0]); + _cupsLangPrintf(stderr, + _("Usage: %s job-id user title copies options [file]"), + argv[0]); return (1); } @@ -162,6 +251,7 @@ main(int argc, /* I - Number of command-line args */ * Browse for different kinds of printers... */ +#ifdef HAVE_DNSSD if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError) { perror("ERROR: Unable to create service connection"); @@ -182,6 +272,10 @@ main(int argc, /* I - Number of command-line args */ DNSServiceBrowse(&ipp_tls_ref, kDNSServiceFlagsShareConnection, 0, "_ipp-tls._tcp", NULL, browse_callback, devices); + ipps_ref = main_ref; + DNSServiceBrowse(&ipps_ref, kDNSServiceFlagsShareConnection, 0, + "_ipps._tcp", NULL, browse_callback, devices); + local_fax_ipp_ref = main_ref; DNSServiceBrowse(&local_fax_ipp_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, @@ -197,12 +291,17 @@ main(int argc, /* I - Number of command-line args */ kDNSServiceInterfaceIndexLocalOnly, "_ipp-tls._tcp", NULL, browse_local_callback, devices); + local_ipps_ref = main_ref; + DNSServiceBrowse(&local_ipps_ref, kDNSServiceFlagsShareConnection, + kDNSServiceInterfaceIndexLocalOnly, + "_ipps._tcp", NULL, browse_local_callback, devices); + local_printer_ref = main_ref; DNSServiceBrowse(&local_printer_ref, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexLocalOnly, "_printer._tcp", NULL, browse_local_callback, devices); - pdl_datastream_ref = main_ref; + pdl_datastream_ref = main_ref; DNSServiceBrowse(&pdl_datastream_ref, kDNSServiceFlagsShareConnection, 0, "_pdl-datastream._tcp", NULL, browse_callback, devices); @@ -213,18 +312,68 @@ main(int argc, /* I - Number of command-line args */ riousbprint_ref = main_ref; DNSServiceBrowse(&riousbprint_ref, kDNSServiceFlagsShareConnection, 0, "_riousbprint._tcp", NULL, browse_callback, devices); +#endif /* HAVE_DNSSD */ + +#ifdef HAVE_AVAHI + if ((simple_poll = avahi_simple_poll_new()) == NULL) + { + fputs("DEBUG: Unable to create Avahi simple poll object.\n", stderr); + return (0); + } + + avahi_simple_poll_set_func(simple_poll, poll_callback, NULL); + + client = avahi_client_new(avahi_simple_poll_get(simple_poll), + 0, client_callback, simple_poll, &error); + if (!client) + { + fputs("DEBUG: Unable to create Avahi client.\n", stderr); + return (0); + } + + browsers = 6; + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_fax-ipp._tcp", NULL, 0, + browse_callback, devices); + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_ipp._tcp", NULL, 0, + browse_callback, devices); + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_ipp-tls._tcp", NULL, 0, + browse_callback, devices); + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_ipps._tcp", NULL, 0, + browse_callback, devices); + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_pdl-datastream._tcp", + NULL, 0, + browse_callback, + devices); + avahi_service_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_printer._tcp", NULL, 0, + browse_callback, devices); +#endif /* HAVE_AVAHI */ /* * Loop until we are killed... */ - for (;;) + while (!job_canceled) { + int announce = 0; /* Announce printers? */ + +#ifdef HAVE_DNSSD FD_ZERO(&input); FD_SET(fd, &input); - timeout.tv_sec = 1; - timeout.tv_usec = 0; + timeout.tv_sec = 0; + timeout.tv_usec = 500000; if (select(fd + 1, &input, NULL, NULL, &timeout) < 0) continue; @@ -238,76 +387,135 @@ main(int argc, /* I - Number of command-line args */ DNSServiceProcessResult(main_ref); } else + announce = 1; + +#elif defined(HAVE_AVAHI) + got_data = 0; + + if ((error = avahi_simple_poll_iterate(simple_poll, 500)) > 0) + { + /* + * We've been told to exit the loop. Perhaps the connection to + * Avahi failed. + */ + + break; + } + + if (!got_data) + announce = 1; +#endif /* HAVE_DNSSD */ + +/* fprintf(stderr, "DEBUG: announce=%d\n", announce);*/ + + if (announce) { /* * Announce any devices we've found... */ +#ifdef HAVE_DNSSD DNSServiceErrorType status; /* DNS query status */ +#endif /* HAVE_DNSSD */ cups_device_t *best; /* Best matching device */ char device_uri[1024]; /* Device URI */ int count; /* Number of queries */ - static const char * const schemes[] = - { "lpd", "ipp", "ipp", "socket", "riousbprint" }; - /* URI schemes for devices */ - + int sent; /* Number of sent */ for (device = (cups_device_t *)cupsArrayFirst(devices), - best = NULL, count = 0; + best = NULL, count = 0, sent = 0; device; device = (cups_device_t *)cupsArrayNext(devices)) + { + if (device->sent) + sent ++; + + if (device->ref) + count ++; + if (!device->ref && !device->sent) { /* * Found the device, now get the TXT record(s) for it... */ - if (count < 10) + if (count < 50) { - device->ref = main_ref; - fprintf(stderr, "DEBUG: Querying \"%s\"...\n", device->fullName); +#ifdef HAVE_DNSSD + device->ref = main_ref; + status = DNSServiceQueryRecord(&(device->ref), kDNSServiceFlagsShareConnection, 0, device->fullName, kDNSServiceType_TXT, kDNSServiceClass_IN, query_callback, - devices); + device); if (status != kDNSServiceErr_NoError) - { - fputs("ERROR: Unable to query for TXT records!\n", stderr); - fprintf(stderr, "DEBUG: DNSServiceQueryRecord returned %d\n", - status); - } + fprintf(stderr, + "ERROR: Unable to query \"%s\" for TXT records: %d\n", + device->fullName, status); + /* Users never see this */ else count ++; + +#else + if ((device->ref = avahi_record_browser_new(client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + device->fullName, + AVAHI_DNS_CLASS_IN, + AVAHI_DNS_TYPE_TXT, + 0, + query_callback, + device)) == NULL) + fprintf(stderr, + "ERROR: Unable to query \"%s\" for TXT records: %s\n", + device->fullName, + avahi_strerror(avahi_client_errno(client))); + /* Users never see this */ + else + count ++; +#endif /* HAVE_AVAHI */ } } else if (!device->sent) { +#ifdef HAVE_DNSSD /* * Got the TXT records, now report the device... */ DNSServiceRefDeallocate(device->ref); - device->ref = 0; +#else + avahi_record_browser_free(device->ref); +#endif /* HAVE_DNSSD */ + + device->ref = NULL; if (!best) best = device; - else if (strcasecmp(best->name, device->name) || - strcasecmp(best->domain, device->domain)) + else if (_cups_strcasecmp(best->name, device->name) || + _cups_strcasecmp(best->domain, device->domain)) { unquote(uriName, best->fullName, sizeof(uriName)); - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), - schemes[best->type], NULL, uriName, 0, - best->cups_shared ? "/cups" : "/"); + if (best->uuid) + httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, + sizeof(device_uri), "dnssd", NULL, uriName, 0, + best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s", + best->uuid); + else + httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, + sizeof(device_uri), "dnssd", NULL, uriName, 0, + best->cups_shared ? "/cups" : "/"); cupsBackendReport("network", device_uri, best->make_and_model, - best->name, NULL, NULL); + best->name, best->device_id, NULL); best->sent = 1; best = device; + + sent ++; } else if (best->priority > device->priority || (best->priority == device->priority && @@ -315,28 +523,54 @@ main(int argc, /* I - Number of command-line args */ { best->sent = 1; best = device; + + sent ++; } else + { device->sent = 1; + + sent ++; + } } + } if (best) { unquote(uriName, best->fullName, sizeof(uriName)); - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), - schemes[best->type], NULL, uriName, 0, - best->cups_shared ? "/cups" : "/"); + if (best->uuid) + httpAssembleURIf(HTTP_URI_CODING_ALL, device_uri, + sizeof(device_uri), "dnssd", NULL, uriName, 0, + best->cups_shared ? "/cups?uuid=%s" : "/?uuid=%s", + best->uuid); + else + httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, + sizeof(device_uri), "dnssd", NULL, uriName, 0, + best->cups_shared ? "/cups" : "/"); cupsBackendReport("network", device_uri, best->make_and_model, - best->name, NULL, NULL); + best->name, best->device_id, NULL); best->sent = 1; + sent ++; } + + fprintf(stderr, "DEBUG: sent=%d, count=%d\n", sent, count); + +#ifdef HAVE_AVAHI + if (sent == cupsArrayCount(devices) && browsers == 0) +#else + if (sent == cupsArrayCount(devices)) +#endif /* HAVE_AVAHI */ + break; } } + + return (CUPS_BACKEND_OK); } +#ifdef HAVE_DNSSD /* * 'browse_callback()' - Browse devices. */ @@ -356,10 +590,7 @@ browse_callback( "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", " "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", sdRef, flags, interfaceIndex, errorCode, - serviceName ? serviceName : "(null)", - regtype ? regtype : "(null)", - replyDomain ? replyDomain : "(null)", - context); + serviceName, regtype, replyDomain, context); /* * Only process "add" data... @@ -398,10 +629,7 @@ browse_local_callback( "interfaceIndex=%d, errorCode=%d, serviceName=\"%s\", " "regtype=\"%s\", replyDomain=\"%s\", context=%p)\n", sdRef, flags, interfaceIndex, errorCode, - serviceName ? serviceName : "(null)", - regtype ? regtype : "(null)", - replyDomain ? replyDomain : "(null)", - context); + serviceName, regtype, replyDomain, context); /* * Only process "add" data... @@ -425,6 +653,100 @@ browse_local_callback( device->fullName); device->sent = 1; } +#endif /* HAVE_DNSSD */ + + +#ifdef HAVE_AVAHI +/* + * 'browse_callback()' - Browse devices. + */ + +static void +browse_callback( + AvahiServiceBrowser *browser, /* I - Browser */ + AvahiIfIndex interface, /* I - Interface index (unused) */ + AvahiProtocol protocol, /* I - Network protocol (unused) */ + AvahiBrowserEvent event, /* I - What happened */ + const char *name, /* I - Service name */ + const char *type, /* I - Registration type */ + const char *domain, /* I - Domain */ + AvahiLookupResultFlags flags, /* I - Flags */ + void *context) /* I - Devices array */ +{ + AvahiClient *client = avahi_service_browser_get_client(browser); + /* Client information */ + + + (void)interface; + (void)protocol; + (void)context; + + switch (event) + { + case AVAHI_BROWSER_FAILURE: + fprintf(stderr, "DEBUG: browse_callback: %s\n", + avahi_strerror(avahi_client_errno(client))); + avahi_simple_poll_quit(simple_poll); + break; + + case AVAHI_BROWSER_NEW: + /* + * This object is new on the network. + */ + + if (flags & AVAHI_LOOKUP_RESULT_LOCAL) + { + /* + * This comes from the local machine so ignore it. + */ + + fprintf(stderr, "DEBUG: Ignoring local service %s.\n", name); + } + else + { + /* + * Create a device entry for it if it doesn't yet exist. + */ + + get_device((cups_array_t *)context, name, type, domain); + } + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + browsers--; + break; + } +} + + +/* + * 'client_callback()' - Avahi client callback function. + */ + +static void +client_callback( + AvahiClient *client, /* I - Client information (unused) */ + AvahiClientState state, /* I - Current state */ + void *context) /* I - User data (unused) */ +{ + (void)client; + (void)context; + + /* + * If the connection drops, quit. + */ + + if (state == AVAHI_CLIENT_FAILURE) + { + fputs("DEBUG: Avahi connection failed.\n", stderr); + avahi_simple_poll_quit(simple_poll); + } +} +#endif /* HAVE_AVAHI */ /* @@ -435,12 +757,7 @@ static int /* O - Result of comparison */ compare_devices(cups_device_t *a, /* I - First device */ cups_device_t *b) /* I - Second device */ { - int result = strcmp(a->name, b->name); - - if (result) - return (result); - else - return (strcmp(a->domain, b->domain)); + return (strcmp(a->name, b->name)); } @@ -463,8 +780,16 @@ exec_backend(char **argv) /* I - Command-line arguments */ * Resolve the device URI... */ - if ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL) - exit(CUPS_BACKEND_FAILED); + job_canceled = -1; + + while ((resolved_uri = cupsBackendDeviceURI(argv)) == NULL) + { + _cupsLangPrintFilter(stderr, "INFO", _("Unable to locate printer.")); + sleep(10); + + if (getenv("CLASS") != NULL) + exit(CUPS_BACKEND_FAILED); + } /* * Extract the scheme from the URI... @@ -484,7 +809,7 @@ exec_backend(char **argv) /* I - Command-line arguments */ snprintf(filename, sizeof(filename), "%s/backend/%s", cups_serverbin, scheme); /* - * Overwrite the device URIs and run the new backend... + * Overwrite the device URI and run the new backend... */ setenv("DEVICE_URI", resolved_uri, 1); @@ -501,6 +826,41 @@ exec_backend(char **argv) /* I - Command-line arguments */ } +/* + * 'device_type()' - Get DNS-SD type enumeration from string. + */ + +static cups_devtype_t /* O - Device type */ +device_type(const char *regtype) /* I - Service registration type */ +{ +#ifdef HAVE_AVAHI + if (!strcmp(regtype, "_ipp._tcp")) + return (CUPS_DEVICE_IPP); + else if (!strcmp(regtype, "_ipps._tcp") || + !strcmp(regtype, "_ipp-tls._tcp")) + return (CUPS_DEVICE_IPPS); + else if (!strcmp(regtype, "_fax-ipp._tcp")) + return (CUPS_DEVICE_FAX_IPP); + else if (!strcmp(regtype, "_printer._tcp")) + return (CUPS_DEVICE_PDL_DATASTREAM); +#else + if (!strcmp(regtype, "_ipp._tcp.")) + return (CUPS_DEVICE_IPP); + else if (!strcmp(regtype, "_ipps._tcp.") || + !strcmp(regtype, "_ipp-tls._tcp.")) + return (CUPS_DEVICE_IPPS); + else if (!strcmp(regtype, "_fax-ipp._tcp.")) + return (CUPS_DEVICE_FAX_IPP); + else if (!strcmp(regtype, "_printer._tcp.")) + return (CUPS_DEVICE_PRINTER); + else if (!strcmp(regtype, "_pdl-datastream._tcp.")) + return (CUPS_DEVICE_PDL_DATASTREAM); +#endif /* HAVE_AVAHI */ + + return (CUPS_DEVICE_RIOUSBPRINT); +} + + /* * 'get_device()' - Create or update a device. */ @@ -521,36 +881,52 @@ get_device(cups_array_t *devices, /* I - Device array */ * See if this is a new device... */ - key.name = (char *)serviceName; - key.domain = (char *)replyDomain; - - if (!strcmp(regtype, "_ipp._tcp.") || - !strcmp(regtype, "_ipp-tls._tcp.")) - key.type = CUPS_DEVICE_IPP; - else if (!strcmp(regtype, "_fax-ipp._tcp.")) - key.type = CUPS_DEVICE_FAX_IPP; - else if (!strcmp(regtype, "_printer._tcp.")) - key.type = CUPS_DEVICE_PRINTER; - else if (!strcmp(regtype, "_pdl-datastream._tcp.")) - key.type = CUPS_DEVICE_PDL_DATASTREAM; - else - key.type = CUPS_DEVICE_RIOUSBPRINT; + key.name = (char *)serviceName; + key.type = device_type(regtype); for (device = cupsArrayFind(devices, &key); device; device = cupsArrayNext(devices)) - if (strcasecmp(device->name, key.name) || - strcasecmp(device->domain, key.domain)) + if (_cups_strcasecmp(device->name, key.name)) break; else if (device->type == key.type) + { + if (!_cups_strcasecmp(device->domain, "local.") && + _cups_strcasecmp(device->domain, replyDomain)) + { + /* + * Update the .local listing to use the "global" domain name instead. + * The backend will try local lookups first, then the global domain name. + */ + + free(device->domain); + device->domain = strdup(replyDomain); + +#ifdef HAVE_DNSSD + DNSServiceConstructFullName(fullName, device->name, regtype, + replyDomain); +#else /* HAVE_AVAHI */ + avahi_service_name_join(fullName, kDNSServiceMaxDomainName, + serviceName, regtype, replyDomain); +#endif /* HAVE_DNSSD */ + + free(device->fullName); + device->fullName = strdup(fullName); + } + return (device); + } /* * Yes, add the device... */ - fprintf(stderr, "DEBUG: Found \"%s.%s%s\"...\n", serviceName, regtype, - replyDomain); +#ifdef HAVE_DNSSD + DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain); +#else /* HAVE_AVAHI */ + avahi_service_name_join(fullName, kDNSServiceMaxDomainName, + serviceName, regtype, replyDomain); +#endif /* HAVE_DNSSD */ device = calloc(sizeof(cups_device_t), 1); device->name = strdup(serviceName); @@ -564,13 +940,55 @@ get_device(cups_array_t *devices, /* I - Device array */ * Set the "full name" of this service, which is used for queries... */ +#ifdef HAVE_DNSSD DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain); +#else /* HAVE_AVAHI */ + avahi_service_name_join(fullName, kDNSServiceMaxDomainName, + serviceName, regtype, replyDomain); +#endif /* HAVE_DNSSD */ + device->fullName = strdup(fullName); return (device); } +#ifdef HAVE_AVAHI +/* + * 'poll_callback()' - Wait for input on the specified file descriptors. + * + * Note: This function is needed because avahi_simple_poll_iterate is broken + * and always uses a timeout of 0 (!) milliseconds. + * (Avahi Ticket #364) + */ + +static int /* O - Number of file descriptors matching */ +poll_callback( + struct pollfd *pollfds, /* I - File descriptors */ + unsigned int num_pollfds, /* I - Number of file descriptors */ + int timeout, /* I - Timeout in milliseconds (unused) */ + void *context) /* I - User data (unused) */ +{ + int val; /* Return value */ + + + (void)timeout; + (void)context; + + val = poll(pollfds, num_pollfds, 500); + + if (val < 0) + fprintf(stderr, "DEBUG: poll_callback: %s\n", strerror(errno)); + else if (val > 0) + got_data = 1; + + return (val); +} +#endif /* HAVE_AVAHI */ + + +#if defined(HAVE_DNSSD) || defined(HAVE_AVAHI) +# ifdef HAVE_DNSSD /* * 'query_callback()' - Process query data. */ @@ -587,15 +1005,47 @@ query_callback( uint16_t rdlen, /* I - Length of record data */ const void *rdata, /* I - Record data */ uint32_t ttl, /* I - Time-to-live */ - void *context) /* I - Devices array */ + void *context) /* I - Device */ { - cups_array_t *devices; /* Device array */ - char name[1024], /* Service name */ - *ptr; /* Pointer into name */ - cups_device_t key, /* Search key */ - *device; /* Device */ +# else +/* + * 'query_callback()' - Process query data. + */ + +static void +query_callback( + AvahiRecordBrowser *browser, /* I - Record browser */ + AvahiIfIndex interfaceIndex, + /* I - Interface index (unused) */ + AvahiProtocol protocol, /* I - Network protocol (unused) */ + AvahiBrowserEvent event, /* I - What happened? */ + const char *fullName, /* I - Service name */ + uint16_t rrclass, /* I - Record class */ + uint16_t rrtype, /* I - Record type */ + const void *rdata, /* I - TXT record */ + size_t rdlen, /* I - Length of TXT record */ + AvahiLookupResultFlags flags, /* I - Flags */ + void *context) /* I - Device */ +{ + AvahiClient *client = avahi_record_browser_get_client(browser); + /* Client information */ +# endif /* HAVE_DNSSD */ + char *ptr; /* Pointer into string */ + cups_device_t *device = (cups_device_t *)context; + /* Device */ + const uint8_t *data, /* Pointer into data */ + *datanext, /* Next key/value pair */ + *dataend; /* End of entire TXT record */ + uint8_t datalen; /* Length of current key/value pair */ + char key[256], /* Key string */ + value[256], /* Value string */ + make_and_model[512], /* Manufacturer and model */ + model[256], /* Model */ + pdl[256], /* PDL */ + device_id[2048]; /* 1284 device ID */ +# ifdef HAVE_DNSSD fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, " "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", " "rrtype=%u, rrclass=%u, rdlen=%u, rdata=%p, ttl=%u, " @@ -611,160 +1061,237 @@ query_callback( if (errorCode != kDNSServiceErr_NoError || !(flags & kDNSServiceFlagsAdd)) return; +# else + fprintf(stderr, "DEBUG2: query_callback(browser=%p, interfaceIndex=%d, " + "protocol=%d, event=%d, fullName=\"%s\", rrclass=%u, " + "rrtype=%u, rdata=%p, rdlen=%u, flags=%x, context=%p)\n", + browser, interfaceIndex, protocol, event, + fullName ? fullName : "(null)", rrclass, rrtype, rdata, + (unsigned)rdlen, flags, context); + /* - * Lookup the service in the devices array. + * Only process "add" data... */ - devices = (cups_array_t *)context; - key.name = name; + if (event != AVAHI_BROWSER_NEW) + { + if (event == AVAHI_BROWSER_FAILURE) + fprintf(stderr, "ERROR: %s\n", + avahi_strerror(avahi_client_errno(client))); - unquote(name, fullName, sizeof(name)); + return; + } +# endif /* HAVE_DNSSD */ - if ((key.domain = strstr(name, "._tcp.")) != NULL) - key.domain += 6; - else - key.domain = (char *)"local."; + /* + * Pull out the priority and make and model from the TXT + * record and save it... + */ - if ((ptr = strstr(name, "._")) != NULL) - *ptr = '\0'; + device_id[0] = '\0'; + make_and_model[0] = '\0'; + pdl[0] = '\0'; - if (strstr(fullName, "_ipp._tcp.") || - strstr(fullName, "_ipp-tls._tcp.")) - key.type = CUPS_DEVICE_IPP; - else if (strstr(fullName, "_fax-ipp._tcp.")) - key.type = CUPS_DEVICE_FAX_IPP; - else if (strstr(fullName, "_printer._tcp.")) - key.type = CUPS_DEVICE_PRINTER; - else if (strstr(fullName, "_pdl-datastream._tcp.")) - key.type = CUPS_DEVICE_PDL_DATASTREAM; - else - key.type = CUPS_DEVICE_RIOUSBPRINT; + strlcpy(model, "Unknown", sizeof(model)); - for (device = cupsArrayFind(devices, &key); - device; - device = cupsArrayNext(devices)) + for (data = rdata, dataend = data + rdlen; + data < dataend; + data = datanext) { - if (strcasecmp(device->name, key.name) || - strcasecmp(device->domain, key.domain)) - { - device = NULL; + /* + * Read a key/value pair starting with an 8-bit length. Since the + * length is 8 bits and the size of the key/value buffers is 256, we + * don't need to check for overflow... + */ + + datalen = *data++; + + if (!datalen || (data + datalen) > dataend) break; + + datanext = data + datalen; + + for (ptr = key; data < datanext && *data != '='; data ++) + *ptr++ = (char)*data; + *ptr = '\0'; + + if (data < datanext && *data == '=') + { + data ++; + + if (data < datanext) + memcpy(value, data, (size_t)(datanext - data)); + value[datanext - data] = '\0'; + + fprintf(stderr, "DEBUG2: query_callback: \"%s=%s\".\n", + key, value); } - else if (device->type == key.type) + else + { + fprintf(stderr, "DEBUG2: query_callback: \"%s\" with no value.\n", + key); + continue; + } + + if (!_cups_strncasecmp(key, "usb_", 4)) { /* - * Found it, pull out the priority and make and model from the TXT - * record and save it... + * Add USB device ID information... */ - const void *value; /* Pointer to value */ - uint8_t valueLen; /* Length of value (max 255) */ - char make_and_model[512], /* Manufacturer and model */ - model[256], /* Model */ - priority[256]; /* Priority */ - - - value = TXTRecordGetValuePtr(rdlen, rdata, "priority", &valueLen); + ptr = device_id + strlen(device_id); + snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "%s:%s;", key + 4, value); + } - if (value && valueLen) + if (!_cups_strcasecmp(key, "usb_MFG") || !_cups_strcasecmp(key, "usb_MANU") || + !_cups_strcasecmp(key, "usb_MANUFACTURER")) + strlcpy(make_and_model, value, sizeof(make_and_model)); + else if (!_cups_strcasecmp(key, "usb_MDL") || !_cups_strcasecmp(key, "usb_MODEL")) + strlcpy(model, value, sizeof(model)); + else if (!_cups_strcasecmp(key, "product") && !strstr(value, "Ghostscript")) + { + if (value[0] == '(') { - memcpy(priority, value, valueLen); - priority[valueLen] = '\0'; - device->priority = atoi(priority); - } + /* + * Strip parenthesis... + */ - if ((value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MFG", - &valueLen)) == NULL) - value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MANUFACTURER", - &valueLen); + if ((ptr = value + strlen(value) - 1) > value && *ptr == ')') + *ptr = '\0'; - if (value && valueLen) - { - memcpy(make_and_model, value, valueLen); - make_and_model[valueLen] = '\0'; + strlcpy(model, value + 1, sizeof(model)); } else - make_and_model[0] = '\0'; + strlcpy(model, value, sizeof(model)); + } + else if (!_cups_strcasecmp(key, "ty")) + { + strlcpy(model, value, sizeof(model)); - if ((value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MDL", - &valueLen)) == NULL) - value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MODEL", &valueLen); + if ((ptr = strchr(model, ',')) != NULL) + *ptr = '\0'; + } + else if (!_cups_strcasecmp(key, "pdl")) + strlcpy(pdl, value, sizeof(pdl)); + else if (!_cups_strcasecmp(key, "priority")) + device->priority = atoi(value); + else if ((device->type == CUPS_DEVICE_IPP || + device->type == CUPS_DEVICE_IPPS || + device->type == CUPS_DEVICE_PRINTER) && + !_cups_strcasecmp(key, "printer-type")) + { + /* + * This is a CUPS printer! + */ - if (value && valueLen) - { - memcpy(model, value, valueLen); - model[valueLen] = '\0'; - } - else if ((value = TXTRecordGetValuePtr(rdlen, rdata, "product", - &valueLen)) != NULL && valueLen > 2) - { - if (((char *)value)[0] == '(') - { - /* - * Strip parenthesis... - */ + device->cups_shared = 1; - memcpy(model, value + 1, valueLen - 2); - model[valueLen - 2] = '\0'; - } - else - { - memcpy(model, value, valueLen); - model[valueLen] = '\0'; - } + if (device->type == CUPS_DEVICE_PRINTER) + device->sent = 1; + } + else if (!_cups_strcasecmp(key, "UUID")) + device->uuid = strdup(value); + } - if (!strcasecmp(model, "GPL Ghostscript") || - !strcasecmp(model, "GNU Ghostscript") || - !strcasecmp(model, "ESP Ghostscript")) - { - if ((value = TXTRecordGetValuePtr(rdlen, rdata, "ty", - &valueLen)) != NULL) - { - memcpy(model, value, valueLen); - model[valueLen] = '\0'; + if (device->device_id) + free(device->device_id); - if ((ptr = strchr(model, ',')) != NULL) - *ptr = '\0'; - } - else - strcpy(model, "Unknown"); - } - } - else - strcpy(model, "Unknown"); + if (!device_id[0] && strcmp(model, "Unknown")) + { + if (make_and_model[0]) + snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s;", + make_and_model, model); + else if (!_cups_strncasecmp(model, "designjet ", 10)) + snprintf(device_id, sizeof(device_id), "MFG:HP;MDL:%s", model + 10); + else if (!_cups_strncasecmp(model, "stylus ", 7)) + snprintf(device_id, sizeof(device_id), "MFG:EPSON;MDL:%s", model + 7); + else if ((ptr = strchr(model, ' ')) != NULL) + { + /* + * Assume the first word is the make... + */ - if (device->make_and_model) - free(device->make_and_model); + memcpy(make_and_model, model, (size_t)(ptr - model)); + make_and_model[ptr - model] = '\0'; - if (make_and_model[0]) - { - strlcat(make_and_model, " ", sizeof(make_and_model)); - strlcat(make_and_model, model, sizeof(make_and_model)); - device->make_and_model = strdup(make_and_model); - } - else - device->make_and_model = strdup(model); + snprintf(device_id, sizeof(device_id), "MFG:%s;MDL:%s", + make_and_model, ptr + 1); + } + } - if ((device->type == CUPS_DEVICE_IPP || - device->type == CUPS_DEVICE_PRINTER) && - TXTRecordGetValuePtr(rdlen, rdata, "printer-type", &valueLen)) - { - /* - * This is a CUPS printer! - */ + if (device_id[0] && + !strstr(device_id, "CMD:") && + !strstr(device_id, "COMMAND SET:") && + (strstr(pdl, "application/pdf") || + strstr(pdl, "application/postscript") || + strstr(pdl, "application/vnd.hp-PCL") || + strstr(pdl, "image/"))) + { + value[0] = '\0'; + if (strstr(pdl, "application/pdf")) + strlcat(value, ",PDF", sizeof(value)); + if (strstr(pdl, "application/postscript")) + strlcat(value, ",PS", sizeof(value)); + if (strstr(pdl, "application/vnd.hp-PCL")) + strlcat(value, ",PCL", sizeof(value)); + for (ptr = strstr(pdl, "image/"); ptr; ptr = strstr(ptr, "image/")) + { + char *valptr = value + strlen(value); + /* Pointer into value */ - device->cups_shared = 1; + if (valptr < (value + sizeof(value) - 1)) + *valptr++ = ','; - if (device->type == CUPS_DEVICE_PRINTER) - device->sent = 1; + ptr += 6; + while (isalnum(*ptr & 255) || *ptr == '-' || *ptr == '.') + { + if (isalnum(*ptr & 255) && valptr < (value + sizeof(value) - 1)) + *valptr++ = (char)toupper(*ptr++ & 255); + else + break; } - break; + *valptr = '\0'; } + + ptr = device_id + strlen(device_id); + snprintf(ptr, sizeof(device_id) - (size_t)(ptr - device_id), "CMD:%s;", value + 1); } - if (!device) - fprintf(stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", fullName); + if (device_id[0]) + device->device_id = strdup(device_id); + else + device->device_id = NULL; + + if (device->make_and_model) + free(device->make_and_model); + + if (make_and_model[0]) + { + strlcat(make_and_model, " ", sizeof(make_and_model)); + strlcat(make_and_model, model, sizeof(make_and_model)); + + device->make_and_model = strdup(make_and_model); + } + else + device->make_and_model = strdup(model); +} +#endif /* HAVE_DNSSD || HAVE_AVAHI */ + + +/* + * 'sigterm_handler()' - Handle termination signals. + */ + +static void +sigterm_handler(int sig) /* I - Signal number (unused) */ +{ + (void)sig; + + if (job_canceled) + exit(CUPS_BACKEND_OK); + else + job_canceled = 1; } @@ -803,5 +1330,5 @@ unquote(char *dst, /* I - Destination buffer */ /* - * End of "$Id$". + * End of "$Id: dnssd.c 12660 2015-05-22 19:09:57Z msweet $". */