Blame backend/snmp.c

Packit 2fc92b
/*
Packit 2fc92b
 * SNMP discovery backend for CUPS.
Packit 2fc92b
 *
Packit 2fc92b
 * Copyright 2007-2014 by Apple Inc.
Packit 2fc92b
 * Copyright 2006-2007 by Easy Software Products, all rights reserved.
Packit 2fc92b
 *
Packit 2fc92b
 * These coded instructions, statements, and computer programs are the
Packit 2fc92b
 * property of Apple Inc. and are protected by Federal copyright
Packit 2fc92b
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
Packit 2fc92b
 * "LICENSE" which should have been included with this file.  If this
Packit 2fc92b
 * file is missing or damaged, see the license at "http://www.cups.org/".
Packit 2fc92b
 *
Packit 2fc92b
 * This file is subject to the Apple OS-Developed Software exception.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * Include necessary headers.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
#include "backend-private.h"
Packit 2fc92b
#include <cups/array.h>
Packit 2fc92b
#include <cups/file.h>
Packit 2fc92b
#include <cups/http-private.h>
Packit 2fc92b
#include <regex.h>
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * This backend implements SNMP printer discovery.  It uses a broadcast-
Packit 2fc92b
 * based approach to get SNMP response packets from potential printers,
Packit 2fc92b
 * requesting OIDs from the Host and Port Monitor MIBs, does a URI
Packit 2fc92b
 * lookup based on the device description string, and finally a probe of
Packit 2fc92b
 * port 9100 (AppSocket) and 515 (LPD).
Packit 2fc92b
 *
Packit 2fc92b
 * The current focus is on printers with internal network cards, although
Packit 2fc92b
 * the code also works with many external print servers as well.
Packit 2fc92b
 *
Packit 2fc92b
 * The backend reads the snmp.conf file from the CUPS_SERVERROOT directory
Packit 2fc92b
 * which can contain comments, blank lines, or any number of the following
Packit 2fc92b
 * directives:
Packit 2fc92b
 *
Packit 2fc92b
 *     Address ip-address
Packit 2fc92b
 *     Address @LOCAL
Packit 2fc92b
 *     Address @IF(name)
Packit 2fc92b
 *     Community name
Packit 2fc92b
 *     DebugLevel N
Packit 2fc92b
 *     DeviceURI "regex pattern" uri
Packit 2fc92b
 *     HostNameLookups on
Packit 2fc92b
 *     HostNameLookups off
Packit 2fc92b
 *     MaxRunTime N
Packit 2fc92b
 *
Packit 2fc92b
 * The default is to use:
Packit 2fc92b
 *
Packit 2fc92b
 *     Address @LOCAL
Packit 2fc92b
 *     Community public
Packit 2fc92b
 *     DebugLevel 0
Packit 2fc92b
 *     HostNameLookups off
Packit 2fc92b
 *     MaxRunTime 120
Packit 2fc92b
 *
Packit 2fc92b
 * This backend is known to work with the following network printers and
Packit 2fc92b
 * print servers:
Packit 2fc92b
 *
Packit 2fc92b
 *     Axis OfficeBasic, 5400, 5600
Packit 2fc92b
 *     Brother
Packit 2fc92b
 *     EPSON
Packit 2fc92b
 *     Genicom
Packit 2fc92b
 *     HP JetDirect
Packit 2fc92b
 *     Lexmark
Packit 2fc92b
 *     Sharp
Packit 2fc92b
 *     Tektronix
Packit 2fc92b
 *     Xerox
Packit 2fc92b
 *
Packit 2fc92b
 * It does not currently work with:
Packit 2fc92b
 *
Packit 2fc92b
 *     DLink
Packit 2fc92b
 *     Linksys
Packit 2fc92b
 *     Netgear
Packit 2fc92b
 *     Okidata
Packit 2fc92b
 *
Packit 2fc92b
 * (for all of these, they do not support the Host MIB)
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * Types...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
enum					/**** Request IDs for each field ****/
Packit 2fc92b
{
Packit 2fc92b
  DEVICE_TYPE = 1,
Packit 2fc92b
  DEVICE_DESCRIPTION,
Packit 2fc92b
  DEVICE_LOCATION,
Packit 2fc92b
  DEVICE_ID,
Packit 2fc92b
  DEVICE_URI,
Packit 2fc92b
  DEVICE_PRODUCT
Packit 2fc92b
};
Packit 2fc92b
Packit 2fc92b
typedef struct device_uri_s		/**** DeviceURI values ****/
Packit 2fc92b
{
Packit 2fc92b
  regex_t	re;			/* Regular expression to match */
Packit 2fc92b
  cups_array_t	*uris;			/* URIs */
Packit 2fc92b
} device_uri_t;
Packit 2fc92b
Packit 2fc92b
typedef struct snmp_cache_s		/**** SNMP scan cache ****/
Packit 2fc92b
{
Packit 2fc92b
  http_addr_t	address;		/* Address of device */
Packit 2fc92b
  char		*addrname,		/* Name of device */
Packit 2fc92b
		*uri,			/* device-uri */
Packit 2fc92b
		*id,			/* device-id */
Packit 2fc92b
		*info,			/* device-info */
Packit 2fc92b
		*location,		/* device-location */
Packit 2fc92b
		*make_and_model;	/* device-make-and-model */
Packit 2fc92b
  int		sent;			/* Has this device been listed? */
Packit 2fc92b
} snmp_cache_t;
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * Local functions...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static char		*add_array(cups_array_t *a, const char *s);
Packit 2fc92b
static void		add_cache(http_addr_t *addr, const char *addrname,
Packit 2fc92b
			          const char *uri, const char *id,
Packit 2fc92b
				  const char *make_and_model);
Packit 2fc92b
static device_uri_t	*add_device_uri(char *value);
Packit 2fc92b
static void		alarm_handler(int sig);
Packit 2fc92b
static int		compare_cache(snmp_cache_t *a, snmp_cache_t *b);
Packit 2fc92b
static void		debug_printf(const char *format, ...);
Packit 2fc92b
static void		fix_make_model(char *make_model,
Packit 2fc92b
			               const char *old_make_model,
Packit 2fc92b
				       int make_model_size);
Packit 2fc92b
static void		free_array(cups_array_t *a);
Packit 2fc92b
static void		free_cache(void);
Packit 2fc92b
static http_addrlist_t	*get_interface_addresses(const char *ifname);
Packit 2fc92b
static void		list_device(snmp_cache_t *cache);
Packit 2fc92b
static const char	*password_cb(const char *prompt);
Packit 2fc92b
static void		probe_device(snmp_cache_t *device);
Packit 2fc92b
static void		read_snmp_conf(const char *address);
Packit 2fc92b
static void		read_snmp_response(int fd);
Packit 2fc92b
static double		run_time(void);
Packit 2fc92b
static void		scan_devices(int ipv4, int ipv6);
Packit 2fc92b
static int		try_connect(http_addr_t *addr, const char *addrname,
Packit 2fc92b
			            int port);
