Home | History | Annotate | Download | only in cups
      1 /*
      2  * SNMP functions for CUPS.
      3  *
      4  * Copyright 2007-2014 by Apple Inc.
      5  * Copyright 2006-2007 by Easy Software Products, all rights reserved.
      6  *
      7  * These coded instructions, statements, and computer programs are the
      8  * property of Apple Inc. and are protected by Federal copyright
      9  * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
     10  * "LICENSE" which should have been included with this file.  If this
     11  * file is missing or damaged, see the license at "http://www.cups.org/".
     12  *
     13  * This file is subject to the Apple OS-Developed Software exception.
     14  */
     15 
     16 /*
     17  * Include necessary headers.
     18  */
     19 
     20 #include "cups-private.h"
     21 #include "snmp-private.h"
     22 #ifdef HAVE_POLL
     23 #  include <poll.h>
     24 #endif /* HAVE_POLL */
     25 
     26 
     27 /*
     28  * Local functions...
     29  */
     30 
     31 static void		asn1_debug(const char *prefix, unsigned char *buffer,
     32 			           size_t len, int indent);
     33 static int		asn1_decode_snmp(unsigned char *buffer, size_t len,
     34 			                 cups_snmp_t *packet);
     35 static int		asn1_encode_snmp(unsigned char *buffer, size_t len,
     36 			                 cups_snmp_t *packet);
     37 static int		asn1_get_integer(unsigned char **buffer,
     38 			                 unsigned char *bufend,
     39 			                 unsigned length);
     40 static int		asn1_get_oid(unsigned char **buffer,
     41 			             unsigned char *bufend,
     42 				     unsigned length, int *oid, int oidsize);
     43 static int		asn1_get_packed(unsigned char **buffer,
     44 			                unsigned char *bufend);
     45 static char		*asn1_get_string(unsigned char **buffer,
     46 			                 unsigned char *bufend,
     47 			                 unsigned length, char *string,
     48 			                 size_t strsize);
     49 static unsigned		asn1_get_length(unsigned char **buffer,
     50 			                unsigned char *bufend);
     51 static int		asn1_get_type(unsigned char **buffer,
     52 			              unsigned char *bufend);
     53 static void		asn1_set_integer(unsigned char **buffer,
     54 			                 int integer);
     55 static void		asn1_set_length(unsigned char **buffer,
     56 			                unsigned length);
     57 static void		asn1_set_oid(unsigned char **buffer,
     58 			             const int *oid);
     59 static void		asn1_set_packed(unsigned char **buffer,
     60 			                int integer);
     61 static unsigned		asn1_size_integer(int integer);
     62 static unsigned		asn1_size_length(unsigned length);
     63 static unsigned		asn1_size_oid(const int *oid);
     64 static unsigned		asn1_size_packed(int integer);
     65 static void		snmp_set_error(cups_snmp_t *packet,
     66 			               const char *message);
     67 
     68 
     69 /*
     70  * '_cupsSNMPClose()' - Close a SNMP socket.
     71  */
     72 
     73 void
     74 _cupsSNMPClose(int fd)			/* I - SNMP socket file descriptor */
     75 {
     76   DEBUG_printf(("4_cupsSNMPClose(fd=%d)", fd));
     77 
     78   httpAddrClose(NULL, fd);
     79 }
     80 
     81 
     82 /*
     83  * '_cupsSNMPCopyOID()' - Copy an OID.
     84  *
     85  * The array pointed to by "src" is terminated by the value -1.
     86  */
     87 
     88 int *					/* O - New OID */
     89 _cupsSNMPCopyOID(int       *dst,	/* I - Destination OID */
     90                  const int *src,	/* I - Source OID */
     91 		 int       dstsize)	/* I - Number of integers in dst */
     92 {
     93   int	i;				/* Looping var */
     94 
     95 
     96   DEBUG_printf(("4_cupsSNMPCopyOID(dst=%p, src=%p, dstsize=%d)", dst, src,
     97                 dstsize));
     98 
     99   for (i = 0, dstsize --; src[i] >= 0 && i < dstsize; i ++)
    100     dst[i] = src[i];
    101 
    102   dst[i] = -1;
    103 
    104   return (dst);
    105 }
    106 
    107 
    108 /*
    109  * '_cupsSNMPDefaultCommunity()' - Get the default SNMP community name.
    110  *
    111  * The default community name is the first community name found in the
    112  * snmp.conf file. If no community name is defined there, "public" is used.
    113  */
    114 
    115 const char *				/* O - Default community name */
    116 _cupsSNMPDefaultCommunity(void)
    117 {
    118   cups_file_t	*fp;			/* snmp.conf file */
    119   char		line[1024],		/* Line from file */
    120 		*value;			/* Value from file */
    121   int		linenum;		/* Line number in file */
    122   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
    123 
    124 
    125   DEBUG_puts("4_cupsSNMPDefaultCommunity()");
    126 
    127   if (!cg->snmp_community[0])
    128   {
    129     strlcpy(cg->snmp_community, "public", sizeof(cg->snmp_community));
    130 
    131     snprintf(line, sizeof(line), "%s/snmp.conf", cg->cups_serverroot);
    132     if ((fp = cupsFileOpen(line, "r")) != NULL)
    133     {
    134       linenum = 0;
    135       while (cupsFileGetConf(fp, line, sizeof(line), &value, &linenum))
    136 	if (!_cups_strcasecmp(line, "Community") && value)
    137 	{
    138 	  strlcpy(cg->snmp_community, value, sizeof(cg->snmp_community));
    139 	  break;
    140 	}
    141 
    142       cupsFileClose(fp);
    143     }
    144   }
    145 
    146   DEBUG_printf(("5_cupsSNMPDefaultCommunity: Returning \"%s\"",
    147                 cg->snmp_community));
    148 
    149   return (cg->snmp_community);
    150 }
    151 
    152 
    153 /*
    154  * '_cupsSNMPIsOID()' - Test whether a SNMP response contains the specified OID.
    155  *
    156  * The array pointed to by "oid" is terminated by the value -1.
    157  */
    158 
    159 int					/* O - 1 if equal, 0 if not equal */
    160 _cupsSNMPIsOID(cups_snmp_t *packet,	/* I - Response packet */
    161                const int   *oid)	/* I - OID */
    162 {
    163   int	i;				/* Looping var */
    164 
    165 
    166  /*
    167   * Range check input...
    168   */
    169 
    170   DEBUG_printf(("4_cupsSNMPIsOID(packet=%p, oid=%p)", packet, oid));
    171 
    172   if (!packet || !oid)
    173   {
    174     DEBUG_puts("5_cupsSNMPIsOID: Returning 0");
    175 
    176     return (0);
    177   }
    178 
    179  /*
    180   * Compare OIDs...
    181   */
    182 
    183   for (i = 0;
    184        i < CUPS_SNMP_MAX_OID && oid[i] >= 0 && packet->object_name[i] >= 0;
    185        i ++)
    186     if (oid[i] != packet->object_name[i])
    187     {
    188       DEBUG_puts("5_cupsSNMPIsOID: Returning 0");
    189 
    190       return (0);
    191     }
    192 
    193   DEBUG_printf(("5_cupsSNMPIsOID: Returning %d",
    194                 i < CUPS_SNMP_MAX_OID && oid[i] == packet->object_name[i]));
    195 
    196   return (i < CUPS_SNMP_MAX_OID && oid[i] == packet->object_name[i]);
    197 }
    198 
    199 
    200 /*
    201  * '_cupsSNMPIsOIDPrefixed()' - Test whether a SNMP response uses the specified
    202  *                              OID prefix.
    203  *
    204  * The array pointed to by "prefix" is terminated by the value -1.
    205  */
    206 
    207 int					/* O - 1 if prefixed, 0 if not prefixed */
    208 _cupsSNMPIsOIDPrefixed(
    209     cups_snmp_t *packet,		/* I - Response packet */
    210     const int   *prefix)		/* I - OID prefix */
    211 {
    212   int	i;				/* Looping var */
    213 
    214 
    215  /*
    216   * Range check input...
    217   */
    218 
    219   DEBUG_printf(("4_cupsSNMPIsOIDPrefixed(packet=%p, prefix=%p)", packet,
    220                 prefix));
    221 
    222   if (!packet || !prefix)
    223   {
    224     DEBUG_puts("5_cupsSNMPIsOIDPrefixed: Returning 0");
    225 
    226     return (0);
    227   }
    228 
    229  /*
    230   * Compare OIDs...
    231   */
    232 
    233   for (i = 0;
    234        i < CUPS_SNMP_MAX_OID && prefix[i] >= 0 && packet->object_name[i] >= 0;
    235        i ++)
    236     if (prefix[i] != packet->object_name[i])
    237     {
    238       DEBUG_puts("5_cupsSNMPIsOIDPrefixed: Returning 0");
    239 
    240       return (0);
    241     }
    242 
    243   DEBUG_printf(("5_cupsSNMPIsOIDPrefixed: Returning %d",
    244                 i < CUPS_SNMP_MAX_OID));
    245 
    246   return (i < CUPS_SNMP_MAX_OID);
    247 }
    248 
    249 
    250 /*
    251  * '_cupsSNMPOIDToString()' - Convert an OID to a string.
    252  */
    253 
    254 
    255 char *					/* O - New string or @code NULL@ on error */
    256 _cupsSNMPOIDToString(const int *src,	/* I - OID */
    257                      char      *dst,	/* I - String buffer */
    258                      size_t    dstsize)	/* I - Size of string buffer */
    259 {
    260   char	*dstptr,			/* Pointer into string buffer */
    261 	*dstend;			/* End of string buffer */
    262 
    263 
    264   DEBUG_printf(("4_cupsSNMPOIDToString(src=%p, dst=%p, dstsize=" CUPS_LLFMT ")",
    265                 src, dst, CUPS_LLCAST dstsize));
    266 
    267  /*
    268   * Range check input...
    269   */
    270 
    271   if (!src || !dst || dstsize < 4)
    272     return (NULL);
    273 
    274  /*
    275   * Loop through the OID array and build a string...
    276   */
    277 
    278   for (dstptr = dst, dstend = dstptr + dstsize - 1;
    279        *src >= 0 && dstptr < dstend;
    280        src ++, dstptr += strlen(dstptr))
    281     snprintf(dstptr, (size_t)(dstend - dstptr + 1), ".%d", *src);
    282 
    283   if (*src >= 0)
    284     return (NULL);
    285   else
    286     return (dst);
    287 }
    288 
    289 
    290 /*
    291  * '_cupsSNMPOpen()' - Open a SNMP socket.
    292  */
    293 
    294 int					/* O - SNMP socket file descriptor */
    295 _cupsSNMPOpen(int family)		/* I - Address family - @code AF_INET@ or @code AF_INET6@ */
    296 {
    297   int		fd;			/* SNMP socket file descriptor */
    298   int		val;			/* Socket option value */
    299 
    300 
    301  /*
    302   * Create the SNMP socket...
    303   */
    304 
    305   DEBUG_printf(("4_cupsSNMPOpen(family=%d)", family));
    306 
    307   if ((fd = socket(family, SOCK_DGRAM, 0)) < 0)
    308   {
    309     DEBUG_printf(("5_cupsSNMPOpen: Returning -1 (%s)", strerror(errno)));
    310 
    311     return (-1);
    312   }
    313 
    314  /*
    315   * Set the "broadcast" flag...
    316   */
    317 
    318   val = 1;
    319 
    320   if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, CUPS_SOCAST &val, sizeof(val)))
    321   {
    322     DEBUG_printf(("5_cupsSNMPOpen: Returning -1 (%s)", strerror(errno)));
    323 
    324     close(fd);
    325 
    326     return (-1);
    327   }
    328 
    329   DEBUG_printf(("5_cupsSNMPOpen: Returning %d", fd));
    330 
    331   return (fd);
    332 }
    333 
    334 
    335 /*
    336  * '_cupsSNMPRead()' - Read and parse a SNMP response.
    337  *
    338  * If "timeout" is negative, @code _cupsSNMPRead@ will wait for a response
    339  * indefinitely.
    340  */
    341 
    342 cups_snmp_t *				/* O - SNMP packet or @code NULL@ if none */
    343 _cupsSNMPRead(int         fd,		/* I - SNMP socket file descriptor */
    344               cups_snmp_t *packet,	/* I - SNMP packet buffer */
    345 	      double      timeout)	/* I - Timeout in seconds */
    346 {
    347   unsigned char	buffer[CUPS_SNMP_MAX_PACKET];
    348 					/* Data packet */
    349   ssize_t	bytes;			/* Number of bytes received */
    350   socklen_t	addrlen;		/* Source address length */
    351   http_addr_t	address;		/* Source address */
    352 
    353 
    354  /*
    355   * Range check input...
    356   */
    357 
    358   DEBUG_printf(("4_cupsSNMPRead(fd=%d, packet=%p, timeout=%.1f)", fd, packet,
    359                 timeout));
    360 
    361   if (fd < 0 || !packet)
    362   {
    363     DEBUG_puts("5_cupsSNMPRead: Returning NULL");
    364 
    365     return (NULL);
    366   }
    367 
    368  /*
    369   * Optionally wait for a response...
    370   */
    371 
    372   if (timeout >= 0.0)
    373   {
    374     int			ready;		/* Data ready on socket? */
    375 #ifdef HAVE_POLL
    376     struct pollfd	pfd;		/* Polled file descriptor */
    377 
    378     pfd.fd     = fd;
    379     pfd.events = POLLIN;
    380 
    381     while ((ready = poll(&pfd, 1, (int)(timeout * 1000.0))) < 0 &&
    382            (errno == EINTR || errno == EAGAIN));
    383 
    384 #else
    385     fd_set		input_set;	/* select() input set */
    386     struct timeval	stimeout;	/* select() timeout */
    387 
    388     do
    389     {
    390       FD_ZERO(&input_set);
    391       FD_SET(fd, &input_set);
    392 
    393       stimeout.tv_sec  = (int)timeout;
    394       stimeout.tv_usec = (int)((timeout - stimeout.tv_sec) * 1000000);
    395 
    396       ready = select(fd + 1, &input_set, NULL, NULL, &stimeout);
    397     }
    398 #  ifdef WIN32
    399     while (ready < 0 && WSAGetLastError() == WSAEINTR);
    400 #  else
    401     while (ready < 0 && (errno == EINTR || errno == EAGAIN));
    402 #  endif /* WIN32 */
    403 #endif /* HAVE_POLL */
    404 
    405    /*
    406     * If we don't have any data ready, return right away...
    407     */
    408 
    409     if (ready <= 0)
    410     {
    411       DEBUG_puts("5_cupsSNMPRead: Returning NULL (timeout)");
    412 
    413       return (NULL);
    414     }
    415   }
    416 
    417  /*
    418   * Read the response data...
    419   */
    420 
    421   addrlen = sizeof(address);
    422 
    423   if ((bytes = recvfrom(fd, buffer, sizeof(buffer), 0, (void *)&address,
    424                         &addrlen)) < 0)
    425   {
    426     DEBUG_printf(("5_cupsSNMPRead: Returning NULL (%s)", strerror(errno)));
    427 
    428     return (NULL);
    429   }
    430 
    431  /*
    432   * Look for the response status code in the SNMP message header...
    433   */
    434 
    435   asn1_debug("DEBUG: IN ", buffer, (size_t)bytes, 0);
    436 
    437   asn1_decode_snmp(buffer, (size_t)bytes, packet);
    438 
    439   memcpy(&(packet->address), &address, sizeof(packet->address));
    440 
    441  /*
    442   * Return decoded data packet...
    443   */
    444 
    445   DEBUG_puts("5_cupsSNMPRead: Returning packet");
    446 
    447   return (packet);
    448 }
    449 
    450 
    451 /*
    452  * '_cupsSNMPSetDebug()' - Enable/disable debug logging to stderr.
    453  */
    454 
    455 void
    456 _cupsSNMPSetDebug(int level)		/* I - 1 to enable debug output, 0 otherwise */
    457 {
    458   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
    459 
    460 
    461   DEBUG_printf(("4_cupsSNMPSetDebug(level=%d)", level));
    462 
    463   cg->snmp_debug = level;
    464 }
    465 
    466 
    467 /*
    468  * '_cupsSNMPStringToOID()' - Convert a numeric OID string to an OID array.
    469  *
    470  * This function converts a string of the form ".N.N.N.N.N" to the
    471  * corresponding OID array terminated by -1.
    472  *
    473  * @code NULL@ is returned if the array is not large enough or the string is
    474  * not a valid OID number.
    475  */
    476 
    477 int *					/* O - Pointer to OID array or @code NULL@ on error */
    478 _cupsSNMPStringToOID(const char *src,	/* I - OID string */
    479                      int        *dst,	/* I - OID array */
    480 		     int        dstsize)/* I - Number of integers in OID array */
    481 {
    482   int	*dstptr,			/* Pointer into OID array */
    483 	*dstend;			/* End of OID array */
    484 
    485 
    486   DEBUG_printf(("4_cupsSNMPStringToOID(src=\"%s\", dst=%p, dstsize=%d)",
    487                 src, dst, dstsize));
    488 
    489  /*
    490   * Range check input...
    491   */
    492 
    493   if (!src || !dst || dstsize < 2)
    494     return (NULL);
    495 
    496  /*
    497   * Skip leading "."...
    498   */
    499 
    500   if (*src == '.')
    501     src ++;
    502 
    503  /*
    504   * Loop to the end of the string...
    505   */
    506 
    507   for (dstend = dst + dstsize - 1, dstptr = dst, *dstptr = 0;
    508        *src && dstptr < dstend;
    509        src ++)
    510   {
    511     if (*src == '.')
    512     {
    513       dstptr ++;
    514       *dstptr = 0;
    515     }
    516     else if (isdigit(*src & 255))
    517       *dstptr = *dstptr * 10 + *src - '0';
    518     else
    519       break;
    520   }
    521 
    522   if (*src)
    523     return (NULL);
    524 
    525  /*
    526   * Terminate the end of the OID array and return...
    527   */
    528 
    529   dstptr[1] = -1;
    530 
    531   return (dst);
    532 }
    533 
    534 
    535 /*
    536  * '_cupsSNMPWalk()' - Enumerate a group of OIDs.
    537  *
    538  * This function queries all of the OIDs with the specified OID prefix,
    539  * calling the "cb" function for every response that is received.
    540  *
    541  * The array pointed to by "prefix" is terminated by the value -1.
    542  *
    543  * If "timeout" is negative, @code _cupsSNMPWalk@ will wait for a response
    544  * indefinitely.
    545  */
    546 
    547 int					/* O - Number of OIDs found or -1 on error */
    548 _cupsSNMPWalk(int            fd,	/* I - SNMP socket */
    549               http_addr_t    *address,	/* I - Address to query */
    550 	      int            version,	/* I - SNMP version */
    551 	      const char     *community,/* I - Community name */
    552               const int      *prefix,	/* I - OID prefix */
    553 	      double         timeout,	/* I - Timeout for each response in seconds */
    554 	      cups_snmp_cb_t cb,	/* I - Function to call for each response */
    555 	      void           *data)	/* I - User data pointer that is passed to the callback function */
    556 {
    557   int		count = 0;		/* Number of OIDs found */
    558   unsigned	request_id = 0;		/* Current request ID */
    559   cups_snmp_t	packet;			/* Current response packet */
    560   int		lastoid[CUPS_SNMP_MAX_OID];
    561 					/* Last OID we got */
    562 
    563 
    564  /*
    565   * Range check input...
    566   */
    567 
    568   DEBUG_printf(("4_cupsSNMPWalk(fd=%d, address=%p, version=%d, "
    569                 "community=\"%s\", prefix=%p, timeout=%.1f, cb=%p, data=%p)",
    570 		fd, address, version, community, prefix, timeout, cb, data));
    571 
    572   if (fd < 0 || !address || version != CUPS_SNMP_VERSION_1 || !community ||
    573       !prefix || !cb)
    574   {
    575     DEBUG_puts("5_cupsSNMPWalk: Returning -1");
    576 
    577     return (-1);
    578   }
    579 
    580  /*
    581   * Copy the OID prefix and then loop until we have no more OIDs...
    582   */
    583 
    584   _cupsSNMPCopyOID(packet.object_name, prefix, CUPS_SNMP_MAX_OID);
    585   lastoid[0] = -1;
    586 
    587   for (;;)
    588   {
    589     request_id ++;
    590 
    591     if (!_cupsSNMPWrite(fd, address, version, community,
    592                         CUPS_ASN1_GET_NEXT_REQUEST, request_id,
    593 		        packet.object_name))
    594     {
    595       DEBUG_puts("5_cupsSNMPWalk: Returning -1");
    596 
    597       return (-1);
    598     }
    599 
    600     if (!_cupsSNMPRead(fd, &packet, timeout))
    601     {
    602       DEBUG_puts("5_cupsSNMPWalk: Returning -1");
    603 
    604       return (-1);
    605     }
    606 
    607     if (!_cupsSNMPIsOIDPrefixed(&packet, prefix) ||
    608         _cupsSNMPIsOID(&packet, lastoid))
    609     {
    610       DEBUG_printf(("5_cupsSNMPWalk: Returning %d", count));
    611 
    612       return (count);
    613     }
    614 
    615     if (packet.error || packet.error_status)
    616     {
    617       DEBUG_printf(("5_cupsSNMPWalk: Returning %d", count > 0 ? count : -1));
    618 
    619       return (count > 0 ? count : -1);
    620     }
    621 
    622     _cupsSNMPCopyOID(lastoid, packet.object_name, CUPS_SNMP_MAX_OID);
    623 
    624     count ++;
    625 
    626     (*cb)(&packet, data);
    627   }
    628 }
    629 
    630 
    631 /*
    632  * '_cupsSNMPWrite()' - Send an SNMP query packet.
    633  *
    634  * The array pointed to by "oid" is terminated by the value -1.
    635  */
    636 
    637 int					/* O - 1 on success, 0 on error */
    638 _cupsSNMPWrite(
    639     int            fd,			/* I - SNMP socket */
    640     http_addr_t    *address,		/* I - Address to send to */
    641     int            version,		/* I - SNMP version */
    642     const char     *community,		/* I - Community name */
    643     cups_asn1_t    request_type,	/* I - Request type */
    644     const unsigned request_id,		/* I - Request ID */
    645     const int      *oid)		/* I - OID */
    646 {
    647   int		i;			/* Looping var */
    648   cups_snmp_t	packet;			/* SNMP message packet */
    649   unsigned char	buffer[CUPS_SNMP_MAX_PACKET];
    650 					/* SNMP message buffer */
    651   ssize_t	bytes;			/* Size of message */
    652   http_addr_t	temp;			/* Copy of address */
    653 
    654 
    655  /*
    656   * Range check input...
    657   */
    658 
    659   DEBUG_printf(("4_cupsSNMPWrite(fd=%d, address=%p, version=%d, "
    660                 "community=\"%s\", request_type=%d, request_id=%u, oid=%p)",
    661 		fd, address, version, community, request_type, request_id, oid));
    662 
    663   if (fd < 0 || !address || version != CUPS_SNMP_VERSION_1 || !community ||
    664       (request_type != CUPS_ASN1_GET_REQUEST &&
    665        request_type != CUPS_ASN1_GET_NEXT_REQUEST) || request_id < 1 || !oid)
    666   {
    667     DEBUG_puts("5_cupsSNMPWrite: Returning 0 (bad arguments)");
    668 
    669     return (0);
    670   }
    671 
    672  /*
    673   * Create the SNMP message...
    674   */
    675 
    676   memset(&packet, 0, sizeof(packet));
    677 
    678   packet.version      = version;
    679   packet.request_type = request_type;
    680   packet.request_id   = request_id;
    681   packet.object_type  = CUPS_ASN1_NULL_VALUE;
    682 
    683   strlcpy(packet.community, community, sizeof(packet.community));
    684 
    685   for (i = 0; oid[i] >= 0 && i < (CUPS_SNMP_MAX_OID - 1); i ++)
    686     packet.object_name[i] = oid[i];
    687   packet.object_name[i] = -1;
    688 
    689   if (oid[i] >= 0)
    690   {
    691     DEBUG_puts("5_cupsSNMPWrite: Returning 0 (OID too big)");
    692 
    693     errno = E2BIG;
    694     return (0);
    695   }
    696 
    697   bytes = asn1_encode_snmp(buffer, sizeof(buffer), &packet);
    698 
    699   if (bytes < 0)
    700   {
    701     DEBUG_puts("5_cupsSNMPWrite: Returning 0 (request too big)");
    702 
    703     errno = E2BIG;
    704     return (0);
    705   }
    706 
    707   asn1_debug("DEBUG: OUT ", buffer, (size_t)bytes, 0);
    708 
    709  /*
    710   * Send the message...
    711   */
    712 
    713   temp = *address;
    714 
    715   _httpAddrSetPort(&temp, CUPS_SNMP_PORT);
    716 
    717   return (sendto(fd, buffer, (size_t)bytes, 0, (void *)&temp, (socklen_t)httpAddrLength(&temp)) == bytes);
    718 }
    719 
    720 
    721 /*
    722  * 'asn1_debug()' - Decode an ASN1-encoded message.
    723  */
    724 
    725 static void
    726 asn1_debug(const char    *prefix,	/* I - Prefix string */
    727            unsigned char *buffer,	/* I - Buffer */
    728            size_t        len,		/* I - Length of buffer */
    729            int           indent)	/* I - Indentation */
    730 {
    731   size_t	i;			/* Looping var */
    732   unsigned char	*bufend;		/* End of buffer */
    733   int		integer;		/* Number value */
    734   int		oid[CUPS_SNMP_MAX_OID];	/* OID value */
    735   char		string[CUPS_SNMP_MAX_STRING];
    736 					/* String value */
    737   unsigned char	value_type;		/* Type of value */
    738   unsigned	value_length;		/* Length of value */
    739   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
    740 
    741 
    742   if (cg->snmp_debug <= 0)
    743     return;
    744 
    745   if (cg->snmp_debug > 1 && indent == 0)
    746   {
    747    /*
    748     * Do a hex dump of the packet...
    749     */
    750 
    751     size_t j;
    752 
    753     fprintf(stderr, "%sHex Dump (%d bytes):\n", prefix, (int)len);
    754 
    755     for (i = 0; i < len; i += 16)
    756     {
    757       fprintf(stderr, "%s%04x:", prefix, (unsigned)i);
    758 
    759       for (j = 0; j < 16 && (i + j) < len; j ++)
    760       {
    761         if (j && !(j & 3))
    762 	  fprintf(stderr, "  %02x", buffer[i + j]);
    763         else
    764 	  fprintf(stderr, " %02x", buffer[i + j]);
    765       }
    766 
    767       while (j < 16)
    768       {
    769         if (j && !(j & 3))
    770 	  fputs("    ", stderr);
    771 	else
    772 	  fputs("   ", stderr);
    773 
    774         j ++;
    775       }
    776 
    777       fputs("    ", stderr);
    778 
    779       for (j = 0; j < 16 && (i + j) < len; j ++)
    780         if (buffer[i + j] < ' ' || buffer[i + j] >= 0x7f)
    781 	  putc('.', stderr);
    782 	else
    783 	  putc(buffer[i + j], stderr);
    784 
    785       putc('\n', stderr);
    786     }
    787   }
    788 
    789   if (indent == 0)
    790     fprintf(stderr, "%sMessage:\n", prefix);
    791 
    792   bufend = buffer + len;
    793 
    794   while (buffer < bufend)
    795   {
    796    /*
    797     * Get value type...
    798     */
    799 
    800     value_type   = (unsigned char)asn1_get_type(&buffer, bufend);
    801     value_length = asn1_get_length(&buffer, bufend);
    802 
    803     switch (value_type)
    804     {
    805       case CUPS_ASN1_BOOLEAN :
    806           integer = asn1_get_integer(&buffer, bufend, value_length);
    807 
    808           fprintf(stderr, "%s%*sBOOLEAN %d bytes %d\n", prefix, indent, "",
    809 	          value_length, integer);
    810           break;
    811 
    812       case CUPS_ASN1_INTEGER :
    813           integer = asn1_get_integer(&buffer, bufend, value_length);
    814 
    815           fprintf(stderr, "%s%*sINTEGER %d bytes %d\n", prefix, indent, "",
    816 	          value_length, integer);
    817           break;
    818 
    819       case CUPS_ASN1_COUNTER :
    820           integer = asn1_get_integer(&buffer, bufend, value_length);
    821 
    822           fprintf(stderr, "%s%*sCOUNTER %d bytes %u\n", prefix, indent, "",
    823 	          value_length, (unsigned)integer);
    824           break;
    825 
    826       case CUPS_ASN1_GAUGE :
    827           integer = asn1_get_integer(&buffer, bufend, value_length);
    828 
    829           fprintf(stderr, "%s%*sGAUGE %d bytes %u\n", prefix, indent, "",
    830 	          value_length, (unsigned)integer);
    831           break;
    832 
    833       case CUPS_ASN1_TIMETICKS :
    834           integer = asn1_get_integer(&buffer, bufend, value_length);
    835 
    836           fprintf(stderr, "%s%*sTIMETICKS %d bytes %u\n", prefix, indent, "",
    837 	          value_length, (unsigned)integer);
    838           break;
    839 
    840       case CUPS_ASN1_OCTET_STRING :
    841           fprintf(stderr, "%s%*sOCTET STRING %d bytes \"%s\"\n", prefix,
    842 	          indent, "", value_length,
    843 		  asn1_get_string(&buffer, bufend, value_length, string,
    844 				  sizeof(string)));
    845           break;
    846 
    847       case CUPS_ASN1_HEX_STRING :
    848 	  asn1_get_string(&buffer, bufend, value_length, string,
    849 			  sizeof(string));
    850           fprintf(stderr, "%s%*sHex-STRING %d bytes", prefix,
    851 	          indent, "", value_length);
    852           for (i = 0; i < value_length; i ++)
    853 	    fprintf(stderr, " %02X", string[i] & 255);
    854 	  putc('\n', stderr);
    855           break;
    856 
    857       case CUPS_ASN1_NULL_VALUE :
    858           fprintf(stderr, "%s%*sNULL VALUE %d bytes\n", prefix, indent, "",
    859 	          value_length);
    860 
    861 	  buffer += value_length;
    862           break;
    863 
    864       case CUPS_ASN1_OID :
    865           integer = asn1_get_oid(&buffer, bufend, value_length, oid,
    866 	                         CUPS_SNMP_MAX_OID);
    867 
    868           fprintf(stderr, "%s%*sOID %d bytes ", prefix, indent, "",
    869 	          value_length);
    870 	  for (i = 0; i < (unsigned)integer; i ++)
    871 	    fprintf(stderr, ".%d", oid[i]);
    872 	  putc('\n', stderr);
    873           break;
    874 
    875       case CUPS_ASN1_SEQUENCE :
    876           fprintf(stderr, "%s%*sSEQUENCE %d bytes\n", prefix, indent, "",
    877 	          value_length);
    878           asn1_debug(prefix, buffer, value_length, indent + 4);
    879 
    880 	  buffer += value_length;
    881           break;
    882 
    883       case CUPS_ASN1_GET_NEXT_REQUEST :
    884           fprintf(stderr, "%s%*sGet-Next-Request-PDU %d bytes\n", prefix,
    885 	          indent, "", value_length);
    886           asn1_debug(prefix, buffer, value_length, indent + 4);
    887 
    888 	  buffer += value_length;
    889           break;
    890 
    891       case CUPS_ASN1_GET_REQUEST :
    892           fprintf(stderr, "%s%*sGet-Request-PDU %d bytes\n", prefix, indent, "",
    893 	          value_length);
    894           asn1_debug(prefix, buffer, value_length, indent + 4);
    895 
    896 	  buffer += value_length;
    897           break;
    898 
    899       case CUPS_ASN1_GET_RESPONSE :
    900           fprintf(stderr, "%s%*sGet-Response-PDU %d bytes\n", prefix, indent,
    901 	          "", value_length);
    902           asn1_debug(prefix, buffer, value_length, indent + 4);
    903 
    904 	  buffer += value_length;
    905           break;
    906 
    907       default :
    908           fprintf(stderr, "%s%*sUNKNOWN(%x) %d bytes\n", prefix, indent, "",
    909 	          value_type, value_length);
    910 
    911 	  buffer += value_length;
    912           break;
    913     }
    914   }
    915 }
    916 
    917 
    918 /*
    919  * 'asn1_decode_snmp()' - Decode a SNMP packet.
    920  */
    921 
    922 static int				/* O - 0 on success, -1 on error */
    923 asn1_decode_snmp(unsigned char *buffer,	/* I - Buffer */
    924                  size_t        len,	/* I - Size of buffer */
    925                  cups_snmp_t   *packet)	/* I - SNMP packet */
    926 {
    927   unsigned char	*bufptr,		/* Pointer into the data */
    928 		*bufend;		/* End of data */
    929   unsigned	length;			/* Length of value */
    930 
    931 
    932  /*
    933   * Initialize the decoding...
    934   */
    935 
    936   memset(packet, 0, sizeof(cups_snmp_t));
    937   packet->object_name[0] = -1;
    938 
    939   bufptr = buffer;
    940   bufend = buffer + len;
    941 
    942   if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
    943     snmp_set_error(packet, _("Packet does not start with SEQUENCE"));
    944   else if (asn1_get_length(&bufptr, bufend) == 0)
    945     snmp_set_error(packet, _("SEQUENCE uses indefinite length"));
    946   else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    947     snmp_set_error(packet, _("No version number"));
    948   else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    949     snmp_set_error(packet, _("Version uses indefinite length"));
    950   else if ((packet->version = asn1_get_integer(&bufptr, bufend, length))
    951                != CUPS_SNMP_VERSION_1)
    952     snmp_set_error(packet, _("Bad SNMP version number"));
    953   else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_OCTET_STRING)
    954     snmp_set_error(packet, _("No community name"));
    955   else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    956     snmp_set_error(packet, _("Community name uses indefinite length"));
    957   else
    958   {
    959     asn1_get_string(&bufptr, bufend, length, packet->community,
    960                     sizeof(packet->community));
    961 
    962     if ((packet->request_type = (cups_asn1_t)asn1_get_type(&bufptr, bufend))
    963             != CUPS_ASN1_GET_RESPONSE)
    964       snmp_set_error(packet, _("Packet does not contain a Get-Response-PDU"));
    965     else if (asn1_get_length(&bufptr, bufend) == 0)
    966       snmp_set_error(packet, _("Get-Response-PDU uses indefinite length"));
    967     else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    968       snmp_set_error(packet, _("No request-id"));
    969     else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    970       snmp_set_error(packet, _("request-id uses indefinite length"));
    971     else
    972     {
    973       packet->request_id = (unsigned)asn1_get_integer(&bufptr, bufend, length);
    974 
    975       if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    976 	snmp_set_error(packet, _("No error-status"));
    977       else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    978 	snmp_set_error(packet, _("error-status uses indefinite length"));
    979       else
    980       {
    981 	packet->error_status = asn1_get_integer(&bufptr, bufend, length);
    982 
    983 	if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    984 	  snmp_set_error(packet, _("No error-index"));
    985 	else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    986 	  snmp_set_error(packet, _("error-index uses indefinite length"));
    987 	else
    988 	{
    989 	  packet->error_index = asn1_get_integer(&bufptr, bufend, length);
    990 
    991           if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
    992 	    snmp_set_error(packet, _("No variable-bindings SEQUENCE"));
    993 	  else if (asn1_get_length(&bufptr, bufend) == 0)
    994 	    snmp_set_error(packet,
    995 	                   _("variable-bindings uses indefinite length"));
    996 	  else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
    997 	    snmp_set_error(packet, _("No VarBind SEQUENCE"));
    998 	  else if (asn1_get_length(&bufptr, bufend) == 0)
    999 	    snmp_set_error(packet, _("VarBind uses indefinite length"));
   1000 	  else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_OID)
   1001 	    snmp_set_error(packet, _("No name OID"));
   1002 	  else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
   1003 	    snmp_set_error(packet, _("Name OID uses indefinite length"));
   1004           else
   1005 	  {
   1006 	    asn1_get_oid(&bufptr, bufend, length, packet->object_name,
   1007 	                 CUPS_SNMP_MAX_OID);
   1008 
   1009             packet->object_type = (cups_asn1_t)asn1_get_type(&bufptr, bufend);
   1010 
   1011 	    if ((length = asn1_get_length(&bufptr, bufend)) == 0 &&
   1012 	        packet->object_type != CUPS_ASN1_NULL_VALUE &&
   1013 	        packet->object_type != CUPS_ASN1_OCTET_STRING)
   1014 	      snmp_set_error(packet, _("Value uses indefinite length"));
   1015 	    else
   1016 	    {
   1017 	      switch (packet->object_type)
   1018 	      {
   1019 	        case CUPS_ASN1_BOOLEAN :
   1020 		    packet->object_value.boolean =
   1021 		        asn1_get_integer(&bufptr, bufend, length);
   1022 	            break;
   1023 
   1024 	        case CUPS_ASN1_INTEGER :
   1025 		    packet->object_value.integer =
   1026 		        asn1_get_integer(&bufptr, bufend, length);
   1027 	            break;
   1028 
   1029 		case CUPS_ASN1_NULL_VALUE :
   1030 		    break;
   1031 
   1032 	        case CUPS_ASN1_OCTET_STRING :
   1033 	        case CUPS_ASN1_BIT_STRING :
   1034 	        case CUPS_ASN1_HEX_STRING :
   1035 		    packet->object_value.string.num_bytes = length;
   1036 		    asn1_get_string(&bufptr, bufend, length,
   1037 		                    (char *)packet->object_value.string.bytes,
   1038 				    sizeof(packet->object_value.string.bytes));
   1039 	            break;
   1040 
   1041 	        case CUPS_ASN1_OID :
   1042 		    asn1_get_oid(&bufptr, bufend, length,
   1043 		                 packet->object_value.oid, CUPS_SNMP_MAX_OID);
   1044 	            break;
   1045 
   1046 	        case CUPS_ASN1_COUNTER :
   1047 		    packet->object_value.counter =
   1048 		        asn1_get_integer(&bufptr, bufend, length);
   1049 	            break;
   1050 
   1051 	        case CUPS_ASN1_GAUGE :
   1052 		    packet->object_value.gauge =
   1053 		        (unsigned)asn1_get_integer(&bufptr, bufend, length);
   1054 	            break;
   1055 
   1056 	        case CUPS_ASN1_TIMETICKS :
   1057 		    packet->object_value.timeticks =
   1058 		        (unsigned)asn1_get_integer(&bufptr, bufend, length);
   1059 	            break;
   1060 
   1061                 default :
   1062 		    snmp_set_error(packet, _("Unsupported value type"));
   1063 		    break;
   1064 	      }
   1065 	    }
   1066           }
   1067 	}
   1068       }
   1069     }
   1070   }
   1071 
   1072   return (packet->error ? -1 : 0);
   1073 }
   1074 
   1075 
   1076 /*
   1077  * 'asn1_encode_snmp()' - Encode a SNMP packet.
   1078  */
   1079 
   1080 static int				/* O - Length on success, -1 on error */
   1081 asn1_encode_snmp(unsigned char *buffer,	/* I - Buffer */
   1082                  size_t        bufsize,	/* I - Size of buffer */
   1083                  cups_snmp_t   *packet)	/* I - SNMP packet */
   1084 {
   1085   unsigned char	*bufptr;		/* Pointer into buffer */
   1086   unsigned	total,			/* Total length */
   1087 		msglen,			/* Length of entire message */
   1088 		commlen,		/* Length of community string */
   1089 		reqlen,			/* Length of request */
   1090 		listlen,		/* Length of variable list */
   1091 		varlen,			/* Length of variable */
   1092 		namelen,		/* Length of object name OID */
   1093 		valuelen;		/* Length of object value */
   1094 
   1095 
   1096  /*
   1097   * Get the lengths of the community string, OID, and message...
   1098   */
   1099 
   1100 
   1101   namelen = asn1_size_oid(packet->object_name);
   1102 
   1103   switch (packet->object_type)
   1104   {
   1105     case CUPS_ASN1_NULL_VALUE :
   1106         valuelen = 0;
   1107 	break;
   1108 
   1109     case CUPS_ASN1_BOOLEAN :
   1110         valuelen = asn1_size_integer(packet->object_value.boolean);
   1111 	break;
   1112 
   1113     case CUPS_ASN1_INTEGER :
   1114         valuelen = asn1_size_integer(packet->object_value.integer);
   1115 	break;
   1116 
   1117     case CUPS_ASN1_OCTET_STRING :
   1118         valuelen = packet->object_value.string.num_bytes;
   1119 	break;
   1120 
   1121     case CUPS_ASN1_OID :
   1122         valuelen = asn1_size_oid(packet->object_value.oid);
   1123 	break;
   1124 
   1125     default :
   1126         packet->error = "Unknown object type";
   1127         return (-1);
   1128   }
   1129 
   1130   varlen  = 1 + asn1_size_length(namelen) + namelen +
   1131             1 + asn1_size_length(valuelen) + valuelen;
   1132   listlen = 1 + asn1_size_length(varlen) + varlen;
   1133   reqlen  = 2 + asn1_size_integer((int)packet->request_id) +
   1134             2 + asn1_size_integer(packet->error_status) +
   1135             2 + asn1_size_integer(packet->error_index) +
   1136             1 + asn1_size_length(listlen) + listlen;
   1137   commlen = (unsigned)strlen(packet->community);
   1138   msglen  = 2 + asn1_size_integer(packet->version) +
   1139             1 + asn1_size_length(commlen) + commlen +
   1140 	    1 + asn1_size_length(reqlen) + reqlen;
   1141   total   = 1 + asn1_size_length(msglen) + msglen;
   1142 
   1143   if (total > bufsize)
   1144   {
   1145     packet->error = "Message too large for buffer";
   1146     return (-1);
   1147   }
   1148 
   1149  /*
   1150   * Then format the message...
   1151   */
   1152 
   1153   bufptr = buffer;
   1154 
   1155   *bufptr++ = CUPS_ASN1_SEQUENCE;	/* SNMPv1 message header */
   1156   asn1_set_length(&bufptr, msglen);
   1157 
   1158   asn1_set_integer(&bufptr, packet->version);
   1159 					/* version */
   1160 
   1161   *bufptr++ = CUPS_ASN1_OCTET_STRING;	/* community */
   1162   asn1_set_length(&bufptr, commlen);
   1163   memcpy(bufptr, packet->community, commlen);
   1164   bufptr += commlen;
   1165 
   1166   *bufptr++ = packet->request_type;	/* Get-Request-PDU/Get-Next-Request-PDU */
   1167   asn1_set_length(&bufptr, reqlen);
   1168 
   1169   asn1_set_integer(&bufptr, (int)packet->request_id);
   1170 
   1171   asn1_set_integer(&bufptr, packet->error_status);
   1172 
   1173   asn1_set_integer(&bufptr, packet->error_index);
   1174 
   1175   *bufptr++ = CUPS_ASN1_SEQUENCE;	/* variable-bindings */
   1176   asn1_set_length(&bufptr, listlen);
   1177 
   1178   *bufptr++ = CUPS_ASN1_SEQUENCE;	/* variable */
   1179   asn1_set_length(&bufptr, varlen);
   1180 
   1181   asn1_set_oid(&bufptr, packet->object_name);
   1182 					/* ObjectName */
   1183 
   1184   switch (packet->object_type)
   1185   {
   1186     case CUPS_ASN1_NULL_VALUE :
   1187 	*bufptr++ = CUPS_ASN1_NULL_VALUE;
   1188 					/* ObjectValue */
   1189 	*bufptr++ = 0;			/* Length */
   1190         break;
   1191 
   1192     case CUPS_ASN1_BOOLEAN :
   1193         asn1_set_integer(&bufptr, packet->object_value.boolean);
   1194 	break;
   1195 
   1196     case CUPS_ASN1_INTEGER :
   1197         asn1_set_integer(&bufptr, packet->object_value.integer);
   1198 	break;
   1199 
   1200     case CUPS_ASN1_OCTET_STRING :
   1201         *bufptr++ = CUPS_ASN1_OCTET_STRING;
   1202 	asn1_set_length(&bufptr, valuelen);
   1203 	memcpy(bufptr, packet->object_value.string.bytes, valuelen);
   1204 	bufptr += valuelen;
   1205 	break;
   1206 
   1207     case CUPS_ASN1_OID :
   1208         asn1_set_oid(&bufptr, packet->object_value.oid);
   1209 	break;
   1210 
   1211     default :
   1212         break;
   1213   }
   1214 
   1215   return ((int)(bufptr - buffer));
   1216 }
   1217 
   1218 
   1219 /*
   1220  * 'asn1_get_integer()' - Get an integer value.
   1221  */
   1222 
   1223 static int				/* O  - Integer value */
   1224 asn1_get_integer(
   1225     unsigned char **buffer,		/* IO - Pointer in buffer */
   1226     unsigned char *bufend,		/* I  - End of buffer */
   1227     unsigned      length)		/* I  - Length of value */
   1228 {
   1229   int	value;				/* Integer value */
   1230 
   1231 
   1232   if (length > sizeof(int))
   1233   {
   1234     (*buffer) += length;
   1235     return (0);
   1236   }
   1237 
   1238   for (value = (**buffer & 0x80) ? -1 : 0;
   1239        length > 0 && *buffer < bufend;
   1240        length --, (*buffer) ++)
   1241     value = (value << 8) | **buffer;
   1242 
   1243   return (value);
   1244 }
   1245 
   1246 
   1247 /*
   1248  * 'asn1_get_length()' - Get a value length.
   1249  */
   1250 
   1251 static unsigned				/* O  - Length */
   1252 asn1_get_length(unsigned char **buffer,	/* IO - Pointer in buffer */
   1253 		unsigned char *bufend)	/* I  - End of buffer */
   1254 {
   1255   unsigned	length;			/* Length */
   1256 
   1257 
   1258   length = **buffer;
   1259   (*buffer) ++;
   1260 
   1261   if (length & 128)
   1262   {
   1263     int	count;				/* Number of bytes for length */
   1264 
   1265 
   1266     if ((count = length & 127) > sizeof(unsigned))
   1267     {
   1268       (*buffer) += count;
   1269       return (0);
   1270     }
   1271 
   1272     for (length = 0;
   1273 	 count > 0 && *buffer < bufend;
   1274 	 count --, (*buffer) ++)
   1275       length = (length << 8) | **buffer;
   1276   }
   1277 
   1278   return (length);
   1279 }
   1280 
   1281 
   1282 /*
   1283  * 'asn1_get_oid()' - Get an OID value.
   1284  */
   1285 
   1286 static int				/* O  - Number of OIDs */
   1287 asn1_get_oid(
   1288     unsigned char **buffer,		/* IO - Pointer in buffer */
   1289     unsigned char *bufend,		/* I  - End of buffer */
   1290     unsigned      length,		/* I  - Length of value */
   1291     int           *oid,			/* I  - OID buffer */
   1292     int           oidsize)		/* I  - Size of OID buffer */
   1293 {
   1294   unsigned char	*valend;		/* End of value */
   1295   int		*oidptr,		/* Current OID */
   1296 		*oidend;		/* End of OID buffer */
   1297   int		number;			/* OID number */
   1298 
   1299 
   1300   valend = *buffer + length;
   1301   oidptr = oid;
   1302   oidend = oid + oidsize - 1;
   1303 
   1304   if (valend > bufend)
   1305     valend = bufend;
   1306 
   1307   number = asn1_get_packed(buffer, bufend);
   1308 
   1309   if (number < 80)
   1310   {
   1311     *oidptr++ = number / 40;
   1312     number    = number % 40;
   1313     *oidptr++ = number;
   1314   }
   1315   else
   1316   {
   1317     *oidptr++ = 2;
   1318     number    -= 80;
   1319     *oidptr++ = number;
   1320   }
   1321 
   1322   while (*buffer < valend)
   1323   {
   1324     number = asn1_get_packed(buffer, bufend);
   1325 
   1326     if (oidptr < oidend)
   1327       *oidptr++ = number;
   1328   }
   1329 
   1330   *oidptr = -1;
   1331 
   1332   return ((int)(oidptr - oid));
   1333 }
   1334 
   1335 
   1336 /*
   1337  * 'asn1_get_packed()' - Get a packed integer value.
   1338  */
   1339 
   1340 static int				/* O  - Value */
   1341 asn1_get_packed(
   1342     unsigned char **buffer,		/* IO - Pointer in buffer */
   1343     unsigned char *bufend)		/* I  - End of buffer */
   1344 {
   1345   int	value;				/* Value */
   1346 
   1347 
   1348   value = 0;
   1349 
   1350   while ((**buffer & 128) && *buffer < bufend)
   1351   {
   1352     value = (value << 7) | (**buffer & 127);
   1353     (*buffer) ++;
   1354   }
   1355 
   1356   if (*buffer < bufend)
   1357   {
   1358     value = (value << 7) | **buffer;
   1359     (*buffer) ++;
   1360   }
   1361 
   1362   return (value);
   1363 }
   1364 
   1365 
   1366 /*
   1367  * 'asn1_get_string()' - Get a string value.
   1368  */
   1369 
   1370 static char *				/* O  - String */
   1371 asn1_get_string(
   1372     unsigned char **buffer,		/* IO - Pointer in buffer */
   1373     unsigned char *bufend,		/* I  - End of buffer */
   1374     unsigned      length,		/* I  - Value length */
   1375     char          *string,		/* I  - String buffer */
   1376     size_t        strsize)		/* I  - String buffer size */
   1377 {
   1378   if (length > (unsigned)(bufend - *buffer))
   1379     length = (unsigned)(bufend - *buffer);
   1380 
   1381   if (length < strsize)
   1382   {
   1383    /*
   1384     * String is smaller than the buffer...
   1385     */
   1386 
   1387     if (length > 0)
   1388       memcpy(string, *buffer, length);
   1389 
   1390     string[length] = '\0';
   1391   }
   1392   else
   1393   {
   1394    /*
   1395     * String is larger than the buffer...
   1396     */
   1397 
   1398     memcpy(string, *buffer, strsize - 1);
   1399     string[strsize - 1] = '\0';
   1400   }
   1401 
   1402   if (length > 0)
   1403     (*buffer) += length;
   1404 
   1405   return (string);
   1406 }
   1407 
   1408 
   1409 /*
   1410  * 'asn1_get_type()' - Get a value type.
   1411  */
   1412 
   1413 static int				/* O  - Type */
   1414 asn1_get_type(unsigned char **buffer,	/* IO - Pointer in buffer */
   1415 	      unsigned char *bufend)	/* I  - End of buffer */
   1416 {
   1417   int	type;				/* Type */
   1418 
   1419 
   1420   type = **buffer;
   1421   (*buffer) ++;
   1422 
   1423   if ((type & 31) == 31)
   1424     type = asn1_get_packed(buffer, bufend);
   1425 
   1426   return (type);
   1427 }
   1428 
   1429 
   1430 /*
   1431  * 'asn1_set_integer()' - Set an integer value.
   1432  */
   1433 
   1434 static void
   1435 asn1_set_integer(unsigned char **buffer,/* IO - Pointer in buffer */
   1436                  int           integer)	/* I  - Integer value */
   1437 {
   1438   **buffer = CUPS_ASN1_INTEGER;
   1439   (*buffer) ++;
   1440 
   1441   if (integer > 0x7fffff || integer < -0x800000)
   1442   {
   1443     **buffer = 4;
   1444     (*buffer) ++;
   1445     **buffer = (unsigned char)(integer >> 24);
   1446     (*buffer) ++;
   1447     **buffer = (unsigned char)(integer >> 16);
   1448     (*buffer) ++;
   1449     **buffer = (unsigned char)(integer >> 8);
   1450     (*buffer) ++;
   1451     **buffer = (unsigned char)integer;
   1452     (*buffer) ++;
   1453   }
   1454   else if (integer > 0x7fff || integer < -0x8000)
   1455   {
   1456     **buffer = 3;
   1457     (*buffer) ++;
   1458     **buffer = (unsigned char)(integer >> 16);
   1459     (*buffer) ++;
   1460     **buffer = (unsigned char)(integer >> 8);
   1461     (*buffer) ++;
   1462     **buffer = (unsigned char)integer;
   1463     (*buffer) ++;
   1464   }
   1465   else if (integer > 0x7f || integer < -0x80)
   1466   {
   1467     **buffer = 2;
   1468     (*buffer) ++;
   1469     **buffer = (unsigned char)(integer >> 8);
   1470     (*buffer) ++;
   1471     **buffer = (unsigned char)integer;
   1472     (*buffer) ++;
   1473   }
   1474   else
   1475   {
   1476     **buffer = 1;
   1477     (*buffer) ++;
   1478     **buffer = (unsigned char)integer;
   1479     (*buffer) ++;
   1480   }
   1481 }
   1482 
   1483 
   1484 /*
   1485  * 'asn1_set_length()' - Set a value length.
   1486  */
   1487 
   1488 static void
   1489 asn1_set_length(unsigned char **buffer,	/* IO - Pointer in buffer */
   1490 		unsigned      length)	/* I  - Length value */
   1491 {
   1492   if (length > 255)
   1493   {
   1494     **buffer = 0x82;			/* 2-byte length */
   1495     (*buffer) ++;
   1496     **buffer = (unsigned char)(length >> 8);
   1497     (*buffer) ++;
   1498     **buffer = (unsigned char)length;
   1499     (*buffer) ++;
   1500   }
   1501   else if (length > 127)
   1502   {
   1503     **buffer = 0x81;			/* 1-byte length */
   1504     (*buffer) ++;
   1505     **buffer = (unsigned char)length;
   1506     (*buffer) ++;
   1507   }
   1508   else
   1509   {
   1510     **buffer = (unsigned char)length;	/* Length */
   1511     (*buffer) ++;
   1512   }
   1513 }
   1514 
   1515 
   1516 /*
   1517  * 'asn1_set_oid()' - Set an OID value.
   1518  */
   1519 
   1520 static void
   1521 asn1_set_oid(unsigned char **buffer,	/* IO - Pointer in buffer */
   1522              const int     *oid)	/* I  - OID value */
   1523 {
   1524   **buffer = CUPS_ASN1_OID;
   1525   (*buffer) ++;
   1526 
   1527   asn1_set_length(buffer, asn1_size_oid(oid));
   1528 
   1529   if (oid[1] < 0)
   1530   {
   1531     asn1_set_packed(buffer, oid[0] * 40);
   1532     return;
   1533   }
   1534 
   1535   asn1_set_packed(buffer, oid[0] * 40 + oid[1]);
   1536 
   1537   for (oid += 2; *oid >= 0; oid ++)
   1538     asn1_set_packed(buffer, *oid);
   1539 }
   1540 
   1541 
   1542 /*
   1543  * 'asn1_set_packed()' - Set a packed integer value.
   1544  */
   1545 
   1546 static void
   1547 asn1_set_packed(unsigned char **buffer,	/* IO - Pointer in buffer */
   1548 		int           integer)	/* I  - Integer value */
   1549 {
   1550   if (integer > 0xfffffff)
   1551   {
   1552     **buffer = ((integer >> 28) & 0x7f) | 0x80;
   1553     (*buffer) ++;
   1554   }
   1555 
   1556   if (integer > 0x1fffff)
   1557   {
   1558     **buffer = ((integer >> 21) & 0x7f) | 0x80;
   1559     (*buffer) ++;
   1560   }
   1561 
   1562   if (integer > 0x3fff)
   1563   {
   1564     **buffer = ((integer >> 14) & 0x7f) | 0x80;
   1565     (*buffer) ++;
   1566   }
   1567 
   1568   if (integer > 0x7f)
   1569   {
   1570     **buffer = ((integer >> 7) & 0x7f) | 0x80;
   1571     (*buffer) ++;
   1572   }
   1573 
   1574   **buffer = integer & 0x7f;
   1575   (*buffer) ++;
   1576 }
   1577 
   1578 
   1579 /*
   1580  * 'asn1_size_integer()' - Figure out the number of bytes needed for an
   1581  *                         integer value.
   1582  */
   1583 
   1584 static unsigned				/* O - Size in bytes */
   1585 asn1_size_integer(int integer)		/* I - Integer value */
   1586 {
   1587   if (integer > 0x7fffff || integer < -0x800000)
   1588     return (4);
   1589   else if (integer > 0x7fff || integer < -0x8000)
   1590     return (3);
   1591   else if (integer > 0x7f || integer < -0x80)
   1592     return (2);
   1593   else
   1594     return (1);
   1595 }
   1596 
   1597 
   1598 /*
   1599  * 'asn1_size_length()' - Figure out the number of bytes needed for a
   1600  *                        length value.
   1601  */
   1602 
   1603 static unsigned				/* O - Size in bytes */
   1604 asn1_size_length(unsigned length)	/* I - Length value */
   1605 {
   1606   if (length > 0xff)
   1607     return (3);
   1608   else if (length > 0x7f)
   1609     return (2);
   1610   else
   1611     return (1);
   1612 }
   1613 
   1614 
   1615 /*
   1616  * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an
   1617  *                     OID value.
   1618  */
   1619 
   1620 static unsigned				/* O - Size in bytes */
   1621 asn1_size_oid(const int *oid)		/* I - OID value */
   1622 {
   1623   unsigned	length;			/* Length of value */
   1624 
   1625 
   1626   if (oid[1] < 0)
   1627     return (asn1_size_packed(oid[0] * 40));
   1628 
   1629   for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2;
   1630        *oid >= 0;
   1631        oid ++)
   1632     length += asn1_size_packed(*oid);
   1633 
   1634   return (length);
   1635 }
   1636 
   1637 
   1638 /*
   1639  * 'asn1_size_packed()' - Figure out the number of bytes needed for a
   1640  *                        packed integer value.
   1641  */
   1642 
   1643 static unsigned				/* O - Size in bytes */
   1644 asn1_size_packed(int integer)		/* I - Integer value */
   1645 {
   1646   if (integer > 0xfffffff)
   1647     return (5);
   1648   else if (integer > 0x1fffff)
   1649     return (4);
   1650   else if (integer > 0x3fff)
   1651     return (3);
   1652   else if (integer > 0x7f)
   1653     return (2);
   1654   else
   1655     return (1);
   1656 }
   1657 
   1658 
   1659 /*
   1660  * 'snmp_set_error()' - Set the localized error for a packet.
   1661  */
   1662 
   1663 static void
   1664 snmp_set_error(cups_snmp_t *packet,	/* I - Packet */
   1665                const char *message)	/* I - Error message */
   1666 {
   1667   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
   1668 
   1669 
   1670   if (!cg->lang_default)
   1671     cg->lang_default = cupsLangDefault();
   1672 
   1673   packet->error = _cupsLangString(cg->lang_default, message);
   1674 }
   1675