]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/snmp.c
Merge changes from CUPS 1.4svn-r7874.
[thirdparty/cups.git] / backend / snmp.c
index fd7d3922ee111fa80b6a35e377a32a9683ae4888..712981cfd48a235cbb385f6cc9eb9d279f10ab80 100644 (file)
@@ -1,25 +1,16 @@
 /*
- * "$Id: snmp.c 6181 2007-01-03 18:51:27Z mike $"
+ * "$Id: snmp.c 7506 2008-04-29 04:38:47Z mike $"
  *
  *   SNMP discovery backend for the Common UNIX Printing System (CUPS).
  *
- *   Copyright 2006 by Easy Software Products, all rights reserved.
+ *   Copyright 2007-2008 by Apple Inc.
+ *   Copyright 2006-2007 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
+ *   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 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
+ *   file is missing or damaged, see the license at "http://www.cups.org/".
  *
  *   This file is subject to the Apple OS-Developed Software exception.
  *
  *   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.
- *   do_request()              - Do a non-blocking IPP request.
  *   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...
+ *   get_interface_addresses() - Get the broadcast address(es) associated with
+ *                               an interface.
  *   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.
@@ -69,7 +37,6 @@
  *   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/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
- * and then interrogates each responder by trying to connect on port
- * 631, 9100, and 515.
+ * based approach to get SNMP response packets from potential printers,
+ * 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
  *     Address @IF(name)
  *     Community name
  *     DebugLevel N
+ *     DeviceURI "regex pattern" uri
  *     HostNameLookups on
  *     HostNameLookups off
+ *     MaxRunTime N
  *
  * The default is to use:
  *
  *     Community public
  *     DebugLevel 0
  *     HostNameLookups off
+ *     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 */
+  cups_array_t *uris;                  /* URIs */
+} device_uri_t;
+
 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 */
+               *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);
+extern void            _cupsSetError(ipp_status_t status, const char *message);
 
 
 /*
@@ -209,64 +155,23 @@ 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 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 ipp_t           *do_request(http_t *http, ipp_t *request,
-                                   const char *resource);
 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_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 int             try_connect(http_addr_t *addr, const char *addrname,
                                    int port);
 static void            update_cache(snmp_cache_t *device, const char *uri,
@@ -281,14 +186,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             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 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;
 
 
@@ -312,7 +221,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);
+    fputs(_("Usage: snmp [host-or-ip-address]\n"), stderr);
     return (1);
   }
 
@@ -343,7 +252,7 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
   * Open the SNMP socket...
   */
 
-  if ((fd = open_snmp_socket()) < 0)
+  if ((fd = _cupsSNMPOpen(AF_INET)) < 0)
     return (1);
 
  /*
@@ -352,6 +261,8 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
 
   read_snmp_conf(argv[1]);
 
+  _cupsSNMPSetDebug(DebugLevel);
+
   Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
 
  /*
@@ -364,7 +275,7 @@ main(int  argc,                             /* I - Number of command-line arguments (6 or 7) */
   * Close, free, and return with no errors...
   */
 
-  close(fd);
+  _cupsSNMPClose(fd);
 
   free_array(Addresses);
   free_array(Communities);
