diff --git a/backend/dnssd.c b/backend/dnssd.c index ec32bba..a5d7a1b 100644 --- a/backend/dnssd.c +++ b/backend/dnssd.c @@ -20,7 +20,8 @@ * browse_local_callback() - Browse local devices. * compare_devices() - Compare two devices. * get_device() - Create or update a device. - * query_callback() - Process query data. + * query_callback() - Process query data from DNS-SD + * find_device() - Process query data. * unquote() - Unquote a name string. */ @@ -30,7 +31,18 @@ #include "backend-private.h" #include -#include +#ifdef HAVE_AVAHI +# include +# include +# include +# include +# include +# include +typedef AvahiServiceBrowser *DNSServiceRef; +#define kDNSServiceMaxDomainName AVAHI_DOMAIN_NAME_MAX +#else +# include +#endif /* Avahi */ /* @@ -49,7 +61,11 @@ typedef enum typedef struct { +#ifdef HAVE_AVAHI + char *address; +#else DNSServiceRef ref; /* Service reference for resolve */ +#endif /* !HAVE_AVAHI */ char *name, /* Service name */ *domain, /* Domain name */ *fullName, /* Full name */ @@ -65,6 +81,26 @@ typedef struct * Local functions... */ +#ifdef HAVE_AVAHI +/* + * Avahi callback functions + */ +static void avahi_client_callback(AvahiClient *client, + AvahiClientState state, + void *context); +static void avahi_browse_callback(AvahiServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *serviceName, + const char *regtype, + const char *replyDomain, + AvahiLookupResultFlags flags, + void *context); +#else +/* + * libdns_sd callback functions + */ static void browse_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -80,12 +116,6 @@ static void browse_local_callback(DNSServiceRef sdRef, const char *regtype, const char *replyDomain, void *context); -static int compare_devices(cups_device_t *a, cups_device_t *b); -static void exec_backend(char **argv); -static cups_device_t *get_device(cups_array_t *devices, - const char *serviceName, - const char *regtype, - const char *replyDomain); static void query_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, @@ -94,7 +124,31 @@ static void query_callback(DNSServiceRef sdRef, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context); +#endif + +static cups_device_t *find_device (cups_array_t *devices, + cups_device_t *key, + const char *priority, size_t priority_len, + const char *mfg, size_t mfg_len, + const char *mdl, size_t mdl_len, + const char *product, size_t product_len, + const char *ty, size_t ty_len, + const char *printer_type, + size_t printer_type_len); +static int compare_devices(cups_device_t *a, cups_device_t *b); +static void exec_backend(char **argv); +static cups_device_t *get_device(cups_array_t *devices, + const char *serviceName, + const char *regtype, + const char *replyDomain); static void unquote(char *dst, const char *src, size_t dstsize); +static int device_type(const char *regtype); + + +#ifdef HAVE_AVAHI +static AvahiSimplePoll *simple_poll = NULL; +static int avahi_got_callback; +#endif /* HAVE_AVAHI */ /* @@ -122,6 +176,10 @@ main(int argc, /* I - Number of command-line args */ struct timeval timeout; /* Timeout for select() */ cups_array_t *devices; /* Device array */ cups_device_t *device; /* Current device */ +#ifdef HAVE_AVAHI + AvahiClient *client; + int error; +#endif /* HAVE_AVAHI */ /* @@ -161,6 +219,48 @@ main(int argc, /* I - Number of command-line args */ * Browse for different kinds of printers... */ +#ifdef HAVE_AVAHI + if ((simple_poll = avahi_simple_poll_new ()) == NULL) + { + perror ("ERROR: Unable to create avahi simple poll object"); + return (1); + } + + client = avahi_client_new (avahi_simple_poll_get (simple_poll), + 0, avahi_client_callback, NULL, &error); + if (!client) + { + perror ("ERROR: Unable to create avahi client"); + return (1); + } + + fax_ipp_ref = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_fax-ipp._tcp", NULL, 0, + avahi_browse_callback, devices); + ipp_ref = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_ipp._tcp", NULL, 0, + avahi_browse_callback, devices); + ipp_tls_ref = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_ipp-tls._tcp", NULL, 0, + avahi_browse_callback, devices); + pdl_datastream_ref = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_pdl-datastream._tcp", + NULL, 0, + avahi_browse_callback, + devices); + printer_ref = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_printer._tcp", NULL, 0, + avahi_browse_callback, devices); + riousbprint_ref = avahi_service_browser_new (client, AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + "_riousbprint._tcp", NULL, 0, + avahi_browse_callback, devices); +#else if (DNSServiceCreateConnection(&main_ref) != kDNSServiceErr_NoError) { perror("ERROR: Unable to create service connection"); @@ -212,6 +312,7 @@ 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_AVAHI */ /* * Loop until we are killed... @@ -219,6 +320,18 @@ main(int argc, /* I - Number of command-line args */ for (;;) { + int announce = 0; + +#ifdef HAVE_AVAHI + int r; + avahi_got_callback = 0; + r = avahi_simple_poll_iterate (simple_poll, 1); + if (r != 0 && r != EINTR) + break; + + if (avahi_got_callback) + announce = 1; +#else FD_ZERO(&input); FD_SET(fd, &input); @@ -238,11 +351,19 @@ main(int argc, /* I - Number of command-line args */ } else { + announce = 1; + } +#endif /* !HAVE_AVAHI */ + + if (announce) + { /* * Announce any devices we've found... */ +#ifndef HAVE_AVAHI DNSServiceErrorType status; /* DNS query status */ +#endif /* !HAVE_AVAHI */ cups_device_t *best; /* Best matching device */ char device_uri[1024]; /* Device URI */ int count; /* Number of queries */ @@ -255,6 +376,7 @@ main(int argc, /* I - Number of command-line args */ best = NULL, count = 0; device; device = (cups_device_t *)cupsArrayNext(devices)) +#ifndef HAVE_AVAHI if (!device->ref && !device->sent) { /* @@ -283,23 +405,36 @@ main(int argc, /* I - Number of command-line args */ count ++; } } - else if (!device->sent) + else +#endif /* !HAVE_AVAHI */ + + if (!device->sent) { +#ifndef HAVE_AVAHI /* * Got the TXT records, now report the device... */ DNSServiceRefDeallocate(device->ref); device->ref = 0; +#endif /* !HAVE_AVAHI */ if (!best) best = device; else if (strcasecmp(best->name, device->name) || strcasecmp(best->domain, device->domain)) { +#ifdef HAVE_AVAHI + httpAssembleURIf(HTTP_URI_CODING_ALL, + device_uri, sizeof(device_uri), + schemes[best->type], NULL, best->address, 0, + "/%s%s", best->cups_shared ? "/printers/" : "", + best->name); +#else httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), schemes[best->type], NULL, best->fullName, 0, best->cups_shared ? "/cups" : "/"); +#endif /* HAVE_AVAHI */ cupsBackendReport("network", device_uri, best->make_and_model, best->name, NULL, NULL); @@ -319,9 +454,17 @@ main(int argc, /* I - Number of command-line args */ if (best) { - httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), - schemes[best->type], NULL, best->fullName, 0, - best->cups_shared ? "/cups" : "/"); +#ifdef HAVE_AVAHI + httpAssembleURIf(HTTP_URI_CODING_ALL, + device_uri, sizeof(device_uri), + schemes[best->type], NULL, best->address, 0, + "/%s%s", best->cups_shared ? "/printers/" : "", + best->name); +#else + httpAssembleURI(HTTP_URI_CODING_ALL, device_uri, sizeof(device_uri), + schemes[best->type], NULL, best->fullName, 0, + best->cups_shared ? "/cups" : "/"); +#endif /* HAVE_AVAHI */ cupsBackendReport("network", device_uri, best->make_and_model, best->name, NULL, NULL); @@ -332,6 +475,164 @@ main(int argc, /* I - Number of command-line args */ } +#ifdef HAVE_AVAHI +static void +avahi_client_callback( + AvahiClient *client, + AvahiClientState state, + void *context) +{ + fprintf (stderr, "DEBUG: client callback, %d\n", state); +} + +static void +avahi_query_callback( + AvahiServiceResolver *resolver, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, + const char *name, + const char *type, + const char *domain, + const char *host_name, + const AvahiAddress *address, + uint16_t port, + AvahiStringList *txt, + AvahiLookupResultFlags flags, + void *context) +{ + AvahiClient *client = avahi_service_resolver_get_client (resolver); + AvahiStringList *pair; + cups_device_t key, + *device; + char uqname[1024], + *ptr; + char *priority = NULL, + *mfg = NULL, + *mdl = NULL, + *product = NULL, + *ty = NULL, + *printer_type = NULL; + size_t priority_len = 0, + mfg_len = 0, + mdl_len = 0, + product_len = 0, + ty_len = 0, + printer_type_len = 0; + + if (event == AVAHI_RESOLVER_FAILURE) + { + fprintf (stderr, "ERROR: %s\n", + avahi_strerror (avahi_client_errno (client))); + avahi_service_resolver_free (resolver); + return; + } + + if (event != AVAHI_RESOLVER_FOUND) + { + avahi_service_resolver_free (resolver); + } + + key.name = uqname; + unquote (uqname, name, sizeof (uqname)); + if ((ptr = strstr(name, "._")) != NULL) + *ptr = '\0'; + + key.domain = domain; + key.type = device_type (type); + + if ((pair = avahi_string_list_find (txt, "priority")) != NULL) + avahi_string_list_get_pair (pair, NULL, &priority, &priority_len); + + if ((pair = avahi_string_list_find (txt, "usb_MFG")) == NULL) + pair = avahi_string_list_find (txt, "usb_MANUFACTURER"); + if (pair != NULL) + avahi_string_list_get_pair (pair, NULL, &mfg, &mfg_len); + + if ((pair = avahi_string_list_find (txt, "usb_MDL")) == NULL) + pair = avahi_string_list_find (txt, "usb_MODEL"); + if (pair != NULL) + avahi_string_list_get_pair (pair, NULL, &mdl, &mdl_len); + + if ((pair = avahi_string_list_find (txt, "product")) != NULL) + avahi_string_list_get_pair (pair, NULL, &product, &product_len); + + if ((pair = avahi_string_list_find (txt, "ty")) != NULL) + avahi_string_list_get_pair (pair, NULL, &ty, &ty_len); + + if ((pair = avahi_string_list_find (txt, "printer-type")) != NULL) + avahi_string_list_get_pair (pair, NULL, &printer_type, &printer_type_len); + + device = find_device ((cups_array_t *) context, + &key, + priority, priority_len, + mfg, mfg_len, + mdl, mdl_len, + product, product_len, + ty, ty_len, + printer_type, printer_type_len); + if (device) + { + device->address = malloc (AVAHI_ADDRESS_STR_MAX); + avahi_address_snprint (device->address, AVAHI_ADDRESS_STR_MAX, address); + avahi_got_callback = 1; + } + else + fprintf (stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", name); + + avahi_service_resolver_free (resolver); +} + +static void +avahi_browse_callback( + AvahiServiceBrowser *browser, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiBrowserEvent event, + const char *name, + const char *type, + const char *domain, + AvahiLookupResultFlags flags, + void *context) +{ + AvahiClient *client = avahi_service_browser_get_client (browser); + + switch (event) + { + case AVAHI_BROWSER_FAILURE: + fprintf (stderr, "ERROR: %s\n", + avahi_strerror (avahi_client_errno (client))); + avahi_simple_poll_quit (simple_poll); + return; + + case AVAHI_BROWSER_NEW: + if (flags & AVAHI_LOOKUP_RESULT_LOCAL) + { + fprintf (stderr, "DEBUG: ignoring local service %s\n", name); + } + else + { + get_device ((cups_array_t *)context, name, type, domain); + if (avahi_service_resolver_new (client, interface, protocol, + name, type, domain, + AVAHI_PROTO_UNSPEC, 0, + avahi_query_callback, context) == NULL) + { + fprintf (stderr, "ERROR: failed to resolve service %s: %s\n", + name, avahi_strerror (avahi_client_errno (client))); + } + } + + break; + + case AVAHI_BROWSER_REMOVE: + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } +} + +#else /* !HAVE_AVAHI */ /* * 'browse_callback()' - Browse devices. */ @@ -420,6 +721,7 @@ browse_local_callback( device->fullName); device->sent = 1; } +#endif /* !HAVE_AVAHI */ /* @@ -518,18 +820,7 @@ get_device(cups_array_t *devices, /* I - Device array */ 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.type = device_type (regtype); for (device = cupsArrayFind(devices, &key); device; @@ -559,13 +850,20 @@ get_device(cups_array_t *devices, /* I - Device array */ * Set the "full name" of this service, which is used for queries... */ +#ifdef HAVE_AVAHI + avahi_service_name_join (fullName, kDNSServiceMaxDomainName, + serviceName, regtype, replyDomain); + device->fullName = strdup(fullName); +#else DNSServiceConstructFullName(fullName, serviceName, regtype, replyDomain); device->fullName = strdup(fullName); +#endif /* !HAVE_AVAHI */ return (device); } +#ifndef HAVE_AVAHI /* * 'query_callback()' - Process query data. */ @@ -584,12 +882,21 @@ query_callback( uint32_t ttl, /* I - Time-to-live */ void *context) /* I - Devices array */ { - cups_array_t *devices; /* Device array */ char name[1024], /* Service name */ *ptr; /* Pointer into name */ - cups_device_t key, /* Search key */ - *device; /* Device */ - + cups_device_t key; /* Search key */ + const char *priority, + *mfg, + *mdl, + *product, + *ty, + *printer_type; + uint8_t priority_len, + mfg_len, + mdl_len, + product_len, + ty_len, + printer_type_len; fprintf(stderr, "DEBUG2: query_callback(sdRef=%p, flags=%x, " "interfaceIndex=%d, errorCode=%d, fullName=\"%s\", " @@ -610,7 +917,6 @@ query_callback( * Lookup the service in the devices array. */ - devices = (cups_array_t *)context; key.name = name; unquote(name, fullName, sizeof(name)); @@ -635,88 +941,111 @@ query_callback( else key.type = CUPS_DEVICE_RIOUSBPRINT; - for (device = cupsArrayFind(devices, &key); + priority = TXTRecordGetValuePtr(rdlen, rdata, "priority", &priority_len); + if ((mfg = TXTRecordGetValuePtr(rdlen, rdata, "usb_MFG", &mfg_len)) == NULL) + mfg = TXTRecordGetValuePtr(rdlen, rdata, "usb_MANUFACTURER", &mfg_len); + + if ((mdl = TXTRecordGetValuePtr(rdlen, rdata, "usb_MDL", &mdl_len)) == NULL) + mdl = TXTRecordGetValuePtr(rdlen, rdata, "usb_MODEL", &mdl_len); + + product = TXTRecordGetValuePtr(rdlen, rdata, "product", &product_len); + ty = TXTRecordGetValuePtr(rdlen, rdata, "ty", &ty_len); + printer_type = TXTRecordGetValuePtr(rdlen, rdata, "printer-type", + &printer_type_len); + + if (!find_device ((cups_array_t *) context, + &key, + priority, priority_len, + mfg, mfg_len, + mdl, mdl_len, + product, product_len, + ty, ty_len, + printer_type, printer_type_len)) + fprintf(stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", fullName); +} +#endif /* !HAVE_AVAHI */ + + +static cups_device_t * +find_device (cups_array_t *devices, + cups_device_t *key, + const char *priority, size_t priority_len, + const char *mfg, size_t mfg_len, + const char *mdl, size_t mdl_len, + const char *product, size_t product_len, + const char *ty, size_t ty_len, + const char *printer_type, size_t printer_type_len) +{ + cups_device_t *device; + + for (device = cupsArrayFind(devices, key); device; device = cupsArrayNext(devices)) { - if (strcasecmp(device->name, key.name) || - strcasecmp(device->domain, key.domain)) + if (strcasecmp(device->name, key->name) || + strcasecmp(device->domain, key->domain)) { device = NULL; break; } - else if (device->type == key.type) + else if (device->type == key->type) { /* * Found it, pull out the priority and make and model from the TXT * record and save it... */ - 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 */ - + priority_buf[256], /* Priority */ + *ptr; - value = TXTRecordGetValuePtr(rdlen, rdata, "priority", &valueLen); - if (value && valueLen) + if (priority && priority_len) { - memcpy(priority, value, valueLen); - priority[valueLen] = '\0'; - device->priority = atoi(priority); + memcpy(priority_buf, priority, priority_len); + priority_buf[priority_len] = '\0'; + device->priority = atoi(priority_buf); } - if ((value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MFG", - &valueLen)) == NULL) - value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MANUFACTURER", - &valueLen); - - if (value && valueLen) + if (mfg && mfg_len) { - memcpy(make_and_model, value, valueLen); - make_and_model[valueLen] = '\0'; + memcpy(make_and_model, mfg, mfg_len); + make_and_model[mfg_len] = '\0'; } else make_and_model[0] = '\0'; - if ((value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MDL", - &valueLen)) == NULL) - value = TXTRecordGetValuePtr(rdlen, rdata, "usb_MODEL", &valueLen); - - if (value && valueLen) + if (mdl && mdl_len) { - memcpy(model, value, valueLen); - model[valueLen] = '\0'; + memcpy(model, mdl, mdl_len); + model[mdl_len] = '\0'; } - else if ((value = TXTRecordGetValuePtr(rdlen, rdata, "product", - &valueLen)) != NULL && valueLen > 2) + else if (product && product_len > 2) { - if (((char *)value)[0] == '(') + if (product[0] == '(') { /* * Strip parenthesis... */ - memcpy(model, value + 1, valueLen - 2); - model[valueLen - 2] = '\0'; + memcpy(model, product + 1, product_len - 2); + model[product_len - 2] = '\0'; } else { - memcpy(model, value, valueLen); - model[valueLen] = '\0'; + memcpy(model, product, product_len); + model[product_len] = '\0'; } if (!strcasecmp(model, "GPL Ghostscript") || !strcasecmp(model, "GNU Ghostscript") || !strcasecmp(model, "ESP Ghostscript")) { - if ((value = TXTRecordGetValuePtr(rdlen, rdata, "ty", - &valueLen)) != NULL) + if (ty && ty_len) { - memcpy(model, value, valueLen); - model[valueLen] = '\0'; + memcpy(model, ty, ty_len); + model[ty_len] = '\0'; if ((ptr = strchr(model, ',')) != NULL) *ptr = '\0'; @@ -742,7 +1071,7 @@ query_callback( if ((device->type == CUPS_DEVICE_IPP || device->type == CUPS_DEVICE_PRINTER) && - TXTRecordGetValuePtr(rdlen, rdata, "printer-type", &valueLen)) + printer_type) { /* * This is a CUPS printer! @@ -758,8 +1087,7 @@ query_callback( } } - if (!device) - fprintf(stderr, "DEBUG: Ignoring TXT record for \"%s\"...\n", fullName); + return device; } @@ -797,6 +1125,35 @@ unquote(char *dst, /* I - Destination buffer */ } +static int +device_type (const char *regtype) +{ +#ifdef HAVE_AVAHI + if (!strcmp(regtype, "_ipp._tcp") || + !strcmp(regtype, "_ipp-tls._tcp")) + return (CUPS_DEVICE_IPP); + 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); +#else + if (!strcmp(regtype, "_ipp._tcp.") || + !strcmp(regtype, "_ipp-tls._tcp.")) + return (CUPS_DEVICE_IPP); + 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); +} + + /* * End of "$Id$". */ diff --git a/config-scripts/cups-dnssd.m4 b/config-scripts/cups-dnssd.m4 index 55829cf..7dc3e0b 100644 --- a/config-scripts/cups-dnssd.m4 +++ b/config-scripts/cups-dnssd.m4 @@ -46,6 +46,18 @@ if test x$enable_dnssd != xno; then ;; esac ]) + + AC_CHECK_HEADER(avahi-client/client.h, [ + case "$uname" in + Linux*) + # Linux, using Avahi. + DNSSDLIBS="-lavahi-common -lavahi-client -ldns_sd" + AC_DEFINE(HAVE_DNSSD) + AC_DEFINE(HAVE_AVAHI) + DNSSD_BACKEND="dnssd" + ;; + esac + ]) fi AC_SUBST(DNSSDLIBS) diff --git a/config.h.in b/config.h.in index e657b09..84df2a3 100644 --- a/config.h.in +++ b/config.h.in @@ -344,6 +344,13 @@ /* + * Does DNS Service Discovery use Avahi? + */ + +#undef HAVE_AVAHI + + +/* * Do we have ? */