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