@@ -434,796 +345,114 @@ add_cache(http_addr_t *addr,            /* I - Device IP address */
 
 
 /*
- * 'alarm_handler()' - Handle alarm signals...
- */
-
-static void
-alarm_handler(int sig)                 /* I - Signal number */
-{
- /*
-  * Do nothing...
-  */
-
-  (void)sig;
-
-#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
-  signal(SIGALRM, alarm_handler);
-#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
-
-  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.
+ * 'add_device_uri()' - Add a device URI to the cache.
+ *
+ * The value string is modified (chopped up) as needed.
  */
 
-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 */
+static device_uri_t *                  /* O - Device URI */
+add_device_uri(char *value)            /* I - Value from snmp.conf */
 {
-  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 */
+  device_uri_t *device_uri;            /* Device URI */
+  char         *start;                 /* Start of value */
 
 
  /*
-  * Get the lengths of the community string, OID, and message...
+  * Allocate memory as needed...
   */
 
-  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;
+  if (!DeviceURIs)
+    DeviceURIs = cupsArrayNew(NULL, NULL);
 
-    case ASN1_OID :
-        valuelen = asn1_size_oid(packet->object_value.oid);
-       break;
+  if (!DeviceURIs)
+    return (NULL);
 
-    default :
-        packet->error = "Unknown object type";
-        return (-1);
-  }
+  if ((device_uri = calloc(1, sizeof(device_uri_t))) == NULL)
+    return (NULL);
 
-  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)
+  if ((device_uri->uris = cupsArrayNew(NULL, NULL)) == NULL)
   {
-    packet->error = "Message too large for buffer";
-    return (-1);
+    free(device_uri);
+    return (NULL);
   }
 
  /*
-  * Then format the message...
+  * Scan the value string for the regular expression and URI(s)...
   */
 
-  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);
+  value ++; /* Skip leading " */
 
-  asn1_set_integer(&bufptr, packet->error_status);
+  for (start = value; *value && *value != '\"'; value ++)
+    if (*value == '\\' && value[1])
+      _cups_strcpy(value, value + 1);
 
-  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)
+  if (!*value)
   {
-    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;
+    fputs("ERROR: Missing end quote for DeviceURI!\n", stderr);
 
-    case ASN1_OCTET_STRING :
-        *bufptr++ = ASN1_OCTET_STRING;
-       asn1_set_length(&bufptr, valuelen);
-       memcpy(bufptr, packet->object_value.string, valuelen);
-       bufptr += valuelen;
-       break;
+    cupsArrayDelete(device_uri->uris);
+    free(device_uri);
 
-    case ASN1_OID :
-        asn1_set_oid(&bufptr, packet->object_value.oid);
-       break;
+    return (NULL);
   }
 
-  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);
-}
-
+  *value++ = '\0';
 
-/*
- * '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)
+  if (regcomp(&(device_uri->re), start, REG_EXTENDED | REG_ICASE))
   {
-    number = asn1_get_packed(buffer, bufend);
-
-    if (oid < oidend)
-      *oid++ = number;
-  }
-
-  *oid = 0;
-
-  return (number);
-}
-
-
-/*
- * 'asn1_get_packed()' - Get a packed integer value.
- */
+    fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr);
 
-static int                             /* O  - Value */
-asn1_get_packed(
-    unsigned char **buffer,            /* IO - Pointer in buffer */
-    unsigned char *bufend)             /* I  - End of buffer */
-{
-  int  value;                          /* Value */
+    cupsArrayDelete(device_uri->uris);
+    free(device_uri);
 
-
-  value = 0;
-
-  while ((**buffer & 128) && *buffer < bufend)
-  {
-    value = (value << 7) | (**buffer & 127);
-    (*buffer) ++;
+    return (NULL);
   }
 
-  if (*buffer < bufend)
+  while (*value)
   {
-    value = (value << 7) | **buffer;
-    (*buffer) ++;
-  }
-
-  return (value);
-}
-
-
-/*
- * 'asn1_get_string()' - Get a string value.
- */
+    while (isspace(*value & 255))
+      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) ++;
-  }
-}
+    if (!*value)
+      break;
 
+    for (start = value; *value && !isspace(*value & 255); value ++);
 
-/*
- * 'asn1_set_length()' - Set a value length.
- */
+    if (*value)
+      *value++ = '\0';
 
-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) ++;
+    cupsArrayAdd(device_uri->uris, strdup(start));
   }
-}
-
-
-/*
- * '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));
+ /*
+  * Add the device URI to the list and return it...
+  */
 
-  asn1_set_packed(buffer, oid[0] * 40 + oid[1]);
+  cupsArrayAdd(DeviceURIs, device_uri);
 
