]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/snmp.c
Fix build errors on Fedora.
[thirdparty/cups.git] / backend / snmp.c
index 916430bd8d218a19c0707cd05ec1674e5010b954..876ca652050b0056b9d51e5d5a15ffcdabbb04fc 100644 (file)
@@ -1,92 +1,40 @@
 /*
- * "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $"
+ * "$Id$"
  *
- *   SNMP discovery backend for the Common UNIX Printing System (CUPS).
+ * SNMP discovery backend for CUPS.
  *
- *   Copyright 2007 by Apple Inc.
- *   Copyright 2006-2007 by Easy Software Products, all rights reserved.
+ * Copyright 2007-2014 by Apple Inc.
+ * Copyright 2006-2007 by Easy Software Products, all rights reserved.
  *
- *   These coded instructions, statements, and computer programs are the
- *   property of Apple Inc. and are protected by Federal copyright
- *   law.  Distribution and use rights are outlined in the file "LICENSE.txt"
- *   "LICENSE" which should have been included with this file.  If this
- *   file is missing or damaged, see the license at "http://www.cups.org/".
+ * These coded instructions, statements, and computer programs are the
+ * property of Apple Inc. and are protected by Federal copyright
+ * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
+ * "LICENSE" which should have been included with this file.  If this
+ * file is missing or damaged, see the license at "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...
- *   add_device_uri()          - Add a device URI to the cache.
- *   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_device()             - List a device 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...
+ * This file is subject to the Apple OS-Developed Software exception.
  */
 
 /*
  * Include necessary headers.
  */
 
-#include <cups/http-private.h>
 #include "backend-private.h"
 #include <cups/array.h>
 #include <cups/file.h>
+#include <cups/http-private.h>
 #include <regex.h>
 
 
 /*
  * This backend implements SNMP printer discovery.  It uses a broadcast-
  * based approach to get SNMP response packets from potential printers,
- * tries a mDNS lookup (Mac OS X only at present), a URI lookup based on
- * the device description string, and finally a probe of port 9100
- * (AppSocket) and 515 (LPD).
+ * requesting OIDs from the Host and Port Monitor MIBs, does a URI
+ * lookup based on the device description string, and finally a probe of
+ * port 9100 (AppSocket) and 515 (LPD).
  *
  * 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 code also works with many external print servers as well.
  *
  * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
  * which can contain comments, blank lines, or any number of the following
  *     Community public
  *     DebugLevel 0
  *     HostNameLookups off
- *     MaxRunTime 10
+ *     MaxRunTime 120
  *
  * This backend is known to work with the following network printers and
  * print servers:
  *
  *     Axis OfficeBasic, 5400, 5600
+ *     Brother
  *     EPSON
  *     Genicom
  *     HP JetDirect
  * (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...
  */
 
+enum                                   /**** Request IDs for each field ****/
+{
+  DEVICE_TYPE = 1,
+  DEVICE_DESCRIPTION,
+  DEVICE_LOCATION,
+  DEVICE_ID,
+  DEVICE_URI,
+  DEVICE_PRODUCT
+};
+
 typedef struct device_uri_s            /**** DeviceURI values ****/
 {
   regex_t      re;                     /* Regular expression to match */
@@ -170,38 +107,12 @@ typedef struct snmp_cache_s               /**** SNMP scan cache ****/
   char         *addrname,              /* Name of device */
                *uri,                   /* device-uri */
                *id,                    /* device-id */
+               *info,                  /* device-info */
+               *location,              /* device-location */
                *make_and_model;        /* device-make-and-model */
+  int          sent;                   /* Has this device been listed? */
 } 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;
