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