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