]> git.ipfire.org Git - thirdparty/cups.git/blame - backend/snmp.c
Merge changes from CUPS 1.4svn-r7199.
[thirdparty/cups.git] / backend / snmp.c
CommitLineData
89d46774 1/*
bc44d920 2 * "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $"
89d46774 3 *
4 * SNMP discovery backend for the Common UNIX Printing System (CUPS).
5 *
bc44d920 6 * Copyright 2007 by Apple Inc.
c0e1af83 7 * Copyright 2006-2007 by Easy Software Products, all rights reserved.
89d46774 8 *
9 * These coded instructions, statements, and computer programs are the
bc44d920 10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
89d46774 12 * "LICENSE" which should have been included with this file. If this
bc44d920 13 * file is missing or damaged, see the license at "http://www.cups.org/".
89d46774 14 *
15 * This file is subject to the Apple OS-Developed Software exception.
16 *
17 * Contents:
18 *
19 * main() - Discover printers via SNMP.
20 * add_array() - Add a string to an array.
21 * add_cache() - Add a cached device...
b94498cf 22 * add_device_uri() - Add a device URI to the cache.
89d46774 23 * alarm_handler() - Handle alarm signals...
24 * asn1_decode_snmp() - Decode a SNMP packet.
25 * asn1_debug() - Decode an ASN1-encoded message.
26 * asn1_encode_snmp() - Encode a SNMP packet.
27 * asn1_get_integer() - Get an integer value.
28 * asn1_get_length() - Get a value length.
29 * asn1_get_oid() - Get an OID value.
30 * asn1_get_packed() - Get a packed integer value.
31 * asn1_get_string() - Get a string value.
32 * asn1_get_type() - Get a value type.
33 * asn1_set_integer() - Set an integer value.
34 * asn1_set_length() - Set a value length.
35 * asn1_set_oid() - Set an OID value.
36 * asn1_set_packed() - Set a packed integer value.
37 * asn1_size_integer() - Figure out the number of bytes needed for an
38 * integer value.
39 * asn1_size_length() - Figure out the number of bytes needed for a
40 * length value.
41 * asn1_size_oid() - Figure out the numebr of bytes needed for an
42 * OID value.
43 * asn1_size_packed() - Figure out the number of bytes needed for a
44 * packed integer value.
45 * compare_cache() - Compare two cache entries.
46 * debug_printf() - Display some debugging information.
47 * fix_make_model() - Fix common problems in the make-and-model
48 * string.
49 * free_array() - Free an array of strings.
50 * free_cache() - Free the array of cached devices.
51 * get_interface_addresses() - Get the broadcast address(es) associated
52 * with an interface.
53 * hex_debug() - Output hex debugging data...
d09495fa 54 * list_device() - List a device we found...
89d46774 55 * open_snmp_socket() - Open the SNMP broadcast socket.
56 * password_cb() - Handle authentication requests.
57 * probe_device() - Probe a device to discover whether it is a
58 * printer.
59 * read_snmp_conf() - Read the snmp.conf file.
60 * read_snmp_response() - Read and parse a SNMP response...
61 * run_time() - Return the total running time...
62 * scan_devices() - Scan for devices using SNMP.
63 * send_snmp_query() - Send an SNMP query packet.
64 * try_connect() - Try connecting on a port...
65 * update_cache() - Update a cached device...
66 */
67
68/*
69 * Include necessary headers.
70 */
71
89d46774 72#include <cups/http-private.h>
ed486911 73#include "backend-private.h"
89d46774 74#include <cups/array.h>
75#include <cups/file.h>
b94498cf 76#include <regex.h>
89d46774 77
78
79/*
80 * This backend implements SNMP printer discovery. It uses a broadcast-
b94498cf 81 * based approach to get SNMP response packets from potential printers,
82 * tries a mDNS lookup (Mac OS X only at present), a URI lookup based on
83 * the device description string, and finally a probe of port 9100
84 * (AppSocket) and 515 (LPD).
89d46774 85 *
86 * The current focus is on printers with internal network cards, although
87 * the code also works with many external print servers as well. Future
88 * versions will support scanning for vendor-specific SNMP OIDs and the
89 * new PWG Port Monitor MIB and not just the Host MIB OIDs.
90 *
91 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
92 * which can contain comments, blank lines, or any number of the following
93 * directives:
94 *
95 * Address ip-address
96 * Address @LOCAL
97 * Address @IF(name)
98 * Community name
99 * DebugLevel N
b94498cf 100 * DeviceURI "regex pattern" uri
89d46774 101 * HostNameLookups on
102 * HostNameLookups off
b94498cf 103 * MaxRunTime N
89d46774 104 *
105 * The default is to use:
106 *
107 * Address @LOCAL
108 * Community public
109 * DebugLevel 0
110 * HostNameLookups off
b94498cf 111 * MaxRunTime 10
89d46774 112 *
113 * This backend is known to work with the following network printers and
114 * print servers:
115 *
116 * Axis OfficeBasic, 5400, 5600
117 * EPSON
118 * Genicom
119 * HP JetDirect
120 * Lexmark
121 * Sharp
122 * Tektronix
123 * Xerox
124 *
125 * It does not currently work with:
126 *
127 * DLink
128 * Linksys
129 * Netgear
130 * Okidata
131 *
132 * (for all of these, they do not support the Host MIB)
133 */
134
135/*
136 * Constants...
137 */
138
139#define SNMP_PORT 161 /* SNMP well-known port */
140#define SNMP_MAX_OID 64 /* Maximum number of OID numbers */
141#define SNMP_MAX_PACKET 1472 /* Maximum size of SNMP packet */
142#define SNMP_MAX_STRING 512 /* Maximum size of string */
143#define SNMP_VERSION_1 0 /* SNMPv1 */
144
145#define ASN1_END_OF_CONTENTS 0x00 /* End-of-contents */
146#define ASN1_BOOLEAN 0x01 /* BOOLEAN */
147#define ASN1_INTEGER 0x02 /* INTEGER or ENUMERATION */
148#define ASN1_BIT_STRING 0x03 /* BIT STRING */
149#define ASN1_OCTET_STRING 0x04 /* OCTET STRING */
150#define ASN1_NULL_VALUE 0x05 /* NULL VALUE */
151#define ASN1_OID 0x06 /* OBJECT IDENTIFIER */
152#define ASN1_SEQUENCE 0x30 /* SEQUENCE */
153#define ASN1_GET_REQUEST 0xa0 /* Get-Request-PDU */
154#define ASN1_GET_RESPONSE 0xa2 /* Get-Response-PDU */
155
156
157/*
158 * Types...
159 */
160
b94498cf 161typedef struct device_uri_s /**** DeviceURI values ****/
162{
163 regex_t re; /* Regular expression to match */
164 cups_array_t *uris; /* URIs */
165} device_uri_t;
166
89d46774 167typedef struct snmp_cache_s /**** SNMP scan cache ****/
168{
169 http_addr_t address; /* Address of device */
170 char *addrname, /* Name of device */
171 *uri, /* device-uri */
172 *id, /* device-id */
173 *make_and_model; /* device-make-and-model */
174} snmp_cache_t;
175
176typedef struct snmp_packet_s /**** SNMP packet ****/
177{
178 const char *error; /* Encode/decode error */
179 int version; /* Version number */
180 char community[SNMP_MAX_STRING];
181 /* Community name */
182 int request_type; /* Request type */
183 int request_id; /* request-id value */
184 int error_status; /* error-status value */
185 int error_index; /* error-index value */
186 int object_name[SNMP_MAX_OID];
187 /* object-name value */
188 int object_type; /* object-value type */
189 union
190 {
191 int boolean; /* Boolean value */
192 int integer; /* Integer value */
193 int oid[SNMP_MAX_OID]; /* OID value */
194 char string[SNMP_MAX_STRING];/* String value */
195 } object_value; /* object-value value */
196} snmp_packet_t;
197
198
d09495fa 199/*
200 * Private CUPS API to set the last error...
201 */
202
203extern void _cupsSetError(ipp_status_t status, const char *message);
204
205
89d46774 206/*
207 * Local functions...
208 */
209
210static char *add_array(cups_array_t *a, const char *s);
211static void add_cache(http_addr_t *addr, const char *addrname,
212 const char *uri, const char *id,
213 const char *make_and_model);
b94498cf 214static device_uri_t *add_device_uri(char *value);
89d46774 215static void alarm_handler(int sig);
216static int asn1_decode_snmp(unsigned char *buffer, size_t len,
217 snmp_packet_t *packet);
218static void asn1_debug(unsigned char *buffer, size_t len,
219 int indent);
220static int asn1_encode_snmp(unsigned char *buffer, size_t len,
221 snmp_packet_t *packet);
222static int asn1_get_integer(unsigned char **buffer,
223 unsigned char *bufend,
224 int length);
225static int asn1_get_oid(unsigned char **buffer,
226 unsigned char *bufend,
227 int length, int *oid, int oidsize);
228static int asn1_get_packed(unsigned char **buffer,
229 unsigned char *bufend);
230static char *asn1_get_string(unsigned char **buffer,
231 unsigned char *bufend,
232 int length, char *string,
233 int strsize);
234static int asn1_get_length(unsigned char **buffer,
235 unsigned char *bufend);
236static int asn1_get_type(unsigned char **buffer,
237 unsigned char *bufend);
238static void asn1_set_integer(unsigned char **buffer,
239 int integer);
240static void asn1_set_length(unsigned char **buffer,
241 int length);
242static void asn1_set_oid(unsigned char **buffer,
243 const int *oid);
244static void asn1_set_packed(unsigned char **buffer,
245 int integer);
246static int asn1_size_integer(int integer);
247static int asn1_size_length(int length);
248static int asn1_size_oid(const int *oid);
249static int asn1_size_packed(int integer);
250static int compare_cache(snmp_cache_t *a, snmp_cache_t *b);
251static void debug_printf(const char *format, ...);
252static void fix_make_model(char *make_model,
253 const char *old_make_model,
254 int make_model_size);
255static void free_array(cups_array_t *a);
256static void free_cache(void);
257static http_addrlist_t *get_interface_addresses(const char *ifname);
258static void hex_debug(unsigned char *buffer, size_t len);
d09495fa 259static void list_device(snmp_cache_t *cache);
89d46774 260static int open_snmp_socket(void);
261static const char *password_cb(const char *prompt);
262static void probe_device(snmp_cache_t *device);
263static void read_snmp_conf(const char *address);
264static void read_snmp_response(int fd);
265static double run_time(void);
266static void scan_devices(int fd);
267static void send_snmp_query(int fd, http_addr_t *addr, int version,
268 const char *community,
269 const unsigned request_id,
270 const int *oid);
271static int try_connect(http_addr_t *addr, const char *addrname,
272 int port);
273static void update_cache(snmp_cache_t *device, const char *uri,
274 const char *id, const char *make_model);
275
276
277/*
278 * Local globals...
279 */
280
281static cups_array_t *Addresses = NULL;
282static cups_array_t *Communities = NULL;
283static cups_array_t *Devices = NULL;
284static int DebugLevel = 0;
89d46774 285static int DeviceDescOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
286 2, 1, 3, 1, 0 };
89d46774 287static unsigned DeviceDescRequest;
b94498cf 288static int DeviceTypeOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
289 2, 1, 2, 1, 0 };
290static unsigned DeviceTypeRequest;
291static cups_array_t *DeviceURIs = NULL;
89d46774 292static int HostNameLookups = 0;
d09495fa 293static int MaxRunTime = 10;
89d46774 294static struct timeval StartTime;
295
296
297/*
298 * 'main()' - Discover printers via SNMP.
299 */
300
301int /* O - Exit status */
302main(int argc, /* I - Number of command-line arguments (6 or 7) */
303 char *argv[]) /* I - Command-line arguments */
304{
305 int fd; /* SNMP socket */
d09495fa 306#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
307 struct sigaction action; /* Actions for POSIX signals */
308#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
89d46774 309
310
311 /*
312 * Check command-line options...
313 */
314
315 if (argc > 2)
316 {
c0e1af83 317 fputs(_("Usage: snmp [host-or-ip-address]\n"), stderr);
89d46774 318 return (1);
319 }
320
321 /*
322 * Set the password callback for IPP operations...
323 */
324
325 cupsSetPasswordCB(password_cb);
326
d09495fa 327 /*
328 * Catch SIGALRM signals...
329 */
330
331#ifdef HAVE_SIGSET
332 sigset(SIGALRM, alarm_handler);
333#elif defined(HAVE_SIGACTION)
334 memset(&action, 0, sizeof(action));
335
336 sigemptyset(&action.sa_mask);
337 sigaddset(&action.sa_mask, SIGALRM);
338 action.sa_handler = alarm_handler;
339 sigaction(SIGALRM, &action, NULL);
340#else
341 signal(SIGALRM, alarm_handler);
342#endif /* HAVE_SIGSET */
343
89d46774 344 /*
345 * Open the SNMP socket...
346 */
347
348 if ((fd = open_snmp_socket()) < 0)
349 return (1);
350
351 /*
352 * Read the configuration file and any cache data...
353 */
354
355 read_snmp_conf(argv[1]);
356
357 Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
358
359 /*
360 * Scan for devices...
361 */
362
363 scan_devices(fd);
364
89d46774 365 /*
366 * Close, free, and return with no errors...
367 */
368
369 close(fd);
370
371 free_array(Addresses);
372 free_array(Communities);
373 free_cache();
374
375 return (0);
376}
377
378
379/*
380 * 'add_array()' - Add a string to an array.
381 */
382
383static char * /* O - New string */
384add_array(cups_array_t *a, /* I - Array */
385 const char *s) /* I - String to add */
386{
387 char *dups; /* New string */
388
389
390 dups = strdup(s);
391
392 cupsArrayAdd(a, dups);
393
394 return (dups);
395}
396
397
398/*
399 * 'add_cache()' - Add a cached device...
400 */
401
402static void
403add_cache(http_addr_t *addr, /* I - Device IP address */
404 const char *addrname, /* I - IP address or name string */
405 const char *uri, /* I - Device URI */
406 const char *id, /* I - 1284 device ID */
407 const char *make_and_model) /* I - Make and model */
408{
409 snmp_cache_t *temp; /* New device entry */
410
411
412 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
413 "id=\"%s\", make_and_model=\"%s\")\n",
d09495fa 414 addr, addrname, uri ? uri : "(null)", id ? id : "(null)",
89d46774 415 make_and_model ? make_and_model : "(null)");
416
417 temp = calloc(1, sizeof(snmp_cache_t));
418 memcpy(&(temp->address), addr, sizeof(temp->address));
419
420 temp->addrname = strdup(addrname);
421
422 if (uri)
423 temp->uri = strdup(uri);
424
425 if (id)
426 temp->id = strdup(id);
427
428 if (make_and_model)
429 temp->make_and_model = strdup(make_and_model);
430
431 cupsArrayAdd(Devices, temp);
d09495fa 432
433 if (uri)
434 list_device(temp);
89d46774 435}
436
437
b94498cf 438/*
439 * 'add_device_uri()' - Add a device URI to the cache.
440 *
441 * The value string is modified (chopped up) as needed.
442 */
443
444static device_uri_t * /* O - Device URI */
445add_device_uri(char *value) /* I - Value from snmp.conf */
446{
447 device_uri_t *device_uri; /* Device URI */
448 char *start; /* Start of value */
449
450
451 /*
452 * Allocate memory as needed...
453 */
454
455 if (!DeviceURIs)
456 DeviceURIs = cupsArrayNew(NULL, NULL);
457
458 if (!DeviceURIs)
459 return (NULL);
460
461 if ((device_uri = calloc(1, sizeof(device_uri_t))) == NULL)
462 return (NULL);
463
464 if ((device_uri->uris = cupsArrayNew(NULL, NULL)) == NULL)
465 {
466 free(device_uri);
467 return (NULL);
468 }
469
470 /*
471 * Scan the value string for the regular expression and URI(s)...
472 */
473
474 value ++; /* Skip leading " */
475
476 for (start = value; *value && *value != '\"'; value ++)
477 if (*value == '\\' && value[1])
478 _cups_strcpy(value, value + 1);
479
480 if (!*value)
481 {
482 fputs("ERROR: Missing end quote for DeviceURI!\n", stderr);
483
484 cupsArrayDelete(device_uri->uris);
485 free(device_uri);
486
487 return (NULL);
488 }
489
490 *value++ = '\0';
491
492 if (regcomp(&(device_uri->re), start, REG_EXTENDED | REG_ICASE))
493 {
494 fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr);
495
496 cupsArrayDelete(device_uri->uris);
497 free(device_uri);
498
499 return (NULL);
500 }
501
502 while (*value)
503 {
504 while (isspace(*value & 255))
505 value ++;
506
507 if (!*value)
508 break;
509
510 for (start = value; *value && !isspace(*value & 255); value ++);
511
512 if (*value)
513 *value++ = '\0';
514
515 cupsArrayAdd(device_uri->uris, strdup(start));
516 }
517
518 /*
519 * Add the device URI to the list and return it...
520 */
521
522 cupsArrayAdd(DeviceURIs, device_uri);
523
524 return (device_uri);
525}
526
527
89d46774 528/*
529 * 'alarm_handler()' - Handle alarm signals...
530 */
531
532static void
533alarm_handler(int sig) /* I - Signal number */
534{
535 /*
536 * Do nothing...
537 */
538
539 (void)sig;
540
d09495fa 541#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
542 signal(SIGALRM, alarm_handler);
543#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
544
89d46774 545 if (DebugLevel)
546 write(2, "DEBUG: ALARM!\n", 14);
547}
548
549
550/*
551 * 'asn1_decode_snmp()' - Decode a SNMP packet.
552 */
553
554static int /* O - 0 on success, -1 on error */
555asn1_decode_snmp(unsigned char *buffer, /* I - Buffer */
556 size_t len, /* I - Size of buffer */
557 snmp_packet_t *packet) /* I - SNMP packet */
558{
559 unsigned char *bufptr, /* Pointer into the data */
560 *bufend; /* End of data */
561 int length; /* Length of value */
562
563
564 /*
565 * Initialize the decoding...
566 */
567
568 memset(packet, 0, sizeof(snmp_packet_t));
569
570 bufptr = buffer;
571 bufend = buffer + len;
572
573 if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE)
574 packet->error = "Packet does not start with SEQUENCE";
575 else if (asn1_get_length(&bufptr, bufend) == 0)
576 packet->error = "SEQUENCE uses indefinite length";
577 else if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
578 packet->error = "No version number";
579 else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
580 packet->error = "Version uses indefinite length";
581 else if ((packet->version = asn1_get_integer(&bufptr, bufend, length))
582 != SNMP_VERSION_1)
583 packet->error = "Bad SNMP version number";
584 else if (asn1_get_type(&bufptr, bufend) != ASN1_OCTET_STRING)
585 packet->error = "No community name";
586 else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
587 packet->error = "Community name uses indefinite length";
588 else
589 {
590 asn1_get_string(&bufptr, bufend, length, packet->community,
591 sizeof(packet->community));
592
593 if ((packet->request_type = asn1_get_type(&bufptr, bufend))
594 != ASN1_GET_RESPONSE)
595 packet->error = "Packet does not contain a Get-Response-PDU";
596 else if (asn1_get_length(&bufptr, bufend) == 0)
597 packet->error = "Get-Response-PDU uses indefinite length";
598 else if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
599 packet->error = "No request-id";
600 else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
601 packet->error = "request-id uses indefinite length";
602 else
603 {
604 packet->request_id = asn1_get_integer(&bufptr, bufend, length);
605
606 if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
607 packet->error = "No error-status";
608 else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
609 packet->error = "error-status uses indefinite length";
610 else
611 {
612 packet->error_status = asn1_get_integer(&bufptr, bufend, length);
613
614 if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
615 packet->error = "No error-index";
616 else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
617 packet->error = "error-index uses indefinite length";
618 else
619 {
620 packet->error_index = asn1_get_integer(&bufptr, bufend, length);
621
622 if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE)
623 packet->error = "No variable-bindings SEQUENCE";
624 else if (asn1_get_length(&bufptr, bufend) == 0)
625 packet->error = "variable-bindings uses indefinite length";
626 else if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE)
627 packet->error = "No VarBind SEQUENCE";
628 else if (asn1_get_length(&bufptr, bufend) == 0)
629 packet->error = "VarBind uses indefinite length";
630 else if (asn1_get_type(&bufptr, bufend) != ASN1_OID)
631 packet->error = "No name OID";
632 else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
633 packet->error = "Name OID uses indefinite length";
634 else
635 {
636 asn1_get_oid(&bufptr, bufend, length, packet->object_name,
637 SNMP_MAX_OID);
638
639 packet->object_type = asn1_get_type(&bufptr, bufend);
640
641 if ((length = asn1_get_length(&bufptr, bufend)) == 0 &&
642 packet->object_type != ASN1_NULL_VALUE &&
643 packet->object_type != ASN1_OCTET_STRING)
644 packet->error = "Value uses indefinite length";
645 else
646 {
647 switch (packet->object_type)
648 {
649 case ASN1_BOOLEAN :
650 packet->object_value.boolean =
651 asn1_get_integer(&bufptr, bufend, length);
652 break;
653
654 case ASN1_INTEGER :
655 packet->object_value.integer =
656 asn1_get_integer(&bufptr, bufend, length);
657 break;
658
659 case ASN1_NULL_VALUE :
660 break;
661
662 case ASN1_OCTET_STRING :
663 asn1_get_string(&bufptr, bufend, length,
664 packet->object_value.string,
665 SNMP_MAX_STRING);
666 break;
667
668 case ASN1_OID :
669 asn1_get_oid(&bufptr, bufend, length,
670 packet->object_value.oid, SNMP_MAX_OID);
671 break;
672
673 default :
674 packet->error = "Unsupported value type";
675 break;
676 }
677 }
678 }
679 }
680 }
681 }
682 }
683
684 return (packet->error ? -1 : 0);
685}
686
687
688/*
689 * 'asn1_debug()' - Decode an ASN1-encoded message.
690 */
691
692static void
693asn1_debug(unsigned char *buffer, /* I - Buffer */
694 size_t len, /* I - Length of buffer */
695 int indent) /* I - Indentation */
696{
697 int i; /* Looping var */
698 unsigned char *bufend; /* End of buffer */
699 int integer; /* Number value */
700 int oid[SNMP_MAX_OID]; /* OID value */
701 char string[SNMP_MAX_STRING];/* String value */
702 unsigned char value_type; /* Type of value */
703 int value_length; /* Length of value */
704
705
706 bufend = buffer + len;
707
708 while (buffer < bufend)
709 {
710 /*
711 * Get value type...
712 */
713
714 value_type = asn1_get_type(&buffer, bufend);
715 value_length = asn1_get_length(&buffer, bufend);
716
717 switch (value_type)
718 {
719 case ASN1_BOOLEAN :
720 integer = asn1_get_integer(&buffer, bufend, value_length);
721
722 fprintf(stderr, "DEBUG: %*sBOOLEAN %d bytes %d\n", indent, "",
723 value_length, integer);
724 break;
725
726 case ASN1_INTEGER :
727 integer = asn1_get_integer(&buffer, bufend, value_length);
728
729 fprintf(stderr, "DEBUG: %*sINTEGER %d bytes %d\n", indent, "",
730 value_length, integer);
731 break;
732
733 case ASN1_OCTET_STRING :
734 fprintf(stderr, "DEBUG: %*sOCTET STRING %d bytes \"%s\"\n", indent, "",
735 value_length, asn1_get_string(&buffer, bufend,
736 value_length, string,
737 sizeof(string)));
738 break;
739
740 case ASN1_NULL_VALUE :
741 fprintf(stderr, "DEBUG: %*sNULL VALUE %d bytes\n", indent, "",
742 value_length);
743
744 buffer += value_length;
745 break;
746
747 case ASN1_OID :
748 asn1_get_oid(&buffer, bufend, value_length, oid, SNMP_MAX_OID);
749
750 fprintf(stderr, "DEBUG: %*sOID %d bytes ", indent, "",
751 value_length);
752 for (i = 0; oid[i]; i ++)
753 fprintf(stderr, ".%d", oid[i]);
754 putc('\n', stderr);
755 break;
756
757 case ASN1_SEQUENCE :
758 fprintf(stderr, "DEBUG: %*sSEQUENCE %d bytes\n", indent, "",
759 value_length);
760 asn1_debug(buffer, value_length, indent + 4);
761
762 buffer += value_length;
763 break;
764
765 case ASN1_GET_REQUEST :
766 fprintf(stderr, "DEBUG: %*sGet-Request-PDU %d bytes\n", indent, "",
767 value_length);
768 asn1_debug(buffer, value_length, indent + 4);
769
770 buffer += value_length;
771 break;
772
773 case ASN1_GET_RESPONSE :
774 fprintf(stderr, "DEBUG: %*sGet-Response-PDU %d bytes\n", indent, "",
775 value_length);
776 asn1_debug(buffer, value_length, indent + 4);
777
778 buffer += value_length;
779 break;
780
781 default :
782 fprintf(stderr, "DEBUG: %*sUNKNOWN(%x) %d bytes\n", indent, "",
783 value_type, value_length);
784
785 buffer += value_length;
786 break;
787 }
788 }
789}
790
791
792/*
793 * 'asn1_encode_snmp()' - Encode a SNMP packet.
794 */
795
796static int /* O - Length on success, -1 on error */
797asn1_encode_snmp(unsigned char *buffer, /* I - Buffer */
798 size_t bufsize, /* I - Size of buffer */
799 snmp_packet_t *packet) /* I - SNMP packet */
800{
801 unsigned char *bufptr; /* Pointer into buffer */
802 int total, /* Total length */
803 msglen, /* Length of entire message */
804 commlen, /* Length of community string */
805 reqlen, /* Length of request */
806 listlen, /* Length of variable list */
807 varlen, /* Length of variable */
808 namelen, /* Length of object name OID */
809 valuelen; /* Length of object value */
810
811
812 /*
813 * Get the lengths of the community string, OID, and message...
814 */
815
816 namelen = asn1_size_oid(packet->object_name);
817
818 switch (packet->object_type)
819 {
820 case ASN1_NULL_VALUE :
821 valuelen = 0;
822 break;
823
824 case ASN1_BOOLEAN :
825 valuelen = asn1_size_integer(packet->object_value.boolean);
826 break;
827
828 case ASN1_INTEGER :
829 valuelen = asn1_size_integer(packet->object_value.integer);
830 break;
831
832 case ASN1_OCTET_STRING :
833 valuelen = strlen(packet->object_value.string);
834 break;
835
836 case ASN1_OID :
837 valuelen = asn1_size_oid(packet->object_value.oid);
838 break;
839
840 default :
841 packet->error = "Unknown object type";
842 return (-1);
843 }
844
845 varlen = 1 + asn1_size_length(namelen) + namelen +
846 1 + asn1_size_length(valuelen) + valuelen;
847 listlen = 1 + asn1_size_length(varlen) + varlen;
848 reqlen = 2 + asn1_size_integer(packet->request_id) +
849 2 + asn1_size_integer(packet->error_status) +
850 2 + asn1_size_integer(packet->error_index) +
851 1 + asn1_size_length(listlen) + listlen;
852 commlen = strlen(packet->community);
853 msglen = 2 + asn1_size_integer(packet->version) +
854 1 + asn1_size_length(commlen) + commlen +
855 1 + asn1_size_length(reqlen) + reqlen;
856 total = 1 + asn1_size_length(msglen) + msglen;
857
858 if (total > bufsize)
859 {
860 packet->error = "Message too large for buffer";
861 return (-1);
862 }
863
864 /*
865 * Then format the message...
866 */
867
868 bufptr = buffer;
869
870 *bufptr++ = ASN1_SEQUENCE; /* SNMPv1 message header */
871 asn1_set_length(&bufptr, msglen);
872
873 asn1_set_integer(&bufptr, packet->version);
874 /* version */
875
876 *bufptr++ = ASN1_OCTET_STRING; /* community */
877 asn1_set_length(&bufptr, commlen);
878 memcpy(bufptr, packet->community, commlen);
879 bufptr += commlen;
880
881 *bufptr++ = packet->request_type; /* Get-Request-PDU */
882 asn1_set_length(&bufptr, reqlen);
883
884 asn1_set_integer(&bufptr, packet->request_id);
885
886 asn1_set_integer(&bufptr, packet->error_status);
887
888 asn1_set_integer(&bufptr, packet->error_index);
889
890 *bufptr++ = ASN1_SEQUENCE; /* variable-bindings */
891 asn1_set_length(&bufptr, listlen);
892
893 *bufptr++ = ASN1_SEQUENCE; /* variable */
894 asn1_set_length(&bufptr, varlen);
895
896 asn1_set_oid(&bufptr, packet->object_name);
897 /* ObjectName */
898
899 switch (packet->object_type)
900 {
901 case ASN1_NULL_VALUE :
902 *bufptr++ = ASN1_NULL_VALUE; /* ObjectValue */
903 *bufptr++ = 0; /* Length */
904 break;
905
906 case ASN1_BOOLEAN :
907 asn1_set_integer(&bufptr, packet->object_value.boolean);
908 break;
909
910 case ASN1_INTEGER :
911 asn1_set_integer(&bufptr, packet->object_value.integer);
912 break;
913
914 case ASN1_OCTET_STRING :
915 *bufptr++ = ASN1_OCTET_STRING;
916 asn1_set_length(&bufptr, valuelen);
917 memcpy(bufptr, packet->object_value.string, valuelen);
918 bufptr += valuelen;
919 break;
920
921 case ASN1_OID :
922 asn1_set_oid(&bufptr, packet->object_value.oid);
923 break;
924 }
925
926 return (bufptr - buffer);
927}
928
929
930/*
931 * 'asn1_get_integer()' - Get an integer value.
932 */
933
934static int /* O - Integer value */
935asn1_get_integer(
936 unsigned char **buffer, /* IO - Pointer in buffer */
937 unsigned char *bufend, /* I - End of buffer */
938 int length) /* I - Length of value */
939{
940 int value; /* Integer value */
941
942
943 for (value = 0;
944 length > 0 && *buffer < bufend;
945 length --, (*buffer) ++)
946 value = (value << 8) | **buffer;
947
948 return (value);
949}
950
951
952/*
953 * 'asn1_get_length()' - Get a value length.
954 */
955
956static int /* O - Length */
957asn1_get_length(unsigned char **buffer, /* IO - Pointer in buffer */
958 unsigned char *bufend) /* I - End of buffer */
959{
960 int length; /* Length */
961
962
963 length = **buffer;
964 (*buffer) ++;
965
966 if (length & 128)
967 length = asn1_get_integer(buffer, bufend, length & 127);
968
969 return (length);
970}
971
972
973/*
974 * 'asn1_get_oid()' - Get an OID value.
975 */
976
977static int /* O - Last OID number */
978asn1_get_oid(
979 unsigned char **buffer, /* IO - Pointer in buffer */
980 unsigned char *bufend, /* I - End of buffer */
981 int length, /* I - Length of value */
982 int *oid, /* I - OID buffer */
983 int oidsize) /* I - Size of OID buffer */
984{
985 unsigned char *valend; /* End of value */
986 int *oidend; /* End of OID buffer */
987 int number; /* OID number */
988
989
990 valend = *buffer + length;
991 oidend = oid + oidsize - 1;
992
993 if (valend > bufend)
994 valend = bufend;
995
996 number = asn1_get_packed(buffer, bufend);
997
998 if (number < 80)
999 {
1000 *oid++ = number / 40;
1001 number = number % 40;
1002 *oid++ = number;
1003 }
1004 else
1005 {
1006 *oid++ = 2;
1007 number -= 80;
1008 *oid++ = number;
1009 }
1010
1011 while (*buffer < valend)
1012 {
1013 number = asn1_get_packed(buffer, bufend);
1014
1015 if (oid < oidend)
1016 *oid++ = number;
1017 }
1018
1019 *oid = 0;
1020
1021 return (number);
1022}
1023
1024
1025/*
1026 * 'asn1_get_packed()' - Get a packed integer value.
1027 */
1028
1029static int /* O - Value */
1030asn1_get_packed(
1031 unsigned char **buffer, /* IO - Pointer in buffer */
1032 unsigned char *bufend) /* I - End of buffer */
1033{
1034 int value; /* Value */
1035
1036
1037 value = 0;
1038
1039 while ((**buffer & 128) && *buffer < bufend)
1040 {
1041 value = (value << 7) | (**buffer & 127);
1042 (*buffer) ++;
1043 }
1044
1045 if (*buffer < bufend)
1046 {
1047 value = (value << 7) | **buffer;
1048 (*buffer) ++;
1049 }
1050
1051 return (value);
1052}
1053
1054
1055/*
1056 * 'asn1_get_string()' - Get a string value.
1057 */
1058
1059static char * /* O - String */
1060asn1_get_string(
1061 unsigned char **buffer, /* IO - Pointer in buffer */
1062 unsigned char *bufend, /* I - End of buffer */
1063 int length, /* I - Value length */
1064 char *string, /* I - String buffer */
1065 int strsize) /* I - String buffer size */
1066{
a4924f6c 1067 if (length < 0)
89d46774 1068 {
a4924f6c
MS
1069 /*
1070 * Disallow negative lengths!
1071 */
1072
1073 fprintf(stderr, "ERROR: Bad ASN1 string length %d!\n", length);
1074 *string = '\0';
1075 }
1076 else if (length < strsize)
1077 {
1078 /*
1079 * String is smaller than the buffer...
1080 */
1081
1082 if (length > 0)
1083 memcpy(string, *buffer, length);
1084
89d46774 1085 string[length] = '\0';
1086 }
1087 else
1088 {
a4924f6c
MS
1089 /*
1090 * String is larger than the buffer...
1091 */
1092
89d46774 1093 memcpy(string, buffer, strsize - 1);
1094 string[strsize - 1] = '\0';
1095 }
1096
a4924f6c
MS
1097 if (length > 0)
1098 (*buffer) += length;
89d46774 1099
1100 return (string);
1101}
1102
1103
1104/*
1105 * 'asn1_get_type()' - Get a value type.
1106 */
1107
1108static int /* O - Type */
1109asn1_get_type(unsigned char **buffer, /* IO - Pointer in buffer */
1110 unsigned char *bufend) /* I - End of buffer */
1111{
1112 int type; /* Type */
1113
1114
1115 type = **buffer;
1116 (*buffer) ++;
1117
1118 if ((type & 31) == 31)
1119 type = asn1_get_packed(buffer, bufend);
1120
1121 return (type);
1122}
1123
1124
1125/*
1126 * 'asn1_set_integer()' - Set an integer value.
1127 */
1128
1129static void
1130asn1_set_integer(unsigned char **buffer,/* IO - Pointer in buffer */
1131 int integer) /* I - Integer value */
1132{
1133 **buffer = ASN1_INTEGER;
1134 (*buffer) ++;
1135
1136 if (integer > 0x7fffff || integer < -0x800000)
1137 {
1138 **buffer = 4;
1139 (*buffer) ++;
1140 **buffer = integer >> 24;
1141 (*buffer) ++;
1142 **buffer = integer >> 16;
1143 (*buffer) ++;
1144 **buffer = integer >> 8;
1145 (*buffer) ++;
1146 **buffer = integer;
1147 (*buffer) ++;
1148 }
1149 else if (integer > 0x7fff || integer < -0x8000)
1150 {
1151 **buffer = 3;
1152 (*buffer) ++;
1153 **buffer = integer >> 16;
1154 (*buffer) ++;
1155 **buffer = integer >> 8;
1156 (*buffer) ++;
1157 **buffer = integer;
1158 (*buffer) ++;
1159 }
1160 else if (integer > 0x7f || integer < -0x80)
1161 {
1162 **buffer = 2;
1163 (*buffer) ++;
1164 **buffer = integer >> 8;
1165 (*buffer) ++;
1166 **buffer = integer;
1167 (*buffer) ++;
1168 }
1169 else
1170 {
1171 **buffer = 1;
1172 (*buffer) ++;
1173 **buffer = integer;
1174 (*buffer) ++;
1175 }
1176}
1177
1178
1179/*
1180 * 'asn1_set_length()' - Set a value length.
1181 */
1182
1183static void
1184asn1_set_length(unsigned char **buffer, /* IO - Pointer in buffer */
1185 int length) /* I - Length value */
1186{
1187 if (length > 255)
1188 {
1189 **buffer = 0x82; /* 2-byte length */
1190 (*buffer) ++;
1191 **buffer = length >> 8;
1192 (*buffer) ++;
1193 **buffer = length;
1194 (*buffer) ++;
1195 }
1196 else if (length > 127)
1197 {
1198 **buffer = 0x81; /* 1-byte length */
1199 (*buffer) ++;
1200 **buffer = length;
1201 (*buffer) ++;
1202 }
1203 else
1204 {
1205 **buffer = length; /* Length */
1206 (*buffer) ++;
1207 }
1208}
1209
1210
1211/*
1212 * 'asn1_set_oid()' - Set an OID value.
1213 */
1214
1215static void
1216asn1_set_oid(unsigned char **buffer, /* IO - Pointer in buffer */
1217 const int *oid) /* I - OID value */
1218{
1219 **buffer = ASN1_OID;
1220 (*buffer) ++;
1221
1222 asn1_set_length(buffer, asn1_size_oid(oid));
1223
1224 asn1_set_packed(buffer, oid[0] * 40 + oid[1]);
1225
1226 for (oid += 2; *oid; oid ++)
1227 asn1_set_packed(buffer, *oid);
1228}
1229
1230
1231/*
1232 * 'asn1_set_packed()' - Set a packed integer value.
1233 */
1234
1235static void
1236asn1_set_packed(unsigned char **buffer, /* IO - Pointer in buffer */
1237 int integer) /* I - Integer value */
1238{
1239 if (integer > 0xfffffff)
1240 {
411affcf 1241 **buffer = (integer >> 28) & 0x7f;
89d46774 1242 (*buffer) ++;
1243 }
1244
1245 if (integer > 0x1fffff)
1246 {
1247 **buffer = (integer >> 21) & 0x7f;
1248 (*buffer) ++;
1249 }
1250
1251 if (integer > 0x3fff)
1252 {
1253 **buffer = (integer >> 14) & 0x7f;
1254 (*buffer) ++;
1255 }
1256
1257 if (integer > 0x7f)
1258 {
1259 **buffer = (integer >> 7) & 0x7f;
1260 (*buffer) ++;
1261 }
1262
1263 **buffer = integer & 0x7f;
1264 (*buffer) ++;
1265}
1266
1267/*
1268 * 'asn1_size_integer()' - Figure out the number of bytes needed for an
1269 * integer value.
1270 */
1271
1272static int /* O - Size in bytes */
1273asn1_size_integer(int integer) /* I - Integer value */
1274{
1275 if (integer > 0x7fffff || integer < -0x800000)
1276 return (4);
1277 else if (integer > 0x7fff || integer < -0x8000)
1278 return (3);
1279 else if (integer > 0x7f || integer < -0x80)
1280 return (2);
1281 else
1282 return (1);
1283}
1284
1285
1286/*
1287 * 'asn1_size_length()' - Figure out the number of bytes needed for a
1288 * length value.
1289 */
1290
1291static int /* O - Size in bytes */
1292asn1_size_length(int length) /* I - Length value */
1293{
1294 if (length > 0xff)
1295 return (3);
1296 else if (length > 0x7f)
1297 return (2);
1298 else
1299 return (1);
1300}
1301
1302
1303/*
1304 * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an
1305 * OID value.
1306 */
1307
1308static int /* O - Size in bytes */
1309asn1_size_oid(const int *oid) /* I - OID value */
1310{
1311 int length; /* Length of value */
1312
1313
1314 for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2; *oid; oid ++)
1315 length += asn1_size_packed(*oid);
1316
1317 return (length);
1318}
1319
1320
1321/*
1322 * 'asn1_size_packed()' - Figure out the number of bytes needed for a
1323 * packed integer value.
1324 */
1325
1326static int /* O - Size in bytes */
1327asn1_size_packed(int integer) /* I - Integer value */
1328{
1329 if (integer > 0xfffffff)
1330 return (5);
1331 else if (integer > 0x1fffff)
1332 return (4);
1333 else if (integer > 0x3fff)
1334 return (3);
1335 else if (integer > 0x7f)
1336 return (2);
1337 else
1338 return (1);
1339}
1340
1341
1342/*
1343 * 'compare_cache()' - Compare two cache entries.
1344 */
1345
1346static int /* O - Result of comparison */
1347compare_cache(snmp_cache_t *a, /* I - First cache entry */
1348 snmp_cache_t *b) /* I - Second cache entry */
1349{
8ca02f3c 1350 return (strcasecmp(a->addrname, b->addrname));
89d46774 1351}
1352
1353
1354/*
1355 * 'debug_printf()' - Display some debugging information.
1356 */
1357
1358static void
1359debug_printf(const char *format, /* I - Printf-style format string */
1360 ...) /* I - Additional arguments as needed */
1361{
1362 va_list ap; /* Pointer to arguments */
1363
1364
1365 if (!DebugLevel)
1366 return;
1367
1368 va_start(ap, format);
1369 vfprintf(stderr, format, ap);
1370 va_end(ap);
1371}
1372
1373
1374/*
1375 * 'fix_make_model()' - Fix common problems in the make-and-model string.
1376 */
1377
1378static void
1379fix_make_model(
1380 char *make_model, /* I - New make-and-model string */
1381 const char *old_make_model, /* I - Old make-and-model string */
1382 int make_model_size) /* I - Size of new string buffer */
1383{
a74454a7 1384 char *mmptr; /* Pointer into make-and-model string */
89d46774 1385
1386
1387 /*
1388 * Fix some common problems with the make-and-model string so
1389 * that printer driver detection works better...
1390 */
1391
1392 if (!strncasecmp(old_make_model, "Hewlett-Packard", 15))
1393 {
1394 /*
1395 * Strip leading Hewlett-Packard and hp prefixes and replace
1396 * with a single HP manufacturer prefix...
1397 */
1398
a74454a7 1399 mmptr = (char *)old_make_model + 15;
89d46774 1400
1401 while (isspace(*mmptr & 255))
1402 mmptr ++;
1403
1404 if (!strncasecmp(mmptr, "hp", 2))
1405 {
1406 mmptr += 2;
1407
1408 while (isspace(*mmptr & 255))
1409 mmptr ++;
1410 }
1411
1412 make_model[0] = 'H';
1413 make_model[1] = 'P';
1414 make_model[2] = ' ';
1415 strlcpy(make_model + 3, mmptr, make_model_size - 3);
1416 }
1417 else if (!strncasecmp(old_make_model, "deskjet", 7))
1418 snprintf(make_model, make_model_size, "HP DeskJet%s", old_make_model + 7);
b86bc4cf 1419 else if (!strncasecmp(old_make_model, "officejet", 9))
1420 snprintf(make_model, make_model_size, "HP OfficeJet%s", old_make_model + 9);
89d46774 1421 else if (!strncasecmp(old_make_model, "stylus_pro_", 11))
1422 snprintf(make_model, make_model_size, "EPSON Stylus Pro %s",
1423 old_make_model + 11);
1424 else
1425 strlcpy(make_model, old_make_model, make_model_size);
1426
1427 if ((mmptr = strstr(make_model, ", Inc.,")) != NULL)
1428 {
1429 /*
1430 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
1431 * becomes "Tektronix Phaser 560"...
1432 */
1433
a74454a7 1434 _cups_strcpy(mmptr, mmptr + 7);
1435 }
1436
d09495fa 1437 if ((mmptr = strstr(make_model, " Network")) != NULL)
1438 {
1439 /*
1440 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
1441 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
1442 */
1443
1444 *mmptr = '\0';
1445 }
1446
a74454a7 1447 if ((mmptr = strchr(make_model, ',')) != NULL)
1448 {
1449 /*
1450 * Drop anything after a trailing comma...
1451 */
1452
1453 *mmptr = '\0';
89d46774 1454 }
1455}
1456
1457
1458/*
1459 * 'free_array()' - Free an array of strings.
1460 */
1461
1462static void
1463free_array(cups_array_t *a) /* I - Array */
1464{
1465 char *s; /* Current string */
1466
1467
1468 for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a))
1469 free(s);
1470
1471 cupsArrayDelete(a);
1472}
1473
1474
1475/*
1476 * 'free_cache()' - Free the array of cached devices.
1477 */
1478
1479static void
1480free_cache(void)
1481{
1482 snmp_cache_t *cache; /* Cached device */
1483
1484
1485 for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
1486 cache;
1487 cache = (snmp_cache_t *)cupsArrayNext(Devices))
1488 {
1489 free(cache->addrname);
1490
1491 if (cache->uri)
1492 free(cache->uri);
1493
1494 if (cache->id)
1495 free(cache->id);
1496
1497 if (cache->make_and_model)
1498 free(cache->make_and_model);
1499
1500 free(cache);
1501 }
1502
1503 cupsArrayDelete(Devices);
1504 Devices = NULL;
1505}
1506
1507
1508/*
1509 * 'get_interface_addresses()' - Get the broadcast address(es) associated
1510 * with an interface.
1511 */
1512
1513static http_addrlist_t * /* O - List of addresses */
1514get_interface_addresses(
1515 const char *ifname) /* I - Interface name */
1516{
1517 struct ifaddrs *addrs, /* Interface address list */
1518 *addr; /* Current interface address */
1519 http_addrlist_t *first, /* First address in list */
1520 *last, /* Last address in list */
1521 *current; /* Current address */
1522
1523
1524 if (getifaddrs(&addrs) < 0)
1525 return (NULL);
1526
1527 for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next)
1528 if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr &&
1529 addr->ifa_broadaddr->sa_family == AF_INET &&
1530 (!ifname || !strcmp(ifname, addr->ifa_name)))
1531 {
1532 current = calloc(1, sizeof(http_addrlist_t));
1533
1534 memcpy(&(current->addr), addr->ifa_broadaddr,
1535 sizeof(struct sockaddr_in));
1536
1537 if (!last)
1538 first = current;
1539 else
1540 last->next = current;
1541
1542 last = current;
1543 }
1544
1545 freeifaddrs(addrs);
1546
1547 return (first);
1548}
1549
1550
1551/*
1552 * 'hex_debug()' - Output hex debugging data...
1553 */
1554
1555static void
1556hex_debug(unsigned char *buffer, /* I - Buffer */
1557 size_t len) /* I - Number of bytes */
1558{
1559 int col; /* Current column */
1560
1561
1562 fputs("DEBUG: Hex dump of packet:\n", stderr);
1563
1564 for (col = 0; len > 0; col ++, buffer ++, len --)
1565 {
1566 if ((col & 15) == 0)
1567 fprintf(stderr, "DEBUG: %04X ", col);
1568
1569 fprintf(stderr, " %02X", *buffer);
1570
1571 if ((col & 15) == 15)
1572 putc('\n', stderr);
1573 }
1574
1575 if (col & 15)
1576 putc('\n', stderr);
1577}
1578
1579
1580/*
d09495fa 1581 * 'list_device()' - List a device we found...
89d46774 1582 */
1583
1584static void
d09495fa 1585list_device(snmp_cache_t *cache) /* I - Cached device */
89d46774 1586{
d09495fa 1587 if (cache->uri)
1588 printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
1589 cache->uri,
1590 cache->make_and_model ? cache->make_and_model : "Unknown",
1591 cache->make_and_model ? cache->make_and_model : "Unknown",
1592 cache->addrname, cache->id ? cache->id : "");
89d46774 1593}
1594
1595
1596/*
1597 * 'open_snmp_socket()' - Open the SNMP broadcast socket.
1598 */
1599
1600static int /* O - SNMP socket file descriptor */
1601open_snmp_socket(void)
1602{
1603 int fd; /* SNMP socket file descriptor */
1604 int val; /* Socket option value */
1605
1606
1607 /*
1608 * Create the SNMP socket...
1609 */
1610
1611 if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
1612 {
1613 fprintf(stderr, "ERROR: Unable to create SNMP socket - %s\n",
1614 strerror(errno));
1615
1616 return (-1);
1617 }
1618
1619 /*
1620 * Set the "broadcast" flag...
1621 */
1622
1623 val = 1;
1624
1625 if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
1626 {
1627 fprintf(stderr, "ERROR: Unable to set broadcast mode - %s\n",
1628 strerror(errno));
1629
1630 close(fd);
1631
1632 return (-1);
1633 }
1634
1635 return (fd);
1636}
1637
1638
1639/*
1640 * 'password_cb()' - Handle authentication requests.
1641 *
1642 * All we do right now is return NULL, indicating that no authentication
1643 * is possible.
1644 */
1645
1646static const char * /* O - Password (NULL) */
1647password_cb(const char *prompt) /* I - Prompt message */
1648{
1649 (void)prompt; /* Anti-compiler-warning-code */
1650
1651 return (NULL);
1652}
1653
1654
1655/*
1656 * 'probe_device()' - Probe a device to discover whether it is a printer.
1657 *
1658 * TODO: Try using the Port Monitor MIB to discover the correct protocol
1659 * to use - first need a commercially-available printer that supports
1660 * it, though...
1661 */
1662
1663static void
1664probe_device(snmp_cache_t *device) /* I - Device */
1665{
b94498cf 1666 char uri[1024], /* Full device URI */
1667 *uriptr, /* Pointer into URI */
1668 *format; /* Format string for device */
1669 device_uri_t *device_uri; /* Current DeviceURI match */
89d46774 1670
1671
89d46774 1672 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
1673
b94498cf 1674#ifdef __APPLE__
1675 /*
1676 * TODO: Try an mDNS query first, and then fallback on direct probes...
1677 */
d09495fa 1678
b94498cf 1679 if (!try_connect(&(device->address), device->addrname, 5353))
d09495fa 1680 {
b94498cf 1681 debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname);
1682 return;
d09495fa 1683 }
b94498cf 1684#endif /* __APPLE__ */
d09495fa 1685
b94498cf 1686 /*
1687 * Lookup the device in the match table...
1688 */
89d46774 1689
b94498cf 1690 for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
1691 device_uri;
1692 device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
1693 if (!regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
89d46774 1694 {
d09495fa 1695 /*
b94498cf 1696 * Found a match, add the URIs...
89d46774 1697 */
1698
b94498cf 1699 for (format = (char *)cupsArrayFirst(device_uri->uris);
1700 format;
1701 format = (char *)cupsArrayNext(device_uri->uris))
89d46774 1702 {
b94498cf 1703 for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);)
1704 if (*format == '%' && format[1] == 's')
89d46774 1705 {
b94498cf 1706 /*
1707 * Insert hostname/address...
1708 */
89d46774 1709
b94498cf 1710 strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri));
1711 uriptr += strlen(uriptr);
1712 format += 2;
1713 }
1714 else
1715 *uriptr++ = *format++;
89d46774 1716
b94498cf 1717 *uriptr = '\0';
89d46774 1718
b94498cf 1719 update_cache(device, uri, NULL, NULL);
89d46774 1720 }
1721
89d46774 1722 return;
b94498cf 1723 }
89d46774 1724
1725 /*
b94498cf 1726 * Then try the standard ports...
89d46774 1727 */
1728
1729 if (!try_connect(&(device->address), device->addrname, 9100))
1730 {
1731 debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname);
1732
1733 snprintf(uri, sizeof(uri), "socket://%s", device->addrname);
1734 update_cache(device, uri, NULL, NULL);
1735 }
1736 else if (!try_connect(&(device->address), device->addrname, 515))
1737 {
1738 debug_printf("DEBUG: %s supports LPD!\n", device->addrname);
1739
1740 snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname);
1741 update_cache(device, uri, NULL, NULL);
1742 }
1743}
1744
1745
1746/*
1747 * 'read_snmp_conf()' - Read the snmp.conf file.
1748 */
1749
1750static void
1751read_snmp_conf(const char *address) /* I - Single address to probe */
1752{
1753 cups_file_t *fp; /* File pointer */
1754 char filename[1024], /* Filename */
1755 line[1024], /* Line from file */
1756 *value; /* Value on line */
1757 int linenum; /* Line number */
1758 const char *cups_serverroot; /* CUPS_SERVERROOT env var */
1759 const char *debug; /* CUPS_DEBUG_LEVEL env var */
d09495fa 1760 const char *runtime; /* CUPS_MAX_RUN_TIME env var */
89d46774 1761
1762
1763 /*
1764 * Initialize the global address and community lists...
1765 */
1766
1767 Addresses = cupsArrayNew(NULL, NULL);
1768 Communities = cupsArrayNew(NULL, NULL);
1769
1770 if (address)
1771 add_array(Addresses, address);
1772
1773 if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
1774 DebugLevel = atoi(debug);
1775
d09495fa 1776 if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL)
1777 MaxRunTime = atoi(runtime);
1778
89d46774 1779 /*
1780 * Find the snmp.conf file...
1781 */
1782
1783 if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
1784 cups_serverroot = CUPS_SERVERROOT;
1785
1786 snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot);
1787
1788 if ((fp = cupsFileOpen(filename, "r")) != NULL)
1789 {
1790 /*
1791 * Read the snmp.conf file...
1792 */
1793
1794 linenum = 0;
1795
1796 while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
1797 {
1798 if (!value)
1799 fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
1800 filename);
1801 else if (!strcasecmp(line, "Address"))
1802 {
1803 if (!address)
1804 add_array(Addresses, value);
1805 }
1806 else if (!strcasecmp(line, "Community"))
1807 add_array(Communities, value);
1808 else if (!strcasecmp(line, "DebugLevel"))
1809 DebugLevel = atoi(value);
b94498cf 1810 else if (!strcasecmp(line, "DeviceURI"))
1811 {
1812 if (*value != '\"')
1813 fprintf(stderr,
1814 "ERROR: Missing double quote for regular expression on "
1815 "line %d of %s!\n", linenum, filename);
1816 else
1817 add_device_uri(value);
1818 }
89d46774 1819 else if (!strcasecmp(line, "HostNameLookups"))
1820 HostNameLookups = !strcasecmp(value, "on") ||
1821 !strcasecmp(value, "yes") ||
1822 !strcasecmp(value, "true") ||
1823 !strcasecmp(value, "double");
d09495fa 1824 else if (!strcasecmp(line, "MaxRunTime"))
1825 MaxRunTime = atoi(value);
89d46774 1826 else
1827 fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
1828 line, linenum, filename);
1829 }
1830
1831 cupsFileClose(fp);
1832 }
1833
1834 /*
1835 * Use defaults if parameters are undefined...
1836 */
1837
1838 if (cupsArrayCount(Addresses) == 0)
1839 {
bc44d920 1840 /*
1841 * If we have no addresses, exit immediately...
1842 */
1843
1844 fprintf(stderr,
1845 "DEBUG: No address specified and no Address line in %s...\n",
1846 filename);
1847 exit(0);
89d46774 1848 }
1849
1850 if (cupsArrayCount(Communities) == 0)
1851 {
1852 fputs("INFO: Using default SNMP Community public\n", stderr);
1853 add_array(Communities, "public");
1854 }
1855}
1856
1857
1858/*
1859 * 'read_snmp_response()' - Read and parse a SNMP response...
1860 */
1861
1862static void
1863read_snmp_response(int fd) /* I - SNMP socket file descriptor */
1864{
1865 unsigned char buffer[SNMP_MAX_PACKET];/* Data packet */
1866 int bytes; /* Number of bytes received */
1867 http_addr_t addr; /* Source address */
1868 socklen_t addrlen; /* Source address length */
1869 char addrname[256]; /* Source address name */
1870 snmp_packet_t packet; /* Decoded packet */
1871 snmp_cache_t key, /* Search key */
1872 *device; /* Matching device */
1873
1874
1875 /*
1876 * Read the response data...
1877 */
1878
1879 addrlen = sizeof(addr);
1880
1881 if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (void *)&addr,
1882 &addrlen)) < 0)
1883 {
1884 fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
1885 strerror(errno));
1886 return;
1887 }
1888
1889 if (HostNameLookups)
1890 httpAddrLookup(&addr, addrname, sizeof(addrname));
1891 else
1892 httpAddrString(&addr, addrname, sizeof(addrname));
1893
1894 debug_printf("DEBUG: %.3f Received %d bytes from %s...\n", run_time(),
1895 bytes, addrname);
1896
1897 /*
1898 * Look for the response status code in the SNMP message header...
1899 */
1900
1901 if (asn1_decode_snmp(buffer, bytes, &packet))
1902 {
c0e1af83 1903 fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n",
1904 addrname, packet.error);
89d46774 1905
1906 asn1_debug(buffer, bytes, 0);
1907 hex_debug(buffer, bytes);
1908
1909 return;
1910 }
1911
1912 debug_printf("DEBUG: community=\"%s\"\n", packet.community);
1913 debug_printf("DEBUG: request-id=%d\n", packet.request_id);
1914 debug_printf("DEBUG: error-status=%d\n", packet.error_status);
1915
1916 if (DebugLevel > 1)
1917 asn1_debug(buffer, bytes, 0);
1918
1919 if (DebugLevel > 2)
1920 hex_debug(buffer, bytes);
1921
1922 if (packet.error_status)
1923 return;
1924
1925 /*
1926 * Find a matching device in the cache...
1927 */
1928
8ca02f3c 1929 key.addrname = addrname;
1930 device = (snmp_cache_t *)cupsArrayFind(Devices, &key);
89d46774 1931
1932 /*
1933 * Process the message...
1934 */
1935
1936 if (packet.request_id == DeviceTypeRequest)
1937 {
1938 /*
1939 * Got the device type response...
1940 */
1941
1942 if (device)
1943 {
1944 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
1945 addrname);
1946 return;
1947 }
1948
1949 /*
1950 * Add the device and request the device description...
1951 */
1952
1953 add_cache(&addr, addrname, NULL, NULL, NULL);
1954
1955 send_snmp_query(fd, &addr, SNMP_VERSION_1, packet.community,
1956 DeviceDescRequest, DeviceDescOID);
1957 }
1958 else if (packet.request_id == DeviceDescRequest &&
1959 packet.object_type == ASN1_OCTET_STRING)
1960 {
1961 /*
1962 * Update an existing cache entry...
1963 */
1964
1965 char make_model[256]; /* Make and model */
1966
1967
1968 if (!device)
1969 {
1970 debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
1971 addrname);
1972 return;
1973 }
1974
1975 /*
1976 * Convert the description to a make and model string...
1977 */
1978
1979 if (strchr(packet.object_value.string, ':') &&
1980 strchr(packet.object_value.string, ';'))
1981 {
1982 /*
1983 * Description is the IEEE-1284 device ID...
1984 */
1985
ed486911 1986 backendGetMakeModel(packet.object_value.string, make_model,
1987 sizeof(make_model));
89d46774 1988 }
1989 else
1990 {
1991 /*
1992 * Description is plain text...
1993 */
1994
1995 fix_make_model(make_model, packet.object_value.string,
1996 sizeof(make_model));
1997 }
1998
1999 if (device->make_and_model)
2000 free(device->make_and_model);
2001
2002 device->make_and_model = strdup(make_model);
2003 }
2004}
2005
2006
2007/*
2008 * 'run_time()' - Return the total running time...
2009 */
2010
2011static double /* O - Number of seconds */
2012run_time(void)
2013{
2014 struct timeval curtime; /* Current time */
2015
2016
2017 gettimeofday(&curtime, NULL);
2018
2019 return (curtime.tv_sec - StartTime.tv_sec +
2020 0.000001 * (curtime.tv_usec - StartTime.tv_usec));
2021}
2022
2023
2024/*
2025 * 'scan_devices()' - Scan for devices using SNMP.
2026 */
2027
2028static void
2029scan_devices(int fd) /* I - SNMP socket */
2030{
2031 char *address, /* Current address */
2032 *community; /* Current community */
2033 fd_set input; /* Input set for select() */
2034 struct timeval timeout; /* Timeout for select() */
2035 time_t endtime; /* End time for scan */
2036 http_addrlist_t *addrs, /* List of addresses */
2037 *addr; /* Current address */
2038 snmp_cache_t *device; /* Current device */
2039
2040
2041 /*
2042 * Setup the request IDs...
2043 */
2044
2045 gettimeofday(&StartTime, NULL);
2046
2047 DeviceTypeRequest = StartTime.tv_sec;
2048 DeviceDescRequest = StartTime.tv_sec + 1;
2049
2050 /*
2051 * First send all of the broadcast queries...
2052 */
2053
2054 for (address = (char *)cupsArrayFirst(Addresses);
2055 address;
2056 address = (char *)cupsArrayNext(Addresses))
2057 {
2058 if (!strcmp(address, "@LOCAL"))
2059 addrs = get_interface_addresses(NULL);
2060 else if (!strncmp(address, "@IF(", 4))
2061 {
2062 char ifname[255]; /* Interface name */
2063
2064
2065 strlcpy(ifname, address + 4, sizeof(ifname));
2066 if (ifname[0])
2067 ifname[strlen(ifname) - 1] = '\0';
2068
2069 addrs = get_interface_addresses(ifname);
2070 }
2071 else
2072 addrs = httpAddrGetList(address, AF_INET, NULL);
2073
2074 if (!addrs)
2075 {
2076 fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address);
2077 continue;
2078 }
2079
2080 for (community = (char *)cupsArrayFirst(Communities);
2081 community;
2082 community = (char *)cupsArrayNext(Communities))
2083 {
2084 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
2085 community, address);
2086
2087 for (addr = addrs; addr; addr = addr->next)
2088 send_snmp_query(fd, &(addr->addr), SNMP_VERSION_1, community,
2089 DeviceTypeRequest, DeviceTypeOID);
2090 }
2091
2092 httpAddrFreeList(addrs);
2093 }
2094
2095 /*
2096 * Then read any responses that come in over the next 3 seconds...
2097 */
2098
2099 endtime = time(NULL) + 3;
2100
2101 FD_ZERO(&input);
2102
2103 while (time(NULL) < endtime)
2104 {
2105 timeout.tv_sec = 1;
2106 timeout.tv_usec = 0;
2107
2108 FD_SET(fd, &input);
2109 if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
2110 {
2111 fprintf(stderr, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
2112 fd, strerror(errno));
2113 break;
2114 }
2115
2116 if (FD_ISSET(fd, &input))
2117 read_snmp_response(fd);
2118 else
2119 break;
2120 }
2121
2122 /*
2123 * Finally, probe all of the printers we discovered to see how they are
2124 * connected...
2125 */
2126
2127 for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
2128 device;
2129 device = (snmp_cache_t *)cupsArrayNext(Devices))
d09495fa 2130 if (MaxRunTime > 0 && run_time() >= MaxRunTime)
2131 break;
2132 else if (!device->uri)
89d46774 2133 probe_device(device);
2134
2135 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
2136}
2137
2138
2139/*
2140 * 'send_snmp_query()' - Send an SNMP query packet.
2141 */
2142
2143static void
2144send_snmp_query(
2145 int fd, /* I - SNMP socket */
2146 http_addr_t *addr, /* I - Address to send to */
2147 int version, /* I - SNMP version */
2148 const char *community, /* I - Community name */
2149 const unsigned request_id, /* I - Request ID */
2150 const int *oid) /* I - OID */
2151{
2152 int i; /* Looping var */
2153 snmp_packet_t packet; /* SNMP message packet */
2154 unsigned char buffer[SNMP_MAX_PACKET];/* SNMP message buffer */
2155 int bytes; /* Size of message */
2156 char addrname[32]; /* Address name */
2157
2158
2159 /*
2160 * Create the SNMP message...
2161 */
2162
2163 memset(&packet, 0, sizeof(packet));
2164
2165 packet.version = version;
2166 packet.request_type = ASN1_GET_REQUEST;
2167 packet.request_id = request_id;
2168 packet.object_type = ASN1_NULL_VALUE;
2169
2170 strlcpy(packet.community, community, sizeof(packet.community));
2171
2172 for (i = 0; oid[i]; i ++)
2173 packet.object_name[i] = oid[i];
2174
2175 bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet);
2176
2177 if (bytes < 0)
2178 {
c0e1af83 2179 fprintf(stderr, "ERROR: Unable to send SNMP query: %s\n",
2180 packet.error);
89d46774 2181 return;
2182 }
2183
2184 /*
2185 * Send the message...
2186 */
2187
2188 debug_printf("DEBUG: %.3f Sending %d bytes to %s...\n", run_time(),
2189 bytes, httpAddrString(addr, addrname, sizeof(addrname)));
2190 if (DebugLevel > 1)
2191 asn1_debug(buffer, bytes, 0);
2192 if (DebugLevel > 2)
2193 hex_debug(buffer, bytes);
2194
2195 addr->ipv4.sin_port = htons(SNMP_PORT);
2196
2197 if (sendto(fd, buffer, bytes, 0, (void *)addr, sizeof(addr->ipv4)) < bytes)
2198 fprintf(stderr, "ERROR: Unable to send %d bytes to %s: %s\n",
2199 bytes, addrname, strerror(errno));
2200}
2201
2202
2203/*
2204 * 'try_connect()' - Try connecting on a port...
2205 */
2206
2207static int /* O - 0 on success or -1 on error */
2208try_connect(http_addr_t *addr, /* I - Socket address */
2209 const char *addrname, /* I - Hostname or IP address */
2210 int port) /* I - Port number */
2211{
2212 int fd; /* Socket */
2213 int status; /* Connection status */
2214
2215
2216 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
2217 port == 515 ? "lpd" : "socket", addrname, port);
2218
2219 if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
2220 {
c0e1af83 2221 fprintf(stderr, "ERROR: Unable to create socket: %s\n",
2222 strerror(errno));
89d46774 2223 return (-1);
2224 }
2225
2226 addr->ipv4.sin_port = htons(port);
2227
89d46774 2228 alarm(1);
2229
2230 status = connect(fd, (void *)addr, httpAddrLength(addr));
2231
2232 close(fd);
2233 alarm(0);
2234
2235 return (status);
2236}
2237
2238
2239/*
2240 * 'update_cache()' - Update a cached device...
2241 */
2242
2243static void
2244update_cache(snmp_cache_t *device, /* I - Device */
2245 const char *uri, /* I - Device URI */
2246 const char *id, /* I - Device ID */
2247 const char *make_model) /* I - Device make and model */
2248{
2249 if (device->uri)
2250 free(device->uri);
2251
2252 device->uri = strdup(uri);
2253
2254 if (id)
2255 {
2256 if (device->id)
2257 free(device->id);
2258
2259 device->id = strdup(id);
2260 }
2261
2262 if (make_model)
2263 {
2264 if (device->make_and_model)
2265 free(device->make_and_model);
2266
2267 device->make_and_model = strdup(make_model);
2268 }
d09495fa 2269
2270 list_device(device);
89d46774 2271}
2272
2273
2274/*
bc44d920 2275 * End of "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $".
89d46774 2276 */