-  for (oid += 2; *oid; oid ++)
-    asn1_set_packed(buffer, *oid);
+  return (device_uri);
 }
 
 
 /*
- * 'asn1_set_packed()' - Set a packed integer value.
+ * 'alarm_handler()' - Handle alarm signals...
  */
 
 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 */
+alarm_handler(int sig)                 /* I - Signal number */
 {
-  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);
-}
+ /*
+  * Do nothing...
+  */
 
+  (void)sig;
 
-/*
- * 'asn1_size_packed()' - Figure out the number of bytes needed for a
- *                        packed integer value.
- */
+#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
+  signal(SIGALRM, alarm_handler);
+#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
 
-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);
+  if (DebugLevel)
+    write(2, "DEBUG: ALARM!\n", 14);
 }
 
 
@@ -1250,179 +479,12 @@ debug_printf(const char *format,        /* I - Printf-style format string */
   va_list      ap;                     /* Pointer to arguments */
 
 
-  if (!DebugLevel)
-    return;
-
-  va_start(ap, format);
-  vfprintf(stderr, format, ap);
-  va_end(ap);
-}
-
-
-/*
- * 'do_request()' - Do a non-blocking IPP request.
- */
-
-static ipp_t *                         /* O - Response data or NULL */
-do_request(http_t     *http,           /* I - HTTP connection to server */
-           ipp_t      *request,                /* I - IPP request */
-           const char *resource)       /* I - HTTP resource for POST */
-{
-  ipp_t                *response;              /* IPP response data */
-  http_status_t        status;                 /* Status of HTTP request */
-  ipp_state_t  state;                  /* State of IPP processing */
-
-
- /*
-  * Setup the HTTP variables needed...
-  */
-
-  httpClearFields(http);
-  httpSetLength(http, ippLength(request));
-  httpSetField(http, HTTP_FIELD_CONTENT_TYPE, "application/ipp");
-
- /*
-  * Do the POST request...
-  */
-
-  debug_printf("DEBUG: %.3f POST %s...\n", run_time(), resource);
-
-  if (httpPost(http, resource))
-  {
-    if (httpReconnect(http))
-    {
-      _cupsSetError(IPP_DEVICE_ERROR, "Unable to reconnect");
-      return (NULL);
-    }
-    else if (httpPost(http, resource))
-    {
-      _cupsSetError(IPP_GONE, "Unable to POST");
-      return (NULL);
-    }
-  }
-
- /*
-  * Send the IPP data...
-  */
-
-  request->state = IPP_IDLE;
-  status         = HTTP_CONTINUE;
-
-  while ((state = ippWrite(http, request)) != IPP_DATA)
-    if (state == IPP_ERROR)
-    {
-      status = HTTP_ERROR;
-      break;
-    }
-    else if (httpCheck(http))
-    {
-      if ((status = httpUpdate(http)) != HTTP_CONTINUE)
-       break;
-    }
-
- /*
-  * Get the server's return status...
-  */
-
-  debug_printf("DEBUG: %.3f Getting response...\n", run_time());
-
-  while (status == HTTP_CONTINUE)
-    if (httpWait(http, 1000))
-      status = httpUpdate(http);
-    else
-    {
-      status      = HTTP_ERROR;
-      http->error = ETIMEDOUT;
-    }
-
-  if (status != HTTP_OK)
-  {
-   /*
-    * Flush any error message...
-    */
-
-    httpFlush(http);
-    response = NULL;
-  }
-  else
-  {
-   /*
-    * Read the response...
-    */
-
-    response = ippNew();
-
-    while ((state = ippRead(http, response)) != IPP_DATA)
-      if (state == IPP_ERROR)
-      {
-       /*
-        * Delete the response...
-       */
-
-       ippDelete(response);
-       response = NULL;
-
-        _cupsSetError(IPP_SERVICE_UNAVAILABLE, strerror(errno));
-       break;
-      }
-  }
-
- /*
-  * Delete the original request and return the response...
-  */
-  
-  ippDelete(request);
-
-  if (response)
-  {
-    ipp_attribute_t    *attr;          /* status-message attribute */
-
-
-    attr = ippFindAttribute(response, "status-message", IPP_TAG_TEXT);
-
-    _cupsSetError(response->request.status.status_code,
-                   attr ? attr->values[0].string.text :
-                      ippErrorString(response->request.status.status_code));
-  }
-  else if (status != HTTP_OK)
-  {
-    switch (status)
-    {
-      case HTTP_NOT_FOUND :
-          _cupsSetError(IPP_NOT_FOUND, httpStatus(status));
-         break;
-
-      case HTTP_UNAUTHORIZED :
-          _cupsSetError(IPP_NOT_AUTHORIZED, httpStatus(status));
-         break;
-
-      case HTTP_FORBIDDEN :
-          _cupsSetError(IPP_FORBIDDEN, httpStatus(status));
-         break;
-
-      case HTTP_BAD_REQUEST :
-          _cupsSetError(IPP_BAD_REQUEST, httpStatus(status));
-         break;
-
-      case HTTP_REQUEST_TOO_LARGE :
-          _cupsSetError(IPP_REQUEST_VALUE, httpStatus(status));
-         break;
-
-      case HTTP_NOT_IMPLEMENTED :
-          _cupsSetError(IPP_OPERATION_NOT_SUPPORTED, httpStatus(status));
-         break;
-
-      case HTTP_NOT_SUPPORTED :
-          _cupsSetError(IPP_VERSION_NOT_SUPPORTED, httpStatus(status));
-         break;
-
-      default :
-         _cupsSetError(IPP_SERVICE_UNAVAILABLE, httpStatus(status));
-         break;
-    }
-  }
+  if (!DebugLevel)
+    return;
 
-  return (response);
+  va_start(ap, format);
+  vfprintf(stderr, format, ap);
+  va_end(ap);
 }
 
 
