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