]> git.ipfire.org Git - thirdparty/cups.git/blobdiff - backend/snmp.c
Fix build errors on Fedora.
[thirdparty/cups.git] / backend / snmp.c
index 0364ee2a2313b48ed0657348bedf44039f766f28..876ca652050b0056b9d51e5d5a15ffcdabbb04fc 100644 (file)
@@ -1,44 +1,18 @@
 /*
- * "$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-2008 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...
- *   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.
- *   list_device()             - List a device we found...
- *   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.
- *   try_connect()             - Try connecting on a port...
- *   update_cache()            - Update a cached device...
+ * This file is subject to the Apple OS-Developed Software exception.
  */
 
 /*
  * 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 */
@@ -124,17 +108,12 @@ typedef struct snmp_cache_s               /**** SNMP scan cache ****/
                *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;
 
 
-/*
- * Private CUPS API to set the last error...
- */
-
-extern void            _cupsSetError(ipp_status_t status, const char *message);
-
-
 /*
  * Local functions...
  */
@@ -159,7 +138,7 @@ 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            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,
@@ -174,14 +153,15 @@ static cups_array_t       *Addresses = NULL;
 static cups_array_t    *Communities = NULL;
 static cups_array_t    *Devices = NULL;
 static int             DebugLevel = 0;
-static const int       DeviceDescOID[] = { CUPS_OID_hrDeviceDescr, 1, -1 };
-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 unsigned                DeviceTypeRequest;
 static const int       DeviceIdOID[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId, 1, -1 };
-static unsigned                DeviceIdRequest;
-static const int       DeviceUriOID[] = { CUPS_OID_ppmPortServiceNameOrURI, 1, 1, -1 };
-static unsigned                DeviceUriRequest;
+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 = 120;
@@ -196,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 */
@@ -208,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);
   }
 
@@ -239,9 +220,16 @@ main(int  argc,                            /* I - Number of command-line arguments (6 or 7) */
   * Open the SNMP socket...
   */
 
-  if ((fd = _cupsSNMPOpen(AF_INET)) < 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...
   */
@@ -256,13 +244,15 @@ main(int  argc,                           /* I - Number of command-line arguments (6 or 7) */
   * Scan for devices...
   */
 
-  scan_devices(fd);
+  scan_devices(ipv4, ipv6);
 
  /*
   * Close, free, and return with no errors...
   */
 
-  _cupsSNMPClose(fd);
+  _cupsSNMPClose(ipv4);
+  if (ipv6 >= 0)
+    _cupsSNMPClose(ipv6);
 
   free_array(Addresses);
   free_array(Communities);
@@ -451,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));
 }
 
 
@@ -493,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
@@ -505,7 +495,7 @@ fix_make_model(
     while (isspace(*mmptr & 255))
       mmptr ++;
 
-    if (!strncasecmp(mmptr, "hp", 2))
+    if (!_cups_strncasecmp(mmptr, "hp", 2))
     {
       mmptr += 2;
 
@@ -516,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)
   {
@@ -660,15 +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->info ? cache->info : "Unknown",
-          cache->addrname,
-          cache->id ? cache->id : "");
-    fflush(stdout);
-  }
+    cupsBackendReport("network", cache->uri, cache->make_and_model,
+                      cache->info, cache->id, cache->location);
 }
 
 
@@ -709,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))
@@ -744,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;
          }
@@ -835,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,
@@ -853,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",
@@ -939,7 +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 (packet.error_status)
+  if (packet.error_status && packet.request_id != DEVICE_TYPE)
     return;
 
  /*
@@ -953,156 +935,194 @@ read_snmp_response(int fd)              /* I - SNMP socket file descriptor */
   * Process the message...
   */
 