@@ -1603,35 +665,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...
  */
@@ -1640,54 +673,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);
 }
 
 
@@ -1718,216 +705,68 @@ password_cb(const char *prompt)         /* I - Prompt message */
 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 */
+  char         uri[1024],              /* Full device URI */
+               *uriptr,                /* Pointer into URI */
+               *format;                /* Format string for device */
+  device_uri_t *device_uri;            /* Current DeviceURI match */
 
 
- /*
-  * Try connecting via IPP first...
-  */
-
   debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
 
-  if (device->make_and_model &&
-      (!strncasecmp(device->make_and_model, "Epson", 5) ||
-       !strncasecmp(device->make_and_model, "HP ", 3) ||
-       !strncasecmp(device->make_and_model, "Hewlett", 7) ||
-       !strncasecmp(device->make_and_model, "Kyocera", 7) ||
-       !strncasecmp(device->make_and_model, "Lexmark", 7) ||
-       !strncasecmp(device->make_and_model, "Tektronix", 9) ||
-       !strncasecmp(device->make_and_model, "Xerox", 5)))
-  {
-   /*
-    * Epson, HP, Kyocera, Lexmark, Tektronix, and Xerox printers often lock
-    * up on IPP probes, so exclude them from the IPP connection test...
-    */
+#ifdef __APPLE__
+ /*
+  * TODO: Try an mDNS query first, and then fallback on direct probes...
+  */
 
-    http = NULL;
-  }
-  else
+  if (!try_connect(&(device->address), device->addrname, 5353))
   {
-   /*
-    * Otherwise, try connecting for up to 1 second...
-    */
-
-    alarm(1);
-    http = httpConnect(device->addrname, 631);
-    alarm(0);
+    debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname);
+    return;
   }
+#endif /* __APPLE__ */
 