Packit 2fc92b
static void		update_cache(snmp_cache_t *device, const char *uri,
Packit 2fc92b
			             const char *id, const char *make_model);
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * Local globals...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static cups_array_t	*Addresses = NULL;
Packit 2fc92b
static cups_array_t	*Communities = NULL;
Packit 2fc92b
static cups_array_t	*Devices = NULL;
Packit 2fc92b
static int		DebugLevel = 0;
Packit 2fc92b
static const int	DescriptionOID[] = { CUPS_OID_hrDeviceDescr, 1, -1 };
Packit 2fc92b
static const int	LocationOID[] = { CUPS_OID_sysLocation, 0, -1 };
Packit 2fc92b
static const int	DeviceTypeOID[] = { CUPS_OID_hrDeviceType, 1, -1 };
Packit 2fc92b
static const int	DeviceIdOID[] = { CUPS_OID_ppmPrinterIEEE1284DeviceId, 1, -1 };
Packit 2fc92b
static const int	UriOID[] = { CUPS_OID_ppmPortServiceNameOrURI, 1, 1, -1 };
Packit 2fc92b
static const int	LexmarkProductOID[] = { 1,3,6,1,4,1,641,2,1,2,1,2,1,-1 };
Packit 2fc92b
static const int	LexmarkProductOID2[] = { 1,3,6,1,4,1,674,10898,100,2,1,2,1,2,1,-1 };
Packit 2fc92b
static const int	LexmarkDeviceIdOID[] = { 1,3,6,1,4,1,641,2,1,2,1,3,1,-1 };
Packit Service f61958
static const int	HPDeviceIdOID[] = { 1,3,6,1,4,1,11,2,3,9,1,1,7,0,-1 };
Packit Service 86ce49
static const int	RicohDeviceIdOID[] = { 1,3,6,1,4,1,367,3,2,1,1,1,11,0,-1 };
Packit 2fc92b
static const int	XeroxProductOID[] = { 1,3,6,1,4,1,128,2,1,3,1,2,0,-1 };
Packit 2fc92b
static cups_array_t	*DeviceURIs = NULL;
Packit 2fc92b
static int		HostNameLookups = 0;
Packit 2fc92b
static int		MaxRunTime = 120;
Packit 2fc92b
static struct timeval	StartTime;
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'main()' - Discover printers via SNMP.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
int					/* O - Exit status */
Packit 2fc92b
main(int  argc,				/* I - Number of command-line arguments (6 or 7) */
Packit 2fc92b
     char *argv[])			/* I - Command-line arguments */