-
-
-/*
- * Private CUPS API to set the last error...
- */
-
-extern void    _cupsSetError(ipp_status_t status, const char *message);
-
 
 /*
  * Local functions...
@@ -213,40 +124,6 @@ static void                add_cache(http_addr_t *addr, const char *addrname,
                                  const char *make_and_model);
 static device_uri_t    *add_device_uri(char *value);
 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,
@@ -255,19 +132,13 @@ static void               fix_make_model(char *make_model,
 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_device(snmp_cache_t *cache);
-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 void            scan_devices(int ipv4, int ipv6);
 static int             try_connect(http_addr_t *addr, const char *addrname,
                                    int port);
 static void            update_cache(snmp_cache_t *device, const char *uri,
@@ -282,15 +153,18 @@ static cups_array_t       *Addresses = NULL;
 static cups_array_t    *Communities = NULL;
 static cups_array_t    *Devices = NULL;
 static int             DebugLevel = 0;
-static int             DeviceDescOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
-                                           2, 1, 3, 1, 0 };
-static unsigned                DeviceDescRequest;
-static int             DeviceTypeOID[] = { 1, 3, 6, 1, 2, 1, 25, 3,
-                                           2, 1, 2, 1, 0 };
-static unsigned                DeviceTypeRequest;
+static const int       DescriptionOID[] = { CUPS_OID_hrDeviceDescr, 1, -1 };
+static const int       LocationOID[] = { CUPS_OID_sysLocation, 0, -1 };
+static const int       DeviceTypeOID[] = { CUPS_OID_hrDeviceType, 1, -1 };
+static const int       DeviceIdOID[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId, 1, -1 };
+static const int       UriOID[] = { CUPS_OID_ppmPortServiceNameOrURI, 1, 1, -1 };
+static const int       LexmarkProductOID[] = { 1,3,6,1,4,1,641,2,1,2,1,2,1,-1 };
+static const int       LexmarkProductOID2[] = { 1,3,6,1,4,1,674,10898,100,2,1,2,1,2,1,-1 };
+static const int       LexmarkDeviceIdOID[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 };
+static const int       XeroxProductOID[] = { 1,3,6,1,4,1,128,2,1,3,1,2,0,-1 };
 static cups_array_t    *DeviceURIs = NULL;
 static int             HostNameLookups = 0;
-static int             MaxRunTime = 10;
+static int             MaxRunTime = 120;
 static struct timeval  StartTime;
 
 
@@ -302,7 +176,8 @@ 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 */
+  int          ipv4,                   /* SNMP IPv4 socket */
+               ipv6;                   /* SNMP IPv6 socket */
 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
   struct sigaction action;             /* Actions for POSIX signals */
 #endif /* HAVE_SIGACTION && !HAVE_SIGSET */
@@ -314,7 +189,7 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
 
   if (argc > 2)
   {
-    fputs(_("Usage: snmp [host-or-ip-address]\n"), stderr);
+    _cupsLangPuts(stderr, _("Usage: snmp [host-or-ip-address]"));
     return (1);
   }
 
@@ -345,28 +220,39 @@ main(int  argc,                           /* I - Number of command-line arguments (6 or 7) */
   * Open the SNMP socket...
   */
 
-  if ((fd = open_snmp_socket()) < 0)
+  if ((ipv4 = _cupsSNMPOpen(AF_INET)) < 0)
     return (1);
 
+#ifdef AF_INET6
+  if ((ipv6 = _cupsSNMPOpen(AF_INET6)) < 0)
+    perror("DEBUG: Unable to create IPv6 socket");
+#else
+  ipv6 = -1;
+#endif /* AF_INET6 */
+
  /*
   * Read the configuration file and any cache data...
   */
 
   read_snmp_conf(argv[1]);
 
+  _cupsSNMPSetDebug(DebugLevel);
+
   Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
 
  /*
   * Scan for devices...
   */
 
-  scan_devices(fd);
+  scan_devices(ipv4, ipv6);
 
  /*
   * Close, free, and return with no errors...
   */
 
-  close(fd);
+  _cupsSNMPClose(ipv4);
+  if (ipv6 >= 0)
+    _cupsSNMPClose(ipv6);
 
   free_array(Addresses);
   free_array(Communities);
@@ -547,778 +433,6 @@ alarm_handler(int sig)                    /* I - Signal number */
 }
 
 
