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