Packit 2fc92b
{
Packit 2fc92b
  int		ipv4,			/* SNMP IPv4 socket */
Packit 2fc92b
		ipv6;			/* SNMP IPv6 socket */
Packit 2fc92b
#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
Packit 2fc92b
  struct sigaction action;		/* Actions for POSIX signals */
Packit 2fc92b
#endif /* HAVE_SIGACTION && !HAVE_SIGSET */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Check command-line options...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (argc > 2)
Packit 2fc92b
  {
Packit 2fc92b
    _cupsLangPuts(stderr, _("Usage: snmp [host-or-ip-address]"));
Packit 2fc92b
    return (1);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Set the password callback for IPP operations...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  cupsSetPasswordCB(password_cb);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Catch SIGALRM signals...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
#ifdef HAVE_SIGSET
Packit 2fc92b
  sigset(SIGALRM, alarm_handler);
Packit 2fc92b
#elif defined(HAVE_SIGACTION)
Packit 2fc92b
  memset(&action, 0, sizeof(action));
Packit 2fc92b
Packit 2fc92b
  sigemptyset(&action.sa_mask);
Packit 2fc92b
  sigaddset(&action.sa_mask, SIGALRM);
Packit 2fc92b
  action.sa_handler = alarm_handler;
Packit 2fc92b
  sigaction(SIGALRM, &action, NULL);
Packit 2fc92b
#else
Packit 2fc92b
  signal(SIGALRM, alarm_handler);
Packit 2fc92b
#endif /* HAVE_SIGSET */
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Open the SNMP socket...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if ((ipv4 = _cupsSNMPOpen(AF_INET)) < 0)
Packit 2fc92b
    return (1);
Packit 2fc92b
Packit 2fc92b
#ifdef AF_INET6
Packit 2fc92b
  if ((ipv6 = _cupsSNMPOpen(AF_INET6)) < 0)
Packit 2fc92b
    perror("DEBUG: Unable to create IPv6 socket");
Packit 2fc92b
#else
Packit 2fc92b
  ipv6 = -1;
Packit 2fc92b
#endif /* AF_INET6 */
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Read the configuration file and any cache data...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  read_snmp_conf(argv[1]);
Packit 2fc92b
Packit 2fc92b
  _cupsSNMPSetDebug(DebugLevel);
Packit 2fc92b
Packit 2fc92b
  Devices = cupsArrayNew((cups_array_func_t)compare_cache, NULL);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Scan for devices...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  scan_devices(ipv4, ipv6);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Close, free, and return with no errors...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  _cupsSNMPClose(ipv4);
Packit 2fc92b
  if (ipv6 >= 0)
Packit 2fc92b
    _cupsSNMPClose(ipv6);
Packit 2fc92b
Packit 2fc92b
  free_array(Addresses);
Packit 2fc92b
  free_array(Communities);
Packit 2fc92b
  free_cache();
Packit 2fc92b
Packit 2fc92b
  return (0);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'add_array()' - Add a string to an array.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static char *				/* O - New string */
Packit 2fc92b
add_array(cups_array_t *a,		/* I - Array */
Packit 2fc92b
          const char   *s)		/* I - String to add */
Packit 2fc92b
{
Packit 2fc92b
  char	*dups;				/* New string */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  dups = strdup(s);
Packit 2fc92b
Packit 2fc92b
  cupsArrayAdd(a, dups);
Packit 2fc92b
Packit 2fc92b
  return (dups);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'add_cache()' - Add a cached device...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
add_cache(http_addr_t *addr,		/* I - Device IP address */
Packit 2fc92b
          const char  *addrname,	/* I - IP address or name string */
Packit 2fc92b
          const char  *uri,		/* I - Device URI */
Packit 2fc92b
          const char  *id,		/* I - 1284 device ID */
Packit 2fc92b
	  const char  *make_and_model)	/* I - Make and model */
Packit 2fc92b
{
Packit 2fc92b
  snmp_cache_t	*temp;			/* New device entry */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  debug_printf("DEBUG: add_cache(addr=%p, addrname=\"%s\", uri=\"%s\", "
Packit 2fc92b
                  "id=\"%s\", make_and_model=\"%s\")\n",
Packit 2fc92b
               addr, addrname, uri ? uri : "(null)", id ? id : "(null)",
Packit 2fc92b
	       make_and_model ? make_and_model : "(null)");
Packit 2fc92b
Packit 2fc92b
  temp = calloc(1, sizeof(snmp_cache_t));
Packit 2fc92b
  memcpy(&(temp->address), addr, sizeof(temp->address));
Packit 2fc92b
Packit 2fc92b
  temp->addrname = strdup(addrname);
Packit 2fc92b
Packit 2fc92b
  if (uri)
Packit 2fc92b
    temp->uri = strdup(uri);
Packit 2fc92b
Packit 2fc92b
  if (id)
Packit 2fc92b
    temp->id = strdup(id);
Packit 2fc92b
Packit 2fc92b
  if (make_and_model)
Packit 2fc92b
    temp->make_and_model = strdup(make_and_model);
Packit 2fc92b
Packit 2fc92b
  cupsArrayAdd(Devices, temp);
Packit 2fc92b
Packit 2fc92b
  if (uri)
Packit 2fc92b
    list_device(temp);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'add_device_uri()' - Add a device URI to the cache.
Packit 2fc92b
 *
Packit 2fc92b
 * The value string is modified (chopped up) as needed.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static device_uri_t *			/* O - Device URI */
Packit 2fc92b
add_device_uri(char *value)		/* I - Value from snmp.conf */
Packit 2fc92b
{
Packit 2fc92b
  device_uri_t	*device_uri;		/* Device URI */
Packit 2fc92b
  char		*start;			/* Start of value */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Allocate memory as needed...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (!DeviceURIs)
Packit 2fc92b
    DeviceURIs = cupsArrayNew(NULL, NULL);
Packit 2fc92b
Packit 2fc92b
  if (!DeviceURIs)
Packit 2fc92b
    return (NULL);
Packit 2fc92b
Packit 2fc92b
  if ((device_uri = calloc(1, sizeof(device_uri_t))) == NULL)
Packit 2fc92b
    return (NULL);
Packit 2fc92b
Packit 2fc92b
  if ((device_uri->uris = cupsArrayNew(NULL, NULL)) == NULL)
Packit 2fc92b
  {
Packit 2fc92b
    free(device_uri);
Packit 2fc92b
    return (NULL);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Scan the value string for the regular expression and URI(s)...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  value ++; /* Skip leading " */
Packit 2fc92b
Packit 2fc92b
  for (start = value; *value && *value != '\"'; value ++)
Packit 2fc92b
    if (*value == '\\' && value[1])
Packit 2fc92b
      _cups_strcpy(value, value + 1);
Packit 2fc92b
Packit 2fc92b
  if (!*value)
Packit 2fc92b
  {
Packit 2fc92b
    fputs("ERROR: Missing end quote for DeviceURI!\n", stderr);
Packit 2fc92b
Packit 2fc92b
    cupsArrayDelete(device_uri->uris);
Packit 2fc92b
    free(device_uri);
Packit 2fc92b
Packit 2fc92b
    return (NULL);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  *value++ = '\0';
Packit 2fc92b
Packit 2fc92b
  if (regcomp(&(device_uri->re), start, REG_EXTENDED | REG_ICASE))
Packit 2fc92b
  {
Packit 2fc92b
    fputs("ERROR: Bad regular expression for DeviceURI!\n", stderr);
Packit 2fc92b
Packit 2fc92b
    cupsArrayDelete(device_uri->uris);
Packit 2fc92b
    free(device_uri);
Packit 2fc92b
Packit 2fc92b
    return (NULL);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  while (*value)
Packit 2fc92b
  {
Packit 2fc92b
    while (isspace(*value & 255))
Packit 2fc92b
      value ++;
Packit 2fc92b
Packit 2fc92b
    if (!*value)
Packit 2fc92b
      break;
Packit 2fc92b
Packit 2fc92b
    for (start = value; *value && !isspace(*value & 255); value ++);
Packit 2fc92b
Packit 2fc92b
    if (*value)
Packit 2fc92b
      *value++ = '\0';
Packit 2fc92b
Packit 2fc92b
    cupsArrayAdd(device_uri->uris, strdup(start));
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Add the device URI to the list and return it...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  cupsArrayAdd(DeviceURIs, device_uri);
Packit 2fc92b
Packit 2fc92b
  return (device_uri);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'alarm_handler()' - Handle alarm signals...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
alarm_handler(int sig)			/* I - Signal number */
Packit 2fc92b
{
Packit 2fc92b
 /*
Packit 2fc92b
  * Do nothing...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  (void)sig;
Packit 2fc92b
Packit 2fc92b
#if !defined(HAVE_SIGSET) && !defined(HAVE_SIGACTION)
Packit 2fc92b
  signal(SIGALRM, alarm_handler);
Packit 2fc92b
#endif /* !HAVE_SIGSET && !HAVE_SIGACTION */
Packit 2fc92b
Packit 2fc92b
  if (DebugLevel)
Packit 2fc92b
    write(2, "DEBUG: ALARM!\n", 14);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'compare_cache()' - Compare two cache entries.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static int				/* O - Result of comparison */
Packit 2fc92b
compare_cache(snmp_cache_t *a,		/* I - First cache entry */
Packit 2fc92b
              snmp_cache_t *b)		/* I - Second cache entry */
Packit 2fc92b
{
Packit 2fc92b
  return (_cups_strcasecmp(a->addrname, b->addrname));
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'debug_printf()' - Display some debugging information.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
debug_printf(const char *format,	/* I - Printf-style format string */
Packit 2fc92b
             ...)			/* I - Additional arguments as needed */
Packit 2fc92b
{
Packit 2fc92b
  va_list	ap;			/* Pointer to arguments */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  if (!DebugLevel)
Packit 2fc92b
    return;
Packit 2fc92b
Packit 2fc92b
  va_start(ap, format);
Packit 2fc92b
  vfprintf(stderr, format, ap);
Packit 2fc92b
  va_end(ap);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'fix_make_model()' - Fix common problems in the make-and-model string.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
fix_make_model(
Packit 2fc92b
    char       *make_model,		/* I - New make-and-model string */
Packit 2fc92b
    const char *old_make_model,		/* I - Old make-and-model string */
Packit 2fc92b
    int        make_model_size)		/* I - Size of new string buffer */
Packit 2fc92b
{
Packit 2fc92b
  char	*mmptr;				/* Pointer into make-and-model string */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Fix some common problems with the make-and-model string so
Packit 2fc92b
  * that printer driver detection works better...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (!_cups_strncasecmp(old_make_model, "Hewlett-Packard", 15))
Packit 2fc92b
  {
Packit 2fc92b
   /*
Packit 2fc92b
    * Strip leading Hewlett-Packard and hp prefixes and replace
Packit 2fc92b
    * with a single HP manufacturer prefix...
Packit 2fc92b
    */
Packit 2fc92b
Packit 2fc92b
    mmptr = (char *)old_make_model + 15;
Packit 2fc92b
Packit 2fc92b
    while (isspace(*mmptr & 255))
Packit 2fc92b
      mmptr ++;
Packit 2fc92b
Packit 2fc92b
    if (!_cups_strncasecmp(mmptr, "hp", 2))
Packit 2fc92b
    {
Packit 2fc92b
      mmptr += 2;
Packit 2fc92b
Packit 2fc92b
      while (isspace(*mmptr & 255))
Packit 2fc92b
	mmptr ++;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    make_model[0] = 'H';
Packit 2fc92b
    make_model[1] = 'P';
Packit 2fc92b
    make_model[2] = ' ';
Packit 2fc92b
    strlcpy(make_model + 3, mmptr, (size_t)make_model_size - 3);
Packit 2fc92b
  }
Packit 2fc92b
  else if (!_cups_strncasecmp(old_make_model, "deskjet", 7))
Packit 2fc92b
    snprintf(make_model, (size_t)make_model_size, "HP DeskJet%s", old_make_model + 7);
Packit 2fc92b
  else if (!_cups_strncasecmp(old_make_model, "officejet", 9))
Packit 2fc92b
    snprintf(make_model, (size_t)make_model_size, "HP OfficeJet%s", old_make_model + 9);
Packit 2fc92b
  else if (!_cups_strncasecmp(old_make_model, "stylus_pro_", 11))
Packit 2fc92b
    snprintf(make_model, (size_t)make_model_size, "EPSON Stylus Pro %s", old_make_model + 11);
Packit 2fc92b
  else
Packit 2fc92b
    strlcpy(make_model, old_make_model, (size_t)make_model_size);
Packit 2fc92b
Packit 2fc92b
  if ((mmptr = strstr(make_model, ", Inc.,")) != NULL)
Packit 2fc92b
  {
Packit 2fc92b
   /*
Packit 2fc92b
    * Strip inc. from name, e.g. "Tektronix, Inc., Phaser 560"
Packit 2fc92b
    * becomes "Tektronix Phaser 560"...
Packit 2fc92b
    */
Packit 2fc92b
Packit 2fc92b
    _cups_strcpy(mmptr, mmptr + 7);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if ((mmptr = strstr(make_model, " Network")) != NULL)
Packit 2fc92b
  {
Packit 2fc92b
   /*
Packit 2fc92b
    * Drop unnecessary informational text, e.g. "Xerox DocuPrint N2025
Packit 2fc92b
    * Network LaserJet - 2.12" becomes "Xerox DocuPrint N2025"...
Packit 2fc92b
    */
Packit 2fc92b
Packit 2fc92b
    *mmptr = '\0';
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if ((mmptr = strchr(make_model, ',')) != NULL)
Packit 2fc92b
  {
Packit 2fc92b
   /*
Packit 2fc92b
    * Drop anything after a trailing comma...
Packit 2fc92b
    */
Packit 2fc92b
Packit 2fc92b
    *mmptr = '\0';
Packit 2fc92b
  }
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'free_array()' - Free an array of strings.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
free_array(cups_array_t *a)		/* I - Array */
Packit 2fc92b
{
Packit 2fc92b
  char	*s;				/* Current string */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  for (s = (char *)cupsArrayFirst(a); s; s = (char *)cupsArrayNext(a))
Packit 2fc92b
    free(s);
Packit 2fc92b
Packit 2fc92b
  cupsArrayDelete(a);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'free_cache()' - Free the array of cached devices.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
free_cache(void)
Packit 2fc92b
{
Packit 2fc92b
  snmp_cache_t	*cache;			/* Cached device */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  for (cache = (snmp_cache_t *)cupsArrayFirst(Devices);
Packit 2fc92b
       cache;
Packit 2fc92b
       cache = (snmp_cache_t *)cupsArrayNext(Devices))
Packit 2fc92b
  {
Packit 2fc92b
    free(cache->addrname);
Packit 2fc92b
Packit 2fc92b
    if (cache->uri)
Packit 2fc92b
      free(cache->uri);
Packit 2fc92b
Packit 2fc92b
    if (cache->id)
Packit 2fc92b
      free(cache->id);
Packit 2fc92b
Packit 2fc92b
    if (cache->make_and_model)
Packit 2fc92b
      free(cache->make_and_model);
Packit 2fc92b
Packit 2fc92b
    free(cache);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  cupsArrayDelete(Devices);
Packit 2fc92b
  Devices = NULL;
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'get_interface_addresses()' - Get the broadcast address(es) associated
Packit 2fc92b
 *                               with an interface.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static http_addrlist_t *		/* O - List of addresses */
Packit 2fc92b
get_interface_addresses(
Packit 2fc92b
    const char *ifname)			/* I - Interface name */
Packit 2fc92b
{
Packit 2fc92b
  struct ifaddrs	*addrs,		/* Interface address list */
Packit 2fc92b
			*addr;		/* Current interface address */
Packit 2fc92b
  http_addrlist_t	*first,		/* First address in list */
Packit 2fc92b
			*last,		/* Last address in list */
Packit 2fc92b
			*current;	/* Current address */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  if (getifaddrs(&addrs) < 0)
Packit 2fc92b
    return (NULL);
Packit 2fc92b
Packit 2fc92b
  for (addr = addrs, first = NULL, last = NULL; addr; addr = addr->ifa_next)
Packit 2fc92b
    if ((addr->ifa_flags & IFF_BROADCAST) && addr->ifa_broadaddr &&
Packit 2fc92b
        addr->ifa_broadaddr->sa_family == AF_INET &&
Packit 2fc92b
	(!ifname || !strcmp(ifname, addr->ifa_name)))
Packit 2fc92b
    {
Packit 2fc92b
      current = calloc(1, sizeof(http_addrlist_t));
Packit 2fc92b
Packit 2fc92b
      memcpy(&(current->addr), addr->ifa_broadaddr,
Packit 2fc92b
             sizeof(struct sockaddr_in));
Packit 2fc92b
Packit 2fc92b
      if (!last)
Packit 2fc92b
        first = current;
Packit 2fc92b
      else
Packit 2fc92b
        last->next = current;
Packit 2fc92b
Packit 2fc92b
      last = current;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
  freeifaddrs(addrs);
Packit 2fc92b
Packit 2fc92b
  return (first);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'list_device()' - List a device we found...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
list_device(snmp_cache_t *cache)	/* I - Cached device */
Packit 2fc92b
{
Packit 2fc92b
  if (cache->uri)
Packit 2fc92b
    cupsBackendReport("network", cache->uri, cache->make_and_model,
Packit 2fc92b
                      cache->info, cache->id, cache->location);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'password_cb()' - Handle authentication requests.
Packit 2fc92b
 *
Packit 2fc92b
 * All we do right now is return NULL, indicating that no authentication
Packit 2fc92b
 * is possible.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static const char *			/* O - Password (NULL) */
Packit 2fc92b
password_cb(const char *prompt)		/* I - Prompt message */
Packit 2fc92b
{
Packit 2fc92b
  (void)prompt;				/* Anti-compiler-warning-code */
Packit 2fc92b
Packit 2fc92b
  return (NULL);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'probe_device()' - Probe a device to discover whether it is a printer.
Packit 2fc92b
 *
Packit 2fc92b
 * TODO: Try using the Port Monitor MIB to discover the correct protocol
Packit 2fc92b
 *       to use - first need a commercially-available printer that supports
Packit 2fc92b
 *       it, though...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
probe_device(snmp_cache_t *device)	/* I - Device */
Packit 2fc92b
{
Packit 2fc92b
  char		uri[1024],		/* Full device URI */
Packit 2fc92b
		*uriptr,		/* Pointer into URI */
Packit 2fc92b
		*format;		/* Format string for device */
Packit 2fc92b
  device_uri_t	*device_uri;		/* Current DeviceURI match */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  debug_printf("DEBUG: %.3f Probing %s...\n", run_time(), device->addrname);
Packit 2fc92b
Packit 2fc92b
#ifdef __APPLE__
Packit 2fc92b
 /*
Packit 2fc92b
  * If the printer supports Bonjour/mDNS, don't report it from the SNMP backend.
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (!try_connect(&(device->address), device->addrname, 5353))
Packit 2fc92b
  {
Packit 2fc92b
    debug_printf("DEBUG: %s supports mDNS, not reporting!\n", device->addrname);
Packit 2fc92b
    return;
Packit 2fc92b
  }
Packit 2fc92b
#endif /* __APPLE__ */
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Lookup the device in the match table...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  for (device_uri = (device_uri_t *)cupsArrayFirst(DeviceURIs);
Packit 2fc92b
       device_uri;
Packit 2fc92b
       device_uri = (device_uri_t *)cupsArrayNext(DeviceURIs))
Packit 2fc92b
    if (device->make_and_model &&
Packit 2fc92b
        !regexec(&(device_uri->re), device->make_and_model, 0, NULL, 0))
Packit 2fc92b
    {
Packit 2fc92b
     /*
Packit 2fc92b
      * Found a match, add the URIs...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      for (format = (char *)cupsArrayFirst(device_uri->uris);
Packit 2fc92b
           format;
Packit 2fc92b
	   format = (char *)cupsArrayNext(device_uri->uris))
Packit 2fc92b
      {
Packit 2fc92b
        for (uriptr = uri; *format && uriptr < (uri + sizeof(uri) - 1);)
Packit 2fc92b
	  if (*format == '%' && format[1] == 's')
Packit 2fc92b
	  {
Packit 2fc92b
	   /*
Packit 2fc92b
	    * Insert hostname/address...
Packit 2fc92b
	    */
Packit 2fc92b
Packit 2fc92b
	    strlcpy(uriptr, device->addrname, sizeof(uri) - (size_t)(uriptr - uri));
Packit 2fc92b
	    uriptr += strlen(uriptr);
Packit 2fc92b
	    format += 2;
Packit 2fc92b
	  }
Packit 2fc92b
	  else
Packit 2fc92b
	    *uriptr++ = *format++;
Packit 2fc92b
Packit 2fc92b
        *uriptr = '\0';
Packit 2fc92b
Packit 2fc92b
        update_cache(device, uri, NULL, NULL);
Packit 2fc92b
      }
Packit 2fc92b
Packit 2fc92b
      return;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Then try the standard ports...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (!try_connect(&(device->address), device->addrname, 9100))
Packit 2fc92b
  {
Packit 2fc92b
    debug_printf("DEBUG: %s supports AppSocket!\n", device->addrname);
Packit 2fc92b
Packit 2fc92b
    snprintf(uri, sizeof(uri), "socket://%s", device->addrname);
Packit 2fc92b
    update_cache(device, uri, NULL, NULL);
Packit 2fc92b
  }
Packit 2fc92b
  else if (!try_connect(&(device->address), device->addrname, 515))
Packit 2fc92b
  {
Packit 2fc92b
    debug_printf("DEBUG: %s supports LPD!\n", device->addrname);
Packit 2fc92b
Packit 2fc92b
    snprintf(uri, sizeof(uri), "lpd://%s/", device->addrname);
Packit 2fc92b
    update_cache(device, uri, NULL, NULL);
Packit 2fc92b
  }
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'read_snmp_conf()' - Read the snmp.conf file.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
read_snmp_conf(const char *address)	/* I - Single address to probe */
Packit 2fc92b
{
Packit 2fc92b
  cups_file_t	*fp;			/* File pointer */
Packit 2fc92b
  char		filename[1024],		/* Filename */
Packit 2fc92b
		line[1024],		/* Line from file */
Packit 2fc92b
		*value;			/* Value on line */
Packit 2fc92b
  int		linenum;		/* Line number */
Packit 2fc92b
  const char	*cups_serverroot;	/* CUPS_SERVERROOT env var */
Packit 2fc92b
  const char	*debug;			/* CUPS_DEBUG_LEVEL env var */
Packit 2fc92b
  const char	*runtime;		/* CUPS_MAX_RUN_TIME env var */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Initialize the global address and community lists...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  Addresses   = cupsArrayNew(NULL, NULL);
Packit 2fc92b
  Communities = cupsArrayNew(NULL, NULL);
Packit 2fc92b
Packit 2fc92b
  if (address)
Packit 2fc92b
    add_array(Addresses, address);
Packit 2fc92b
Packit 2fc92b
  if ((debug = getenv("CUPS_DEBUG_LEVEL")) != NULL)
Packit 2fc92b
    DebugLevel = atoi(debug);
Packit 2fc92b
Packit 2fc92b
  if ((runtime = getenv("CUPS_MAX_RUN_TIME")) != NULL)
Packit 2fc92b
    MaxRunTime = atoi(runtime);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Find the snmp.conf file...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if ((cups_serverroot = getenv("CUPS_SERVERROOT")) == NULL)
Packit 2fc92b
    cups_serverroot = CUPS_SERVERROOT;
Packit 2fc92b
Packit 2fc92b
  snprintf(filename, sizeof(filename), "%s/snmp.conf", cups_serverroot);
Packit 2fc92b
Packit 2fc92b
  if ((fp = cupsFileOpen(filename, "r")) != NULL)
Packit 2fc92b
  {
Packit 2fc92b
   /*
Packit 2fc92b
    * Read the snmp.conf file...
Packit 2fc92b
    */
Packit 2fc92b
Packit 2fc92b
    linenum = 0;
Packit 2fc92b
Packit 2fc92b
    while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
Packit 2fc92b
    {
Packit 2fc92b
      if (!value)
Packit 2fc92b
        fprintf(stderr, "ERROR: Missing value on line %d of %s!\n", linenum,
Packit 2fc92b
	        filename);
Packit 2fc92b
      else if (!_cups_strcasecmp(line, "Address"))
Packit 2fc92b
      {
Packit 2fc92b
        if (!address)
Packit 2fc92b
          add_array(Addresses, value);
Packit 2fc92b
      }
Packit 2fc92b
      else if (!_cups_strcasecmp(line, "Community"))
Packit 2fc92b
        add_array(Communities, value);
Packit 2fc92b
      else if (!_cups_strcasecmp(line, "DebugLevel"))
Packit 2fc92b
        DebugLevel = atoi(value);
Packit 2fc92b
      else if (!_cups_strcasecmp(line, "DeviceURI"))
Packit 2fc92b
      {
Packit 2fc92b
        if (*value != '\"')
Packit 2fc92b
	  fprintf(stderr,
Packit 2fc92b
	          "ERROR: Missing double quote for regular expression on "
Packit 2fc92b
		  "line %d of %s!\n", linenum, filename);
Packit 2fc92b
        else
Packit 2fc92b
	  add_device_uri(value);
Packit 2fc92b
      }
Packit 2fc92b
      else if (!_cups_strcasecmp(line, "HostNameLookups"))
Packit 2fc92b
        HostNameLookups = !_cups_strcasecmp(value, "on") ||
Packit 2fc92b
	                  !_cups_strcasecmp(value, "yes") ||
Packit 2fc92b
	                  !_cups_strcasecmp(value, "true") ||
Packit 2fc92b
	                  !_cups_strcasecmp(value, "double");
Packit 2fc92b
      else if (!_cups_strcasecmp(line, "MaxRunTime"))
Packit 2fc92b
        MaxRunTime = atoi(value);
Packit 2fc92b
      else
Packit 2fc92b
        fprintf(stderr, "ERROR: Unknown directive %s on line %d of %s!\n",
Packit 2fc92b
	        line, linenum, filename);
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    cupsFileClose(fp);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Use defaults if parameters are undefined...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (cupsArrayCount(Addresses) == 0)
Packit 2fc92b
  {
Packit 2fc92b
   /*
Packit 2fc92b
    * If we have no addresses, exit immediately...
Packit 2fc92b
    */
Packit 2fc92b
Packit 2fc92b
    fprintf(stderr,
Packit 2fc92b
            "DEBUG: No address specified and no Address line in %s...\n",
Packit 2fc92b
	    filename);
Packit 2fc92b
    exit(0);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if (cupsArrayCount(Communities) == 0)
Packit 2fc92b
  {
Packit 2fc92b
    fputs("INFO: Using default SNMP Community public\n", stderr);
Packit 2fc92b
    add_array(Communities, "public");
Packit 2fc92b
  }
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'read_snmp_response()' - Read and parse a SNMP response...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
read_snmp_response(int fd)		/* I - SNMP socket file descriptor */
Packit 2fc92b
{
Packit 2fc92b
  char		addrname[256];		/* Source address name */
Packit 2fc92b
  cups_snmp_t	packet;			/* Decoded packet */
Packit 2fc92b
  snmp_cache_t	key,			/* Search key */
Packit 2fc92b
		*device;		/* Matching device */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Read the response data...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (!_cupsSNMPRead(fd, &packet, -1.0))
Packit 2fc92b
  {
Packit 2fc92b
    fprintf(stderr, "ERROR: Unable to read data from socket: %s\n",
Packit 2fc92b
            strerror(errno));
Packit 2fc92b
    return;
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if (HostNameLookups)
Packit 2fc92b
    httpAddrLookup(&(packet.address), addrname, sizeof(addrname));
Packit 2fc92b
  else
Packit 2fc92b
    httpAddrString(&(packet.address), addrname, sizeof(addrname));
Packit 2fc92b
Packit 2fc92b
  debug_printf("DEBUG: %.3f Received data from %s...\n", run_time(), addrname);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Look for the response status code in the SNMP message header...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  if (packet.error)
Packit 2fc92b
  {
Packit 2fc92b
    fprintf(stderr, "ERROR: Bad SNMP packet from %s: %s\n", addrname,
Packit 2fc92b
            packet.error);
Packit 2fc92b
Packit 2fc92b
    return;
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  debug_printf("DEBUG: community=\"%s\"\n", packet.community);
Packit 2fc92b
  debug_printf("DEBUG: request-id=%d\n", packet.request_id);
Packit 2fc92b
  debug_printf("DEBUG: error-status=%d\n", packet.error_status);
Packit 2fc92b
Packit 2fc92b
  if (packet.error_status && packet.request_id != DEVICE_TYPE)
Packit 2fc92b
    return;
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Find a matching device in the cache...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  key.addrname = addrname;
Packit 2fc92b
  device       = (snmp_cache_t *)cupsArrayFind(Devices, &key);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Process the message...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  switch (packet.request_id)
Packit 2fc92b
  {
Packit 2fc92b
    case DEVICE_TYPE :
Packit 2fc92b
       /*
Packit 2fc92b
	* Got the device type response...
Packit 2fc92b
	*/
Packit 2fc92b
Packit 2fc92b
	if (device)
Packit 2fc92b
	{
Packit 2fc92b
	  debug_printf("DEBUG: Discarding duplicate device type for \"%s\"...\n",
Packit 2fc92b
		       addrname);
Packit 2fc92b
	  return;
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
       /*
Packit 2fc92b
	* Add the device and request the device data...
Packit 2fc92b
	*/
Packit 2fc92b
Packit 2fc92b
	add_cache(&(packet.address), addrname, NULL, NULL, NULL);
Packit 2fc92b
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_DESCRIPTION, DescriptionOID);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_ID, DeviceIdOID);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_URI, UriOID);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_LOCATION, LocationOID);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_PRODUCT, LexmarkProductOID);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_PRODUCT, LexmarkProductOID2);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_ID, LexmarkDeviceIdOID);
Packit 2fc92b
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit Service 86ce49
		       packet.community, CUPS_ASN1_GET_REQUEST,
Packit Service 86ce49
		       DEVICE_ID, RicohDeviceIdOID);
Packit Service 86ce49
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit 2fc92b
	               packet.community, CUPS_ASN1_GET_REQUEST,
Packit 2fc92b
		       DEVICE_PRODUCT, XeroxProductOID);
Packit Service f61958
	_cupsSNMPWrite(fd, &(packet.address), CUPS_SNMP_VERSION_1,
Packit Service f61958
		       packet.community, CUPS_ASN1_GET_REQUEST,
Packit Service f61958
		       DEVICE_ID, HPDeviceIdOID);
Packit 2fc92b
        break;
Packit 2fc92b
Packit 2fc92b
    case DEVICE_DESCRIPTION :
Packit 2fc92b
	if (device && packet.object_type == CUPS_ASN1_OCTET_STRING)
Packit 2fc92b
	{
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Update an existing cache entry...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
	  char	make_model[256];	/* Make and model */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
	  if (strchr((char *)packet.object_value.string.bytes, ':') &&
Packit 2fc92b
	      strchr((char *)packet.object_value.string.bytes, ';'))
Packit 2fc92b
	  {
Packit 2fc92b
	   /*
Packit 2fc92b
	    * Description is the IEEE-1284 device ID...
Packit 2fc92b
	    */
Packit 2fc92b
Packit 2fc92b
            char *ptr;			/* Pointer into device ID */
Packit 2fc92b
Packit 2fc92b
            for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++)
Packit 2fc92b
              if (*ptr == '\n')
Packit 2fc92b
                *ptr = ';';		/* A lot of bad printers put a newline */
Packit 2fc92b
	    if (!device->id)
Packit 2fc92b
	      device->id = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
Packit 2fc92b
	    backendGetMakeModel((char *)packet.object_value.string.bytes,
Packit 2fc92b
				make_model, sizeof(make_model));
Packit 2fc92b
Packit 2fc92b
            if (device->info)
Packit 2fc92b
	      free(device->info);
Packit 2fc92b
Packit 2fc92b
	    device->info = strdup(make_model);
Packit 2fc92b
	  }
Packit 2fc92b
	  else
Packit 2fc92b
	  {
Packit 2fc92b
	   /*
Packit 2fc92b
	    * Description is plain text...
Packit 2fc92b
	    */
Packit 2fc92b
Packit 2fc92b
	    fix_make_model(make_model, (char *)packet.object_value.string.bytes,
Packit 2fc92b
			   sizeof(make_model));
Packit 2fc92b
Packit 2fc92b
            if (device->info)
Packit 2fc92b
	      free(device->info);
Packit 2fc92b
Packit 2fc92b
	    device->info = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
	  }
Packit 2fc92b
Packit 2fc92b
	  if (!device->make_and_model)
Packit 2fc92b
	    device->make_and_model = strdup(make_model);
Packit 2fc92b
        }
Packit 2fc92b
	break;
Packit 2fc92b
Packit 2fc92b
    case DEVICE_ID :
Packit 2fc92b
	if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
Packit 2fc92b
	    (!device->id ||
Packit 2fc92b
	     strlen(device->id) < packet.object_value.string.num_bytes))
Packit 2fc92b
	{
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Update an existing cache entry...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
	  char	make_model[256];	/* Make and model */
Packit 2fc92b
          char *ptr;			/* Pointer into device ID */
Packit 2fc92b
Packit 2fc92b
          for (ptr = (char *)packet.object_value.string.bytes; *ptr; ptr ++)
Packit 2fc92b
            if (*ptr == '\n')
Packit 2fc92b
              *ptr = ';';		/* A lot of bad printers put a newline */
Packit 2fc92b
	  if (device->id)
Packit 2fc92b
	    free(device->id);
Packit 2fc92b
Packit 2fc92b
	  device->id = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Convert the ID to a make and model string...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
	  backendGetMakeModel((char *)packet.object_value.string.bytes,
Packit 2fc92b
	                      make_model, sizeof(make_model));
Packit 2fc92b
	  if (device->make_and_model)
Packit 2fc92b
	    free(device->make_and_model);
Packit 2fc92b
Packit 2fc92b
	  device->make_and_model = strdup(make_model);
Packit 2fc92b
	}
Packit 2fc92b
	break;
Packit 2fc92b
Packit 2fc92b
    case DEVICE_LOCATION :
Packit 2fc92b
	if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
Packit 2fc92b
	    !device->location)
Packit 2fc92b
	  device->location = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
	break;
Packit 2fc92b
Packit 2fc92b
    case DEVICE_PRODUCT :
Packit 2fc92b
	if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
Packit 2fc92b
	    !device->id)
Packit 2fc92b
	{
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Update an existing cache entry...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
          if (!device->info)
Packit 2fc92b
	    device->info = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
Packit 2fc92b
          if (device->make_and_model)
Packit 2fc92b
	    free(device->make_and_model);
Packit 2fc92b
Packit 2fc92b
	  device->make_and_model = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
	}
Packit 2fc92b
	break;
Packit 2fc92b
Packit 2fc92b
    case DEVICE_URI :
Packit 2fc92b
	if (device && packet.object_type == CUPS_ASN1_OCTET_STRING &&
Packit 2fc92b
	    !device->uri && packet.object_value.string.num_bytes > 3)
Packit 2fc92b
	{
Packit 2fc92b
	 /*
Packit 2fc92b
	  * Update an existing cache entry...
Packit 2fc92b
	  */
Packit 2fc92b
Packit 2fc92b
          char	scheme[32],		/* URI scheme */
Packit 2fc92b
		userpass[256],		/* Username:password in URI */
Packit 2fc92b
		hostname[256],		/* Hostname in URI */
Packit 2fc92b
		resource[1024];		/* Resource path in URI */
Packit 2fc92b
	  int	port;			/* Port number in URI */
Packit 2fc92b
Packit 2fc92b
	  if (!strncmp((char *)packet.object_value.string.bytes, "lpr:", 4))
Packit 2fc92b
	  {
Packit 2fc92b
	   /*
Packit 2fc92b
	    * We want "lpd://..." for the URI...
Packit 2fc92b
	    */
Packit 2fc92b
Packit 2fc92b
	    packet.object_value.string.bytes[2] = 'd';
Packit 2fc92b
	  }
Packit 2fc92b
Packit 2fc92b
          if (httpSeparateURI(HTTP_URI_CODING_ALL,
Packit 2fc92b
                              (char *)packet.object_value.string.bytes,
Packit 2fc92b
                              scheme, sizeof(scheme),
Packit 2fc92b
                              userpass, sizeof(userpass),
Packit 2fc92b
                              hostname, sizeof(hostname), &port,
Packit 2fc92b
                              resource, sizeof(resource)) >= HTTP_URI_OK)
Packit 2fc92b
	    device->uri = strdup((char *)packet.object_value.string.bytes);
Packit 2fc92b
	}
Packit 2fc92b
	break;
Packit 2fc92b
  }
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'run_time()' - Return the total running time...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static double				/* O - Number of seconds */
Packit 2fc92b
run_time(void)
Packit 2fc92b
{
Packit 2fc92b
  struct timeval	curtime;	/* Current time */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  gettimeofday(&curtime, NULL);
Packit 2fc92b
Packit 2fc92b
  return (curtime.tv_sec - StartTime.tv_sec +
Packit 2fc92b
          0.000001 * (curtime.tv_usec - StartTime.tv_usec));
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'scan_devices()' - Scan for devices using SNMP.
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
scan_devices(int ipv4,			/* I - SNMP IPv4 socket */
Packit 2fc92b
             int ipv6)			/* I - SNMP IPv6 socket */
Packit 2fc92b
{
Packit 2fc92b
  int			fd,		/* File descriptor for this address */
Packit 2fc92b
			busy;		/* Are we busy processing something? */
Packit 2fc92b
  char			*address,	/* Current address */
Packit 2fc92b
			*community;	/* Current community */
Packit 2fc92b
  fd_set		input;		/* Input set for select() */
Packit 2fc92b
  struct timeval	timeout;	/* Timeout for select() */
Packit 2fc92b
  time_t		endtime;	/* End time for scan */
Packit 2fc92b
  http_addrlist_t	*addrs,		/* List of addresses */
Packit 2fc92b
			*addr;		/* Current address */
Packit 2fc92b
  snmp_cache_t		*device;	/* Current device */
Packit 2fc92b
  char			temp[1024];	/* Temporary address string */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  gettimeofday(&StartTime, NULL);
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * First send all of the broadcast queries...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  for (address = (char *)cupsArrayFirst(Addresses);
Packit 2fc92b
       address;
Packit 2fc92b
       address = (char *)cupsArrayNext(Addresses))
Packit 2fc92b
  {
Packit 2fc92b
    if (!strcmp(address, "@LOCAL"))
Packit 2fc92b
      addrs = get_interface_addresses(NULL);
Packit 2fc92b
    else if (!strncmp(address, "@IF(", 4))
Packit 2fc92b
    {
Packit 2fc92b
      char	ifname[255];		/* Interface name */
Packit 2fc92b
Packit 2fc92b
      strlcpy(ifname, address + 4, sizeof(ifname));
Packit 2fc92b
      if (ifname[0])
Packit 2fc92b
        ifname[strlen(ifname) - 1] = '\0';
Packit 2fc92b
Packit 2fc92b
      addrs = get_interface_addresses(ifname);
Packit 2fc92b
    }
Packit 2fc92b
    else
Packit 2fc92b
      addrs = httpAddrGetList(address, AF_UNSPEC, NULL);
Packit 2fc92b
Packit 2fc92b
    if (!addrs)
Packit 2fc92b
    {
Packit 2fc92b
      fprintf(stderr, "ERROR: Unable to scan \"%s\"!\n", address);
Packit 2fc92b
      continue;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    for (community = (char *)cupsArrayFirst(Communities);
Packit 2fc92b
         community;
Packit 2fc92b
	 community = (char *)cupsArrayNext(Communities))
Packit 2fc92b
    {
Packit 2fc92b
      debug_printf("DEBUG: Scanning for devices in \"%s\" via \"%s\"...\n",
Packit 2fc92b
        	   community, address);
Packit 2fc92b
Packit 2fc92b
      for (addr = addrs; addr; addr = addr->next)
Packit 2fc92b
      {
Packit 2fc92b
#ifdef AF_INET6
Packit 2fc92b
        if (httpAddrFamily(&(addr->addr)) == AF_INET6)
Packit 2fc92b
	  fd = ipv6;
Packit 2fc92b
	else
Packit 2fc92b
#endif /* AF_INET6 */
Packit 2fc92b
        fd = ipv4;
Packit 2fc92b
Packit 2fc92b
        debug_printf("DEBUG: Sending get request to %s...\n",
Packit 2fc92b
	             httpAddrString(&(addr->addr), temp, sizeof(temp)));
Packit 2fc92b
Packit 2fc92b
        _cupsSNMPWrite(fd, &(addr->addr), CUPS_SNMP_VERSION_1, community,
Packit 2fc92b
	               CUPS_ASN1_GET_REQUEST, DEVICE_TYPE, DeviceTypeOID);
Packit 2fc92b
      }
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    httpAddrFreeList(addrs);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
 /*
Packit 2fc92b
  * Then read any responses that come in over the next 3 seconds...
Packit 2fc92b
  */
Packit 2fc92b
Packit 2fc92b
  endtime = time(NULL) + MaxRunTime;
Packit 2fc92b
Packit 2fc92b
  FD_ZERO(&input);
Packit 2fc92b
Packit 2fc92b
  while (time(NULL) < endtime)
Packit 2fc92b
  {
Packit 2fc92b
    timeout.tv_sec  = 2;
Packit 2fc92b
    timeout.tv_usec = 0;
Packit 2fc92b
Packit 2fc92b
    FD_SET(ipv4, &input);
Packit 2fc92b
    if (ipv6 >= 0)
Packit 2fc92b
      FD_SET(ipv6, &input);
Packit 2fc92b
Packit 2fc92b
    fd = ipv4 > ipv6 ? ipv4 : ipv6;
Packit 2fc92b
    if (select(fd + 1, &input, NULL, NULL, &timeout) < 0)
Packit 2fc92b
    {
Packit 2fc92b
      fprintf(stderr, "ERROR: %.3f select() for %d/%d failed: %s\n", run_time(),
Packit 2fc92b
              ipv4, ipv6, strerror(errno));
Packit 2fc92b
      break;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    busy = 0;
Packit 2fc92b
Packit 2fc92b
    if (FD_ISSET(ipv4, &input))
Packit 2fc92b
    {
Packit 2fc92b
      read_snmp_response(ipv4);
Packit 2fc92b
      busy = 1;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    if (ipv6 >= 0 && FD_ISSET(ipv6, &input))
Packit 2fc92b
    {
Packit 2fc92b
      read_snmp_response(ipv6);
Packit 2fc92b
      busy = 1;
Packit 2fc92b
    }
Packit 2fc92b
Packit 2fc92b
    if (!busy)
Packit 2fc92b
    {
Packit 2fc92b
     /*
Packit 2fc92b
      * List devices with complete information...
Packit 2fc92b
      */
Packit 2fc92b
Packit 2fc92b
      int sent_something = 0;
Packit 2fc92b
Packit 2fc92b
      for (device = (snmp_cache_t *)cupsArrayFirst(Devices);
Packit 2fc92b
           device;
Packit 2fc92b
	   device = (snmp_cache_t *)cupsArrayNext(Devices))
Packit 2fc92b
        if (!device->sent && device->info && device->make_and_model)
Packit 2fc92b
	{
Packit 2fc92b
	  if (device->uri)
Packit 2fc92b
	    list_device(device);
Packit 2fc92b
	  else
Packit 2fc92b
	    probe_device(device);
Packit 2fc92b
Packit 2fc92b
	  device->sent = sent_something = 1;
Packit 2fc92b
	}
Packit 2fc92b
Packit 2fc92b
      if (!sent_something)
Packit 2fc92b
        break;
Packit 2fc92b
    }
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  debug_printf("DEBUG: %.3f Scan complete!\n", run_time());
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'try_connect()' - Try connecting on a port...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static int				/* O - 0 on success or -1 on error */
Packit 2fc92b
try_connect(http_addr_t *addr,		/* I - Socket address */
Packit 2fc92b
            const char  *addrname,	/* I - Hostname or IP address */
Packit 2fc92b
            int         port)		/* I - Port number */
Packit 2fc92b
{
Packit 2fc92b
  int	fd;				/* Socket */
Packit 2fc92b
  int	status;				/* Connection status */
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
  debug_printf("DEBUG: %.3f Trying %s://%s:%d...\n", run_time(),
Packit 2fc92b
               port == 515 ? "lpd" : "socket", addrname, port);
Packit 2fc92b
Packit 2fc92b
  if ((fd = socket(httpAddrFamily(addr), SOCK_STREAM, 0)) < 0)
Packit 2fc92b
  {
Packit 2fc92b
    fprintf(stderr, "ERROR: Unable to create socket: %s\n",
Packit 2fc92b
            strerror(errno));
Packit 2fc92b
    return (-1);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  _httpAddrSetPort(addr, port);
Packit 2fc92b
Packit 2fc92b
  alarm(1);
Packit 2fc92b
Packit 2fc92b
  status = connect(fd, (void *)addr, (socklen_t)httpAddrLength(addr));
Packit 2fc92b
Packit 2fc92b
  close(fd);
Packit 2fc92b
  alarm(0);
Packit 2fc92b
Packit 2fc92b
  return (status);
Packit 2fc92b
}
Packit 2fc92b
Packit 2fc92b
Packit 2fc92b
/*
Packit 2fc92b
 * 'update_cache()' - Update a cached device...
Packit 2fc92b
 */
Packit 2fc92b
Packit 2fc92b
static void
Packit 2fc92b
update_cache(snmp_cache_t *device,	/* I - Device */
Packit 2fc92b
             const char   *uri,		/* I - Device URI */
Packit 2fc92b
	     const char   *id,		/* I - Device ID */
Packit 2fc92b
	     const char   *make_model)	/* I - Device make and model */
Packit 2fc92b
{
Packit 2fc92b
  if (device->uri)
Packit 2fc92b
    free(device->uri);
Packit 2fc92b
Packit 2fc92b
  device->uri = strdup(uri);
Packit 2fc92b
Packit 2fc92b
  if (id)
Packit 2fc92b
  {
Packit 2fc92b
    if (device->id)
Packit 2fc92b
      free(device->id);
Packit 2fc92b
Packit 2fc92b
    device->id = strdup(id);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  if (make_model)
Packit 2fc92b
  {
Packit 2fc92b
    if (device->make_and_model)
Packit 2fc92b
      free(device->make_and_model);
Packit 2fc92b
Packit 2fc92b
    device->make_and_model = strdup(make_model);
Packit 2fc92b
  }
Packit 2fc92b
Packit 2fc92b
  list_device(device);
Packit 2fc92b
}