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