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