-/*
- * '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 >> 28) & 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.
  */
@@ -1327,7 +441,7 @@ 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 (strcasecmp(a->addrname, b->addrname));
+  return (_cups_strcasecmp(a->addrname, b->addrname));
 }
 
 
@@ -1369,7 +483,7 @@ fix_make_model(
   * that printer driver detection works better...
   */
 
-  if (!strncasecmp(old_make_model, "Hewlett-Packard", 15))
+  if (!_cups_strncasecmp(old_make_model, "Hewlett-Packard", 15))
   {
    /*
     * Strip leading Hewlett-Packard and hp prefixes and replace
@@ -1381,7 +495,7 @@ fix_make_model(
     while (isspace(*mmptr & 255))
       mmptr ++;
 
-    if (!strncasecmp(mmptr, "hp", 2))
+    if (!_cups_strncasecmp(mmptr, "hp", 2))
     {
       mmptr += 2;
 
@@ -1392,17 +506,16 @@ fix_make_model(
     make_model[0] = 'H';
     make_model[1] = 'P';
     make_model[2] = ' ';
-    strlcpy(make_model + 3, mmptr, make_model_size - 3);
+    strlcpy(make_model + 3, mmptr, (size_t)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, "officejet", 9))
-    snprintf(make_model, make_model_size, "HP OfficeJet%s", old_make_model + 9);
-  else if (!strncasecmp(old_make_model, "stylus_pro_", 11))
-    snprintf(make_model, make_model_size, "EPSON Stylus Pro %s",
-             old_make_model + 11);
+  else if (!_cups_strncasecmp(old_make_model, "deskjet", 7))
+    snprintf(make_model, (size_t)make_model_size, "HP DeskJet%s", old_make_model + 7);
+  else if (!_cups_strncasecmp(old_make_model, "officejet", 9))
+    snprintf(make_model, (size_t)make_model_size, "HP OfficeJet%s", old_make_model + 9);
+  else if (!_cups_strncasecmp(old_make_model, "stylus_pro_", 11))
+    snprintf(make_model, (size_t)make_model_size, "EPSON Stylus Pro %s", old_make_model + 11);
   else
-    strlcpy(make_model, old_make_model, make_model_size);
+    strlcpy(make_model, old_make_model, (size_t)make_model_size);
 
   if ((mmptr = strstr(make_model, ", Inc.,")) != NULL)
   {
@@ -1528,35 +641,6 @@ get_interface_addresses(
 }
 
 
-/*
- * '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_device()' - List a device we found...
  */
@@ -1565,54 +649,8 @@ static void
 list_device(snmp_cache_t *cache)       /* I - Cached device */
 {
   if (cache->uri)
-    printf("network %s \"%s\" \"%s %s\" \"%s\"\n",
-           cache->uri,
-          cache->make_and_model ? cache->make_and_model : "Unknown",
-          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);
+    cupsBackendReport("network", cache->uri, cache->make_and_model,
+                      cache->info, cache->id, cache->location);
 }
 
 
@@ -1653,7 +691,7 @@ probe_device(snmp_cache_t *device) /* I - Device */
 
 #ifdef __APPLE__
  /*
-  * TODO: Try an mDNS query first, and then fallback on direct probes...
+  * If the printer supports Bonjour/mDNS, don't report it from the SNMP backend.
   */
 
   if (!try_connect(&(device->address), device->addrname, 5353))
@@ -1670,7 +708,8 @@ probe_device(snmp_cache_t *device) /* I - Device */
   for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
        device_uri;
        device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
-    if (!regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
+    if (device->make_and_model &&
+        !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
     {
      /*
       * Found a match, add the URIs...
@@ -1687,7 +726,7 @@ probe_device(snmp_cache_t *device) /* I - Device */
            * Insert hostname/address...
            */
 
-           strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri));
+           strlcpy(uriptr, device->addrname, sizeof(uri) - (size_t)(uriptr - uri));
            uriptr += strlen(uriptr);
            format += 2;
          }
@@ -1778,16 +817,16 @@ read_snmp_conf(const char *address)      /* I - Single address to probe */
       if (!value)
         fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
                filename);
-      else if (!strcasecmp(line, "Address"))
+      else if (!_cups_strcasecmp(line, "Address"))
       {
         if (!address)
           add_array(Addresses, value);
       }
-      else if (!strcasecmp(line, "Community"))
+      else if (!_cups_strcasecmp(line, "Community"))
         add_array(Communities, value);
-      else if (!strcasecmp(line, "DebugLevel"))
+      else if (!_cups_strcasecmp(line, "DebugLevel"))
         DebugLevel = atoi(value);
-      else if (!strcasecmp(line, "DeviceURI"))
+      else if (!_cups_strcasecmp(line, "DeviceURI"))
       {
         if (*value != '\"')
          fprintf(stderr,
@@ -1796,12 +835,12 @@ read_snmp_conf(const char *address)      /* I - Single address to probe */
         else
          add_device_uri(value);
       }
-      else if (!strcasecmp(line, "HostNameLookups"))
-        HostNameLookups = !strcasecmp(value, "on") ||
-                         !strcasecmp(value, "yes") ||
-                         !strcasecmp(value, "true") ||
-                         !strcasecmp(value, "double");
-      else if (!strcasecmp(line, "MaxRunTime"))
+      else if (!_cups_strcasecmp(line, "HostNameLookups"))
+        HostNameLookups = !_cups_strcasecmp(value, "on") ||
+                         !_cups_strcasecmp(value, "yes") ||
+                         !_cups_strcasecmp(value, "true") ||
+                         !_cups_strcasecmp(value, "double");
+      else if (!_cups_strcasecmp(line, "MaxRunTime"))
         MaxRunTime = atoi(value);
       else
         fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
@@ -1842,12 +881,8 @@ read_snmp_conf(const char *address)       /* I - Single address to probe */
 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 */
+  cups_snmp_t  packet;                 /* Decoded packet */
   snmp_cache_t key,                    /* Search key */
                *device;                /* Matching device */
 
@@ -1856,10 +891,7 @@ read_snmp_response(int fd)                /* I - SNMP socket file descriptor */
   * Read the response data...
   */
 
-  addrlen = sizeof(addr);
-
-  if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (void *)&addr,
-                        &addrlen)) < 0)
+  if (!_cupsSNMPRead(fd, &packet, -1.0))
   {
     fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
             strerror(errno));