-  if (http)
-  {
-   /*
-    * 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",
-                         "/"
-                       };
-
-
-   /*
-    * 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...
-    */
+ /*
+  * Lookup the device in the match table...
+  */
 
-    for (i = 0, num_uris = 0;
-         i < (int)(sizeof(resources) / sizeof(resources[0]));
-         i ++)
+  for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
+       device_uri;
+       device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
+    if (device->make_and_model &&
+        !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
     {
      /*
-      * Stop early if we are out of time...
+      * Found a match, add the URIs...
       */
 
-      if (MaxRunTime > 0 && run_time() >= MaxRunTime)
-        break;
-
-     /*
-      * 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]);
-
-      request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
-
-      ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
-                   NULL, uri);
-
-      response = do_request(http, request, resources[i]);
-
-      debug_printf("DEBUG: %.3f %s %s (%s)\n", run_time(), uri,
-                  ippErrorString(cupsLastError()), cupsLastErrorString());
-
-      if (response && response->request.status.status_code == IPP_OK)
+      for (format = (char *)cupsArrayFirst(device_uri->uris);
+           format;
+          format = (char *)cupsArrayNext(device_uri->uris))
       {
-        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)
+        for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);)
+         if (*format == '%' && format[1] == 's')
          {
-           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;
-
-       /*
-        * Don't use the printer-make-and-model if it contains a generic
-       * string like "Ricoh IPP Printer"...
-       */
-
-       if (model && strstr(model->values[0].string.text, "IPP Printer"))
-         model = 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)
-         backendGetMakeModel(info->values[0].string.text, temp, sizeof(temp));
-        else
-         temp[0] = '\0';
-
-        fix_make_model(make_model, temp, sizeof(make_model));
+          /*
+           * Insert hostname/address...
+           */
 
-       /*
-        * Update the current device or add a new printer to the cache...
-       */
+           strlcpy(uriptr, device->addrname, sizeof(uri) - (uriptr - uri));
+           uriptr += strlen(uriptr);
+           format += 2;
+         }
+         else
+           *uriptr++ = *format++;
 
-        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);
+        *uriptr = '\0';
 
-        num_uris ++;
+        update_cache(device, uri, NULL, NULL);
       }
 
-      ippDelete(response);
-
-      if (num_uris > 0 && cupsLastError() != IPP_OK)
-        break;
-    }
-
-    httpClose(http);
-
-    if (num_uris > 0)
       return;
-  }
+    }
 
  /*
-  * OK, now try the standard ports...
+  * Then try the standard ports...
   */
 
   if (!try_connect(&(device->address), device->addrname, 9100))
@@ -2011,6 +850,15 @@ read_snmp_conf(const char *address)       /* I - Single address to probe */
         add_array(Communities, value);
       else if (!strcasecmp(line, "DebugLevel"))
         DebugLevel = atoi(value);
+      else if (!strcasecmp(line, "DeviceURI"))
+      {
+        if (*value != '\"')
+         fprintf(stderr,
+                 "ERROR: Missing double quote for regular expression on "
+                 "line %d of %s!\n", linenum, filename);
+        else
+         add_device_uri(value);
+      }
       else if (!strcasecmp(line, "HostNameLookups"))
         HostNameLookups = !strcasecmp(value, "on") ||
                          !strcasecmp(value, "yes") ||
@@ -2032,8 +880,14 @@ read_snmp_conf(const char *address)       /* I - Single address to probe */
 
   if (cupsArrayCount(Addresses) == 0)
   {
-    fputs("INFO: Using default SNMP Address @LOCAL\n", stderr);
-    add_array(Addresses, "@LOCAL");
+   /*
+    * If we have no addresses, exit immediately...
+    */
+
+    fprintf(stderr,
+            "DEBUG: No address specified and no Address line in %s...\n",
+           filename);
+    exit(0);
   }
 
   if (cupsArrayCount(Communities) == 0)
@@ -2051,12 +905,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 */
 
@@ -2065,10 +915,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));
@@ -2076,25 +923,21 @@ 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);
-
     return;
   }
 