-  if (packet.request_id == DeviceTypeRequest)
-  {
-   /*
-    * Got the device type response...
-    */
-
-    if (device)
-    {
-      debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
-                  addrname);
-      return;
-    }
-
-   /*
-    * Add the device and request the device description...
-    */
-
-    add_cache(&(packet.address), addrname, NULL, NULL, NULL);
-
-    _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, packet.community,
-                  CUPS_ASN1_GET_REQUEST, DeviceDescRequest, DeviceDescOID);
-    _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, packet.community,
-                  CUPS_ASN1_GET_REQUEST, DeviceIdRequest, DeviceIdOID);
-    _cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1, packet.community,
-                  CUPS_ASN1_GET_REQUEST, DeviceUriRequest, DeviceUriOID);
-  }
-  else if (packet.request_id == DeviceDescRequest &&
-           packet.object_type == CUPS_ASN1_OCTET_STRING)
-  {
-   /*
-    * Update an existing cache entry...
-    */
-
-    char       make_model[256];        /* Make and model */
-
-
-    if (!device)
-    {
-      debug_printf("DEBUG: Discarding device description for \"%s\"...\n",
-                  addrname);
-      return;
-    }
-
-    if (strchr(packet.object_value.string, ':') &&
-       strchr(packet.object_value.string, ';'))
-    {
-     /*
-      * Description is the IEEE-1284 device ID...
-      */
-
-      if (!device->id)
-       device->id = strdup(packet.object_value.string);
-
-      backendGetMakeModel(packet.object_value.string, make_model,
-                         sizeof(make_model));
-      device->info = strdup(make_model);
-    }
-    else
-    {
-     /*
-      * Description is plain text...
-      */
-
-      fix_make_model(make_model, packet.object_value.string,
-                    sizeof(make_model));
-
-      device->info = strdup(packet.object_value.string);
-    }
-
-    if (!device->make_and_model)
-      device->make_and_model = strdup(make_model);
-
-   /*
-    * List the device now if we have all the info...
-    */
-
-    if (device->id && device->info && device->make_and_model && device->uri)
-      list_device(device);
-  }
-  else if (packet.request_id == DeviceIdRequest &&
-           packet.object_type == CUPS_ASN1_OCTET_STRING)
+  switch (packet.request_id)
   {
-   /*
-    * Update an existing cache entry...
-    */
-
-    char       make_model[256];        /* Make and model */
-
-
-    if (!device)
-    {
-      debug_printf("DEBUG: Discarding device ID for \"%s\"...\n",
-                  addrname);
-      return;
-    }
-
-    if (device->id)
-      free(device->id);
+    case DEVICE_TYPE :
+       /*
+       * Got the device type response...
+       */
+
+       if (device)
+       {
+         debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
+                      addrname);
+         return;
+       }
+
+       /*
+       * Add the device and request the device 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;
+
+    case DEVICE_DESCRIPTION :
+       if (device && packet.object_type == CUPS_ASN1_OCTET_STRING)
+       {
+        /*
+         * Update an existing cache entry...
+         */
+
+         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...
+           */
 
-    device->id = strdup(packet.object_value.string);
+            char *ptr;                 /* Pointer into device ID */
 
-   /*
-    * Convert the ID 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);
 
-    backendGetMakeModel(packet.object_value.string, make_model,
-                       sizeof(make_model));
-    if (device->make_and_model)
-      free(device->make_and_model);
+           backendGetMakeModel((char *)packet.object_value.string.bytes,
+                               make_model, sizeof(make_model));
 
-    device->make_and_model = strdup(make_model);
+            if (device->info)
+             free(device->info);
 
-   /*
-    * List the device now if we have all the info...
-    */
-
-    if (device->id && device->info && device->make_and_model && device->uri)
-      list_device(device);
-  }
-  else if (packet.request_id == DeviceUriRequest &&
-           packet.object_type == CUPS_ASN1_OCTET_STRING)
-  {
-   /*
-    * Update an existing cache entry...
-    */
+           device->info = strdup(make_model);
+         }
+         else
+         {
+          /*
+           * Description is plain text...
+           */
 
-    if (!device)
-    {
-      debug_printf("DEBUG: Discarding device URI for \"%s\"...\n",
-                  addrname);
-      return;
-    }
+           fix_make_model(make_model, (char *)packet.object_value.string.bytes,
+                          sizeof(make_model));
 
-    if (!strncmp(packet.object_value.string, "lpr:", 4))
-    {
-     /*
-      * We want "lpd://..." for the URI...
-      */
+            if (device->info)
+             free(device->info);
 
-      packet.object_value.string[2] = 'd';
-    }
+           device->info = strdup((char *)packet.object_value.string.bytes);
+         }
 
-    device->uri = 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 &&
+           (!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...
+           */
 
-   /*
-    * List the device now if we have all the info...
-    */
+           packet.object_value.string.bytes[2] = 'd';
+         }
 
-    if (device->id && device->info && device->make_and_model && device->uri)
-      list_device(device);
+          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;
   }
 }
 
@@ -1129,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() */
@@ -1139,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...
   */
@@ -1164,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';
@@ -1172,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)
     {
@@ -1188,8 +1204,20 @@ scan_devices(int fd)                     /* I - SNMP socket */
                   community, address);
 
       for (addr = addrs; addr; addr = addr->next)
+      {
+#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, DeviceTypeRequest, DeviceTypeOID);
+                      CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID);
+      }
     }
 
     httpAddrFreeList(addrs);
@@ -1208,32 +1236,57 @@ scan_devices(int fd)                    /* I - SNMP socket */
     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;
-  }
+    busy = 0;
 
- /*
-  * Finally, probe all of the printers we discovered to see how they are
-  * connected...
-  */
+    if (FD_ISSET(ipv4, &input))
+    {
+      read_snmp_response(ipv4);
+      busy = 1;
+    }
 
-  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);
+    if (ipv6 >= 0 && FD_ISSET(ipv6, &input))
+    {
+      read_snmp_response(ipv6);
+      busy = 1;
+    }
+
+    if (!busy)
+    {
+     /*
+      * List devices with complete information...
+      */
+
+      int sent_something = 0;
+
+      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);
+
+         device->sent = sent_something = 1;
+       }
+
+      if (!sent_something)
+        break;
+    }
+  }
 
   debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
 }
@@ -1255,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);
@@ -1311,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$".
  */