@@ -1867,24 +899,20 @@ read_snmp_response(int fd)               /* I - SNMP socket file descriptor */
   }
 
   if (HostNameLookups)
-    httpAddrLookup(&addr, addrname, sizeof(addrname));
+    httpAddrLookup(&(packet.address), addrname, sizeof(addrname));
   else
-    httpAddrString(&addr, addrname, sizeof(addrname));
+    httpAddrString(&(packet.address), addrname, sizeof(addrname));
 
-  debug_printf("DEBUG: %.3f Received %d bytes from %s...\n", run_time(),
-               bytes, addrname);
+  debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname);
 
  /*
   * Look for the response status code in the SNMP message header...
   */
 
-  if (asn1_decode_snmp(buffer, bytes, &packet))
+  if (packet.error)
   {
-    fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n",
-            addrname, packet.error);
-
-    asn1_debug(buffer, bytes, 0);
-    hex_debug(buffer, bytes);
+    fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname,
+            packet.error);
 
     return;
   }
@@ -1893,13 +921,7 @@ read_snmp_response(int fd)                /* I - SNMP socket file descriptor */
   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)
+  if (packet.error_status && packet.request_id != DEVICE_TYPE)
     return;
 
  /*
@@ -1913,73 +935,194 @@ read_snmp_response(int fd)              /* I - SNMP socket file descriptor */
   * Process the message...
   */
 