@@ -2102,12 +945,6 @@ 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)
     return;
 
@@ -2122,73 +959,172 @@ 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(&addr, addrname, NULL, NULL, NULL);
+       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_URI, LexmarkDeviceIdOID);
+       _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
+                      packet.community, CUPS_ASN1_GET_REQUEST,
+                      DEVICE_PRODUCT, XeroxProductOID);
+        break;
 
-    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...
-    */
+    case DEVICE_DESCRIPTION :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING)
+       {
+        /*
+         * Update an existing cache entry...
+         */
 
-    char       make_model[256];        /* Make and model */
+         char  make_model[256];        /* Make and model */
 
 
-    if (!device)
-    {
-      debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
-                  addrname);
-      return;
-    }
+         if (strchr(packet.object_value.string, ':') &&
+             strchr(packet.object_value.string, ';'))
+         {
+          /*
+           * Description is the IEEE-1284 device ID...
+           */
 
-   /*
-    * Convert the description to a make and model string...
-    */
+           if (!device->id)
+             device->id = strdup(packet.object_value.string);
 
-    if (strchr(packet.object_value.string, ':') &&
-        strchr(packet.object_value.string, ';'))
-    {
-     /*
-      * Description is the IEEE-1284 device ID...
-      */
+           backendGetMakeModel(packet.object_value.string, 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, packet.object_value.string,
+                          sizeof(make_model));
 
-    device->make_and_model = strdup(make_model);
+            if (device->info)
+             free(device->info);
+
+           device->info = strdup(packet.object_value.string);
+         }
+
+         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)
+       {
+        /*
+         * Update an existing cache entry...
+         */
+
+         char  make_model[256];        /* Make and model */
+
+
+         if (device->id)
+           free(device->id);
+
+         device->id = strdup(packet.object_value.string);
+
+        /*
+         * Convert the ID to a make and model string...
+         */
+
+         backendGetMakeModel(packet.object_value.string, 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(packet.object_value.string);
+       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(packet.object_value.string);
+
+          if (device->make_and_model)
+           free(device->make_and_model);
+
+         device->make_and_model = strdup(packet.object_value.string);
+       }
+       break;
+
+    case DEVICE_URI :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
+           !device->uri)
+       {
+        /*
+         * Update an existing cache entry...
+         */
+
+         if (!strncmp(packet.object_value.string, "lpr:", 4))
+         {
+          /*
+           * We want "lpd://..." for the URI...
+           */
+
+           packet.object_value.string[2] = 'd';
+         }
+
+         device->uri = strdup(packet.object_value.string);
+       }
+       break;
   }
 }
 
@@ -2227,15 +1163,8 @@ scan_devices(int fd)                     /* I - SNMP socket */
   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...
   */
@@ -2274,8 +1203,8 @@ 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);
+        _cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community,
+                      CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID);
     }
 
     httpAddrFreeList(addrs);
@@ -2285,13 +1214,13 @@ 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);
@@ -2305,86 +1234,32 @@ scan_devices(int fd)                    /* I - SNMP socket */
     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 */
-
-
- /*
-  * Create the SNMP message...
-  */
-
-  memset(&packet, 0, sizeof(packet));
+    {
+     /*
+      * 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());
 }
 
 
@@ -2406,7 +1281,8 @@ try_connect(http_addr_t *addr,            /* I - Socket address */
 
   if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
   {
-    fprintf(stderr, "ERROR: Unable to create socket: %s\n", strerror(errno));
+    fprintf(stderr, "ERROR: Unable to create socket: %s\n",
+            strerror(errno));
     return (-1);
   }
 
@@ -2459,5 +1335,5 @@ update_cache(snmp_cache_t *device,        /* I - Device */
 
 
 /*
- * End of "$Id: snmp.c 6181 2007-01-03 18:51:27Z mike $".
+ * End of "$Id: snmp.c 7506 2008-04-29 04:38:47Z mike $".
  */