1/* 2 * "$Id: snmp.c 11093 2013-07-03 20:48:42Z msweet $" 3 * 4 * SNMP discovery backend for CUPS. 5 * 6 * Copyright 2007-2012 by Apple Inc. 7 * Copyright 2006-2007 by Easy Software Products, all rights reserved. 8 * 9 * These coded instructions, statements, and computer programs are the 10 * property of Apple Inc. and are protected by Federal copyright 11 * law. Distribution and use rights are outlined in the file "LICENSE.txt" 12 * "LICENSE" which should have been included with this file. If this 13 * file is missing or damaged, see the license at "http://www.cups.org/". 14 * 15 * This file is subject to the Apple OS-Developed Software exception. 16 * 17 * Contents: 18 * 19 * main() - Discover printers via SNMP. 20 * add_array() - Add a string to an array. 21 * add_cache() - Add a cached device... 22 * add_device_uri() - Add a device URI to the cache. 23 * alarm_handler() - Handle alarm signals... 24 * compare_cache() - Compare two cache entries. 25 * debug_printf() - Display some debugging information. 26 * fix_make_model() - Fix common problems in the make-and-model 27 * string. 28 * free_array() - Free an array of strings. 29 * free_cache() - Free the array of cached devices. 30 * get_interface_addresses() - Get the broadcast address(es) associated with 31 * an interface. 32 * list_device() - List a device we found... 33 * password_cb() - Handle authentication requests. 34 * probe_device() - Probe a device to discover whether it is a 35 * printer. 36 * read_snmp_conf() - Read the snmp.conf file. 37 * read_snmp_response() - Read and parse a SNMP response... 38 * run_time() - Return the total running time... 39 * scan_devices() - Scan for devices using SNMP. 40 * try_connect() - Try connecting on a port... 41 * update_cache() - Update a cached device... 42 */ 43 44/* 45 * Include necessary headers. 46 */ 47 48#include "backend-private.h" 49#include <cups/array.h> 50#include <cups/file.h> 51#include <cups/http-private.h> 52#include <regex.h> 53 54 55/* 56 * This backend implements SNMP printer discovery. It uses a broadcast- 57 * based approach to get SNMP response packets from potential printers, 58 * requesting OIDs from the Host and Port Monitor MIBs, does a URI 59 * lookup based on the device description string, and finally a probe of 60 * port 9100 (AppSocket) and 515 (LPD). 61 * 62 * The current focus is on printers with internal network cards, although 63 * the code also works with many external print servers as well. 64 * 65 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory 66 * which can contain comments, blank lines, or any number of the following 67 * directives: 68 * 69 * Address ip-address 70 * Address @LOCAL 71 * Address @IF(name) 72 * Community name 73 * DebugLevel N 74 * DeviceURI "regex pattern" uri 75 * HostNameLookups on 76 * HostNameLookups off 77 * MaxRunTime N 78 * 79 * The default is to use: 80 * 81 * Address @LOCAL 82 * Community public 83 * DebugLevel 0 84 * HostNameLookups off 85 * MaxRunTime 120 86 * 87 * This backend is known to work with the following network printers and 88 * print servers: 89 * 90 * Axis OfficeBasic, 5400, 5600 91 * Brother 92 * EPSON 93 * Genicom 94 * HP JetDirect 95 * Lexmark 96 * Sharp 97 * Tektronix 98 * Xerox 99 * 100 * It does not currently work with: 101 * 102 * DLink 103 * Linksys 104 * Netgear 105 * Okidata 106 * 107 * (for all of these, they do not support the Host MIB) 108 */ 109 110/* 111 * Types... 112 */ 113 114enum /**** Request IDs for each field ****/ 115{ 116 DEVICE_TYPE = 1, 117 DEVICE_DESCRIPTION, 118 DEVICE_LOCATION, 119 DEVICE_ID, 120 DEVICE_URI, 121 DEVICE_PRODUCT 122}; 123 124typedef struct device_uri_s /**** DeviceURI values ****/ 125{ 126 regex_t re; /* Regular expression to match */ 127 cups_array_t *uris; /* URIs */ 128} device_uri_t; 129 130typedef struct snmp_cache_s /**** SNMP scan cache ****/ 131{ 132 http_addr_t address; /* Address of device */ 133 char *addrname, /* Name of device */ 134 *uri, /* device-uri */ 135 *id, /* device-id */ 136 *info, /* device-info */ 137 *location, /* device-location */ 138 *make_and_model; /* device-make-and-model */ 139 int sent; /* Has this device been listed? */ 140} snmp_cache_t; 141 142 143/* 144 * Local functions... 145 */ 146 147static char *add_array(cups_array_t *a, const char *s); 148static void add_cache(http_addr_t *addr, const char *addrname, 149 const char *uri, const char *id, 150 const char *make_and_model); 151static device_uri_t *add_device_uri(char *value); 152static void alarm_handler(int sig); 153static int compare_cache(snmp_cache_t *a, snmp_cache_t *b); 154static void debug_printf(const char *format, ...); 155static void fix_make_model(char *make_model, 156 const char *old_make_model, 157 int make_model_size); 158static void free_array(cups_array_t *a); 159static void free_cache(void); 160static http_addrlist_t *get_interface_addresses(const char *ifname); 161static void list_device(snmp_cache_t *cache); 162static const char *password_cb(const char *prompt); 163static void probe_device(snmp_cache_t *device); 164static void read_snmp_conf(const char *address); 165static void read_snmp_response(int fd); 166static double run_time(void); 167static void scan_devices(int ipv4, int ipv6); 168static int try_connect(http_addr_t *addr, const char *addrname, 169 int port); 170static void update_cache(snmp_cache_t *device, const char *uri, 171 const char *id, const char *make_model); 172 173 174/* 175 * Local globals... 176 */ 177 178static cups_array_t *Addresses = NULL; 179static cups_array_t *Communities = NULL; 180static cups_array_t *Devices = NULL; 181static int DebugLevel = 0; 182static const int DescriptionOID[] = { CUPS_OID_hrDeviceDescr, 1, -1 }; 183static const int LocationOID[] = { CUPS_OID_sysLocation, 0, -1 }; 184static const int DeviceTypeOID[] = { CUPS_OID_hrDeviceType, 1, -1 }; 185static const int DeviceIdOID[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId, 1, -1 }; 186static const int UriOID[] = { CUPS_OID_ppmPortServiceNameOrURI, 1, 1, -1 }; 187static const int LexmarkProductOID[] = { 1,3,6,1,4,1,641,2,1,2,1,2,1,-1 }; 188static const int LexmarkProductOID2[] = { 1,3,6,1,4,1,674,10898,100,2,1,2,1,2,1,-1 }; 189static const int LexmarkDeviceIdOID[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 }; 190static const int XeroxProductOID[] = { 1,3,6,1,4,1,128,2,1,3,1,2,0,-1 }; 191static cups_array_t *DeviceURIs = NULL; 192static int HostNameLookups = 0; 193static int MaxRunTime = 120; 194static struct timeval StartTime; 195 196 197/* 198 * 'main()' - Discover printers via SNMP. 199 */ 200 201int /* O - Exit status */ 202main(int argc, /* I - Number of command-line arguments (6 or 7) */ 203 char *argv[]) /* I - Command-line arguments */ 204{ 205 int ipv4, /* SNMP IPv4 socket */ 206 ipv6; /* SNMP IPv6 socket */ 207#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET) 208 struct sigaction action; /* Actions for POSIX signals */ 209#endif /* HAVE_SIGACTION && !HAVE_SIGSET */ 210 211 212 /* 213 * Check command-line options... 214 */ 215 216 if (argc > 2) 217 { 218 _cupsLangPuts(stderr, _("Usage: snmp [host-or-ip-address]")); 219 return (1); 220 } 221 222 /* 223 * Set the password callback for IPP operations... 224 */ 225 226 cupsSetPasswordCB(password_cb); 227 228 /* 229 * Catch SIGALRM signals... 230 */ 231 232#ifdef HAVE_SIGSET 233 sigset(SIGALRM, alarm_handler); 234#elif defined(HAVE_SIGACTION) 235 memset(&action, 0, sizeof(action)); 236 237 sigemptyset(&action.sa_mask); 238 sigaddset(&action.sa_mask, SIGALRM); 239 action.sa_handler = alarm_handler; 240 sigaction(SIGALRM, &action, NULL); 241#else 242 signal(SIGALRM, alarm_handler); 243#endif /* HAVE_SIGSET */ 244 245 /* 246 * Open the SNMP socket... 247 */ 248 249 if ((ipv4 = _cupsSNMPOpen(AF_INET)) < 0) 250 return (1); 251 252#ifdef AF_INET6 253 if ((ipv6 = _cupsSNMPOpen(AF_INET6)) < 0) 254 perror("DEBUG: Unable to create IPv6 socket"); 255#else 256 ipv6 = -1; 257#endif /* AF_INET6 */ 258 259 /* 260 * Read the configuration file and any cache data... 261 */ 262 263 read_snmp_conf(argv[1]); 264 265 _cupsSNMPSetDebug(DebugLevel); 266 267 Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL); 268 269 /* 270 * Scan for devices... 271 */ 272 273 scan_devices(ipv4, ipv6); 274 275 /* 276 * Close, free, and return with no errors... 277 */ 278 279 _cupsSNMPClose(ipv4); 280 if (ipv6 >= 0) 281 _cupsSNMPClose(ipv6); 282 283 free_array(Addresses); 284 free_array(Communities); 285 free_cache(); 286 287 return (0); 288} 289 290 291/* 292 * 'add_array()' - Add a string to an array. 293 */ 294 295static char * /* O - New string */ 296add_array(cups_array_t *a, /* I - Array */ 297 const char *s) /* I - String to add */ 298{ 299 char *dups; /* New string */ 300 301 302 dups = strdup(s); 303 304 cupsArrayAdd(a, dups); 305 306 return (dups); 307} 308 309 310/* 311 * 'add_cache()' - Add a cached device... 312 */ 313 314static void 315add_cache(http_addr_t *addr, /* I - Device IP address */ 316 const char *addrname, /* I - IP address or name string */ 317 const char *uri, /* I - Device URI */ 318 const char *id, /* I - 1284 device ID */ 319 const char *make_and_model) /* I - Make and model */ 320{ 321 snmp_cache_t *temp; /* New device entry */ 322 323 324 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", " 325 "id=\"%s\", make_and_model=\"%s\")\n", 326 addr, addrname, uri ? uri : "(null)", id ? id : "(null)", 327 make_and_model ? make_and_model : "(null)"); 328 329 temp = calloc(1, sizeof(snmp_cache_t)); 330 memcpy(&(temp->address), addr, sizeof(temp->address)); 331 332 temp->addrname = strdup(addrname); 333 334 if (uri) 335 temp->uri = strdup(uri); 336 337 if (id) 338 temp->id = strdup(id); 339 340 if (make_and_model) 341 temp->make_and_model = strdup(make_and_model); 342 343 cupsArrayAdd(Devices, temp); 344 345 if (uri) 346 list_device(temp); 347} 348 349 350/* 351 * 'add_device_uri()' - Add a device URI to the cache. 352 * 353 * The value string is modified (chopped up) as needed. 354 */ 355 356static device_uri_t * /* O - Device URI */ 357add_device_uri(char *value) /* I - Value from snmp.conf */ 358{ 359 device_uri_t *device_uri; /* Device URI */ 360 char *start; /* Start of value */ 361 362 363 /* 364 * Allocate memory as needed... 365 */ 366 367 if (!DeviceURIs) 368 DeviceURIs = cupsArrayNew(NULL, NULL); 369 370 if (!DeviceURIs) 371 return (NULL); 372 373 if ((device_uri = calloc(1, sizeof(device_uri_t))) == NULL) 374 return (NULL); 375 376 if ((device_uri->uris = cupsArrayNew(NULL, NULL)) == NULL) 377 { 378 free(device_uri); 379 return (NULL); 380 } 381 382 /* 383 * Scan the value string for the regular expression and URI(s)... 384 */ 385 386 value ++; /* Skip leading " */ 387 388 for (start = value; *value && *value != '\"'; value ++) 389 if (*value == '\\' && value[1]) 390 _cups_strcpy(value, value + 1); 391 392 if (!*value) 393 { 394 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr); 395 396 cupsArrayDelete(device_uri->uris); 397 free(device_uri); 398 399 return (NULL); 400 } 401 402 *value++ = '\0'; 403 404 if (regcomp(&(device_uri->re), start, REG_EXTENDED | REG_ICASE)) 405 { 406 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr); 407 408 cupsArrayDelete(device_uri->uris); 409 free(device_uri); 410 411 return (NULL); 412 } 413 414 while (*value) 415 { 416 while (isspace(*value & 255)) 417 value ++; 418 419 if (!*value) 420 break; 421 422 for (start = value; *value && !isspace(*value & 255); value ++); 423 424 if (*value) 425 *value++ = '\0'; 426 427 cupsArrayAdd(device_uri->uris, strdup(start)); 428 } 429 430 /* 431 * Add the device URI to the list and return it... 432 */ 433 434 cupsArrayAdd(DeviceURIs, device_uri); 435 436 return (device_uri); 437} 438 439 440/* 441 * 'alarm_handler()' - Handle alarm signals... 442 */ 443 444static void 445alarm_handler(int sig) /* I - Signal number */ 446{ 447 /* 448 * Do nothing... 449 */ 450 451 (void)sig; 452 453#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION) 454 signal(SIGALRM, alarm_handler); 455#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */ 456 457 if (DebugLevel) 458 write(2, "DEBUG: ALARM!\n", 14); 459} 460 461 462/* 463 * 'compare_cache()' - Compare two cache entries. 464 */ 465 466static int /* O - Result of comparison */ 467compare_cache(snmp_cache_t *a, /* I - First cache entry */ 468 snmp_cache_t *b) /* I - Second cache entry */ 469{ 470 return (_cups_strcasecmp(a->addrname, b->addrname)); 471} 472 473 474/* 475 * 'debug_printf()' - Display some debugging information. 476 */ 477 478static void 479debug_printf(const char *format, /* I - Printf-style format string */ 480 ...) /* I - Additional arguments as needed */ 481{ 482 va_list ap; /* Pointer to arguments */ 483 484 485 if (!DebugLevel) 486 return; 487 488 va_start(ap, format); 489 vfprintf(stderr, format, ap); 490 va_end(ap); 491} 492 493 494/* 495 * 'fix_make_model()' - Fix common problems in the make-and-model string. 496 */ 497 498static void 499fix_make_model( 500 char *make_model, /* I - New make-and-model string */ 501 const char *old_make_model, /* I - Old make-and-model string */ 502 int make_model_size) /* I - Size of new string buffer */ 503{ 504 char *mmptr; /* Pointer into make-and-model string */ 505 506 507 /* 508 * Fix some common problems with the make-and-model string so 509 * that printer driver detection works better... 510 */ 511 512 if (!_cups_strncasecmp(old_make_model, "Hewlett-Packard", 15)) 513 { 514 /* 515 * Strip leading Hewlett-Packard and hp prefixes and replace 516 * with a single HP manufacturer prefix... 517 */ 518 519 mmptr = (char *)old_make_model + 15; 520 521 while (isspace(*mmptr & 255)) 522 mmptr ++; 523 524 if (!_cups_strncasecmp(mmptr, "hp", 2)) 525 { 526 mmptr += 2; 527 528 while (isspace(*mmptr & 255)) 529 mmptr ++; 530 } 531 532 make_model[0] = 'H'; 533 make_model[1] = 'P'; 534 make_model[2] = ' '; 535 strlcpy(make_model + 3, mmptr, make_model_size - 3); 536 } 537 else if (!_cups_strncasecmp(old_make_model, "deskjet", 7)) 538 snprintf(make_model, make_model_size, "HP DeskJet%s", old_make_model + 7); 539 else if (!_cups_strncasecmp(old_make_model, "officejet", 9)) 540 snprintf(make_model, make_model_size, "HP OfficeJet%s", old_make_model + 9); 541 else if (!_cups_strncasecmp(old_make_model, "stylus_pro_", 11)) 542 snprintf(make_model, make_model_size, "EPSON Stylus Pro %s", 543 old_make_model + 11); 544 else 545 strlcpy(make_model, old_make_model, make_model_size); 546 547 if ((mmptr = strstr(make_model, ", Inc.,")) != NULL) 548 { 549 /* 550 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560" 551 * becomes "Tektronix Phaser 560"... 552 */ 553 554 _cups_strcpy(mmptr, mmptr + 7); 555 } 556 557 if ((mmptr = strstr(make_model, " Network")) != NULL) 558 { 559 /* 560 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025 561 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"... 562 */ 563 564 *mmptr = '\0'; 565 } 566 567 if ((mmptr = strchr(make_model, ',')) != NULL) 568 { 569 /* 570 * Drop anything after a trailing comma... 571 */ 572 573 *mmptr = '\0'; 574 } 575} 576 577 578/* 579 * 'free_array()' - Free an array of strings. 580 */ 581 582static void 583free_array(cups_array_t *a) /* I - Array */ 584{ 585 char *s; /* Current string */ 586 587 588 for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a)) 589 free(s); 590 591 cupsArrayDelete(a); 592} 593 594 595/* 596 * 'free_cache()' - Free the array of cached devices. 597 */ 598 599static void 600free_cache(void) 601{ 602 snmp_cache_t *cache; /* Cached device */ 603 604 605 for (cache = (snmp_cache_t *)cupsArrayFirst(Devices); 606 cache; 607 cache = (snmp_cache_t *)cupsArrayNext(Devices)) 608 { 609 free(cache->addrname); 610 611 if (cache->uri) 612 free(cache->uri); 613 614 if (cache->id) 615 free(cache->id); 616 617 if (cache->make_and_model) 618 free(cache->make_and_model); 619 620 free(cache); 621 } 622 623 cupsArrayDelete(Devices); 624 Devices = NULL; 625} 626 627 628/* 629 * 'get_interface_addresses()' - Get the broadcast address(es) associated 630 * with an interface. 631 */ 632 633static http_addrlist_t * /* O - List of addresses */ 634get_interface_addresses( 635 const char *ifname) /* I - Interface name */ 636{ 637 struct ifaddrs *addrs, /* Interface address list */ 638 *addr; /* Current interface address */ 639 http_addrlist_t *first, /* First address in list */ 640 *last, /* Last address in list */ 641 *current; /* Current address */ 642 643 644 if (getifaddrs(&addrs) < 0) 645 return (NULL); 646 647 for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next) 648 if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr && 649 addr->ifa_broadaddr->sa_family == AF_INET && 650 (!ifname || !strcmp(ifname, addr->ifa_name))) 651 { 652 current = calloc(1, sizeof(http_addrlist_t)); 653 654 memcpy(&(current->addr), addr->ifa_broadaddr, 655 sizeof(struct sockaddr_in)); 656 657 if (!last) 658 first = current; 659 else 660 last->next = current; 661 662 last = current; 663 } 664 665 freeifaddrs(addrs); 666 667 return (first); 668} 669 670 671/* 672 * 'list_device()' - List a device we found... 673 */ 674 675static void 676list_device(snmp_cache_t *cache) /* I - Cached device */ 677{ 678 if (cache->uri) 679 cupsBackendReport("network", cache->uri, cache->make_and_model, 680 cache->info, cache->id, cache->location); 681} 682 683 684/* 685 * 'password_cb()' - Handle authentication requests. 686 * 687 * All we do right now is return NULL, indicating that no authentication 688 * is possible. 689 */ 690 691static const char * /* O - Password (NULL) */ 692password_cb(const char *prompt) /* I - Prompt message */ 693{ 694 (void)prompt; /* Anti-compiler-warning-code */ 695 696 return (NULL); 697} 698 699 700/* 701 * 'probe_device()' - Probe a device to discover whether it is a printer. 702 * 703 * TODO: Try using the Port Monitor MIB to discover the correct protocol 704 * to use - first need a commercially-available printer that supports 705 * it, though... 706 */ 707 708static void 709probe_device(snmp_cache_t *device) /* I - Device */ 710{ 711 char uri[1024], /* Full device URI */ 712 *uriptr, /* Pointer into URI */ 713 *format; /* Format string for device */ 714 device_uri_t *device_uri; /* Current DeviceURI match */ 715 716 717 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname); 718 719#ifdef __APPLE__ 720 /* 721 * If the printer supports Bonjour/mDNS, don't report it from the SNMP backend. 722 */ 723 724 if (!try_connect(&(device->address), device->addrname, 5353)) 725 { 726 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname); 727 return; 728 } 729#endif /* __APPLE__ */ 730 731 /* 732 * Lookup the device in the match table... 733 */ 734 735 for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs); 736 device_uri; 737 device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs)) 738 if (device->make_and_model && 739 !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0)) 740 { 741 /* 742 * Found a match, add the URIs... 743 */ 744 745 for (format = (char *)cupsArrayFirst(device_uri->uris); 746 format; 747 format = (char *)cupsArrayNext(device_uri->uris)) 748 { 749 for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);) 750 if (*format == '%' && format[1] == 's') 751 { 752 /* 753 * Insert hostname/address... 754 */ 755 756 strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri)); 757 uriptr += strlen(uriptr); 758 format += 2; 759 } 760 else 761 *uriptr++ = *format++; 762 763 *uriptr = '\0'; 764 765 update_cache(device, uri, NULL, NULL); 766 } 767 768 return; 769 } 770 771 /* 772 * Then try the standard ports... 773 */ 774 775 if (!try_connect(&(device->address), device->addrname, 9100)) 776 { 777 debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname); 778 779 snprintf(uri, sizeof(uri), "socket://%s", device->addrname); 780 update_cache(device, uri, NULL, NULL); 781 } 782 else if (!try_connect(&(device->address), device->addrname, 515)) 783 { 784 debug_printf("DEBUG: %s supports LPD!\n", device->addrname); 785 786 snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname); 787 update_cache(device, uri, NULL, NULL); 788 } 789} 790 791 792/* 793 * 'read_snmp_conf()' - Read the snmp.conf file. 794 */ 795 796static void 797read_snmp_conf(const char *address) /* I - Single address to probe */ 798{ 799 cups_file_t *fp; /* File pointer */ 800 char filename[1024], /* Filename */ 801 line[1024], /* Line from file */ 802 *value; /* Value on line */ 803 int linenum; /* Line number */ 804 const char *cups_serverroot; /* CUPS_SERVERROOT env var */ 805 const char *debug; /* CUPS_DEBUG_LEVEL env var */ 806 const char *runtime; /* CUPS_MAX_RUN_TIME env var */ 807 808 809 /* 810 * Initialize the global address and community lists... 811 */ 812 813 Addresses = cupsArrayNew(NULL, NULL); 814 Communities = cupsArrayNew(NULL, NULL); 815 816 if (address) 817 add_array(Addresses, address); 818 819 if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL) 820 DebugLevel = atoi(debug); 821 822 if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL) 823 MaxRunTime = atoi(runtime); 824 825 /* 826 * Find the snmp.conf file... 827 */ 828 829 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL) 830 cups_serverroot = CUPS_SERVERROOT; 831 832 snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot); 833 834 if ((fp = cupsFileOpen(filename, "r")) != NULL) 835 { 836 /* 837 * Read the snmp.conf file... 838 */ 839 840 linenum = 0; 841 842 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum)) 843 { 844 if (!value) 845 fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum, 846 filename); 847 else if (!_cups_strcasecmp(line, "Address")) 848 { 849 if (!address) 850 add_array(Addresses, value); 851 } 852 else if (!_cups_strcasecmp(line, "Community")) 853 add_array(Communities, value); 854 else if (!_cups_strcasecmp(line, "DebugLevel")) 855 DebugLevel = atoi(value); 856 else if (!_cups_strcasecmp(line, "DeviceURI")) 857 { 858 if (*value != '\"') 859 fprintf(stderr, 860 "ERROR: Missing double quote for regular expression on " 861 "line %d of %s!\n", linenum, filename); 862 else 863 add_device_uri(value); 864 } 865 else if (!_cups_strcasecmp(line, "HostNameLookups")) 866 HostNameLookups = !_cups_strcasecmp(value, "on") || 867 !_cups_strcasecmp(value, "yes") || 868 !_cups_strcasecmp(value, "true") || 869 !_cups_strcasecmp(value, "double"); 870 else if (!_cups_strcasecmp(line, "MaxRunTime")) 871 MaxRunTime = atoi(value); 872 else 873 fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n", 874 line, linenum, filename); 875 } 876 877 cupsFileClose(fp); 878 } 879 880 /* 881 * Use defaults if parameters are undefined... 882 */ 883 884 if (cupsArrayCount(Addresses) == 0) 885 { 886 /* 887 * If we have no addresses, exit immediately... 888 */ 889 890 fprintf(stderr, 891 "DEBUG: No address specified and no Address line in %s...\n", 892 filename); 893 exit(0); 894 } 895 896 if (cupsArrayCount(Communities) == 0) 897 { 898 fputs("INFO: Using default SNMP Community public\n", stderr); 899 add_array(Communities, "public"); 900 } 901} 902 903 904/* 905 * 'read_snmp_response()' - Read and parse a SNMP response... 906 */ 907 908static void 909read_snmp_response(int fd) /* I - SNMP socket file descriptor */ 910{ 911 char addrname[256]; /* Source address name */ 912 cups_snmp_t packet; /* Decoded packet */ 913 snmp_cache_t key, /* Search key */ 914 *device; /* Matching device */ 915 916 917 /* 918 * Read the response data... 919 */ 920 921 if (!_cupsSNMPRead(fd, &packet, -1.0)) 922 { 923 fprintf(stderr, "ERROR: Unable to read data from socket: %s\n", 924 strerror(errno)); 925 return; 926 } 927 928 if (HostNameLookups) 929 httpAddrLookup(&(packet.address), addrname, sizeof(addrname)); 930 else 931 httpAddrString(&(packet.address), addrname, sizeof(addrname)); 932 933 debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname); 934 935 /* 936 * Look for the response status code in the SNMP message header... 937 */ 938 939 if (packet.error) 940 { 941 fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname, 942 packet.error); 943 944 return; 945 } 946 947 debug_printf("DEBUG: community=\"%s\"\n", packet.community); 948 debug_printf("DEBUG: request-id=%d\n", packet.request_id); 949 debug_printf("DEBUG: error-status=%d\n", packet.error_status); 950 951 if (packet.error_status && packet.request_id != DEVICE_TYPE) 952 return; 953 954 /* 955 * Find a matching device in the cache... 956 */ 957 958 key.addrname = addrname; 959 device = (snmp_cache_t *)cupsArrayFind(Devices, &key); 960 961 /* 962 * Process the message... 963 */ 964 965 switch (packet.request_id) 966 { 967 case DEVICE_TYPE : 968 /* 969 * Got the device type response... 970 */ 971 972 if (device) 973 { 974 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n", 975 addrname); 976 return; 977 } 978 979 /* 980 * Add the device and request the device data... 981 */ 982 983 add_cache(&(packet.address), addrname, NULL, NULL, NULL); 984 985 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 986 packet.community, CUPS_ASN1_GET_REQUEST, 987 DEVICE_DESCRIPTION, DescriptionOID); 988 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 989 packet.community, CUPS_ASN1_GET_REQUEST, 990 DEVICE_ID, DeviceIdOID); 991 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 992 packet.community, CUPS_ASN1_GET_REQUEST, 993 DEVICE_URI, UriOID); 994 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 995 packet.community, CUPS_ASN1_GET_REQUEST, 996 DEVICE_LOCATION, LocationOID); 997 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 998 packet.community, CUPS_ASN1_GET_REQUEST, 999 DEVICE_PRODUCT, LexmarkProductOID); 1000 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 1001 packet.community, CUPS_ASN1_GET_REQUEST, 1002 DEVICE_PRODUCT, LexmarkProductOID2); 1003 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 1004 packet.community, CUPS_ASN1_GET_REQUEST, 1005 DEVICE_ID, LexmarkDeviceIdOID); 1006 _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, 1007 packet.community, CUPS_ASN1_GET_REQUEST, 1008 DEVICE_PRODUCT, XeroxProductOID); 1009 break; 1010 1011 case DEVICE_DESCRIPTION : 1012 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING) 1013 { 1014 /* 1015 * Update an existing cache entry... 1016 */ 1017 1018 char make_model[256]; /* Make and model */ 1019 1020 1021 if (strchr((char *)packet.object_value.string.bytes, ':') && 1022 strchr((char *)packet.object_value.string.bytes, ';')) 1023 { 1024 /* 1025 * Description is the IEEE-1284 device ID... 1026 */ 1027 1028 char *ptr; /* Pointer into device ID */ 1029 1030 for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++) 1031 if (*ptr == '\n') 1032 *ptr = ';'; /* A lot of bad printers put a newline */ 1033 if (!device->id) 1034 device->id = strdup((char *)packet.object_value.string.bytes); 1035 1036 backendGetMakeModel((char *)packet.object_value.string.bytes, 1037 make_model, sizeof(make_model)); 1038 1039 if (device->info) 1040 free(device->info); 1041 1042 device->info = strdup(make_model); 1043 } 1044 else 1045 { 1046 /* 1047 * Description is plain text... 1048 */ 1049 1050 fix_make_model(make_model, (char *)packet.object_value.string.bytes, 1051 sizeof(make_model)); 1052 1053 if (device->info) 1054 free(device->info); 1055 1056 device->info = strdup((char *)packet.object_value.string.bytes); 1057 } 1058 1059 if (!device->make_and_model) 1060 device->make_and_model = strdup(make_model); 1061 } 1062 break; 1063 1064 case DEVICE_ID : 1065 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING && 1066 (!device->id || 1067 strlen(device->id) < packet.object_value.string.num_bytes)) 1068 { 1069 /* 1070 * Update an existing cache entry... 1071 */ 1072 1073 char make_model[256]; /* Make and model */ 1074 char *ptr; /* Pointer into device ID */ 1075 1076 for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++) 1077 if (*ptr == '\n') 1078 *ptr = ';'; /* A lot of bad printers put a newline */ 1079 if (device->id) 1080 free(device->id); 1081 1082 device->id = strdup((char *)packet.object_value.string.bytes); 1083 1084 /* 1085 * Convert the ID to a make and model string... 1086 */ 1087 1088 backendGetMakeModel((char *)packet.object_value.string.bytes, 1089 make_model, sizeof(make_model)); 1090 if (device->make_and_model) 1091 free(device->make_and_model); 1092 1093 device->make_and_model = strdup(make_model); 1094 } 1095 break; 1096 1097 case DEVICE_LOCATION : 1098 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING && 1099 !device->location) 1100 device->location = strdup((char *)packet.object_value.string.bytes); 1101 break; 1102 1103 case DEVICE_PRODUCT : 1104 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING && 1105 !device->id) 1106 { 1107 /* 1108 * Update an existing cache entry... 1109 */ 1110 1111 if (!device->info) 1112 device->info = strdup((char *)packet.object_value.string.bytes); 1113 1114 if (device->make_and_model) 1115 free(device->make_and_model); 1116 1117 device->make_and_model = strdup((char *)packet.object_value.string.bytes); 1118 } 1119 break; 1120 1121 case DEVICE_URI : 1122 if (device && packet.object_type == CUPS_ASN1_OCTET_STRING && 1123 !device->uri && packet.object_value.string.num_bytes > 3) 1124 { 1125 /* 1126 * Update an existing cache entry... 1127 */ 1128 1129 char scheme[32], /* URI scheme */ 1130 userpass[256], /* Username:password in URI */ 1131 hostname[256], /* Hostname in URI */ 1132 resource[1024]; /* Resource path in URI */ 1133 int port; /* Port number in URI */ 1134 1135 if (!strncmp((char *)packet.object_value.string.bytes, "lpr:", 4)) 1136 { 1137 /* 1138 * We want "lpd://..." for the URI... 1139 */ 1140 1141 packet.object_value.string.bytes[2] = 'd'; 1142 } 1143 1144 if (httpSeparateURI(HTTP_URI_CODING_ALL, 1145 (char *)packet.object_value.string.bytes, 1146 scheme, sizeof(scheme), 1147 userpass, sizeof(userpass), 1148 hostname, sizeof(hostname), &port, 1149 resource, sizeof(resource)) >= HTTP_URI_OK) 1150 device->uri = strdup((char *)packet.object_value.string.bytes); 1151 } 1152 break; 1153 } 1154} 1155 1156 1157/* 1158 * 'run_time()' - Return the total running time... 1159 */ 1160 1161static double /* O - Number of seconds */ 1162run_time(void) 1163{ 1164 struct timeval curtime; /* Current time */ 1165 1166 1167 gettimeofday(&curtime, NULL); 1168 1169 return (curtime.tv_sec - StartTime.tv_sec + 1170 0.000001 * (curtime.tv_usec - StartTime.tv_usec)); 1171} 1172 1173 1174/* 1175 * 'scan_devices()' - Scan for devices using SNMP. 1176 */ 1177 1178static void 1179scan_devices(int ipv4, /* I - SNMP IPv4 socket */ 1180 int ipv6) /* I - SNMP IPv6 socket */ 1181{ 1182 int fd, /* File descriptor for this address */ 1183 busy; /* Are we busy processing something? */ 1184 char *address, /* Current address */ 1185 *community; /* Current community */ 1186 fd_set input; /* Input set for select() */ 1187 struct timeval timeout; /* Timeout for select() */ 1188 time_t endtime; /* End time for scan */ 1189 http_addrlist_t *addrs, /* List of addresses */ 1190 *addr; /* Current address */ 1191 snmp_cache_t *device; /* Current device */ 1192 char temp[1024]; /* Temporary address string */ 1193 1194 1195 gettimeofday(&StartTime, NULL); 1196 1197 /* 1198 * First send all of the broadcast queries... 1199 */ 1200 1201 for (address = (char *)cupsArrayFirst(Addresses); 1202 address; 1203 address = (char *)cupsArrayNext(Addresses)) 1204 { 1205 if (!strcmp(address, "@LOCAL")) 1206 addrs = get_interface_addresses(NULL); 1207 else if (!strncmp(address, "@IF(", 4)) 1208 { 1209 char ifname[255]; /* Interface name */ 1210 1211 strlcpy(ifname, address + 4, sizeof(ifname)); 1212 if (ifname[0]) 1213 ifname[strlen(ifname) - 1] = '\0'; 1214 1215 addrs = get_interface_addresses(ifname); 1216 } 1217 else 1218 addrs = httpAddrGetList(address, AF_UNSPEC, NULL); 1219 1220 if (!addrs) 1221 { 1222 fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address); 1223 continue; 1224 } 1225 1226 for (community = (char *)cupsArrayFirst(Communities); 1227 community; 1228 community = (char *)cupsArrayNext(Communities)) 1229 { 1230 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n", 1231 community, address); 1232 1233 for (addr = addrs; addr; addr = addr->next) 1234 { 1235#ifdef AF_INET6 1236 if (_httpAddrFamily(&(addr->addr)) == AF_INET6) 1237 fd = ipv6; 1238 else 1239#endif /* AF_INET6 */ 1240 fd = ipv4; 1241 1242 debug_printf("DEBUG: Sending get request to %s...\n", 1243 httpAddrString(&(addr->addr), temp, sizeof(temp))); 1244 1245 _cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community, 1246 CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID); 1247 } 1248 } 1249 1250 httpAddrFreeList(addrs); 1251 } 1252 1253 /* 1254 * Then read any responses that come in over the next 3 seconds... 1255 */ 1256 1257 endtime = time(NULL) + MaxRunTime; 1258 1259 FD_ZERO(&input); 1260 1261 while (time(NULL) < endtime) 1262 { 1263 timeout.tv_sec = 2; 1264 timeout.tv_usec = 0; 1265 1266 FD_SET(ipv4, &input); 1267 if (ipv6 >= 0) 1268 FD_SET(ipv6, &input); 1269 1270 fd = ipv4 > ipv6 ? ipv4 : ipv6; 1271 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0) 1272 { 1273 fprintf(stderr, "ERROR: %.3f select() for %d/%d failed: %s\n", run_time(), 1274 ipv4, ipv6, strerror(errno)); 1275 break; 1276 } 1277 1278 busy = 0; 1279 1280 if (FD_ISSET(ipv4, &input)) 1281 { 1282 read_snmp_response(ipv4); 1283 busy = 1; 1284 } 1285 1286 if (ipv6 >= 0 && FD_ISSET(ipv6, &input)) 1287 { 1288 read_snmp_response(ipv6); 1289 busy = 1; 1290 } 1291 1292 if (!busy) 1293 { 1294 /* 1295 * List devices with complete information... 1296 */ 1297 1298 int sent_something = 0; 1299 1300 for (device = (snmp_cache_t *)cupsArrayFirst(Devices); 1301 device; 1302 device = (snmp_cache_t *)cupsArrayNext(Devices)) 1303 if (!device->sent && device->info && device->make_and_model) 1304 { 1305 if (device->uri) 1306 list_device(device); 1307 else 1308 probe_device(device); 1309 1310 device->sent = sent_something = 1; 1311 } 1312 1313 if (!sent_something) 1314 break; 1315 } 1316 } 1317 1318 debug_printf("DEBUG: %.3f Scan complete!\n", run_time()); 1319} 1320 1321 1322/* 1323 * 'try_connect()' - Try connecting on a port... 1324 */ 1325 1326static int /* O - 0 on success or -1 on error */ 1327try_connect(http_addr_t *addr, /* I - Socket address */ 1328 const char *addrname, /* I - Hostname or IP address */ 1329 int port) /* I - Port number */ 1330{ 1331 int fd; /* Socket */ 1332 int status; /* Connection status */ 1333 1334 1335 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(), 1336 port == 515 ? "lpd" : "socket", addrname, port); 1337 1338 if ((fd = socket(_httpAddrFamily(addr), SOCK_STREAM, 0)) < 0) 1339 { 1340 fprintf(stderr, "ERROR: Unable to create socket: %s\n", 1341 strerror(errno)); 1342 return (-1); 1343 } 1344 1345 _httpAddrSetPort(addr, port); 1346 1347 alarm(1); 1348 1349 status = connect(fd, (void *)addr, httpAddrLength(addr)); 1350 1351 close(fd); 1352 alarm(0); 1353 1354 return (status); 1355} 1356 1357 1358/* 1359 * 'update_cache()' - Update a cached device... 1360 */ 1361 1362static void 1363update_cache(snmp_cache_t *device, /* I - Device */ 1364 const char *uri, /* I - Device URI */ 1365 const char *id, /* I - Device ID */ 1366 const char *make_model) /* I - Device make and model */ 1367{ 1368 if (device->uri) 1369 free(device->uri); 1370 1371 device->uri = strdup(uri); 1372 1373 if (id) 1374 { 1375 if (device->id) 1376 free(device->id); 1377 1378 device->id = strdup(id); 1379 } 1380 1381 if (make_model) 1382 { 1383 if (device->make_and_model) 1384 free(device->make_and_model); 1385 1386 device->make_and_model = strdup(make_model); 1387 } 1388 1389 list_device(device); 1390} 1391 1392 1393/* 1394 * End of "$Id: snmp.c 11093 2013-07-03 20:48:42Z msweet $". 1395 */ 1396