-  if (packet.request_id == DeviceTypeRequest)
+  switch (packet.request_id)
   {
-   /*
-    * Got the device type response...
-    */
+    case DEVICE_TYPE :
+       /*
+       * Got the device type response...
+       */
 
-    if (device)
-    {
-      debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
-                  addrname);
-      return;
-    }
+       if (device)
+       {
+         debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
+                      addrname);
+         return;
+       }
 
-   /*
-    * Add the device and request the device description...
-    */
+       /*
+       * Add the device and request the device data...
+       */
+
+       add_cache(&(packet.address), addrname, NULL, NULL, NULL);
+
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_DESCRIPTION, DescriptionOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_ID, DeviceIdOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_URI, UriOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_LOCATION, LocationOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_PRODUCT, LexmarkProductOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_PRODUCT, LexmarkProductOID2);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_ID, LexmarkDeviceIdOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_PRODUCT, XeroxProductOID);
+        break;
 
-    add_cache(&addr, addrname, NULL, NULL, NULL);
+    case DEVICE_DESCRIPTION :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING)
+       {
+        /*
+         * Update an existing cache entry...
+         */
 
-    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 */
 
-    char       make_model[256];        /* Make and model */
 
+         if (strchr((char *)packet.object_value.string.bytes, ':') &&
+             strchr((char *)packet.object_value.string.bytes, ';'))
+         {
+          /*
+           * Description is the IEEE-1284 device ID...
+           */
 
-    if (!device)
-    {
-      debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
-                  addrname);
-      return;
-    }
+            char *ptr;                 /* Pointer into device ID */
 
-   /*
-    * Convert the description to a make and model string...
-    */
+            for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++)
+              if (*ptr == '\n')
+                *ptr = ';';            /* A lot of bad printers put a newline */
+           if (!device->id)
+             device->id = strdup((char *)packet.object_value.string.bytes);
 
-    if (strchr(packet.object_value.string, ':') &&
-        strchr(packet.object_value.string, ';'))
-    {
-     /*
-      * Description is the IEEE-1284 device ID...
-      */
+           backendGetMakeModel((char *)packet.object_value.string.bytes,
+                               make_model, sizeof(make_model));
 
-      backendGetMakeModel(packet.object_value.string, make_model,
-                         sizeof(make_model));
-    }
-    else
-    {
-     /*
-      * Description is plain text...
-      */
+            if (device->info)
+             free(device->info);
 
-      fix_make_model(make_model, packet.object_value.string,
-                     sizeof(make_model));
-    }
+           device->info = strdup(make_model);
+         }
+         else
+         {
+          /*
+           * Description is plain text...
+           */
 
-    if (device->make_and_model)
-      free(device->make_and_model);
+           fix_make_model(make_model, (char *)packet.object_value.string.bytes,
+                          sizeof(make_model));
 
-    device->make_and_model = strdup(make_model);
+            if (device->info)
+             free(device->info);
+
+           device->info = strdup((char *)packet.object_value.string.bytes);
+         }
+
+         if (!device->make_and_model)
+           device->make_and_model = strdup(make_model);
+        }
+       break;
+
+    case DEVICE_ID :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
+           (!device->id ||
+            strlen(device->id) < packet.object_value.string.num_bytes))
+       {
+        /*
+         * Update an existing cache entry...
+         */
+
+         char  make_model[256];        /* Make and model */
+          char *ptr;                   /* Pointer into device ID */
+
+          for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++)
+            if (*ptr == '\n')
+              *ptr = ';';              /* A lot of bad printers put a newline */
+         if (device->id)
+           free(device->id);
+
+         device->id = strdup((char *)packet.object_value.string.bytes);
+
+        /*
+         * Convert the ID to a make and model string...
+         */
+
+         backendGetMakeModel((char *)packet.object_value.string.bytes,
+                             make_model, sizeof(make_model));
+         if (device->make_and_model)
+           free(device->make_and_model);
+
+         device->make_and_model = strdup(make_model);
+       }
+       break;
+
+    case DEVICE_LOCATION :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
+           !device->location)
+         device->location = strdup((char *)packet.object_value.string.bytes);
+       break;
+
+    case DEVICE_PRODUCT :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
+           !device->id)
+       {
+        /*
+         * Update an existing cache entry...
+         */
+
+          if (!device->info)
+           device->info = strdup((char *)packet.object_value.string.bytes);
+
+          if (device->make_and_model)
+           free(device->make_and_model);
+
+         device->make_and_model = strdup((char *)packet.object_value.string.bytes);
+       }
+       break;
+
+    case DEVICE_URI :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
+           !device->uri && packet.object_value.string.num_bytes > 3)
+       {
+        /*
+         * Update an existing cache entry...
+         */
+
+          char scheme[32],             /* URI scheme */
+               userpass[256],          /* Username:password in URI */
+               hostname[256],          /* Hostname in URI */
+               resource[1024];         /* Resource path in URI */
+         int   port;                   /* Port number in URI */
+
+         if (!strncmp((char *)packet.object_value.string.bytes, "lpr:", 4))
+         {
+          /*
+           * We want "lpd://..." for the URI...
+           */
+
+           packet.object_value.string.bytes[2] = 'd';
+         }
+
+          if (httpSeparateURI(HTTP_URI_CODING_ALL,
+                              (char *)packet.object_value.string.bytes,
+                              scheme, sizeof(scheme),
+                              userpass, sizeof(userpass),
+                              hostname, sizeof(hostname), &port,
+                              resource, sizeof(resource)) >= HTTP_URI_OK)
+           device->uri = strdup((char *)packet.object_value.string.bytes);
+       }
+       break;
   }
 }
 
