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