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