@@ -2006,8 +1149,11 @@ run_time(void)
  */
 
 static void
-scan_devices(int fd)                   /* I - SNMP socket */
+scan_devices(int ipv4,                 /* I - SNMP IPv4 socket */
+             int ipv6)                 /* I - SNMP IPv6 socket */
 {
+  int                  fd,             /* File descriptor for this address */
+                       busy;           /* Are we busy processing something? */
   char                 *address,       /* Current address */
                        *community;     /* Current community */
   fd_set               input;          /* Input set for select() */
@@ -2016,17 +1162,11 @@ scan_devices(int fd)                    /* I - SNMP socket */
   http_addrlist_t      *addrs,         /* List of addresses */
                        *addr;          /* Current address */
   snmp_cache_t         *device;        /* Current device */
+  char                 temp[1024];     /* Temporary address string */
 
 
- /*
-  * Setup the request IDs...
-  */
-
   gettimeofday(&StartTime, NULL);
 
-  DeviceTypeRequest = StartTime.tv_sec;
-  DeviceDescRequest = StartTime.tv_sec + 1;
-
  /*
   * First send all of the broadcast queries...
   */
@@ -2041,7 +1181,6 @@ scan_devices(int fd)                      /* I - SNMP socket */
     {
       char     ifname[255];            /* Interface name */
 
-
       strlcpy(ifname, address + 4, sizeof(ifname));
       if (ifname[0])
         ifname[strlen(ifname) - 1] = '\0';
@@ -2049,7 +1188,7 @@ scan_devices(int fd)                      /* I - SNMP socket */
       addrs = get_interface_addresses(ifname);
     }
     else
-      addrs = httpAddrGetList(address, AF_INET, NULL);
+      addrs = httpAddrGetList(address, AF_UNSPEC, NULL);
 
     if (!addrs)
     {
@@ -2065,8 +1204,20 @@ scan_devices(int fd)                     /* I - SNMP socket */
                   community, address);
 
       for (addr = addrs; addr; addr = addr->next)
-        send_snmp_query(fd, &(addr->addr), SNMP_VERSION_1, community,
-                       DeviceTypeRequest, DeviceTypeOID);
+      {
+#ifdef AF_INET6
+        if (httpAddrFamily(&(addr->addr)) == AF_INET6)
+         fd = ipv6;
+       else
+#endif /* AF_INET6 */
+        fd = ipv4;
+
+        debug_printf("DEBUG: Sending get request to %s...\n",
+                    httpAddrString(&(addr->addr), temp, sizeof(temp)));
+
+        _cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community,
+                      CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID);
+      }
     }
 
     httpAddrFreeList(addrs);
