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