2 * "$Id: snmp.c 6180 2007-01-03 18:19:32Z mike $"
4 * SNMP discovery backend for the Common UNIX Printing System (CUPS).
6 * Copyright 2006 by Easy Software Products, all rights reserved.
8 * These coded instructions, statements, and computer programs are the
9 * property of Easy Software Products and are protected by Federal
10 * copyright law. Distribution and use rights are outlined in the file
11 * "LICENSE" which should have been included with this file. If this
12 * file is missing or damaged please contact Easy Software Products
15 * Attn: CUPS Licensing Information
16 * Easy Software Products
17 * 44141 Airport View Drive, Suite 204
18 * Hollywood, Maryland 20636 USA
20 * Voice: (301) 373-9600
21 * EMail: cups-info@cups.org
22 * WWW: http://www.cups.org
24 * This file is subject to the Apple OS-Developed Software exception.
28 * main() - Discover printers via SNMP.
29 * add_array() - Add a string to an array.
30 * add_cache() - Add a cached device...
31 * alarm_handler() - Handle alarm signals...
32 * asn1_decode_snmp() - Decode a SNMP packet.
33 * asn1_debug() - Decode an ASN1-encoded message.
34 * asn1_encode_snmp() - Encode a SNMP packet.
35 * asn1_get_integer() - Get an integer value.
36 * asn1_get_length() - Get a value length.
37 * asn1_get_oid() - Get an OID value.
38 * asn1_get_packed() - Get a packed integer value.
39 * asn1_get_string() - Get a string value.
40 * asn1_get_type() - Get a value type.
41 * asn1_set_integer() - Set an integer value.
42 * asn1_set_length() - Set a value length.
43 * asn1_set_oid() - Set an OID value.
44 * asn1_set_packed() - Set a packed integer value.
45 * asn1_size_integer() - Figure out the number of bytes needed for an
47 * asn1_size_length() - Figure out the number of bytes needed for a
49 * asn1_size_oid() - Figure out the numebr of bytes needed for an
51 * asn1_size_packed() - Figure out the number of bytes needed for a
52 * packed integer value.
53 * compare_cache() - Compare two cache entries.
54 * debug_printf() - Display some debugging information.
55 * do_request() - Do a non-blocking IPP request.
56 * fix_make_model() - Fix common problems in the make-and-model
58 * free_array() - Free an array of strings.
59 * free_cache() - Free the array of cached devices.
60 * get_interface_addresses() - Get the broadcast address(es) associated
62 * hex_debug() - Output hex debugging data...
63 * list_device() - List a device we found...
64 * open_snmp_socket() - Open the SNMP broadcast socket.
65 * password_cb() - Handle authentication requests.
66 * probe_device() - Probe a device to discover whether it is a
68 * read_snmp_conf() - Read the snmp.conf file.
69 * read_snmp_response() - Read and parse a SNMP response...
70 * run_time() - Return the total running time...
71 * scan_devices() - Scan for devices using SNMP.
72 * send_snmp_query() - Send an SNMP query packet.
73 * try_connect() - Try connecting on a port...
74 * update_cache() - Update a cached device...
78 * Include necessary headers.
81 #include <cups/http-private.h>
82 #include "backend-private.h"
83 #include <cups/array.h>
84 #include <cups/file.h>
88 * This backend implements SNMP printer discovery. It uses a broadcast-
89 * based approach to get SNMP response packets from potential printers
90 * and then interrogates each responder by trying to connect on port
93 * The current focus is on printers with internal network cards, although
94 * the code also works with many external print servers as well. Future
95 * versions will support scanning for vendor-specific SNMP OIDs and the
96 * new PWG Port Monitor MIB and not just the Host MIB OIDs.
98 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
99 * which can contain comments, blank lines, or any number of the following
108 * HostNameLookups off
110 * The default is to use:
115 * HostNameLookups off
117 * This backend is known to work with the following network printers and
120 * Axis OfficeBasic, 5400, 5600
129 * It does not currently work with:
136 * (for all of these, they do not support the Host MIB)
143 #define SNMP_PORT 161 /* SNMP well-known port */
144 #define SNMP_MAX_OID 64 /* Maximum number of OID numbers */
145 #define SNMP_MAX_PACKET 1472 /* Maximum size of SNMP packet */
146 #define SNMP_MAX_STRING 512 /* Maximum size of string */
147 #define SNMP_VERSION_1 0 /* SNMPv1 */
149 #define ASN1_END_OF_CONTENTS 0x00 /* End-of-contents */
150 #define ASN1_BOOLEAN 0x01 /* BOOLEAN */
151 #define ASN1_INTEGER 0x02 /* INTEGER or ENUMERATION */
152 #define ASN1_BIT_STRING 0x03 /* BIT STRING */
153 #define ASN1_OCTET_STRING 0x04 /* OCTET STRING */
154 #define ASN1_NULL_VALUE 0x05 /* NULL VALUE */
155 #define ASN1_OID 0x06 /* OBJECT IDENTIFIER */
156 #define ASN1_SEQUENCE 0x30 /* SEQUENCE */
157 #define ASN1_GET_REQUEST 0xa0 /* Get-Request-PDU */
158 #define ASN1_GET_RESPONSE 0xa2 /* Get-Response-PDU */
165 typedef struct snmp_cache_s
/**** SNMP scan cache ****/
167 http_addr_t address
; /* Address of device */
168 char *addrname
, /* Name of device */
169 *uri
, /* device-uri */
171 *make_and_model
; /* device-make-and-model */
174 typedef struct snmp_packet_s
/**** SNMP packet ****/
176 const char *error
; /* Encode/decode error */
177 int version
; /* Version number */
178 char community
[SNMP_MAX_STRING
];
180 int request_type
; /* Request type */
181 int request_id
; /* request-id value */
182 int error_status
; /* error-status value */
183 int error_index
; /* error-index value */
184 int object_name
[SNMP_MAX_OID
];
185 /* object-name value */
186 int object_type
; /* object-value type */
189 int boolean
; /* Boolean value */
190 int integer
; /* Integer value */
191 int oid
[SNMP_MAX_OID
]; /* OID value */
192 char string
[SNMP_MAX_STRING
];/* String value */
193 } object_value
; /* object-value value */
198 * Private CUPS API to set the last error...
201 extern void _cupsSetError(ipp_status_t status
, const char *message
);
208 static char *add_array(cups_array_t
*a
, const char *s
);
209 static void add_cache(http_addr_t
*addr
, const char *addrname
,
210 const char *uri
, const char *id
,
211 const char *make_and_model
);
212 static void alarm_handler(int sig
);
213 static int asn1_decode_snmp(unsigned char *buffer
, size_t len
,
214 snmp_packet_t
*packet
);
215 static void asn1_debug(unsigned char *buffer
, size_t len
,
217 static int asn1_encode_snmp(unsigned char *buffer
, size_t len
,
218 snmp_packet_t
*packet
);
219 static int asn1_get_integer(unsigned char **buffer
,
220 unsigned char *bufend
,
222 static int asn1_get_oid(unsigned char **buffer
,
223 unsigned char *bufend
,
224 int length
, int *oid
, int oidsize
);
225 static int asn1_get_packed(unsigned char **buffer
,
226 unsigned char *bufend
);
227 static char *asn1_get_string(unsigned char **buffer
,
228 unsigned char *bufend
,
229 int length
, char *string
,
231 static int asn1_get_length(unsigned char **buffer
,
232 unsigned char *bufend
);
233 static int asn1_get_type(unsigned char **buffer
,
234 unsigned char *bufend
);
235 static void asn1_set_integer(unsigned char **buffer
,
237 static void asn1_set_length(unsigned char **buffer
,
239 static void asn1_set_oid(unsigned char **buffer
,
241 static void asn1_set_packed(unsigned char **buffer
,
243 static int asn1_size_integer(int integer
);
244 static int asn1_size_length(int length
);
245 static int asn1_size_oid(const int *oid
);
246 static int asn1_size_packed(int integer
);
247 static int compare_cache(snmp_cache_t
*a
, snmp_cache_t
*b
);
248 static void debug_printf(const char *format
, ...);
249 static ipp_t
*do_request(http_t
*http
, ipp_t
*request
,
250 const char *resource
);
251 static void fix_make_model(char *make_model
,
252 const char *old_make_model
,
253 int make_model_size
);
254 static void free_array(cups_array_t
*a
);
255 static void free_cache(void);
256 static http_addrlist_t
*get_interface_addresses(const char *ifname
);
257 static void hex_debug(unsigned char *buffer
, size_t len
);
258 static void list_device(snmp_cache_t
*cache
);
259 static int open_snmp_socket(void);
260 static const char *password_cb(const char *prompt
);
261 static void probe_device(snmp_cache_t
*device
);
262 static void read_snmp_conf(const char *address
);
263 static void read_snmp_response(int fd
);
264 static double run_time(void);
265 static void scan_devices(int fd
);
266 static void send_snmp_query(int fd
, http_addr_t
*addr
, int version
,
267 const char *community
,
268 const unsigned request_id
,
270 static int try_connect(http_addr_t
*addr
, const char *addrname
,
272 static void update_cache(snmp_cache_t
*device
, const char *uri
,
273 const char *id
, const char *make_model
);
280 static cups_array_t
*Addresses
= NULL
;
281 static cups_array_t
*Communities
= NULL
;
282 static cups_array_t
*Devices
= NULL
;
283 static int DebugLevel
= 0;
284 static int DeviceTypeOID
[] = { 1, 3, 6, 1, 2, 1, 25, 3,
286 static int DeviceDescOID
[] = { 1, 3, 6, 1, 2, 1, 25, 3,
288 static unsigned DeviceTypeRequest
;
289 static unsigned DeviceDescRequest
;
290 static int HostNameLookups
= 0;
291 static int MaxRunTime
= 10;
292 static struct timeval StartTime
;
296 * 'main()' - Discover printers via SNMP.
299 int /* O - Exit status */
300 main(int argc
, /* I - Number of command-line arguments (6 or 7) */
301 char *argv
[]) /* I - Command-line arguments */
303 int fd
; /* SNMP socket */
304 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
305 struct sigaction action
; /* Actions for POSIX signals */
306 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
310 * Check command-line options...
315 fputs("Usage: snmp [host-or-ip-address]\n", stderr
);
320 * Set the password callback for IPP operations...
323 cupsSetPasswordCB(password_cb
);
326 * Catch SIGALRM signals...
330 sigset(SIGALRM
, alarm_handler
);
331 #elif defined(HAVE_SIGACTION)
332 memset(&action
, 0, sizeof(action
));
334 sigemptyset(&action
.sa_mask
);
335 sigaddset(&action
.sa_mask
, SIGALRM
);
336 action
.sa_handler
= alarm_handler
;
337 sigaction(SIGALRM
, &action
, NULL
);
339 signal(SIGALRM
, alarm_handler
);
340 #endif /* HAVE_SIGSET */
343 * Open the SNMP socket...
346 if ((fd
= open_snmp_socket()) < 0)
350 * Read the configuration file and any cache data...
353 read_snmp_conf(argv
[1]);
355 Devices
= cupsArrayNew((cups_array_func_t
)compare_cache
, NULL
);
358 * Scan for devices...
364 * Close, free, and return with no errors...
369 free_array(Addresses
);
370 free_array(Communities
);
378 * 'add_array()' - Add a string to an array.
381 static char * /* O - New string */
382 add_array(cups_array_t
*a
, /* I - Array */
383 const char *s
) /* I - String to add */
385 char *dups
; /* New string */
390 cupsArrayAdd(a
, dups
);
397 * 'add_cache()' - Add a cached device...
401 add_cache(http_addr_t
*addr
, /* I - Device IP address */
402 const char *addrname
, /* I - IP address or name string */
403 const char *uri
, /* I - Device URI */
404 const char *id
, /* I - 1284 device ID */
405 const char *make_and_model
) /* I - Make and model */
407 snmp_cache_t
*temp
; /* New device entry */
410 debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
411 "id=\"%s\", make_and_model=\"%s\")\n",
412 addr
, addrname
, uri
? uri
: "(null)", id
? id
: "(null)",
413 make_and_model
? make_and_model
: "(null)");
415 temp
= calloc(1, sizeof(snmp_cache_t
));
416 memcpy(&(temp
->address
), addr
, sizeof(temp
->address
));
418 temp
->addrname
= strdup(addrname
);
421 temp
->uri
= strdup(uri
);
424 temp
->id
= strdup(id
);
427 temp
->make_and_model
= strdup(make_and_model
);
429 cupsArrayAdd(Devices
, temp
);
437 * 'alarm_handler()' - Handle alarm signals...
441 alarm_handler(int sig
) /* I - Signal number */
449 #if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
450 signal(SIGALRM
, alarm_handler
);
451 #endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
454 write(2, "DEBUG: ALARM!\n", 14);
459 * 'asn1_decode_snmp()' - Decode a SNMP packet.
462 static int /* O - 0 on success, -1 on error */
463 asn1_decode_snmp(unsigned char *buffer
, /* I - Buffer */
464 size_t len
, /* I - Size of buffer */
465 snmp_packet_t
*packet
) /* I - SNMP packet */
467 unsigned char *bufptr
, /* Pointer into the data */
468 *bufend
; /* End of data */
469 int length
; /* Length of value */
473 * Initialize the decoding...
476 memset(packet
, 0, sizeof(snmp_packet_t
));
479 bufend
= buffer
+ len
;
481 if (asn1_get_type(&bufptr
, bufend
) != ASN1_SEQUENCE
)
482 packet
->error
= "Packet does not start with SEQUENCE";
483 else if (asn1_get_length(&bufptr
, bufend
) == 0)
484 packet
->error
= "SEQUENCE uses indefinite length";
485 else if (asn1_get_type(&bufptr
, bufend
) != ASN1_INTEGER
)
486 packet
->error
= "No version number";
487 else if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0)
488 packet
->error
= "Version uses indefinite length";
489 else if ((packet
->version
= asn1_get_integer(&bufptr
, bufend
, length
))
491 packet
->error
= "Bad SNMP version number";
492 else if (asn1_get_type(&bufptr
, bufend
) != ASN1_OCTET_STRING
)
493 packet
->error
= "No community name";
494 else if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0)
495 packet
->error
= "Community name uses indefinite length";
498 asn1_get_string(&bufptr
, bufend
, length
, packet
->community
,
499 sizeof(packet
->community
));
501 if ((packet
->request_type
= asn1_get_type(&bufptr
, bufend
))
502 != ASN1_GET_RESPONSE
)
503 packet
->error
= "Packet does not contain a Get-Response-PDU";
504 else if (asn1_get_length(&bufptr
, bufend
) == 0)
505 packet
->error
= "Get-Response-PDU uses indefinite length";
506 else if (asn1_get_type(&bufptr
, bufend
) != ASN1_INTEGER
)
507 packet
->error
= "No request-id";
508 else if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0)
509 packet
->error
= "request-id uses indefinite length";
512 packet
->request_id
= asn1_get_integer(&bufptr
, bufend
, length
);
514 if (asn1_get_type(&bufptr
, bufend
) != ASN1_INTEGER
)
515 packet
->error
= "No error-status";
516 else if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0)
517 packet
->error
= "error-status uses indefinite length";
520 packet
->error_status
= asn1_get_integer(&bufptr
, bufend
, length
);
522 if (asn1_get_type(&bufptr
, bufend
) != ASN1_INTEGER
)
523 packet
->error
= "No error-index";
524 else if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0)
525 packet
->error
= "error-index uses indefinite length";
528 packet
->error_index
= asn1_get_integer(&bufptr
, bufend
, length
);
530 if (asn1_get_type(&bufptr
, bufend
) != ASN1_SEQUENCE
)
531 packet
->error
= "No variable-bindings SEQUENCE";
532 else if (asn1_get_length(&bufptr
, bufend
) == 0)
533 packet
->error
= "variable-bindings uses indefinite length";
534 else if (asn1_get_type(&bufptr
, bufend
) != ASN1_SEQUENCE
)
535 packet
->error
= "No VarBind SEQUENCE";
536 else if (asn1_get_length(&bufptr
, bufend
) == 0)
537 packet
->error
= "VarBind uses indefinite length";
538 else if (asn1_get_type(&bufptr
, bufend
) != ASN1_OID
)
539 packet
->error
= "No name OID";
540 else if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0)
541 packet
->error
= "Name OID uses indefinite length";
544 asn1_get_oid(&bufptr
, bufend
, length
, packet
->object_name
,
547 packet
->object_type
= asn1_get_type(&bufptr
, bufend
);
549 if ((length
= asn1_get_length(&bufptr
, bufend
)) == 0 &&
550 packet
->object_type
!= ASN1_NULL_VALUE
&&
551 packet
->object_type
!= ASN1_OCTET_STRING
)
552 packet
->error
= "Value uses indefinite length";
555 switch (packet
->object_type
)
558 packet
->object_value
.boolean
=
559 asn1_get_integer(&bufptr
, bufend
, length
);
563 packet
->object_value
.integer
=
564 asn1_get_integer(&bufptr
, bufend
, length
);
567 case ASN1_NULL_VALUE
:
570 case ASN1_OCTET_STRING
:
571 asn1_get_string(&bufptr
, bufend
, length
,
572 packet
->object_value
.string
,
577 asn1_get_oid(&bufptr
, bufend
, length
,
578 packet
->object_value
.oid
, SNMP_MAX_OID
);
582 packet
->error
= "Unsupported value type";
592 return (packet
->error
? -1 : 0);
597 * 'asn1_debug()' - Decode an ASN1-encoded message.
601 asn1_debug(unsigned char *buffer
, /* I - Buffer */
602 size_t len
, /* I - Length of buffer */
603 int indent
) /* I - Indentation */
605 int i
; /* Looping var */
606 unsigned char *bufend
; /* End of buffer */
607 int integer
; /* Number value */
608 int oid
[SNMP_MAX_OID
]; /* OID value */
609 char string
[SNMP_MAX_STRING
];/* String value */
610 unsigned char value_type
; /* Type of value */
611 int value_length
; /* Length of value */
614 bufend
= buffer
+ len
;
616 while (buffer
< bufend
)
622 value_type
= asn1_get_type(&buffer
, bufend
);
623 value_length
= asn1_get_length(&buffer
, bufend
);
628 integer
= asn1_get_integer(&buffer
, bufend
, value_length
);
630 fprintf(stderr
, "DEBUG: %*sBOOLEAN %d bytes %d\n", indent
, "",
631 value_length
, integer
);
635 integer
= asn1_get_integer(&buffer
, bufend
, value_length
);
637 fprintf(stderr
, "DEBUG: %*sINTEGER %d bytes %d\n", indent
, "",
638 value_length
, integer
);
641 case ASN1_OCTET_STRING
:
642 fprintf(stderr
, "DEBUG: %*sOCTET STRING %d bytes \"%s\"\n", indent
, "",
643 value_length
, asn1_get_string(&buffer
, bufend
,
644 value_length
, string
,
648 case ASN1_NULL_VALUE
:
649 fprintf(stderr
, "DEBUG: %*sNULL VALUE %d bytes\n", indent
, "",
652 buffer
+= value_length
;
656 asn1_get_oid(&buffer
, bufend
, value_length
, oid
, SNMP_MAX_OID
);
658 fprintf(stderr
, "DEBUG: %*sOID %d bytes ", indent
, "",
660 for (i
= 0; oid
[i
]; i
++)
661 fprintf(stderr
, ".%d", oid
[i
]);
666 fprintf(stderr
, "DEBUG: %*sSEQUENCE %d bytes\n", indent
, "",
668 asn1_debug(buffer
, value_length
, indent
+ 4);
670 buffer
+= value_length
;
673 case ASN1_GET_REQUEST
:
674 fprintf(stderr
, "DEBUG: %*sGet-Request-PDU %d bytes\n", indent
, "",
676 asn1_debug(buffer
, value_length
, indent
+ 4);
678 buffer
+= value_length
;
681 case ASN1_GET_RESPONSE
:
682 fprintf(stderr
, "DEBUG: %*sGet-Response-PDU %d bytes\n", indent
, "",
684 asn1_debug(buffer
, value_length
, indent
+ 4);
686 buffer
+= value_length
;
690 fprintf(stderr
, "DEBUG: %*sUNKNOWN(%x) %d bytes\n", indent
, "",
691 value_type
, value_length
);
693 buffer
+= value_length
;
701 * 'asn1_encode_snmp()' - Encode a SNMP packet.
704 static int /* O - Length on success, -1 on error */
705 asn1_encode_snmp(unsigned char *buffer
, /* I - Buffer */
706 size_t bufsize
, /* I - Size of buffer */
707 snmp_packet_t
*packet
) /* I - SNMP packet */
709 unsigned char *bufptr
; /* Pointer into buffer */
710 int total
, /* Total length */
711 msglen
, /* Length of entire message */
712 commlen
, /* Length of community string */
713 reqlen
, /* Length of request */
714 listlen
, /* Length of variable list */
715 varlen
, /* Length of variable */
716 namelen
, /* Length of object name OID */
717 valuelen
; /* Length of object value */
721 * Get the lengths of the community string, OID, and message...
724 namelen
= asn1_size_oid(packet
->object_name
);
726 switch (packet
->object_type
)
728 case ASN1_NULL_VALUE
:
733 valuelen
= asn1_size_integer(packet
->object_value
.boolean
);
737 valuelen
= asn1_size_integer(packet
->object_value
.integer
);
740 case ASN1_OCTET_STRING
:
741 valuelen
= strlen(packet
->object_value
.string
);
745 valuelen
= asn1_size_oid(packet
->object_value
.oid
);
749 packet
->error
= "Unknown object type";
753 varlen
= 1 + asn1_size_length(namelen
) + namelen
+
754 1 + asn1_size_length(valuelen
) + valuelen
;
755 listlen
= 1 + asn1_size_length(varlen
) + varlen
;
756 reqlen
= 2 + asn1_size_integer(packet
->request_id
) +
757 2 + asn1_size_integer(packet
->error_status
) +
758 2 + asn1_size_integer(packet
->error_index
) +
759 1 + asn1_size_length(listlen
) + listlen
;
760 commlen
= strlen(packet
->community
);
761 msglen
= 2 + asn1_size_integer(packet
->version
) +
762 1 + asn1_size_length(commlen
) + commlen
+
763 1 + asn1_size_length(reqlen
) + reqlen
;
764 total
= 1 + asn1_size_length(msglen
) + msglen
;
768 packet
->error
= "Message too large for buffer";
773 * Then format the message...
778 *bufptr
++ = ASN1_SEQUENCE
; /* SNMPv1 message header */
779 asn1_set_length(&bufptr
, msglen
);
781 asn1_set_integer(&bufptr
, packet
->version
);
784 *bufptr
++ = ASN1_OCTET_STRING
; /* community */
785 asn1_set_length(&bufptr
, commlen
);
786 memcpy(bufptr
, packet
->community
, commlen
);
789 *bufptr
++ = packet
->request_type
; /* Get-Request-PDU */
790 asn1_set_length(&bufptr
, reqlen
);
792 asn1_set_integer(&bufptr
, packet
->request_id
);
794 asn1_set_integer(&bufptr
, packet
->error_status
);
796 asn1_set_integer(&bufptr
, packet
->error_index
);
798 *bufptr
++ = ASN1_SEQUENCE
; /* variable-bindings */
799 asn1_set_length(&bufptr
, listlen
);
801 *bufptr
++ = ASN1_SEQUENCE
; /* variable */
802 asn1_set_length(&bufptr
, varlen
);
804 asn1_set_oid(&bufptr
, packet
->object_name
);
807 switch (packet
->object_type
)
809 case ASN1_NULL_VALUE
:
810 *bufptr
++ = ASN1_NULL_VALUE
; /* ObjectValue */
811 *bufptr
++ = 0; /* Length */
815 asn1_set_integer(&bufptr
, packet
->object_value
.boolean
);
819 asn1_set_integer(&bufptr
, packet
->object_value
.integer
);
822 case ASN1_OCTET_STRING
:
823 *bufptr
++ = ASN1_OCTET_STRING
;
824 asn1_set_length(&bufptr
, valuelen
);
825 memcpy(bufptr
, packet
->object_value
.string
, valuelen
);
830 asn1_set_oid(&bufptr
, packet
->object_value
.oid
);
834 return (bufptr
- buffer
);
839 * 'asn1_get_integer()' - Get an integer value.
842 static int /* O - Integer value */
844 unsigned char **buffer
, /* IO - Pointer in buffer */
845 unsigned char *bufend
, /* I - End of buffer */
846 int length
) /* I - Length of value */
848 int value
; /* Integer value */
852 length
> 0 && *buffer
< bufend
;
853 length
--, (*buffer
) ++)
854 value
= (value
<< 8) | **buffer
;
861 * 'asn1_get_length()' - Get a value length.
864 static int /* O - Length */
865 asn1_get_length(unsigned char **buffer
, /* IO - Pointer in buffer */
866 unsigned char *bufend
) /* I - End of buffer */
868 int length
; /* Length */
875 length
= asn1_get_integer(buffer
, bufend
, length
& 127);
882 * 'asn1_get_oid()' - Get an OID value.
885 static int /* O - Last OID number */
887 unsigned char **buffer
, /* IO - Pointer in buffer */
888 unsigned char *bufend
, /* I - End of buffer */
889 int length
, /* I - Length of value */
890 int *oid
, /* I - OID buffer */
891 int oidsize
) /* I - Size of OID buffer */
893 unsigned char *valend
; /* End of value */
894 int *oidend
; /* End of OID buffer */
895 int number
; /* OID number */
898 valend
= *buffer
+ length
;
899 oidend
= oid
+ oidsize
- 1;
904 number
= asn1_get_packed(buffer
, bufend
);
908 *oid
++ = number
/ 40;
909 number
= number
% 40;
919 while (*buffer
< valend
)
921 number
= asn1_get_packed(buffer
, bufend
);
934 * 'asn1_get_packed()' - Get a packed integer value.
937 static int /* O - Value */
939 unsigned char **buffer
, /* IO - Pointer in buffer */
940 unsigned char *bufend
) /* I - End of buffer */
942 int value
; /* Value */
947 while ((**buffer
& 128) && *buffer
< bufend
)
949 value
= (value
<< 7) | (**buffer
& 127);
953 if (*buffer
< bufend
)
955 value
= (value
<< 7) | **buffer
;
964 * 'asn1_get_string()' - Get a string value.
967 static char * /* O - String */
969 unsigned char **buffer
, /* IO - Pointer in buffer */
970 unsigned char *bufend
, /* I - End of buffer */
971 int length
, /* I - Value length */
972 char *string
, /* I - String buffer */
973 int strsize
) /* I - String buffer size */
975 if (length
< strsize
)
977 memcpy(string
, *buffer
, length
);
978 string
[length
] = '\0';
982 memcpy(string
, buffer
, strsize
- 1);
983 string
[strsize
- 1] = '\0';
993 * 'asn1_get_type()' - Get a value type.
996 static int /* O - Type */
997 asn1_get_type(unsigned char **buffer
, /* IO - Pointer in buffer */
998 unsigned char *bufend
) /* I - End of buffer */
1000 int type
; /* Type */
1006 if ((type
& 31) == 31)
1007 type
= asn1_get_packed(buffer
, bufend
);
1014 * 'asn1_set_integer()' - Set an integer value.
1018 asn1_set_integer(unsigned char **buffer
,/* IO - Pointer in buffer */
1019 int integer
) /* I - Integer value */
1021 **buffer
= ASN1_INTEGER
;
1024 if (integer
> 0x7fffff || integer
< -0x800000)
1028 **buffer
= integer
>> 24;
1030 **buffer
= integer
>> 16;
1032 **buffer
= integer
>> 8;
1037 else if (integer
> 0x7fff || integer
< -0x8000)
1041 **buffer
= integer
>> 16;
1043 **buffer
= integer
>> 8;
1048 else if (integer
> 0x7f || integer
< -0x80)
1052 **buffer
= integer
>> 8;
1068 * 'asn1_set_length()' - Set a value length.
1072 asn1_set_length(unsigned char **buffer
, /* IO - Pointer in buffer */
1073 int length
) /* I - Length value */
1077 **buffer
= 0x82; /* 2-byte length */
1079 **buffer
= length
>> 8;
1084 else if (length
> 127)
1086 **buffer
= 0x81; /* 1-byte length */
1093 **buffer
= length
; /* Length */
1100 * 'asn1_set_oid()' - Set an OID value.
1104 asn1_set_oid(unsigned char **buffer
, /* IO - Pointer in buffer */
1105 const int *oid
) /* I - OID value */
1107 **buffer
= ASN1_OID
;
1110 asn1_set_length(buffer
, asn1_size_oid(oid
));
1112 asn1_set_packed(buffer
, oid
[0] * 40 + oid
[1]);
1114 for (oid
+= 2; *oid
; oid
++)
1115 asn1_set_packed(buffer
, *oid
);
1120 * 'asn1_set_packed()' - Set a packed integer value.
1124 asn1_set_packed(unsigned char **buffer
, /* IO - Pointer in buffer */
1125 int integer
) /* I - Integer value */
1127 if (integer
> 0xfffffff)
1129 **buffer
= (integer
>> 28) & 0x7f;
1133 if (integer
> 0x1fffff)
1135 **buffer
= (integer
>> 21) & 0x7f;
1139 if (integer
> 0x3fff)
1141 **buffer
= (integer
>> 14) & 0x7f;
1147 **buffer
= (integer
>> 7) & 0x7f;
1151 **buffer
= integer
& 0x7f;
1156 * 'asn1_size_integer()' - Figure out the number of bytes needed for an
1160 static int /* O - Size in bytes */
1161 asn1_size_integer(int integer
) /* I - Integer value */
1163 if (integer
> 0x7fffff || integer
< -0x800000)
1165 else if (integer
> 0x7fff || integer
< -0x8000)
1167 else if (integer
> 0x7f || integer
< -0x80)
1175 * 'asn1_size_length()' - Figure out the number of bytes needed for a
1179 static int /* O - Size in bytes */
1180 asn1_size_length(int length
) /* I - Length value */
1184 else if (length
> 0x7f)
1192 * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an
1196 static int /* O - Size in bytes */
1197 asn1_size_oid(const int *oid
) /* I - OID value */
1199 int length
; /* Length of value */
1202 for (length
= asn1_size_packed(oid
[0] * 40 + oid
[1]), oid
+= 2; *oid
; oid
++)
1203 length
+= asn1_size_packed(*oid
);
1210 * 'asn1_size_packed()' - Figure out the number of bytes needed for a
1211 * packed integer value.
1214 static int /* O - Size in bytes */
1215 asn1_size_packed(int integer
) /* I - Integer value */
1217 if (integer
> 0xfffffff)
1219 else if (integer
> 0x1fffff)
1221 else if (integer
> 0x3fff)
1223 else if (integer
> 0x7f)
1231 * 'compare_cache()' - Compare two cache entries.
1234 static int /* O - Result of comparison */
1235 compare_cache(snmp_cache_t
*a
, /* I - First cache entry */
1236 snmp_cache_t
*b
) /* I - Second cache entry */
1238 return (strcasecmp(a
->addrname
, b
->addrname
));
1243 * 'debug_printf()' - Display some debugging information.
1247 debug_printf(const char *format
, /* I - Printf-style format string */
1248 ...) /* I - Additional arguments as needed */
1250 va_list ap
; /* Pointer to arguments */
1256 va_start(ap
, format
);
1257 vfprintf(stderr
, format
, ap
);
1263 * 'do_request()' - Do a non-blocking IPP request.
1266 static ipp_t
* /* O - Response data or NULL */
1267 do_request(http_t
*http
, /* I - HTTP connection to server */
1268 ipp_t
*request
, /* I - IPP request */
1269 const char *resource
) /* I - HTTP resource for POST */
1271 ipp_t
*response
; /* IPP response data */
1272 http_status_t status
; /* Status of HTTP request */
1273 ipp_state_t state
; /* State of IPP processing */
1277 * Setup the HTTP variables needed...
1280 httpClearFields(http
);
1281 httpSetLength(http
, ippLength(request
));
1282 httpSetField(http
, HTTP_FIELD_CONTENT_TYPE
, "application/ipp");
1285 * Do the POST request...
1288 debug_printf("DEBUG: %.3f POST %s...\n", run_time(), resource
);
1290 if (httpPost(http
, resource
))
1292 if (httpReconnect(http
))
1294 _cupsSetError(IPP_DEVICE_ERROR
, "Unable to reconnect");
1297 else if (httpPost(http
, resource
))
1299 _cupsSetError(IPP_GONE
, "Unable to POST");
1305 * Send the IPP data...
1308 request
->state
= IPP_IDLE
;
1309 status
= HTTP_CONTINUE
;
1311 while ((state
= ippWrite(http
, request
)) != IPP_DATA
)
1312 if (state
== IPP_ERROR
)
1314 status
= HTTP_ERROR
;
1317 else if (httpCheck(http
))
1319 if ((status
= httpUpdate(http
)) != HTTP_CONTINUE
)
1324 * Get the server's return status...
1327 debug_printf("DEBUG: %.3f Getting response...\n", run_time());
1329 while (status
== HTTP_CONTINUE
)
1330 if (httpWait(http
, 1000))
1331 status
= httpUpdate(http
);
1334 status
= HTTP_ERROR
;
1335 http
->error
= ETIMEDOUT
;
1338 if (status
!= HTTP_OK
)
1341 * Flush any error message...
1350 * Read the response...
1353 response
= ippNew();
1355 while ((state
= ippRead(http
, response
)) != IPP_DATA
)
1356 if (state
== IPP_ERROR
)
1359 * Delete the response...
1362 ippDelete(response
);
1365 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, strerror(errno
));
1371 * Delete the original request and return the response...
1378 ipp_attribute_t
*attr
; /* status-message attribute */
1381 attr
= ippFindAttribute(response
, "status-message", IPP_TAG_TEXT
);
1383 _cupsSetError(response
->request
.status
.status_code
,
1384 attr
? attr
->values
[0].string
.text
:
1385 ippErrorString(response
->request
.status
.status_code
));
1387 else if (status
!= HTTP_OK
)
1391 case HTTP_NOT_FOUND
:
1392 _cupsSetError(IPP_NOT_FOUND
, httpStatus(status
));
1395 case HTTP_UNAUTHORIZED
:
1396 _cupsSetError(IPP_NOT_AUTHORIZED
, httpStatus(status
));
1399 case HTTP_FORBIDDEN
:
1400 _cupsSetError(IPP_FORBIDDEN
, httpStatus(status
));
1403 case HTTP_BAD_REQUEST
:
1404 _cupsSetError(IPP_BAD_REQUEST
, httpStatus(status
));
1407 case HTTP_REQUEST_TOO_LARGE
:
1408 _cupsSetError(IPP_REQUEST_VALUE
, httpStatus(status
));
1411 case HTTP_NOT_IMPLEMENTED
:
1412 _cupsSetError(IPP_OPERATION_NOT_SUPPORTED
, httpStatus(status
));
1415 case HTTP_NOT_SUPPORTED
:
1416 _cupsSetError(IPP_VERSION_NOT_SUPPORTED
, httpStatus(status
));
1420 _cupsSetError(IPP_SERVICE_UNAVAILABLE
, httpStatus(status
));
1430 * 'fix_make_model()' - Fix common problems in the make-and-model string.
1435 char *make_model
, /* I - New make-and-model string */
1436 const char *old_make_model
, /* I - Old make-and-model string */
1437 int make_model_size
) /* I - Size of new string buffer */
1439 char *mmptr
; /* Pointer into make-and-model string */
1443 * Fix some common problems with the make-and-model string so
1444 * that printer driver detection works better...
1447 if (!strncasecmp(old_make_model
, "Hewlett-Packard", 15))
1450 * Strip leading Hewlett-Packard and hp prefixes and replace
1451 * with a single HP manufacturer prefix...
1454 mmptr
= (char *)old_make_model
+ 15;
1456 while (isspace(*mmptr
& 255))
1459 if (!strncasecmp(mmptr
, "hp", 2))
1463 while (isspace(*mmptr
& 255))
1467 make_model
[0] = 'H';
1468 make_model
[1] = 'P';
1469 make_model
[2] = ' ';
1470 strlcpy(make_model
+ 3, mmptr
, make_model_size
- 3);
1472 else if (!strncasecmp(old_make_model
, "deskjet", 7))
1473 snprintf(make_model
, make_model_size
, "HP DeskJet%s", old_make_model
+ 7);
1474 else if (!strncasecmp(old_make_model
, "officejet", 9))
1475 snprintf(make_model
, make_model_size
, "HP OfficeJet%s", old_make_model
+ 9);
1476 else if (!strncasecmp(old_make_model
, "stylus_pro_", 11))
1477 snprintf(make_model
, make_model_size
, "EPSON Stylus Pro %s",
1478 old_make_model
+ 11);
1480 strlcpy(make_model
, old_make_model
, make_model_size
);
1482 if ((mmptr
= strstr(make_model
, ", Inc.,")) != NULL
)
1485 * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
1486 * becomes "Tektronix Phaser 560"...
1489 _cups_strcpy(mmptr
, mmptr
+ 7);
1492 if ((mmptr
= strstr(make_model
, " Network")) != NULL
)
1495 * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
1496 * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
1502 if ((mmptr
= strchr(make_model
, ',')) != NULL
)
1505 * Drop anything after a trailing comma...
1514 * 'free_array()' - Free an array of strings.
1518 free_array(cups_array_t
*a
) /* I - Array */
1520 char *s
; /* Current string */
1523 for (s
= (char *)cupsArrayFirst(a
); s
; s
= (char *)cupsArrayNext(a
))
1531 * 'free_cache()' - Free the array of cached devices.
1537 snmp_cache_t
*cache
; /* Cached device */
1540 for (cache
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
1542 cache
= (snmp_cache_t
*)cupsArrayNext(Devices
))
1544 free(cache
->addrname
);
1552 if (cache
->make_and_model
)
1553 free(cache
->make_and_model
);
1558 cupsArrayDelete(Devices
);
1564 * 'get_interface_addresses()' - Get the broadcast address(es) associated
1565 * with an interface.
1568 static http_addrlist_t
* /* O - List of addresses */
1569 get_interface_addresses(
1570 const char *ifname
) /* I - Interface name */
1572 struct ifaddrs
*addrs
, /* Interface address list */
1573 *addr
; /* Current interface address */
1574 http_addrlist_t
*first
, /* First address in list */
1575 *last
, /* Last address in list */
1576 *current
; /* Current address */
1579 if (getifaddrs(&addrs
) < 0)
1582 for (addr
= addrs
, first
= NULL
, last
= NULL
; addr
; addr
= addr
->ifa_next
)
1583 if ((addr
->ifa_flags
& IFF_BROADCAST
) && addr
->ifa_broadaddr
&&
1584 addr
->ifa_broadaddr
->sa_family
== AF_INET
&&
1585 (!ifname
|| !strcmp(ifname
, addr
->ifa_name
)))
1587 current
= calloc(1, sizeof(http_addrlist_t
));
1589 memcpy(&(current
->addr
), addr
->ifa_broadaddr
,
1590 sizeof(struct sockaddr_in
));
1595 last
->next
= current
;
1607 * 'hex_debug()' - Output hex debugging data...
1611 hex_debug(unsigned char *buffer
, /* I - Buffer */
1612 size_t len
) /* I - Number of bytes */
1614 int col
; /* Current column */
1617 fputs("DEBUG: Hex dump of packet:\n", stderr
);
1619 for (col
= 0; len
> 0; col
++, buffer
++, len
--)
1621 if ((col
& 15) == 0)
1622 fprintf(stderr
, "DEBUG: %04X ", col
);
1624 fprintf(stderr
, " %02X", *buffer
);
1626 if ((col
& 15) == 15)
1636 * 'list_device()' - List a device we found...
1640 list_device(snmp_cache_t
*cache
) /* I - Cached device */
1643 printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
1645 cache
->make_and_model
? cache
->make_and_model
: "Unknown",
1646 cache
->make_and_model
? cache
->make_and_model
: "Unknown",
1647 cache
->addrname
, cache
->id
? cache
->id
: "");
1652 * 'open_snmp_socket()' - Open the SNMP broadcast socket.
1655 static int /* O - SNMP socket file descriptor */
1656 open_snmp_socket(void)
1658 int fd
; /* SNMP socket file descriptor */
1659 int val
; /* Socket option value */
1663 * Create the SNMP socket...
1666 if ((fd
= socket(AF_INET
, SOCK_DGRAM
, 0)) < 0)
1668 fprintf(stderr
, "ERROR: Unable to create SNMP socket - %s\n",
1675 * Set the "broadcast" flag...
1680 if (setsockopt(fd
, SOL_SOCKET
, SO_BROADCAST
, &val
, sizeof(val
)))
1682 fprintf(stderr
, "ERROR: Unable to set broadcast mode - %s\n",
1695 * 'password_cb()' - Handle authentication requests.
1697 * All we do right now is return NULL, indicating that no authentication
1701 static const char * /* O - Password (NULL) */
1702 password_cb(const char *prompt
) /* I - Prompt message */
1704 (void)prompt
; /* Anti-compiler-warning-code */
1711 * 'probe_device()' - Probe a device to discover whether it is a printer.
1713 * TODO: Try using the Port Monitor MIB to discover the correct protocol
1714 * to use - first need a commercially-available printer that supports
1719 probe_device(snmp_cache_t
*device
) /* I - Device */
1721 int i
, j
; /* Looping vars */
1722 http_t
*http
; /* HTTP connection for IPP */
1723 char uri
[1024]; /* Full device URI */
1727 * Try connecting via IPP first...
1730 debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device
->addrname
);
1732 if (device
->make_and_model
&&
1733 (!strncasecmp(device
->make_and_model
, "Epson", 5) ||
1734 !strncasecmp(device
->make_and_model
, "HP ", 3) ||
1735 !strncasecmp(device
->make_and_model
, "Hewlett", 7) ||
1736 !strncasecmp(device
->make_and_model
, "Kyocera", 7) ||
1737 !strncasecmp(device
->make_and_model
, "Lexmark", 7) ||
1738 !strncasecmp(device
->make_and_model
, "Tektronix", 9) ||
1739 !strncasecmp(device
->make_and_model
, "Xerox", 5)))
1742 * Epson, HP, Kyocera, Lexmark, Tektronix, and Xerox printers often lock
1743 * up on IPP probes, so exclude them from the IPP connection test...
1751 * Otherwise, try connecting for up to 1 second...
1755 http
= httpConnect(device
->addrname
, 631);
1762 * IPP is supported...
1765 ipp_t
*request
, /* IPP request */
1766 *response
; /* IPP response */
1767 ipp_attribute_t
*model
, /* printer-make-and-model attribute */
1768 *info
, /* printer-info attribute */
1769 *supported
; /* printer-uri-supported attribute */
1770 char make_model
[256],/* Make and model string to use */
1771 temp
[256]; /* Temporary make/model string */
1772 int num_uris
; /* Number of good URIs */
1773 static const char * const resources
[] =
1774 { /* Common resource paths for IPP */
1778 /*"/EPSON_IPP_Printer",*/
1787 * Use non-blocking IO...
1790 httpBlocking(http
, 0);
1793 * Loop through a list of common resources that covers 99% of the
1794 * IPP-capable printers on the market today...
1797 for (i
= 0, num_uris
= 0;
1798 i
< (int)(sizeof(resources
) / sizeof(resources
[0]));
1802 * Stop early if we are out of time...
1805 if (MaxRunTime
> 0 && run_time() >= MaxRunTime
)
1809 * Don't look past /ipp if we have found a working URI...
1812 if (num_uris
> 0 && strncmp(resources
[i
], "/ipp", 4))
1815 httpAssembleURI(HTTP_URI_CODING_ALL
, uri
, sizeof(uri
), "ipp", NULL
,
1816 device
->addrname
, 631, resources
[i
]);
1818 request
= ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES
);
1820 ippAddString(request
, IPP_TAG_OPERATION
, IPP_TAG_URI
, "printer-uri",
1823 response
= do_request(http
, request
, resources
[i
]);
1825 debug_printf("DEBUG: %.3f %s %s (%s)\n", run_time(), uri
,
1826 ippErrorString(cupsLastError()), cupsLastErrorString());
1828 if (response
&& response
->request
.status
.status_code
== IPP_OK
)
1830 model
= ippFindAttribute(response
, "printer-make-and-model",
1832 info
= ippFindAttribute(response
, "printer-info", IPP_TAG_TEXT
);
1833 supported
= ippFindAttribute(response
, "printer-uri-supported",
1838 fprintf(stderr
, "ERROR: Missing printer-uri-supported from %s!\n",
1845 debug_printf("DEBUG: printer-info=\"%s\"\n",
1846 info
? info
->values
[0].string
.text
: "(null)");
1847 debug_printf("DEBUG: printer-make-and-model=\"%s\"\n",
1848 model
? model
->values
[0].string
.text
: "(null)");
1851 * Don't advertise this port if the printer actually only supports
1852 * a more generic version...
1855 if (!strncmp(resources
[i
], "/ipp/", 5))
1857 for (j
= 0; j
< supported
->num_values
; j
++)
1858 if (strstr(supported
->values
[j
].string
.text
, "/ipp/"))
1861 if (j
>= supported
->num_values
)
1863 ippDelete(response
);
1869 * Don't use the printer-info attribute if it does not contain the
1870 * IEEE-1284 device ID data...
1874 (!strchr(info
->values
[0].string
.text
, ':') ||
1875 !strchr(info
->values
[0].string
.text
, ';')))
1879 * Don't use the printer-make-and-model if it contains a generic
1880 * string like "Ricoh IPP Printer"...
1883 if (model
&& strstr(model
->values
[0].string
.text
, "IPP Printer"))
1887 * If we don't have a printer-make-and-model string from the printer
1888 * but do have the 1284 device ID string, generate a make-and-model
1889 * string from the device ID info...
1893 strlcpy(temp
, model
->values
[0].string
.text
, sizeof(temp
));
1895 backendGetMakeModel(info
->values
[0].string
.text
, temp
, sizeof(temp
));
1899 fix_make_model(make_model
, temp
, sizeof(make_model
));
1902 * Update the current device or add a new printer to the cache...
1906 update_cache(device
, uri
,
1907 info
? info
->values
[0].string
.text
: NULL
,
1908 make_model
[0] ? make_model
: NULL
);
1910 add_cache(&(device
->address
), device
->addrname
, uri
,
1911 info
? info
->values
[0].string
.text
: NULL
,
1912 make_model
[0] ? make_model
: NULL
);
1917 ippDelete(response
);
1919 if (num_uris
> 0 && cupsLastError() != IPP_OK
)
1930 * OK, now try the standard ports...
1933 if (!try_connect(&(device
->address
), device
->addrname
, 9100))
1935 debug_printf("DEBUG: %s supports AppSocket!\n", device
->addrname
);
1937 snprintf(uri
, sizeof(uri
), "socket://%s", device
->addrname
);
1938 update_cache(device
, uri
, NULL
, NULL
);
1940 else if (!try_connect(&(device
->address
), device
->addrname
, 515))
1942 debug_printf("DEBUG: %s supports LPD!\n", device
->addrname
);
1944 snprintf(uri
, sizeof(uri
), "lpd://%s/", device
->addrname
);
1945 update_cache(device
, uri
, NULL
, NULL
);
1951 * 'read_snmp_conf()' - Read the snmp.conf file.
1955 read_snmp_conf(const char *address
) /* I - Single address to probe */
1957 cups_file_t
*fp
; /* File pointer */
1958 char filename
[1024], /* Filename */
1959 line
[1024], /* Line from file */
1960 *value
; /* Value on line */
1961 int linenum
; /* Line number */
1962 const char *cups_serverroot
; /* CUPS_SERVERROOT env var */
1963 const char *debug
; /* CUPS_DEBUG_LEVEL env var */
1964 const char *runtime
; /* CUPS_MAX_RUN_TIME env var */
1968 * Initialize the global address and community lists...
1971 Addresses
= cupsArrayNew(NULL
, NULL
);
1972 Communities
= cupsArrayNew(NULL
, NULL
);
1975 add_array(Addresses
, address
);
1977 if ((debug
= getenv("CUPS_DEBUG_LEVEL")) != NULL
)
1978 DebugLevel
= atoi(debug
);
1980 if ((runtime
= getenv("CUPS_MAX_RUN_TIME")) != NULL
)
1981 MaxRunTime
= atoi(runtime
);
1984 * Find the snmp.conf file...
1987 if ((cups_serverroot
= getenv("CUPS_SERVERROOT")) == NULL
)
1988 cups_serverroot
= CUPS_SERVERROOT
;
1990 snprintf(filename
, sizeof(filename
), "%s/snmp.conf", cups_serverroot
);
1992 if ((fp
= cupsFileOpen(filename
, "r")) != NULL
)
1995 * Read the snmp.conf file...
2000 while (cupsFileGetConf(fp
, line
, sizeof(line
), &value
, &linenum
))
2003 fprintf(stderr
, "ERROR: Missing value on line %d of %s!\n", linenum
,
2005 else if (!strcasecmp(line
, "Address"))
2008 add_array(Addresses
, value
);
2010 else if (!strcasecmp(line
, "Community"))
2011 add_array(Communities
, value
);
2012 else if (!strcasecmp(line
, "DebugLevel"))
2013 DebugLevel
= atoi(value
);
2014 else if (!strcasecmp(line
, "HostNameLookups"))
2015 HostNameLookups
= !strcasecmp(value
, "on") ||
2016 !strcasecmp(value
, "yes") ||
2017 !strcasecmp(value
, "true") ||
2018 !strcasecmp(value
, "double");
2019 else if (!strcasecmp(line
, "MaxRunTime"))
2020 MaxRunTime
= atoi(value
);
2022 fprintf(stderr
, "ERROR: Unknown directive %s on line %d of %s!\n",
2023 line
, linenum
, filename
);
2030 * Use defaults if parameters are undefined...
2033 if (cupsArrayCount(Addresses
) == 0)
2035 fputs("INFO: Using default SNMP Address @LOCAL\n", stderr
);
2036 add_array(Addresses
, "@LOCAL");
2039 if (cupsArrayCount(Communities
) == 0)
2041 fputs("INFO: Using default SNMP Community public\n", stderr
);
2042 add_array(Communities
, "public");
2048 * 'read_snmp_response()' - Read and parse a SNMP response...
2052 read_snmp_response(int fd
) /* I - SNMP socket file descriptor */
2054 unsigned char buffer
[SNMP_MAX_PACKET
];/* Data packet */
2055 int bytes
; /* Number of bytes received */
2056 http_addr_t addr
; /* Source address */
2057 socklen_t addrlen
; /* Source address length */
2058 char addrname
[256]; /* Source address name */
2059 snmp_packet_t packet
; /* Decoded packet */
2060 snmp_cache_t key
, /* Search key */
2061 *device
; /* Matching device */
2065 * Read the response data...
2068 addrlen
= sizeof(addr
);
2070 if ((bytes
= recvfrom(fd
, buffer
, sizeof(buffer
), 0, (void *)&addr
,
2073 fprintf(stderr
, "ERROR: Unable to read data from socket: %s\n",
2078 if (HostNameLookups
)
2079 httpAddrLookup(&addr
, addrname
, sizeof(addrname
));
2081 httpAddrString(&addr
, addrname
, sizeof(addrname
));
2083 debug_printf("DEBUG: %.3f Received %d bytes from %s...\n", run_time(),
2087 * Look for the response status code in the SNMP message header...
2090 if (asn1_decode_snmp(buffer
, bytes
, &packet
))
2092 fprintf(stderr
, "ERROR: Bad SNMP packet from %s: %s\n", addrname
,
2095 asn1_debug(buffer
, bytes
, 0);
2096 hex_debug(buffer
, bytes
);
2101 debug_printf("DEBUG: community=\"%s\"\n", packet
.community
);
2102 debug_printf("DEBUG: request-id=%d\n", packet
.request_id
);
2103 debug_printf("DEBUG: error-status=%d\n", packet
.error_status
);
2106 asn1_debug(buffer
, bytes
, 0);
2109 hex_debug(buffer
, bytes
);
2111 if (packet
.error_status
)
2115 * Find a matching device in the cache...
2118 key
.addrname
= addrname
;
2119 device
= (snmp_cache_t
*)cupsArrayFind(Devices
, &key
);
2122 * Process the message...
2125 if (packet
.request_id
== DeviceTypeRequest
)
2128 * Got the device type response...
2133 debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
2139 * Add the device and request the device description...
2142 add_cache(&addr
, addrname
, NULL
, NULL
, NULL
);
2144 send_snmp_query(fd
, &addr
, SNMP_VERSION_1
, packet
.community
,
2145 DeviceDescRequest
, DeviceDescOID
);
2147 else if (packet
.request_id
== DeviceDescRequest
&&
2148 packet
.object_type
== ASN1_OCTET_STRING
)
2151 * Update an existing cache entry...
2154 char make_model
[256]; /* Make and model */
2159 debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
2165 * Convert the description to a make and model string...
2168 if (strchr(packet
.object_value
.string
, ':') &&
2169 strchr(packet
.object_value
.string
, ';'))
2172 * Description is the IEEE-1284 device ID...
2175 backendGetMakeModel(packet
.object_value
.string
, make_model
,
2176 sizeof(make_model
));
2181 * Description is plain text...
2184 fix_make_model(make_model
, packet
.object_value
.string
,
2185 sizeof(make_model
));
2188 if (device
->make_and_model
)
2189 free(device
->make_and_model
);
2191 device
->make_and_model
= strdup(make_model
);
2197 * 'run_time()' - Return the total running time...
2200 static double /* O - Number of seconds */
2203 struct timeval curtime
; /* Current time */
2206 gettimeofday(&curtime
, NULL
);
2208 return (curtime
.tv_sec
- StartTime
.tv_sec
+
2209 0.000001 * (curtime
.tv_usec
- StartTime
.tv_usec
));
2214 * 'scan_devices()' - Scan for devices using SNMP.
2218 scan_devices(int fd
) /* I - SNMP socket */
2220 char *address
, /* Current address */
2221 *community
; /* Current community */
2222 fd_set input
; /* Input set for select() */
2223 struct timeval timeout
; /* Timeout for select() */
2224 time_t endtime
; /* End time for scan */
2225 http_addrlist_t
*addrs
, /* List of addresses */
2226 *addr
; /* Current address */
2227 snmp_cache_t
*device
; /* Current device */
2231 * Setup the request IDs...
2234 gettimeofday(&StartTime
, NULL
);
2236 DeviceTypeRequest
= StartTime
.tv_sec
;
2237 DeviceDescRequest
= StartTime
.tv_sec
+ 1;
2240 * First send all of the broadcast queries...
2243 for (address
= (char *)cupsArrayFirst(Addresses
);
2245 address
= (char *)cupsArrayNext(Addresses
))
2247 if (!strcmp(address
, "@LOCAL"))
2248 addrs
= get_interface_addresses(NULL
);
2249 else if (!strncmp(address
, "@IF(", 4))
2251 char ifname
[255]; /* Interface name */
2254 strlcpy(ifname
, address
+ 4, sizeof(ifname
));
2256 ifname
[strlen(ifname
) - 1] = '\0';
2258 addrs
= get_interface_addresses(ifname
);
2261 addrs
= httpAddrGetList(address
, AF_INET
, NULL
);
2265 fprintf(stderr
, "ERROR: Unable to scan \"%s\"!\n", address
);
2269 for (community
= (char *)cupsArrayFirst(Communities
);
2271 community
= (char *)cupsArrayNext(Communities
))
2273 debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
2274 community
, address
);
2276 for (addr
= addrs
; addr
; addr
= addr
->next
)
2277 send_snmp_query(fd
, &(addr
->addr
), SNMP_VERSION_1
, community
,
2278 DeviceTypeRequest
, DeviceTypeOID
);
2281 httpAddrFreeList(addrs
);
2285 * Then read any responses that come in over the next 3 seconds...
2288 endtime
= time(NULL
) + 3;
2292 while (time(NULL
) < endtime
)
2295 timeout
.tv_usec
= 0;
2298 if (select(fd
+ 1, &input
, NULL
, NULL
, &timeout
) < 0)
2300 fprintf(stderr
, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
2301 fd
, strerror(errno
));
2305 if (FD_ISSET(fd
, &input
))
2306 read_snmp_response(fd
);
2312 * Finally, probe all of the printers we discovered to see how they are
2316 for (device
= (snmp_cache_t
*)cupsArrayFirst(Devices
);
2318 device
= (snmp_cache_t
*)cupsArrayNext(Devices
))
2319 if (MaxRunTime
> 0 && run_time() >= MaxRunTime
)
2321 else if (!device
->uri
)
2322 probe_device(device
);
2324 debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
2329 * 'send_snmp_query()' - Send an SNMP query packet.
2334 int fd
, /* I - SNMP socket */
2335 http_addr_t
*addr
, /* I - Address to send to */
2336 int version
, /* I - SNMP version */
2337 const char *community
, /* I - Community name */
2338 const unsigned request_id
, /* I - Request ID */
2339 const int *oid
) /* I - OID */
2341 int i
; /* Looping var */
2342 snmp_packet_t packet
; /* SNMP message packet */
2343 unsigned char buffer
[SNMP_MAX_PACKET
];/* SNMP message buffer */
2344 int bytes
; /* Size of message */
2345 char addrname
[32]; /* Address name */
2349 * Create the SNMP message...
2352 memset(&packet
, 0, sizeof(packet
));
2354 packet
.version
= version
;
2355 packet
.request_type
= ASN1_GET_REQUEST
;
2356 packet
.request_id
= request_id
;
2357 packet
.object_type
= ASN1_NULL_VALUE
;
2359 strlcpy(packet
.community
, community
, sizeof(packet
.community
));
2361 for (i
= 0; oid
[i
]; i
++)
2362 packet
.object_name
[i
] = oid
[i
];
2364 bytes
= asn1_encode_snmp(buffer
, sizeof(buffer
), &packet
);
2368 fprintf(stderr
, "ERROR: Unable to send SNMP query: %s\n", packet
.error
);
2373 * Send the message...
2376 debug_printf("DEBUG: %.3f Sending %d bytes to %s...\n", run_time(),
2377 bytes
, httpAddrString(addr
, addrname
, sizeof(addrname
)));
2379 asn1_debug(buffer
, bytes
, 0);
2381 hex_debug(buffer
, bytes
);
2383 addr
->ipv4
.sin_port
= htons(SNMP_PORT
);
2385 if (sendto(fd
, buffer
, bytes
, 0, (void *)addr
, sizeof(addr
->ipv4
)) < bytes
)
2386 fprintf(stderr
, "ERROR: Unable to send %d bytes to %s: %s\n",
2387 bytes
, addrname
, strerror(errno
));
2392 * 'try_connect()' - Try connecting on a port...
2395 static int /* O - 0 on success or -1 on error */
2396 try_connect(http_addr_t
*addr
, /* I - Socket address */
2397 const char *addrname
, /* I - Hostname or IP address */
2398 int port
) /* I - Port number */
2400 int fd
; /* Socket */
2401 int status
; /* Connection status */
2404 debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
2405 port
== 515 ? "lpd" : "socket", addrname
, port
);
2407 if ((fd
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0)
2409 fprintf(stderr
, "ERROR: Unable to create socket: %s\n", strerror(errno
));
2413 addr
->ipv4
.sin_port
= htons(port
);
2417 status
= connect(fd
, (void *)addr
, httpAddrLength(addr
));
2427 * 'update_cache()' - Update a cached device...
2431 update_cache(snmp_cache_t
*device
, /* I - Device */
2432 const char *uri
, /* I - Device URI */
2433 const char *id
, /* I - Device ID */
2434 const char *make_model
) /* I - Device make and model */
2439 device
->uri
= strdup(uri
);
2446 device
->id
= strdup(id
);
2451 if (device
->make_and_model
)
2452 free(device
->make_and_model
);
2454 device
->make_and_model
= strdup(make_model
);
2457 list_device(device
);
2462 * End of "$Id: snmp.c 6180 2007-01-03 18:19:32Z mike $".