@@ -2076,107 +1227,68 @@ scan_devices(int fd)                   /* I - SNMP socket */
   * Then read any responses that come in over the next 3 seconds...
   */
 
-  endtime = time(NULL) + 3;
+  endtime = time(NULL) + MaxRunTime;
 
   FD_ZERO(&input);
 
   while (time(NULL) < endtime)
   {
-    timeout.tv_sec  = 1;
+    timeout.tv_sec  = 2;
     timeout.tv_usec = 0;
 
-    FD_SET(fd, &input);
+    FD_SET(ipv4, &input);
+    if (ipv6 >= 0)
+      FD_SET(ipv6, &input);
+
+    fd = ipv4 > ipv6 ? ipv4 : ipv6;
     if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
     {
-      fprintf(stderr, "ERROR: %.3f select() for %d failed: %s\n", run_time(),
-              fd, strerror(errno));
+      fprintf(stderr, "ERROR: %.3f select() for %d/%d failed: %s\n", run_time(),
+              ipv4, ipv6, 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 (MaxRunTime > 0 && run_time() >= MaxRunTime)
-      break;
-    else 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 */
+    busy = 0;
 
+    if (FD_ISSET(ipv4, &input))
+    {
+      read_snmp_response(ipv4);
+      busy = 1;
+    }
 
- /*
-  * Create the SNMP message...
-  */
+    if (ipv6 >= 0 && FD_ISSET(ipv6, &input))
+    {
+      read_snmp_response(ipv6);
+      busy = 1;
+    }
 
-  memset(&packet, 0, sizeof(packet));
+    if (!busy)
+    {
+     /*
+      * List devices with complete information...
+      */
 
-  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));
+      int sent_something = 0;
 
-  for (i = 0; oid[i]; i ++)
-    packet.object_name[i] = oid[i];
+      for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
+           device;
+          device = (snmp_cache_t *)cupsArrayNext(Devices))
+        if (!device->sent && device->info && device->make_and_model)
+       {
+         if (device->uri)
+           list_device(device);
+         else
+           probe_device(device);
 
-  bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet);
+         device->sent = sent_something = 1;
+       }
 
-  if (bytes < 0)
-  {
-    fprintf(stderr, "ERROR: Unable to send SNMP query: %s\n",
-            packet.error);
-    return;
+      if (!sent_something)
+        break;
+    }
   }
 
- /*
-  * 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));
+  debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
 }
 
 
@@ -2196,18 +1308,18 @@ try_connect(http_addr_t *addr,          /* I - Socket address */
   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)
+  if ((fd = socket(httpAddrFamily(addr), SOCK_STREAM, 0)) < 0)
   {
     fprintf(stderr, "ERROR: Unable to create socket: %s\n",
             strerror(errno));
     return (-1);
   }
 
-  addr->ipv4.sin_port = htons(port);
+  _httpAddrSetPort(addr, port);
 
   alarm(1);
 
-  status = connect(fd, (void *)addr, httpAddrLength(addr));
+  status = connect(fd, (void *)addr, (socklen_t)httpAddrLength(addr));
 
   close(fd);
   alarm(0);
@@ -2252,5 +1364,5 @@ update_cache(snmp_cache_t *device,        /* I - Device */
 
 
 /*
- * End of "$Id: snmp.c 6649 2007-07-11 21:46:42Z mike $".
+ * End of "$Id$".
  */