summaryrefslogtreecommitdiff
path: root/backend/snmp.c
diff options
context:
space:
mode:
authorjlovell <jlovell@a1ca3aef-8c08-0410-bb20-df032aa958be>2006-04-24 18:03:36 +0000
committerjlovell <jlovell@a1ca3aef-8c08-0410-bb20-df032aa958be>2006-04-24 18:03:36 +0000
commit89d46774ee527faaaf27d1b696554f4508bf105b (patch)
tree27ae92ec2e5df36836fc505515ab45f9a06cebc1 /backend/snmp.c
parente53920b9224e07b7d5f3e5a3ffea1f64ded479d2 (diff)
downloadcups-89d46774ee527faaaf27d1b696554f4508bf105b.tar.gz
Load cups into easysw/current.
git-svn-id: svn+ssh://src.apple.com/svn/cups/easysw/current@136 a1ca3aef-8c08-0410-bb20-df032aa958be
Diffstat (limited to 'backend/snmp.c')
-rw-r--r--backend/snmp.c2206
1 files changed, 2206 insertions, 0 deletions
diff --git a/backend/snmp.c b/backend/snmp.c
new file mode 100644
index 000000000..d571bf105
--- /dev/null
+++ b/backend/snmp.c
@@ -0,0 +1,2206 @@
+/*
+ * "$Id: snmp.c 5453 2006-04-23 12:08:18Z mike $"
+ *
+ * SNMP discovery backend for the Common UNIX Printing System (CUPS).
+ *
+ * Copyright 2006 by Easy Software Products, all rights reserved.
+ *
+ * These coded instructions, statements, and computer programs are the
+ * property of Easy Software Products and are protected by Federal
+ * copyright law. Distribution and use rights are outlined in the file
+ * "LICENSE" which should have been included with this file. If this
+ * file is missing or damaged please contact Easy Software Products
+ * at:
+ *
+ * Attn: CUPS Licensing Information
+ * Easy Software Products
+ * 44141 Airport View Drive, Suite 204
+ * Hollywood, Maryland 20636 USA
+ *
+ * Voice: (301) 373-9600
+ * EMail: cups-info@cups.org
+ * WWW: http://www.cups.org
+ *
+ * This file is subject to the Apple OS-Developed Software exception.
+ *
+ * Contents:
+ *
+ * main() - Discover printers via SNMP.
+ * add_array() - Add a string to an array.
+ * add_cache() - Add a cached device...
+ * alarm_handler() - Handle alarm signals...
+ * asn1_decode_snmp() - Decode a SNMP packet.
+ * asn1_debug() - Decode an ASN1-encoded message.
+ * asn1_encode_snmp() - Encode a SNMP packet.
+ * asn1_get_integer() - Get an integer value.
+ * asn1_get_length() - Get a value length.
+ * asn1_get_oid() - Get an OID value.
+ * asn1_get_packed() - Get a packed integer value.
+ * asn1_get_string() - Get a string value.
+ * asn1_get_type() - Get a value type.
+ * asn1_set_integer() - Set an integer value.
+ * asn1_set_length() - Set a value length.
+ * asn1_set_oid() - Set an OID value.
+ * asn1_set_packed() - Set a packed integer value.
+ * asn1_size_integer() - Figure out the number of bytes needed for an
+ * integer value.
+ * asn1_size_length() - Figure out the number of bytes needed for a
+ * length value.
+ * asn1_size_oid() - Figure out the numebr of bytes needed for an
+ * OID value.
+ * asn1_size_packed() - Figure out the number of bytes needed for a
+ * packed integer value.
+ * compare_cache() - Compare two cache entries.
+ * debug_printf() - Display some debugging information.
+ * fix_make_model() - Fix common problems in the make-and-model
+ * string.
+ * free_array() - Free an array of strings.
+ * free_cache() - Free the array of cached devices.
+ * get_interface_addresses() - Get the broadcast address(es) associated
+ * with an interface.
+ * hex_debug() - Output hex debugging data...
+ * list_devices() - List all of the devices we found...
+ * open_snmp_socket() - Open the SNMP broadcast socket.
+ * password_cb() - Handle authentication requests.
+ * probe_device() - Probe a device to discover whether it is a
+ * printer.
+ * read_snmp_conf() - Read the snmp.conf file.
+ * read_snmp_response() - Read and parse a SNMP response...
+ * run_time() - Return the total running time...
+ * scan_devices() - Scan for devices using SNMP.
+ * send_snmp_query() - Send an SNMP query packet.
+ * try_connect() - Try connecting on a port...
+ * update_cache() - Update a cached device...
+ */
+
+/*
+ * Include necessary headers.
+ */
+
+#include <cups/backend.h>
+#include <cups/http-private.h>
+#include <cups/cups.h>
+#include <cups/string.h>
+#include <cups/array.h>
+#include <cups/file.h>
+#include <errno.h>
+#include <signal.h>
+
+#define SNMP_BACKEND
+#include "ieee1284.c"
+
+
+/*
+ * This backend implements SNMP printer discovery. It uses a broadcast-
+ * based approach to get SNMP response packets from potential printers
+ * and then interrogates each responder by trying to connect on port
+ * 631, 9100, and 515.
+ *
+ * The current focus is on printers with internal network cards, although
+ * the code also works with many external print servers as well. Future
+ * versions will support scanning for vendor-specific SNMP OIDs and the
+ * new PWG Port Monitor MIB and not just the Host MIB OIDs.
+ *
+ * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
+ * which can contain comments, blank lines, or any number of the following
+ * directives:
+ *
+ * Address ip-address
+ * Address @LOCAL
+ * Address @IF(name)
+ * Community name
+ * DebugLevel N
+ * HostNameLookups on
+ * HostNameLookups off
+ *
+ * The default is to use:
+ *
+ * Address @LOCAL
+ * Community public
+ * DebugLevel 0
+ * HostNameLookups off
+ *
+ * This backend is known to work with the following network printers and
+ * print servers:
+ *
+ * Axis OfficeBasic, 5400, 5600
+ * EPSON
+ * Genicom
+ * HP JetDirect
+ * Lexmark
+ * Sharp
+ * Tektronix
+ * Xerox
+ *
+ * It does not currently work with:
+ *
+ * DLink
+ * Linksys
+ * Netgear
+ * Okidata
+ *
+ * (for all of these, they do not support the Host MIB)
+ */
+
+/*
+ * Constants...
+ */
+
+#define SNMP_PORT 161 /* SNMP well-known port */
+#define SNMP_MAX_OID 64 /* Maximum number of OID numbers */
+#define SNMP_MAX_PACKET 1472 /* Maximum size of SNMP packet */
+#define SNMP_MAX_STRING 512 /* Maximum size of string */
+#define SNMP_VERSION_1 0 /* SNMPv1 */
+
+#define ASN1_END_OF_CONTENTS 0x00 /* End-of-contents */
+#define ASN1_BOOLEAN 0x01 /* BOOLEAN */
+#define ASN1_INTEGER 0x02 /* INTEGER or ENUMERATION */
+#define ASN1_BIT_STRING 0x03 /* BIT STRING */
+#define ASN1_OCTET_STRING 0x04 /* OCTET STRING */
+#define ASN1_NULL_VALUE 0x05 /* NULL VALUE */
+#define ASN1_OID 0x06 /* OBJECT IDENTIFIER */
+#define ASN1_SEQUENCE 0x30 /* SEQUENCE */
+#define ASN1_GET_REQUEST 0xa0 /* Get-Request-PDU */
+#define ASN1_GET_RESPONSE 0xa2 /* Get-Response-PDU */
+
+
+/*
+ * Types...
+ */
+
+typedef struct snmp_cache_s /**** SNMP scan cache ****/
+{
+ http_addr_t address; /* Address of device */
+ char *addrname, /* Name of device */
+ *uri, /* device-uri */
+ *id, /* device-id */
+ *make_and_model; /* device-make-and-model */
+} snmp_cache_t;
+
+typedef struct snmp_packet_s /**** SNMP packet ****/
+{
+ const char *error; /* Encode/decode error */
+ int version; /* Version number */
+ char community[SNMP_MAX_STRING];
+ /* Community name */
+ int request_type; /* Request type */
+ int request_id; /* request-id value */
+ int error_status; /* error-status value */
+ int error_index; /* error-index value */
+ int object_name[SNMP_MAX_OID];
+ /* object-name value */
+ int object_type; /* object-value type */
+ union
+ {
+ int boolean; /* Boolean value */
+ int integer; /* Integer value */
+ int oid[SNMP_MAX_OID]; /* OID value */
+ char string[SNMP_MAX_STRING];/* String value */
+ } object_value; /* object-value value */
+} snmp_packet_t;
+
+
+/*
+ * Local functions...
+ */
+
+static char *add_array(cups_array_t *a, const char *s);
+static void add_cache(http_addr_t *addr, const char *addrname,
+ const char *uri, const char *id,
+ const char *make_and_model);
+static void alarm_handler(int sig);
+static int asn1_decode_snmp(unsigned char *buffer, size_t len,
+ snmp_packet_t *packet);
+static void asn1_debug(unsigned char *buffer, size_t len,
+ int indent);
+static int asn1_encode_snmp(unsigned char *buffer, size_t len,
+ snmp_packet_t *packet);
+static int asn1_get_integer(unsigned char **buffer,
+ unsigned char *bufend,
+ int length);
+static int asn1_get_oid(unsigned char **buffer,
+ unsigned char *bufend,
+ int length, int *oid, int oidsize);
+static int asn1_get_packed(unsigned char **buffer,
+ unsigned char *bufend);
+static char *asn1_get_string(unsigned char **buffer,
+ unsigned char *bufend,
+ int length, char *string,
+ int strsize);
+static int asn1_get_length(unsigned char **buffer,
+ unsigned char *bufend);
+static int asn1_get_type(unsigned char **buffer,
+ unsigned char *bufend);
+static void asn1_set_integer(unsigned char **buffer,
+ int integer);
+static void asn1_set_length(unsigned char **buffer,
+ int length);
+static void asn1_set_oid(unsigned char **buffer,
+ const int *oid);
+static void asn1_set_packed(unsigned char **buffer,
+ int integer);
+static int asn1_size_integer(int integer);
+static int asn1_size_length(int length);
+static int asn1_size_oid(const int *oid);
+static int asn1_size_packed(int integer);
+static int compare_cache(snmp_cache_t *a, snmp_cache_t *b);
+static void debug_printf(const char *format, ...);
+static void fix_make_model(char *make_model,
+ const char *old_make_model,
+ int make_model_size);
+static void free_array(cups_array_t *a);
+static void free_cache(void);
+static http_addrlist_t *get_interface_addresses(const char *ifname);
+static void hex_debug(unsigned char *buffer, size_t len);
+static void list_devices(void);
+static int open_snmp_socket(void);
+static const char *password_cb(const char *prompt);
+static void probe_device(snmp_cache_t *device);
+static void read_snmp_conf(const char *address);
+static void read_snmp_response(int fd);
+static double run_time(void);
+static void scan_devices(int fd);
+static void send_snmp_query(int fd, http_addr_t *addr, int version,
+ const char *community,
+ const unsigned request_id,
+ const int *oid);
+static int try_connect(http_addr_t *addr, const char *addrname,
+ int port);
+static void update_cache(snmp_cache_t *device, const char *uri,
+ const char *id, const char *make_model);
+
+
+/*
+ * Local globals...
+ */
+
+static cups_array_t *Addresses = NULL;
+static cups_array_t *Communities = NULL;
+static cups_array_t *Devices = NULL;
+static int DebugLevel = 0;
+static int DeviceTypeOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
+ 2, 1, 2, 1, 0 };
+static int DeviceDescOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
+ 2, 1, 3, 1, 0 };
+static unsigned DeviceTypeRequest;
+static unsigned DeviceDescRequest;
+static int HostNameLookups = 0;
+static struct timeval StartTime;
+
+
+/*
+ * 'main()' - Discover printers via SNMP.
+ */
+
+int /* O - Exit status */
+main(int argc, /* I - Number of command-line arguments (6 or 7) */
+ char *argv[]) /* I - Command-line arguments */
+{
+ int fd; /* SNMP socket */
+
+
+ /*
+ * Check command-line options...
+ */
+
+ if (argc > 2)
+ {
+ fputs("Usage: snmp [host-or-ip-address]\n", stderr);
+ return (1);
+ }
+
+ /*
+ * Set the password callback for IPP operations...
+ */
+
+ cupsSetPasswordCB(password_cb);
+
+ /*
+ * Open the SNMP socket...
+ */
+
+ if ((fd = open_snmp_socket()) < 0)
+ return (1);
+
+ /*
+ * Read the configuration file and any cache data...
+ */
+
+ read_snmp_conf(argv[1]);
+
+ Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
+
+ /*
+ * Scan for devices...
+ */
+
+ scan_devices(fd);
+
+ /*
+ * Display the results...
+ */
+
+ list_devices();
+
+ /*
+ * Close, free, and return with no errors...
+ */
+
+ close(fd);
+
+ free_array(Addresses);
+ free_array(Communities);
+ free_cache();
+
+ return (0);
+}
+
+
+/*
+ * 'add_array()' - Add a string to an array.
+ */
+
+static char * /* O - New string */
+add_array(cups_array_t *a, /* I - Array */
+ const char *s) /* I - String to add */
+{
+ char *dups; /* New string */
+
+
+ dups = strdup(s);
+
+ cupsArrayAdd(a, dups);
+
+ return (dups);
+}
+
+
+/*
+ * 'add_cache()' - Add a cached device...
+ */
+
+static void
+add_cache(http_addr_t *addr, /* I - Device IP address */
+ const char *addrname, /* I - IP address or name string */
+ const char *uri, /* I - Device URI */
+ const char *id, /* I - 1284 device ID */
+ const char *make_and_model) /* I - Make and model */
+{
+ snmp_cache_t *temp; /* New device entry */
+
+
+ debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
+ "id=\"%s\", make_and_model=\"%s\")\n",
+ addr, addrname, uri, id ? id : "(null)",
+ make_and_model ? make_and_model : "(null)");
+
+ temp = calloc(1, sizeof(snmp_cache_t));
+ memcpy(&(temp->address), addr, sizeof(temp->address));
+
+ temp->addrname = strdup(addrname);
+
+ if (uri)
+ temp->uri = strdup(uri);
+
+ if (id)
+ temp->id = strdup(id);
+
+ if (make_and_model)
+ temp->make_and_model = strdup(make_and_model);
+
+ cupsArrayAdd(Devices, temp);
+}
+
+
+/*
+ * 'alarm_handler()' - Handle alarm signals...
+ */
+
+static void
+alarm_handler(int sig) /* I - Signal number */
+{
+ /*
+ * Do nothing...
+ */
+
+ (void)sig;
+
+ if (DebugLevel)
+ write(2, "DEBUG: ALARM!\n", 14);
+}
+
+
+/*
+ * 'asn1_decode_snmp()' - Decode a SNMP packet.
+ */
+
+static int /* O - 0 on success, -1 on error */
+asn1_decode_snmp(unsigned char *buffer, /* I - Buffer */
+ size_t len, /* I - Size of buffer */
+ snmp_packet_t *packet) /* I - SNMP packet */
+{
+ unsigned char *bufptr, /* Pointer into the data */
+ *bufend; /* End of data */
+ int length; /* Length of value */
+
+
+ /*
+ * Initialize the decoding...
+ */
+
+ memset(packet, 0, sizeof(snmp_packet_t));
+
+ bufptr = buffer;
+ bufend = buffer + len;
+
+ if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE)
+ packet->error = "Packet does not start with SEQUENCE";
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ packet->error = "SEQUENCE uses indefinite length";
+ else if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
+ packet->error = "No version number";
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ packet->error = "Version uses indefinite length";
+ else if ((packet->version = asn1_get_integer(&bufptr, bufend, length))
+ != SNMP_VERSION_1)
+ packet->error = "Bad SNMP version number";
+ else if (asn1_get_type(&bufptr, bufend) != ASN1_OCTET_STRING)
+ packet->error = "No community name";
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ packet->error = "Community name uses indefinite length";
+ else
+ {
+ asn1_get_string(&bufptr, bufend, length, packet->community,
+ sizeof(packet->community));
+
+ if ((packet->request_type = asn1_get_type(&bufptr, bufend))
+ != ASN1_GET_RESPONSE)
+ packet->error = "Packet does not contain a Get-Response-PDU";
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ packet->error = "Get-Response-PDU uses indefinite length";
+ else if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
+ packet->error = "No request-id";
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ packet->error = "request-id uses indefinite length";
+ else
+ {
+ packet->request_id = asn1_get_integer(&bufptr, bufend, length);
+
+ if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
+ packet->error = "No error-status";
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ packet->error = "error-status uses indefinite length";
+ else
+ {
+ packet->error_status = asn1_get_integer(&bufptr, bufend, length);
+
+ if (asn1_get_type(&bufptr, bufend) != ASN1_INTEGER)
+ packet->error = "No error-index";
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ packet->error = "error-index uses indefinite length";
+ else
+ {
+ packet->error_index = asn1_get_integer(&bufptr, bufend, length);
+
+ if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE)
+ packet->error = "No variable-bindings SEQUENCE";
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ packet->error = "variable-bindings uses indefinite length";
+ else if (asn1_get_type(&bufptr, bufend) != ASN1_SEQUENCE)
+ packet->error = "No VarBind SEQUENCE";
+ else if (asn1_get_length(&bufptr, bufend) == 0)
+ packet->error = "VarBind uses indefinite length";
+ else if (asn1_get_type(&bufptr, bufend) != ASN1_OID)
+ packet->error = "No name OID";
+ else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
+ packet->error = "Name OID uses indefinite length";
+ else
+ {
+ asn1_get_oid(&bufptr, bufend, length, packet->object_name,
+ SNMP_MAX_OID);
+
+ packet->object_type = asn1_get_type(&bufptr, bufend);
+
+ if ((length = asn1_get_length(&bufptr, bufend)) == 0 &&
+ packet->object_type != ASN1_NULL_VALUE &&
+ packet->object_type != ASN1_OCTET_STRING)
+ packet->error = "Value uses indefinite length";
+ else
+ {
+ switch (packet->object_type)
+ {
+ case ASN1_BOOLEAN :
+ packet->object_value.boolean =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ case ASN1_INTEGER :
+ packet->object_value.integer =
+ asn1_get_integer(&bufptr, bufend, length);
+ break;
+
+ case ASN1_NULL_VALUE :
+ break;
+
+ case ASN1_OCTET_STRING :
+ asn1_get_string(&bufptr, bufend, length,
+ packet->object_value.string,
+ SNMP_MAX_STRING);
+ break;
+
+ case ASN1_OID :
+ asn1_get_oid(&bufptr, bufend, length,
+ packet->object_value.oid, SNMP_MAX_OID);
+ break;
+
+ default :
+ packet->error = "Unsupported value type";
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return (packet->error ? -1 : 0);
+}
+
+
+/*
+ * 'asn1_debug()' - Decode an ASN1-encoded message.
+ */
+
+static void
+asn1_debug(unsigned char *buffer, /* I - Buffer */
+ size_t len, /* I - Length of buffer */
+ int indent) /* I - Indentation */
+{
+ int i; /* Looping var */
+ unsigned char *bufend; /* End of buffer */
+ int integer; /* Number value */
+ int oid[SNMP_MAX_OID]; /* OID value */
+ char string[SNMP_MAX_STRING];/* String value */
+ unsigned char value_type; /* Type of value */
+ int value_length; /* Length of value */
+
+
+ bufend = buffer + len;
+
+ while (buffer < bufend)
+ {
+ /*
+ * Get value type...
+ */
+
+ value_type = asn1_get_type(&buffer, bufend);
+ value_length = asn1_get_length(&buffer, bufend);
+
+ switch (value_type)
+ {
+ case ASN1_BOOLEAN :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "DEBUG: %*sBOOLEAN %d bytes %d\n", indent, "",
+ value_length, integer);
+ break;
+
+ case ASN1_INTEGER :
+ integer = asn1_get_integer(&buffer, bufend, value_length);
+
+ fprintf(stderr, "DEBUG: %*sINTEGER %d bytes %d\n", indent, "",
+ value_length, integer);
+ break;
+
+ case ASN1_OCTET_STRING :
+ fprintf(stderr, "DEBUG: %*sOCTET STRING %d bytes \"%s\"\n", indent, "",
+ value_length, asn1_get_string(&buffer, bufend,
+ value_length, string,
+ sizeof(string)));
+ break;
+
+ case ASN1_NULL_VALUE :
+ fprintf(stderr, "DEBUG: %*sNULL VALUE %d bytes\n", indent, "",
+ value_length);
+
+ buffer += value_length;
+ break;
+
+ case ASN1_OID :
+ asn1_get_oid(&buffer, bufend, value_length, oid, SNMP_MAX_OID);
+
+ fprintf(stderr, "DEBUG: %*sOID %d bytes ", indent, "",
+ value_length);
+ for (i = 0; oid[i]; i ++)
+ fprintf(stderr, ".%d", oid[i]);
+ putc('\n', stderr);
+ break;
+
+ case ASN1_SEQUENCE :
+ fprintf(stderr, "DEBUG: %*sSEQUENCE %d bytes\n", indent, "",
+ value_length);
+ asn1_debug(buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ case ASN1_GET_REQUEST :
+ fprintf(stderr, "DEBUG: %*sGet-Request-PDU %d bytes\n", indent, "",
+ value_length);
+ asn1_debug(buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ case ASN1_GET_RESPONSE :
+ fprintf(stderr, "DEBUG: %*sGet-Response-PDU %d bytes\n", indent, "",
+ value_length);
+ asn1_debug(buffer, value_length, indent + 4);
+
+ buffer += value_length;
+ break;
+
+ default :
+ fprintf(stderr, "DEBUG: %*sUNKNOWN(%x) %d bytes\n", indent, "",
+ value_type, value_length);
+
+ buffer += value_length;
+ break;
+ }
+ }
+}
+
+
+/*
+ * 'asn1_encode_snmp()' - Encode a SNMP packet.
+ */
+
+static int /* O - Length on success, -1 on error */
+asn1_encode_snmp(unsigned char *buffer, /* I - Buffer */
+ size_t bufsize, /* I - Size of buffer */
+ snmp_packet_t *packet) /* I - SNMP packet */
+{
+ unsigned char *bufptr; /* Pointer into buffer */
+ int total, /* Total length */
+ msglen, /* Length of entire message */
+ commlen, /* Length of community string */
+ reqlen, /* Length of request */
+ listlen, /* Length of variable list */
+ varlen, /* Length of variable */
+ namelen, /* Length of object name OID */
+ valuelen; /* Length of object value */
+
+
+ /*
+ * Get the lengths of the community string, OID, and message...
+ */
+
+ namelen = asn1_size_oid(packet->object_name);
+
+ switch (packet->object_type)
+ {
+ case ASN1_NULL_VALUE :
+ valuelen = 0;
+ break;
+
+ case ASN1_BOOLEAN :
+ valuelen = asn1_size_integer(packet->object_value.boolean);
+ break;
+
+ case ASN1_INTEGER :
+ valuelen = asn1_size_integer(packet->object_value.integer);
+ break;
+
+ case ASN1_OCTET_STRING :
+ valuelen = strlen(packet->object_value.string);
+ break;
+
+ case ASN1_OID :
+ valuelen = asn1_size_oid(packet->object_value.oid);
+ break;
+
+ default :
+ packet->error = "Unknown object type";
+ return (-1);
+ }
+
+ varlen = 1 + asn1_size_length(namelen) + namelen +
+ 1 + asn1_size_length(valuelen) + valuelen;
+ listlen = 1 + asn1_size_length(varlen) + varlen;
+ reqlen = 2 + asn1_size_integer(packet->request_id) +
+ 2 + asn1_size_integer(packet->error_status) +
+ 2 + asn1_size_integer(packet->error_index) +
+ 1 + asn1_size_length(listlen) + listlen;
+ commlen = strlen(packet->community);
+ msglen = 2 + asn1_size_integer(packet->version) +
+ 1 + asn1_size_length(commlen) + commlen +
+ 1 + asn1_size_length(reqlen) + reqlen;
+ total = 1 + asn1_size_length(msglen) + msglen;
+
+ if (total > bufsize)
+ {
+ packet->error = "Message too large for buffer";
+ return (-1);
+ }
+
+ /*
+ * Then format the message...
+ */
+
+ bufptr = buffer;
+
+ *bufptr++ = ASN1_SEQUENCE; /* SNMPv1 message header */
+ asn1_set_length(&bufptr, msglen);
+
+ asn1_set_integer(&bufptr, packet->version);
+ /* version */
+
+ *bufptr++ = ASN1_OCTET_STRING; /* community */
+ asn1_set_length(&bufptr, commlen);
+ memcpy(bufptr, packet->community, commlen);
+ bufptr += commlen;
+
+ *bufptr++ = packet->request_type; /* Get-Request-PDU */
+ asn1_set_length(&bufptr, reqlen);
+
+ asn1_set_integer(&bufptr, packet->request_id);
+
+ asn1_set_integer(&bufptr, packet->error_status);
+
+ asn1_set_integer(&bufptr, packet->error_index);
+
+ *bufptr++ = ASN1_SEQUENCE; /* variable-bindings */
+ asn1_set_length(&bufptr, listlen);
+
+ *bufptr++ = ASN1_SEQUENCE; /* variable */
+ asn1_set_length(&bufptr, varlen);
+
+ asn1_set_oid(&bufptr, packet->object_name);
+ /* ObjectName */
+
+ switch (packet->object_type)
+ {
+ case ASN1_NULL_VALUE :
+ *bufptr++ = ASN1_NULL_VALUE; /* ObjectValue */
+ *bufptr++ = 0; /* Length */
+ break;
+
+ case ASN1_BOOLEAN :
+ asn1_set_integer(&bufptr, packet->object_value.boolean);
+ break;
+
+ case ASN1_INTEGER :
+ asn1_set_integer(&bufptr, packet->object_value.integer);
+ break;
+
+ case ASN1_OCTET_STRING :
+ *bufptr++ = ASN1_OCTET_STRING;
+ asn1_set_length(&bufptr, valuelen);
+ memcpy(bufptr, packet->object_value.string, valuelen);
+ bufptr += valuelen;
+ break;
+
+ case ASN1_OID :
+ asn1_set_oid(&bufptr, packet->object_value.oid);
+ break;
+ }
+
+ return (bufptr - buffer);
+}
+
+
+/*
+ * 'asn1_get_integer()' - Get an integer value.
+ */
+
+static int /* O - Integer value */
+asn1_get_integer(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend, /* I - End of buffer */
+ int length) /* I - Length of value */
+{
+ int value; /* Integer value */
+
+
+ for (value = 0;
+ length > 0 && *buffer < bufend;
+ length --, (*buffer) ++)
+ value = (value << 8) | **buffer;
+
+ return (value);
+}
+
+
+/*
+ * 'asn1_get_length()' - Get a value length.
+ */
+
+static int /* O - Length */
+asn1_get_length(unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend) /* I - End of buffer */
+{
+ int length; /* Length */
+
+
+ length = **buffer;
+ (*buffer) ++;
+
+ if (length & 128)
+ length = asn1_get_integer(buffer, bufend, length & 127);
+
+ return (length);
+}
+
+
+/*
+ * 'asn1_get_oid()' - Get an OID value.
+ */
+
+static int /* O - Last OID number */
+asn1_get_oid(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend, /* I - End of buffer */
+ int length, /* I - Length of value */
+ int *oid, /* I - OID buffer */
+ int oidsize) /* I - Size of OID buffer */
+{
+ unsigned char *valend; /* End of value */
+ int *oidend; /* End of OID buffer */
+ int number; /* OID number */
+
+
+ valend = *buffer + length;
+ oidend = oid + oidsize - 1;
+
+ if (valend > bufend)
+ valend = bufend;
+
+ number = asn1_get_packed(buffer, bufend);
+
+ if (number < 80)
+ {
+ *oid++ = number / 40;
+ number = number % 40;
+ *oid++ = number;
+ }
+ else
+ {
+ *oid++ = 2;
+ number -= 80;
+ *oid++ = number;
+ }
+
+ while (*buffer < valend)
+ {
+ number = asn1_get_packed(buffer, bufend);
+
+ if (oid < oidend)
+ *oid++ = number;
+ }
+
+ *oid = 0;
+
+ return (number);
+}
+
+
+/*
+ * 'asn1_get_packed()' - Get a packed integer value.
+ */
+
+static int /* O - Value */
+asn1_get_packed(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend) /* I - End of buffer */
+{
+ int value; /* Value */
+
+
+ value = 0;
+
+ while ((**buffer & 128) && *buffer < bufend)
+ {
+ value = (value << 7) | (**buffer & 127);
+ (*buffer) ++;
+ }
+
+ if (*buffer < bufend)
+ {
+ value = (value << 7) | **buffer;
+ (*buffer) ++;
+ }
+
+ return (value);
+}
+
+
+/*
+ * 'asn1_get_string()' - Get a string value.
+ */
+
+static char * /* O - String */
+asn1_get_string(
+ unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend, /* I - End of buffer */
+ int length, /* I - Value length */
+ char *string, /* I - String buffer */
+ int strsize) /* I - String buffer size */
+{
+ if (length < strsize)
+ {
+ memcpy(string, *buffer, length);
+ string[length] = '\0';
+ }
+ else
+ {
+ memcpy(string, buffer, strsize - 1);
+ string[strsize - 1] = '\0';
+ }
+
+ (*buffer) += length;
+
+ return (string);
+}
+
+
+/*
+ * 'asn1_get_type()' - Get a value type.
+ */
+
+static int /* O - Type */
+asn1_get_type(unsigned char **buffer, /* IO - Pointer in buffer */
+ unsigned char *bufend) /* I - End of buffer */
+{
+ int type; /* Type */
+
+
+ type = **buffer;
+ (*buffer) ++;
+
+ if ((type & 31) == 31)
+ type = asn1_get_packed(buffer, bufend);
+
+ return (type);
+}
+
+
+/*
+ * 'asn1_set_integer()' - Set an integer value.
+ */
+
+static void
+asn1_set_integer(unsigned char **buffer,/* IO - Pointer in buffer */
+ int integer) /* I - Integer value */
+{
+ **buffer = ASN1_INTEGER;
+ (*buffer) ++;
+
+ if (integer > 0x7fffff || integer < -0x800000)
+ {
+ **buffer = 4;
+ (*buffer) ++;
+ **buffer = integer >> 24;
+ (*buffer) ++;
+ **buffer = integer >> 16;
+ (*buffer) ++;
+ **buffer = integer >> 8;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+ else if (integer > 0x7fff || integer < -0x8000)
+ {
+ **buffer = 3;
+ (*buffer) ++;
+ **buffer = integer >> 16;
+ (*buffer) ++;
+ **buffer = integer >> 8;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+ else if (integer > 0x7f || integer < -0x80)
+ {
+ **buffer = 2;
+ (*buffer) ++;
+ **buffer = integer >> 8;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+ else
+ {
+ **buffer = 1;
+ (*buffer) ++;
+ **buffer = integer;
+ (*buffer) ++;
+ }
+}
+
+
+/*
+ * 'asn1_set_length()' - Set a value length.
+ */
+
+static void
+asn1_set_length(unsigned char **buffer, /* IO - Pointer in buffer */
+ int length) /* I - Length value */
+{
+ if (length > 255)
+ {
+ **buffer = 0x82; /* 2-byte length */
+ (*buffer) ++;
+ **buffer = length >> 8;
+ (*buffer) ++;
+ **buffer = length;
+ (*buffer) ++;
+ }
+ else if (length > 127)
+ {
+ **buffer = 0x81; /* 1-byte length */
+ (*buffer) ++;
+ **buffer = length;
+ (*buffer) ++;
+ }
+ else
+ {
+ **buffer = length; /* Length */
+ (*buffer) ++;
+ }
+}
+
+
+/*
+ * 'asn1_set_oid()' - Set an OID value.
+ */
+
+static void
+asn1_set_oid(unsigned char **buffer, /* IO - Pointer in buffer */
+ const int *oid) /* I - OID value */
+{
+ **buffer = ASN1_OID;
+ (*buffer) ++;
+
+ asn1_set_length(buffer, asn1_size_oid(oid));
+
+ asn1_set_packed(buffer, oid[0] * 40 + oid[1]);
+
+ for (oid += 2; *oid; oid ++)
+ asn1_set_packed(buffer, *oid);
+}
+
+
+/*
+ * 'asn1_set_packed()' - Set a packed integer value.
+ */
+
+static void
+asn1_set_packed(unsigned char **buffer, /* IO - Pointer in buffer */
+ int integer) /* I - Integer value */
+{
+ if (integer > 0xfffffff)
+ {
+ **buffer = (integer >> 14) & 0x7f;
+ (*buffer) ++;
+ }
+
+ if (integer > 0x1fffff)
+ {
+ **buffer = (integer >> 21) & 0x7f;
+ (*buffer) ++;
+ }
+
+ if (integer > 0x3fff)
+ {
+ **buffer = (integer >> 14) & 0x7f;
+ (*buffer) ++;
+ }
+
+ if (integer > 0x7f)
+ {
+ **buffer = (integer >> 7) & 0x7f;
+ (*buffer) ++;
+ }
+
+ **buffer = integer & 0x7f;
+ (*buffer) ++;
+}
+
+/*
+ * 'asn1_size_integer()' - Figure out the number of bytes needed for an
+ * integer value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_integer(int integer) /* I - Integer value */
+{
+ if (integer > 0x7fffff || integer < -0x800000)
+ return (4);
+ else if (integer > 0x7fff || integer < -0x8000)
+ return (3);
+ else if (integer > 0x7f || integer < -0x80)
+ return (2);
+ else
+ return (1);
+}
+
+
+/*
+ * 'asn1_size_length()' - Figure out the number of bytes needed for a
+ * length value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_length(int length) /* I - Length value */
+{
+ if (length > 0xff)
+ return (3);
+ else if (length > 0x7f)
+ return (2);
+ else
+ return (1);
+}
+
+
+/*
+ * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an
+ * OID value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_oid(const int *oid) /* I - OID value */
+{
+ int length; /* Length of value */
+
+
+ for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2; *oid; oid ++)
+ length += asn1_size_packed(*oid);
+
+ return (length);
+}
+
+
+/*
+ * 'asn1_size_packed()' - Figure out the number of bytes needed for a
+ * packed integer value.
+ */
+
+static int /* O - Size in bytes */
+asn1_size_packed(int integer) /* I - Integer value */
+{
+ if (integer > 0xfffffff)
+ return (5);
+ else if (integer > 0x1fffff)
+ return (4);
+ else if (integer > 0x3fff)
+ return (3);
+ else if (integer > 0x7f)
+ return (2);
+ else
+ return (1);
+}
+
+
+/*
+ * 'compare_cache()' - Compare two cache entries.
+ */
+
+static int /* O - Result of comparison */
+compare_cache(snmp_cache_t *a, /* I - First cache entry */
+ snmp_cache_t *b) /* I - Second cache entry */
+{
+ return (a->address.ipv4.sin_addr.s_addr - b->address.ipv4.sin_addr.s_addr);
+}
+
+
+/*
+ * 'debug_printf()' - Display some debugging information.
+ */
+
+static void
+debug_printf(const char *format, /* I - Printf-style format string */
+ ...) /* I - Additional arguments as needed */
+{
+ va_list ap; /* Pointer to arguments */
+
+
+ if (!DebugLevel)
+ return;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+}
+
+
+/*
+ * 'fix_make_model()' - Fix common problems in the make-and-model string.
+ */
+
+static void
+fix_make_model(
+ char *make_model, /* I - New make-and-model string */
+ const char *old_make_model, /* I - Old make-and-model string */
+ int make_model_size) /* I - Size of new string buffer */
+{
+ const char *mmptr; /* Pointer into make-and-model string */
+
+
+ /*
+ * Fix some common problems with the make-and-model string so
+ * that printer driver detection works better...
+ */
+
+ if (!strncasecmp(old_make_model, "Hewlett-Packard", 15))
+ {
+ /*
+ * Strip leading Hewlett-Packard and hp prefixes and replace
+ * with a single HP manufacturer prefix...
+ */
+
+ mmptr = old_make_model + 15;
+
+ while (isspace(*mmptr & 255))
+ mmptr ++;
+
+ if (!strncasecmp(mmptr, "hp", 2))
+ {
+ mmptr += 2;
+
+ while (isspace(*mmptr & 255))
+ mmptr ++;
+ }
+
+ make_model[0] = 'H';
+ make_model[1] = 'P';
+ make_model[2] = ' ';
+ strlcpy(make_model + 3, mmptr, make_model_size - 3);
+ }
+ else if (!strncasecmp(old_make_model, "deskjet", 7))
+ snprintf(make_model, make_model_size, "HP DeskJet%s", old_make_model + 7);
+ else if (!strncasecmp(old_make_model, "stylus_pro_", 11))
+ snprintf(make_model, make_model_size, "EPSON Stylus Pro %s",
+ old_make_model + 11);
+ else
+ strlcpy(make_model, old_make_model, make_model_size);
+
+ if ((mmptr = strstr(make_model, ", Inc.,")) != NULL)
+ {
+ /*
+ * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
+ * becomes "Tektronix Phaser 560"...
+ */
+
+ _cups_strcpy((char *)mmptr, mmptr + 7);
+ }
+}
+
+
+/*
+ * 'free_array()' - Free an array of strings.
+ */
+
+static void
+free_array(cups_array_t *a) /* I - Array */
+{
+ char *s; /* Current string */
+
+
+ for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a))
+ free(s);
+
+ cupsArrayDelete(a);
+}
+
+
+/*
+ * 'free_cache()' - Free the array of cached devices.
+ */
+
+static void
+free_cache(void)
+{
+ snmp_cache_t *cache; /* Cached device */
+
+
+ for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
+ cache;
+ cache = (snmp_cache_t *)cupsArrayNext(Devices))
+ {
+ free(cache->addrname);
+
+ if (cache->uri)
+ free(cache->uri);
+
+ if (cache->id)
+ free(cache->id);
+
+ if (cache->make_and_model)
+ free(cache->make_and_model);
+
+ free(cache);
+ }
+
+ cupsArrayDelete(Devices);
+ Devices = NULL;
+}
+
+
+/*
+ * 'get_interface_addresses()' - Get the broadcast address(es) associated
+ * with an interface.
+ */
+
+static http_addrlist_t * /* O - List of addresses */
+get_interface_addresses(
+ const char *ifname) /* I - Interface name */
+{
+ struct ifaddrs *addrs, /* Interface address list */
+ *addr; /* Current interface address */
+ http_addrlist_t *first, /* First address in list */
+ *last, /* Last address in list */
+ *current; /* Current address */
+
+
+ if (getifaddrs(&addrs) < 0)
+ return (NULL);
+
+ for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next)
+ if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr &&
+ addr->ifa_broadaddr->sa_family == AF_INET &&
+ (!ifname || !strcmp(ifname, addr->ifa_name)))
+ {
+ current = calloc(1, sizeof(http_addrlist_t));
+
+ memcpy(&(current->addr), addr->ifa_broadaddr,
+ sizeof(struct sockaddr_in));
+
+ if (!last)
+ first = current;
+ else
+ last->next = current;
+
+ last = current;
+ }
+
+ freeifaddrs(addrs);
+
+ return (first);
+}
+
+
+/*
+ * 'hex_debug()' - Output hex debugging data...
+ */
+
+static void
+hex_debug(unsigned char *buffer, /* I - Buffer */
+ size_t len) /* I - Number of bytes */
+{
+ int col; /* Current column */
+
+
+ fputs("DEBUG: Hex dump of packet:\n", stderr);
+
+ for (col = 0; len > 0; col ++, buffer ++, len --)
+ {
+ if ((col & 15) == 0)
+ fprintf(stderr, "DEBUG: %04X ", col);
+
+ fprintf(stderr, " %02X", *buffer);
+
+ if ((col & 15) == 15)
+ putc('\n', stderr);
+ }
+
+ if (col & 15)
+ putc('\n', stderr);
+}
+
+
+/*
+ * 'list_devices()' - List all of the devices we found...
+ */
+
+static void
+list_devices(void)
+{
+ snmp_cache_t *cache; /* Cached device */
+
+
+ for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
+ cache;
+ cache = (snmp_cache_t *)cupsArrayNext(Devices))
+ if (cache->uri)
+ printf("network %s \"%s\" \"%s\" \"%s\"\n",
+ cache->uri,
+ cache->make_and_model ? cache->make_and_model : "Unknown",
+ cache->addrname, cache->id ? cache->id : "");
+}
+
+
+/*
+ * 'open_snmp_socket()' - Open the SNMP broadcast socket.
+ */
+
+static int /* O - SNMP socket file descriptor */
+open_snmp_socket(void)
+{
+ int fd; /* SNMP socket file descriptor */
+ int val; /* Socket option value */
+
+
+ /*
+ * Create the SNMP socket...
+ */
+
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to create SNMP socket - %s\n",
+ strerror(errno));
+
+ return (-1);
+ }
+
+ /*
+ * Set the "broadcast" flag...
+ */
+
+ val = 1;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &val, sizeof(val)))
+ {
+ fprintf(stderr, "ERROR: Unable to set broadcast mode - %s\n",
+ strerror(errno));
+
+ close(fd);
+
+ return (-1);
+ }
+
+ return (fd);
+}
+
+
+/*
+ * 'password_cb()' - Handle authentication requests.
+ *
+ * All we do right now is return NULL, indicating that no authentication
+ * is possible.
+ */
+
+static const char * /* O - Password (NULL) */
+password_cb(const char *prompt) /* I - Prompt message */
+{
+ (void)prompt; /* Anti-compiler-warning-code */
+
+ return (NULL);
+}
+
+
+/*
+ * 'probe_device()' - Probe a device to discover whether it is a printer.
+ *
+ * TODO: Try using the Port Monitor MIB to discover the correct protocol
+ * to use - first need a commercially-available printer that supports
+ * it, though...
+ */
+
+static void
+probe_device(snmp_cache_t *device) /* I - Device */
+{
+ int i, j; /* Looping vars */
+ http_t *http; /* HTTP connection for IPP */
+ char uri[1024]; /* Full device URI */
+
+
+ /*
+ * Try connecting via IPP first...
+ */
+
+ debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
+
+ if ((http = httpConnect(device->addrname, 631)) != NULL)
+ {
+ /*
+ * IPP is supported...
+ */
+
+ ipp_t *request, /* IPP request */
+ *response; /* IPP response */
+ ipp_attribute_t *model, /* printer-make-and-model attribute */
+ *info, /* printer-info attribute */
+ *supported; /* printer-uri-supported attribute */
+ char make_model[256],/* Make and model string to use */
+ temp[256]; /* Temporary make/model string */
+ int num_uris; /* Number of good URIs */
+ static const char * const resources[] =
+ { /* Common resource paths for IPP */
+ "/ipp",
+ "/ipp/port2",
+ "/ipp/port3",
+ "/EPSON_IPP_Printer",
+ "/LPT1",
+ "/LPT2",
+ "/COM1",
+ "/"
+ };
+
+
+ debug_printf("DEBUG: %s supports IPP!\n", device->addrname);
+
+ /*
+ * Use non-blocking IO...
+ */
+
+ httpBlocking(http, 0);
+
+ /*
+ * Loop through a list of common resources that covers 99% of the
+ * IPP-capable printers on the market today...
+ */
+
+ for (i = 0, num_uris = 0;
+ i < (int)(sizeof(resources) / sizeof(resources[0]));
+ i ++)
+ {
+ /*
+ * Don't look past /ipp if we have found a working URI...
+ */
+
+ if (num_uris > 0 && strncmp(resources[i], "/ipp", 4))
+ break;
+
+ httpAssembleURI(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
+ device->addrname, 631, resources[i]);
+
+ debug_printf("DEBUG: Trying %s (num_uris=%d)\n", uri, num_uris);
+
+ request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
+
+ ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
+ NULL, uri);
+
+ response = cupsDoRequest(http, request, resources[i]);
+
+ debug_printf("DEBUG: %s %s (%s)\n", uri,
+ ippErrorString(cupsLastError()), cupsLastErrorString());
+
+ if (response && response->request.status.status_code == IPP_OK)
+ {
+ model = ippFindAttribute(response, "printer-make-and-model",
+ IPP_TAG_TEXT);
+ info = ippFindAttribute(response, "printer-info", IPP_TAG_TEXT);
+ supported = ippFindAttribute(response, "printer-uri-supported",
+ IPP_TAG_URI);
+
+ if (!supported)
+ {
+ fprintf(stderr, "ERROR: Missing printer-uri-supported from %s!\n",
+ device->addrname);
+
+ httpClose(http);
+ return;
+ }
+
+ debug_printf("DEBUG: printer-info=\"%s\"\n",
+ info ? info->values[0].string.text : "(null)");
+ debug_printf("DEBUG: printer-make-and-model=\"%s\"\n",
+ model ? model->values[0].string.text : "(null)");
+
+ /*
+ * Don't advertise this port if the printer actually only supports
+ * a more generic version...
+ */
+
+ if (!strncmp(resources[i], "/ipp/", 5))
+ {
+ for (j = 0; j < supported->num_values; j ++)
+ if (strstr(supported->values[j].string.text, "/ipp/"))
+ break;
+
+ if (j >= supported->num_values)
+ {
+ ippDelete(response);
+ break;
+ }
+ }
+
+ /*
+ * Don't use the printer-info attribute if it does not contain the
+ * IEEE-1284 device ID data...
+ */
+
+ if (info &&
+ (!strchr(info->values[0].string.text, ':') ||
+ !strchr(info->values[0].string.text, ';')))
+ info = NULL;
+
+ /*
+ * If we don't have a printer-make-and-model string from the printer
+ * but do have the 1284 device ID string, generate a make-and-model
+ * string from the device ID info...
+ */
+
+ if (model)
+ strlcpy(temp, model->values[0].string.text, sizeof(temp));
+ else if (info)
+ get_make_model(info->values[0].string.text, temp, sizeof(temp));
+
+ fix_make_model(make_model, temp, sizeof(make_model));
+
+ /*
+ * Update the current device or add a new printer to the cache...
+ */
+
+ if (num_uris == 0)
+ update_cache(device, uri,
+ info ? info->values[0].string.text : NULL,
+ make_model[0] ? make_model : NULL);
+ else
+ add_cache(&(device->address), device->addrname, uri,
+ info ? info->values[0].string.text : NULL,
+ make_model[0] ? make_model : NULL);
+
+ num_uris ++;
+ }
+
+ ippDelete(response);
+
+ if (num_uris > 0 && cupsLastError() != IPP_OK)
+ break;
+ }
+
+ httpClose(http);
+
+ if (num_uris > 0)
+ return;
+ }
+
+ /*
+ * OK, now try the standard ports...
+ */
+
+ if (!try_connect(&(device->address), device->addrname, 9100))
+ {
+ debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname);
+
+ snprintf(uri, sizeof(uri), "socket://%s", device->addrname);
+ update_cache(device, uri, NULL, NULL);
+ }
+ else if (!try_connect(&(device->address), device->addrname, 515))
+ {
+ debug_printf("DEBUG: %s supports LPD!\n", device->addrname);
+
+ snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname);
+ update_cache(device, uri, NULL, NULL);
+ }
+}
+
+
+/*
+ * 'read_snmp_conf()' - Read the snmp.conf file.
+ */
+
+static void
+read_snmp_conf(const char *address) /* I - Single address to probe */
+{
+ cups_file_t *fp; /* File pointer */
+ char filename[1024], /* Filename */
+ line[1024], /* Line from file */
+ *value; /* Value on line */
+ int linenum; /* Line number */
+ const char *cups_serverroot; /* CUPS_SERVERROOT env var */
+ const char *debug; /* CUPS_DEBUG_LEVEL env var */
+
+
+ /*
+ * Initialize the global address and community lists...
+ */
+
+ Addresses = cupsArrayNew(NULL, NULL);
+ Communities = cupsArrayNew(NULL, NULL);
+
+ if (address)
+ add_array(Addresses, address);
+
+ if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
+ DebugLevel = atoi(debug);
+
+ /*
+ * Find the snmp.conf file...
+ */
+
+ if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
+ cups_serverroot = CUPS_SERVERROOT;
+
+ snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot);
+
+ if ((fp = cupsFileOpen(filename, "r")) != NULL)
+ {
+ /*
+ * Read the snmp.conf file...
+ */
+
+ linenum = 0;
+
+ while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
+ {
+ if (!value)
+ fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
+ filename);
+ else if (!strcasecmp(line, "Address"))
+ {
+ if (!address)
+ add_array(Addresses, value);
+ }
+ else if (!strcasecmp(line, "Community"))
+ add_array(Communities, value);
+ else if (!strcasecmp(line, "DebugLevel"))
+ DebugLevel = atoi(value);
+ else if (!strcasecmp(line, "HostNameLookups"))
+ HostNameLookups = !strcasecmp(value, "on") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "true") ||
+ !strcasecmp(value, "double");
+ else
+ fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
+ line, linenum, filename);
+ }
+
+ cupsFileClose(fp);
+ }
+
+ /*
+ * Use defaults if parameters are undefined...
+ */
+
+ if (cupsArrayCount(Addresses) == 0)
+ {
+ fputs("INFO: Using default SNMP Address @LOCAL\n", stderr);
+ add_array(Addresses, "@LOCAL");
+ }
+
+ if (cupsArrayCount(Communities) == 0)
+ {
+ fputs("INFO: Using default SNMP Community public\n", stderr);
+ add_array(Communities, "public");
+ }
+}
+
+
+/*
+ * 'read_snmp_response()' - Read and parse a SNMP response...
+ */
+
+static void
+read_snmp_response(int fd) /* I - SNMP socket file descriptor */
+{
+ unsigned char buffer[SNMP_MAX_PACKET];/* Data packet */
+ int bytes; /* Number of bytes received */
+ http_addr_t addr; /* Source address */
+ socklen_t addrlen; /* Source address length */
+ char addrname[256]; /* Source address name */
+ snmp_packet_t packet; /* Decoded packet */
+ snmp_cache_t key, /* Search key */
+ *device; /* Matching device */
+
+
+ /*
+ * Read the response data...
+ */
+
+ addrlen = sizeof(addr);
+
+ if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (void *)&addr,
+ &addrlen)) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ if (HostNameLookups)
+ httpAddrLookup(&addr, addrname, sizeof(addrname));
+ else
+ httpAddrString(&addr, addrname, sizeof(addrname));
+
+ debug_printf("DEBUG: %.3f Received %d bytes from %s...\n", run_time(),
+ bytes, addrname);
+
+ /*
+ * Look for the response status code in the SNMP message header...
+ */
+
+ if (asn1_decode_snmp(buffer, bytes, &packet))
+ {
+ fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname,
+ packet.error);
+
+ asn1_debug(buffer, bytes, 0);
+ hex_debug(buffer, bytes);
+
+ return;
+ }
+
+ debug_printf("DEBUG: community=\"%s\"\n", packet.community);
+ debug_printf("DEBUG: request-id=%d\n", packet.request_id);
+ debug_printf("DEBUG: error-status=%d\n", packet.error_status);
+
+ if (DebugLevel > 1)
+ asn1_debug(buffer, bytes, 0);
+
+ if (DebugLevel > 2)
+ hex_debug(buffer, bytes);
+
+ if (packet.error_status)
+ return;
+
+ /*
+ * Find a matching device in the cache...
+ */
+
+ key.address = addr;
+ device = (snmp_cache_t *)cupsArrayFind(Devices, &key);
+
+ /*
+ * Process the message...
+ */
+
+ if (packet.request_id == DeviceTypeRequest)
+ {
+ /*
+ * Got the device type response...
+ */
+
+ if (device)
+ {
+ debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
+ addrname);
+ return;
+ }
+
+ /*
+ * Add the device and request the device description...
+ */
+
+ add_cache(&addr, addrname, NULL, NULL, NULL);
+
+ send_snmp_query(fd, &addr, SNMP_VERSION_1, packet.community,
+ DeviceDescRequest, DeviceDescOID);
+ }
+ else if (packet.request_id == DeviceDescRequest &&
+ packet.object_type == ASN1_OCTET_STRING)
+ {
+ /*
+ * Update an existing cache entry...
+ */
+
+ char make_model[256]; /* Make and model */
+
+
+ if (!device)
+ {
+ debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
+ addrname);
+ return;
+ }
+
+ /*
+ * Convert the description to a make and model string...
+ */
+
+ if (strchr(packet.object_value.string, ':') &&
+ strchr(packet.object_value.string, ';'))
+ {
+ /*
+ * Description is the IEEE-1284 device ID...
+ */
+
+ get_make_model(packet.object_value.string, make_model,
+ sizeof(make_model));
+ }
+ else
+ {
+ /*
+ * Description is plain text...
+ */
+
+ fix_make_model(make_model, packet.object_value.string,
+ sizeof(make_model));
+ }
+
+ if (device->make_and_model)
+ free(device->make_and_model);
+
+ device->make_and_model = strdup(make_model);
+ }
+}
+
+
+/*
+ * 'run_time()' - Return the total running time...
+ */
+
+static double /* O - Number of seconds */
+run_time(void)
+{
+ struct timeval curtime; /* Current time */
+
+
+ gettimeofday(&curtime, NULL);
+
+ return (curtime.tv_sec - StartTime.tv_sec +
+ 0.000001 * (curtime.tv_usec - StartTime.tv_usec));
+}
+
+
+/*
+ * 'scan_devices()' - Scan for devices using SNMP.
+ */
+
+static void
+scan_devices(int fd) /* I - SNMP socket */
+{
+ char *address, /* Current address */
+ *community; /* Current community */
+ fd_set input; /* Input set for select() */
+ struct timeval timeout; /* Timeout for select() */
+ time_t endtime; /* End time for scan */
+ http_addrlist_t *addrs, /* List of addresses */
+ *addr; /* Current address */
+ snmp_cache_t *device; /* Current device */
+
+
+ /*
+ * Setup the request IDs...
+ */
+
+ gettimeofday(&StartTime, NULL);
+
+ DeviceTypeRequest = StartTime.tv_sec;
+ DeviceDescRequest = StartTime.tv_sec + 1;
+
+ /*
+ * First send all of the broadcast queries...
+ */
+
+ for (address = (char *)cupsArrayFirst(Addresses);
+ address;
+ address = (char *)cupsArrayNext(Addresses))
+ {
+ if (!strcmp(address, "@LOCAL"))
+ addrs = get_interface_addresses(NULL);
+ else if (!strncmp(address, "@IF(", 4))
+ {
+ char ifname[255]; /* Interface name */
+
+
+ strlcpy(ifname, address + 4, sizeof(ifname));
+ if (ifname[0])
+ ifname[strlen(ifname) - 1] = '\0';
+
+ addrs = get_interface_addresses(ifname);
+ }
+ else
+ addrs = httpAddrGetList(address, AF_INET, NULL);
+
+ if (!addrs)
+ {
+ fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address);
+ continue;
+ }
+
+ for (community = (char *)cupsArrayFirst(Communities);
+ community;
+ community = (char *)cupsArrayNext(Communities))
+ {
+ debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
+ community, address);
+
+ for (addr = addrs; addr; addr = addr->next)
+ send_snmp_query(fd, &(addr->addr), SNMP_VERSION_1, community,
+ DeviceTypeRequest, DeviceTypeOID);
+ }
+
+ httpAddrFreeList(addrs);
+ }
+
+ /*
+ * Then read any responses that come in over the next 3 seconds...
+ */
+
+ endtime = time(NULL) + 3;
+
+ FD_ZERO(&input);
+
+ while (time(NULL) < endtime)
+ {
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ FD_SET(fd, &input);
+ if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
+ {
+ fprintf(stderr, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
+ fd, strerror(errno));
+ break;
+ }
+
+ if (FD_ISSET(fd, &input))
+ read_snmp_response(fd);
+ else
+ break;
+ }
+
+ /*
+ * Finally, probe all of the printers we discovered to see how they are
+ * connected...
+ */
+
+ for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
+ device;
+ device = (snmp_cache_t *)cupsArrayNext(Devices))
+ if (!device->uri)
+ probe_device(device);
+
+ debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
+}
+
+
+/*
+ * 'send_snmp_query()' - Send an SNMP query packet.
+ */
+
+static void
+send_snmp_query(
+ int fd, /* I - SNMP socket */
+ http_addr_t *addr, /* I - Address to send to */
+ int version, /* I - SNMP version */
+ const char *community, /* I - Community name */
+ const unsigned request_id, /* I - Request ID */
+ const int *oid) /* I - OID */
+{
+ int i; /* Looping var */
+ snmp_packet_t packet; /* SNMP message packet */
+ unsigned char buffer[SNMP_MAX_PACKET];/* SNMP message buffer */
+ int bytes; /* Size of message */
+ char addrname[32]; /* Address name */
+
+
+ /*
+ * Create the SNMP message...
+ */
+
+ memset(&packet, 0, sizeof(packet));
+
+ packet.version = version;
+ packet.request_type = ASN1_GET_REQUEST;
+ packet.request_id = request_id;
+ packet.object_type = ASN1_NULL_VALUE;
+
+ strlcpy(packet.community, community, sizeof(packet.community));
+
+ for (i = 0; oid[i]; i ++)
+ packet.object_name[i] = oid[i];
+
+ bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet);
+
+ if (bytes < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to send SNMP query: %s\n", packet.error);
+ return;
+ }
+
+ /*
+ * Send the message...
+ */
+
+ debug_printf("DEBUG: %.3f Sending %d bytes to %s...\n", run_time(),
+ bytes, httpAddrString(addr, addrname, sizeof(addrname)));
+ if (DebugLevel > 1)
+ asn1_debug(buffer, bytes, 0);
+ if (DebugLevel > 2)
+ hex_debug(buffer, bytes);
+
+ addr->ipv4.sin_port = htons(SNMP_PORT);
+
+ if (sendto(fd, buffer, bytes, 0, (void *)addr, sizeof(addr->ipv4)) < bytes)
+ fprintf(stderr, "ERROR: Unable to send %d bytes to %s: %s\n",
+ bytes, addrname, strerror(errno));
+}
+
+
+/*
+ * 'try_connect()' - Try connecting on a port...
+ */
+
+static int /* O - 0 on success or -1 on error */
+try_connect(http_addr_t *addr, /* I - Socket address */
+ const char *addrname, /* I - Hostname or IP address */
+ int port) /* I - Port number */
+{
+ int fd; /* Socket */
+ int status; /* Connection status */
+
+
+ debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
+ port == 515 ? "lpd" : "socket", addrname, port);
+
+ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ fprintf(stderr, "ERROR: Unable to create socket: %s\n", strerror(errno));
+ return (-1);
+ }
+
+ addr->ipv4.sin_port = htons(port);
+
+ signal(SIGALRM, alarm_handler);
+ alarm(1);
+
+ status = connect(fd, (void *)addr, httpAddrLength(addr));
+
+ close(fd);
+ alarm(0);
+
+ return (status);
+}
+
+
+/*
+ * 'update_cache()' - Update a cached device...
+ */
+
+static void
+update_cache(snmp_cache_t *device, /* I - Device */
+ const char *uri, /* I - Device URI */
+ const char *id, /* I - Device ID */
+ const char *make_model) /* I - Device make and model */
+{
+ if (device->uri)
+ free(device->uri);
+
+ device->uri = strdup(uri);
+
+ if (id)
+ {
+ if (device->id)
+ free(device->id);
+
+ device->id = strdup(id);
+ }
+
+ if (make_model)
+ {
+ if (device->make_and_model)
+ free(device->make_and_model);
+
+ device->make_and_model = strdup(make_model);
+ }
+}
+
+
+/*
+ * End of "$Id: snmp.c 5453 2006-04-23 12:08:18Z mike $".
+ */