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