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 #ifdef __clang_analyzer__ /* Suppress bogus clang error */
    743   memset(string, 0, sizeof(string));
    744 #endif /* __clang_analyzer__ */
    745 
    746   if (cg->snmp_debug <= 0)
    747     return;
    748 
    749   if (cg->snmp_debug > 1 && indent == 0)
    750   {
    751    /*
    752     * Do a hex dump of the packet...
    753     */
    754 
    755     size_t j;
    756 
    757     fprintf(stderr, "%sHex Dump (%d bytes):\n", prefix, (int)len);
    758 
    759     for (i = 0; i < len; i += 16)
    760     {
    761       fprintf(stderr, "%s%04x:", prefix, (unsigned)i);
    762 
    763       for (j = 0; j < 16 && (i + j) < len; j ++)
    764       {
    765         if (j && !(j & 3))
    766 	  fprintf(stderr, "  %02x", buffer[i + j]);
    767         else
    768 	  fprintf(stderr, " %02x", buffer[i + j]);
    769       }
    770 
    771       while (j < 16)
    772       {
    773         if (j && !(j & 3))
    774 	  fputs("    ", stderr);
    775 	else
    776 	  fputs("   ", stderr);
    777 
    778         j ++;
    779       }
    780 
    781       fputs("    ", stderr);
    782 
    783       for (j = 0; j < 16 && (i + j) < len; j ++)
    784         if (buffer[i + j] < ' ' || buffer[i + j] >= 0x7f)
    785 	  putc('.', stderr);
    786 	else
    787 	  putc(buffer[i + j], stderr);
    788 
    789       putc('\n', stderr);
    790     }
    791   }
    792 
    793   if (indent == 0)
    794     fprintf(stderr, "%sMessage:\n", prefix);
    795 
    796   bufend = buffer + len;
    797 
    798   while (buffer < bufend)
    799   {
    800    /*
    801     * Get value type...
    802     */
    803 
    804     value_type   = (unsigned char)asn1_get_type(&buffer, bufend);
    805     value_length = asn1_get_length(&buffer, bufend);
    806 
    807     switch (value_type)
    808     {
    809       case CUPS_ASN1_BOOLEAN :
    810           integer = asn1_get_integer(&buffer, bufend, value_length);
    811 
    812           fprintf(stderr, "%s%*sBOOLEAN %d bytes %d\n", prefix, indent, "",
    813 	          value_length, integer);
    814           break;
    815 
    816       case CUPS_ASN1_INTEGER :
    817           integer = asn1_get_integer(&buffer, bufend, value_length);
    818 
    819           fprintf(stderr, "%s%*sINTEGER %d bytes %d\n", prefix, indent, "",
    820 	          value_length, integer);
    821           break;
    822 
    823       case CUPS_ASN1_COUNTER :
    824           integer = asn1_get_integer(&buffer, bufend, value_length);
    825 
    826           fprintf(stderr, "%s%*sCOUNTER %d bytes %u\n", prefix, indent, "",
    827 	          value_length, (unsigned)integer);
    828           break;
    829 
    830       case CUPS_ASN1_GAUGE :
    831           integer = asn1_get_integer(&buffer, bufend, value_length);
    832 
    833           fprintf(stderr, "%s%*sGAUGE %d bytes %u\n", prefix, indent, "",
    834 	          value_length, (unsigned)integer);
    835           break;
    836 
    837       case CUPS_ASN1_TIMETICKS :
    838           integer = asn1_get_integer(&buffer, bufend, value_length);
    839 
    840           fprintf(stderr, "%s%*sTIMETICKS %d bytes %u\n", prefix, indent, "",
    841 	          value_length, (unsigned)integer);
    842           break;
    843 
    844       case CUPS_ASN1_OCTET_STRING :
    845           fprintf(stderr, "%s%*sOCTET STRING %d bytes \"%s\"\n", prefix,
    846 	          indent, "", value_length,
    847 		  asn1_get_string(&buffer, bufend, value_length, string,
    848 				  sizeof(string)));
    849           break;
    850 
    851       case CUPS_ASN1_HEX_STRING :
    852 	  asn1_get_string(&buffer, bufend, value_length, string,
    853 			  sizeof(string));
    854           fprintf(stderr, "%s%*sHex-STRING %d bytes", prefix,
    855 	          indent, "", value_length);
    856           for (i = 0; i < value_length; i ++)
    857 	    fprintf(stderr, " %02X", string[i] & 255);
    858 	  putc('\n', stderr);
    859           break;
    860 
    861       case CUPS_ASN1_NULL_VALUE :
    862           fprintf(stderr, "%s%*sNULL VALUE %d bytes\n", prefix, indent, "",
    863 	          value_length);
    864 
    865 	  buffer += value_length;
    866           break;
    867 
    868       case CUPS_ASN1_OID :
    869           integer = asn1_get_oid(&buffer, bufend, value_length, oid,
    870 	                         CUPS_SNMP_MAX_OID);
    871 
    872           fprintf(stderr, "%s%*sOID %d bytes ", prefix, indent, "",
    873 	          value_length);
    874 	  for (i = 0; i < (unsigned)integer; i ++)
    875 	    fprintf(stderr, ".%d", oid[i]);
    876 	  putc('\n', stderr);
    877           break;
    878 
    879       case CUPS_ASN1_SEQUENCE :
    880           fprintf(stderr, "%s%*sSEQUENCE %d bytes\n", prefix, indent, "",
    881 	          value_length);
    882           asn1_debug(prefix, buffer, value_length, indent + 4);
    883 
    884 	  buffer += value_length;
    885           break;
    886 
    887       case CUPS_ASN1_GET_NEXT_REQUEST :
    888           fprintf(stderr, "%s%*sGet-Next-Request-PDU %d bytes\n", prefix,
    889 	          indent, "", value_length);
    890           asn1_debug(prefix, buffer, value_length, indent + 4);
    891 
    892 	  buffer += value_length;
    893           break;
    894 
    895       case CUPS_ASN1_GET_REQUEST :
    896           fprintf(stderr, "%s%*sGet-Request-PDU %d bytes\n", prefix, indent, "",
    897 	          value_length);
    898           asn1_debug(prefix, buffer, value_length, indent + 4);
    899 
    900 	  buffer += value_length;
    901           break;
    902 
    903       case CUPS_ASN1_GET_RESPONSE :
    904           fprintf(stderr, "%s%*sGet-Response-PDU %d bytes\n", prefix, indent,
    905 	          "", value_length);
    906           asn1_debug(prefix, buffer, value_length, indent + 4);
    907 
    908 	  buffer += value_length;
    909           break;
    910 
    911       default :
    912           fprintf(stderr, "%s%*sUNKNOWN(%x) %d bytes\n", prefix, indent, "",
    913 	          value_type, value_length);
    914 
    915 	  buffer += value_length;
    916           break;
    917     }
    918   }
    919 }
    920 
    921 
    922 /*
    923  * 'asn1_decode_snmp()' - Decode a SNMP packet.
    924  */
    925 
    926 static int				/* O - 0 on success, -1 on error */
    927 asn1_decode_snmp(unsigned char *buffer,	/* I - Buffer */
    928                  size_t        len,	/* I - Size of buffer */
    929                  cups_snmp_t   *packet)	/* I - SNMP packet */
    930 {
    931   unsigned char	*bufptr,		/* Pointer into the data */
    932 		*bufend;		/* End of data */
    933   unsigned	length;			/* Length of value */
    934 
    935 
    936  /*
    937   * Initialize the decoding...
    938   */
    939 
    940   memset(packet, 0, sizeof(cups_snmp_t));
    941   packet->object_name[0] = -1;
    942 
    943   bufptr = buffer;
    944   bufend = buffer + len;
    945 
    946   if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
    947     snmp_set_error(packet, _("Packet does not start with SEQUENCE"));
    948   else if (asn1_get_length(&bufptr, bufend) == 0)
    949     snmp_set_error(packet, _("SEQUENCE uses indefinite length"));
    950   else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    951     snmp_set_error(packet, _("No version number"));
    952   else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    953     snmp_set_error(packet, _("Version uses indefinite length"));
    954   else if ((packet->version = asn1_get_integer(&bufptr, bufend, length))
    955                != CUPS_SNMP_VERSION_1)
    956     snmp_set_error(packet, _("Bad SNMP version number"));
    957   else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_OCTET_STRING)
    958     snmp_set_error(packet, _("No community name"));
    959   else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    960     snmp_set_error(packet, _("Community name uses indefinite length"));
    961   else
    962   {
    963     asn1_get_string(&bufptr, bufend, length, packet->community,
    964                     sizeof(packet->community));
    965 
    966     if ((packet->request_type = (cups_asn1_t)asn1_get_type(&bufptr, bufend))
    967             != CUPS_ASN1_GET_RESPONSE)
    968       snmp_set_error(packet, _("Packet does not contain a Get-Response-PDU"));
    969     else if (asn1_get_length(&bufptr, bufend) == 0)
    970       snmp_set_error(packet, _("Get-Response-PDU uses indefinite length"));
    971     else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    972       snmp_set_error(packet, _("No request-id"));
    973     else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    974       snmp_set_error(packet, _("request-id uses indefinite length"));
    975     else
    976     {
    977       packet->request_id = (unsigned)asn1_get_integer(&bufptr, bufend, length);
    978 
    979       if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    980 	snmp_set_error(packet, _("No error-status"));
    981       else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    982 	snmp_set_error(packet, _("error-status uses indefinite length"));
    983       else
    984       {
    985 	packet->error_status = asn1_get_integer(&bufptr, bufend, length);
    986 
    987 	if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_INTEGER)
    988 	  snmp_set_error(packet, _("No error-index"));
    989 	else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
    990 	  snmp_set_error(packet, _("error-index uses indefinite length"));
    991 	else
    992 	{
    993 	  packet->error_index = asn1_get_integer(&bufptr, bufend, length);
    994 
    995           if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
    996 	    snmp_set_error(packet, _("No variable-bindings SEQUENCE"));
    997 	  else if (asn1_get_length(&bufptr, bufend) == 0)
    998 	    snmp_set_error(packet,
    999 	                   _("variable-bindings uses indefinite length"));
   1000 	  else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_SEQUENCE)
   1001 	    snmp_set_error(packet, _("No VarBind SEQUENCE"));
   1002 	  else if (asn1_get_length(&bufptr, bufend) == 0)
   1003 	    snmp_set_error(packet, _("VarBind uses indefinite length"));
   1004 	  else if (asn1_get_type(&bufptr, bufend) != CUPS_ASN1_OID)
   1005 	    snmp_set_error(packet, _("No name OID"));
   1006 	  else if ((length = asn1_get_length(&bufptr, bufend)) == 0)
   1007 	    snmp_set_error(packet, _("Name OID uses indefinite length"));
   1008           else
   1009 	  {
   1010 	    asn1_get_oid(&bufptr, bufend, length, packet->object_name,
   1011 	                 CUPS_SNMP_MAX_OID);
   1012 
   1013             packet->object_type = (cups_asn1_t)asn1_get_type(&bufptr, bufend);
   1014 
   1015 	    if ((length = asn1_get_length(&bufptr, bufend)) == 0 &&
   1016 	        packet->object_type != CUPS_ASN1_NULL_VALUE &&
   1017 	        packet->object_type != CUPS_ASN1_OCTET_STRING)
   1018 	      snmp_set_error(packet, _("Value uses indefinite length"));
   1019 	    else
   1020 	    {
   1021 	      switch (packet->object_type)
   1022 	      {
   1023 	        case CUPS_ASN1_BOOLEAN :
   1024 		    packet->object_value.boolean =
   1025 		        asn1_get_integer(&bufptr, bufend, length);
   1026 	            break;
   1027 
   1028 	        case CUPS_ASN1_INTEGER :
   1029 		    packet->object_value.integer =
   1030 		        asn1_get_integer(&bufptr, bufend, length);
   1031 	            break;
   1032 
   1033 		case CUPS_ASN1_NULL_VALUE :
   1034 		    break;
   1035 
   1036 	        case CUPS_ASN1_OCTET_STRING :
   1037 	        case CUPS_ASN1_BIT_STRING :
   1038 	        case CUPS_ASN1_HEX_STRING :
   1039 		    packet->object_value.string.num_bytes = length;
   1040 		    asn1_get_string(&bufptr, bufend, length,
   1041 		                    (char *)packet->object_value.string.bytes,
   1042 				    sizeof(packet->object_value.string.bytes));
   1043 	            break;
   1044 
   1045 	        case CUPS_ASN1_OID :
   1046 		    asn1_get_oid(&bufptr, bufend, length,
   1047 		                 packet->object_value.oid, CUPS_SNMP_MAX_OID);
   1048 	            break;
   1049 
   1050 	        case CUPS_ASN1_COUNTER :
   1051 		    packet->object_value.counter =
   1052 		        asn1_get_integer(&bufptr, bufend, length);
   1053 	            break;
   1054 
   1055 	        case CUPS_ASN1_GAUGE :
   1056 		    packet->object_value.gauge =
   1057 		        (unsigned)asn1_get_integer(&bufptr, bufend, length);
   1058 	            break;
   1059 
   1060 	        case CUPS_ASN1_TIMETICKS :
   1061 		    packet->object_value.timeticks =
   1062 		        (unsigned)asn1_get_integer(&bufptr, bufend, length);
   1063 	            break;
   1064 
   1065                 default :
   1066 		    snmp_set_error(packet, _("Unsupported value type"));
   1067 		    break;
   1068 	      }
   1069 	    }
   1070           }
   1071 	}
   1072       }
   1073     }
   1074   }
   1075 
   1076   return (packet->error ? -1 : 0);
   1077 }
   1078 
   1079 
   1080 /*
   1081  * 'asn1_encode_snmp()' - Encode a SNMP packet.
   1082  */
   1083 
   1084 static int				/* O - Length on success, -1 on error */
   1085 asn1_encode_snmp(unsigned char *buffer,	/* I - Buffer */
   1086                  size_t        bufsize,	/* I - Size of buffer */
   1087                  cups_snmp_t   *packet)	/* I - SNMP packet */
   1088 {
   1089   unsigned char	*bufptr;		/* Pointer into buffer */
   1090   unsigned	total,			/* Total length */
   1091 		msglen,			/* Length of entire message */
   1092 		commlen,		/* Length of community string */
   1093 		reqlen,			/* Length of request */
   1094 		listlen,		/* Length of variable list */
   1095 		varlen,			/* Length of variable */
   1096 		namelen,		/* Length of object name OID */
   1097 		valuelen;		/* Length of object value */
   1098 
   1099 
   1100  /*
   1101   * Get the lengths of the community string, OID, and message...
   1102   */
   1103 
   1104 
   1105   namelen = asn1_size_oid(packet->object_name);
   1106 
   1107   switch (packet->object_type)
   1108   {
   1109     case CUPS_ASN1_NULL_VALUE :
   1110         valuelen = 0;
   1111 	break;
   1112 
   1113     case CUPS_ASN1_BOOLEAN :
   1114         valuelen = asn1_size_integer(packet->object_value.boolean);
   1115 	break;
   1116 
   1117     case CUPS_ASN1_INTEGER :
   1118         valuelen = asn1_size_integer(packet->object_value.integer);
   1119 	break;
   1120 
   1121     case CUPS_ASN1_OCTET_STRING :
   1122         valuelen = packet->object_value.string.num_bytes;
   1123 	break;
   1124 
   1125     case CUPS_ASN1_OID :
   1126         valuelen = asn1_size_oid(packet->object_value.oid);
   1127 	break;
   1128 
   1129     default :
   1130         packet->error = "Unknown object type";
   1131         return (-1);
   1132   }
   1133 
   1134   varlen  = 1 + asn1_size_length(namelen) + namelen +
   1135             1 + asn1_size_length(valuelen) + valuelen;
   1136   listlen = 1 + asn1_size_length(varlen) + varlen;
   1137   reqlen  = 2 + asn1_size_integer((int)packet->request_id) +
   1138             2 + asn1_size_integer(packet->error_status) +
   1139             2 + asn1_size_integer(packet->error_index) +
   1140             1 + asn1_size_length(listlen) + listlen;
   1141   commlen = (unsigned)strlen(packet->community);
   1142   msglen  = 2 + asn1_size_integer(packet->version) +
   1143             1 + asn1_size_length(commlen) + commlen +
   1144 	    1 + asn1_size_length(reqlen) + reqlen;
   1145   total   = 1 + asn1_size_length(msglen) + msglen;
   1146 
   1147   if (total > bufsize)
   1148   {
   1149     packet->error = "Message too large for buffer";
   1150     return (-1);
   1151   }
   1152 
   1153  /*
   1154   * Then format the message...
   1155   */
   1156 
   1157   bufptr = buffer;
   1158 
   1159   *bufptr++ = CUPS_ASN1_SEQUENCE;	/* SNMPv1 message header */
   1160   asn1_set_length(&bufptr, msglen);
   1161 
   1162   asn1_set_integer(&bufptr, packet->version);
   1163 					/* version */
   1164 
   1165   *bufptr++ = CUPS_ASN1_OCTET_STRING;	/* community */
   1166   asn1_set_length(&bufptr, commlen);
   1167   memcpy(bufptr, packet->community, commlen);
   1168   bufptr += commlen;
   1169 
   1170   *bufptr++ = packet->request_type;	/* Get-Request-PDU/Get-Next-Request-PDU */
   1171   asn1_set_length(&bufptr, reqlen);
   1172 
   1173   asn1_set_integer(&bufptr, (int)packet->request_id);
   1174 
   1175   asn1_set_integer(&bufptr, packet->error_status);
   1176 
   1177   asn1_set_integer(&bufptr, packet->error_index);
   1178 
   1179   *bufptr++ = CUPS_ASN1_SEQUENCE;	/* variable-bindings */
   1180   asn1_set_length(&bufptr, listlen);
   1181 
   1182   *bufptr++ = CUPS_ASN1_SEQUENCE;	/* variable */
   1183   asn1_set_length(&bufptr, varlen);
   1184 
   1185   asn1_set_oid(&bufptr, packet->object_name);
   1186 					/* ObjectName */
   1187 
   1188   switch (packet->object_type)
   1189   {
   1190     case CUPS_ASN1_NULL_VALUE :
   1191 	*bufptr++ = CUPS_ASN1_NULL_VALUE;
   1192 					/* ObjectValue */
   1193 	*bufptr++ = 0;			/* Length */
   1194         break;
   1195 
   1196     case CUPS_ASN1_BOOLEAN :
   1197         asn1_set_integer(&bufptr, packet->object_value.boolean);
   1198 	break;
   1199 
   1200     case CUPS_ASN1_INTEGER :
   1201         asn1_set_integer(&bufptr, packet->object_value.integer);
   1202 	break;
   1203 
   1204     case CUPS_ASN1_OCTET_STRING :
   1205         *bufptr++ = CUPS_ASN1_OCTET_STRING;
   1206 	asn1_set_length(&bufptr, valuelen);
   1207 	memcpy(bufptr, packet->object_value.string.bytes, valuelen);
   1208 	bufptr += valuelen;
   1209 	break;
   1210 
   1211     case CUPS_ASN1_OID :
   1212         asn1_set_oid(&bufptr, packet->object_value.oid);
   1213 	break;
   1214 
   1215     default :
   1216         break;
   1217   }
   1218 
   1219   return ((int)(bufptr - buffer));
   1220 }
   1221 
   1222 
   1223 /*
   1224  * 'asn1_get_integer()' - Get an integer value.
   1225  */
   1226 
   1227 static int				/* O  - Integer value */
   1228 asn1_get_integer(
   1229     unsigned char **buffer,		/* IO - Pointer in buffer */
   1230     unsigned char *bufend,		/* I  - End of buffer */
   1231     unsigned      length)		/* I  - Length of value */
   1232 {
   1233   int	value;				/* Integer value */
   1234 
   1235 
   1236   if (length > sizeof(int))
   1237   {
   1238     (*buffer) += length;
   1239     return (0);
   1240   }
   1241 
   1242   for (value = (**buffer & 0x80) ? -1 : 0;
   1243        length > 0 && *buffer < bufend;
   1244        length --, (*buffer) ++)
   1245     value = (value << 8) | **buffer;
   1246 
   1247   return (value);
   1248 }
   1249 
   1250 
   1251 /*
   1252  * 'asn1_get_length()' - Get a value length.
   1253  */
   1254 
   1255 static unsigned				/* O  - Length */
   1256 asn1_get_length(unsigned char **buffer,	/* IO - Pointer in buffer */
   1257 		unsigned char *bufend)	/* I  - End of buffer */
   1258 {
   1259   unsigned	length;			/* Length */
   1260 
   1261 
   1262   length = **buffer;
   1263   (*buffer) ++;
   1264 
   1265   if (length & 128)
   1266   {
   1267     int	count;				/* Number of bytes for length */
   1268 
   1269 
   1270     if ((count = length & 127) > sizeof(unsigned))
   1271     {
   1272       (*buffer) += count;
   1273       return (0);
   1274     }
   1275 
   1276     for (length = 0;
   1277 	 count > 0 && *buffer < bufend;
   1278 	 count --, (*buffer) ++)
   1279       length = (length << 8) | **buffer;
   1280   }
   1281 
   1282   return (length);
   1283 }
   1284 
   1285 
   1286 /*
   1287  * 'asn1_get_oid()' - Get an OID value.
   1288  */
   1289 
   1290 static int				/* O  - Number of OIDs */
   1291 asn1_get_oid(
   1292     unsigned char **buffer,		/* IO - Pointer in buffer */
   1293     unsigned char *bufend,		/* I  - End of buffer */
   1294     unsigned      length,		/* I  - Length of value */
   1295     int           *oid,			/* I  - OID buffer */
   1296     int           oidsize)		/* I  - Size of OID buffer */
   1297 {
   1298   unsigned char	*valend;		/* End of value */
   1299   int		*oidptr,		/* Current OID */
   1300 		*oidend;		/* End of OID buffer */
   1301   int		number;			/* OID number */
   1302 
   1303 
   1304   valend = *buffer + length;
   1305   oidptr = oid;
   1306   oidend = oid + oidsize - 1;
   1307 
   1308   if (valend > bufend)
   1309     valend = bufend;
   1310 
   1311   number = asn1_get_packed(buffer, bufend);
   1312 
   1313   if (number < 80)
   1314   {
   1315     *oidptr++ = number / 40;
   1316     number    = number % 40;
   1317     *oidptr++ = number;
   1318   }
   1319   else
   1320   {
   1321     *oidptr++ = 2;
   1322     number    -= 80;
   1323     *oidptr++ = number;
   1324   }
   1325 
   1326   while (*buffer < valend)
   1327   {
   1328     number = asn1_get_packed(buffer, bufend);
   1329 
   1330     if (oidptr < oidend)
   1331       *oidptr++ = number;
   1332   }
   1333 
   1334   *oidptr = -1;
   1335 
   1336   return ((int)(oidptr - oid));
   1337 }
   1338 
   1339 
   1340 /*
   1341  * 'asn1_get_packed()' - Get a packed integer value.
   1342  */
   1343 
   1344 static int				/* O  - Value */
   1345 asn1_get_packed(
   1346     unsigned char **buffer,		/* IO - Pointer in buffer */
   1347     unsigned char *bufend)		/* I  - End of buffer */
   1348 {
   1349   int	value;				/* Value */
   1350 
   1351 
   1352   value = 0;
   1353 
   1354   while ((**buffer & 128) && *buffer < bufend)
   1355   {
   1356     value = (value << 7) | (**buffer & 127);
   1357     (*buffer) ++;
   1358   }
   1359 
   1360   if (*buffer < bufend)
   1361   {
   1362     value = (value << 7) | **buffer;
   1363     (*buffer) ++;
   1364   }
   1365 
   1366   return (value);
   1367 }
   1368 
   1369 
   1370 /*
   1371  * 'asn1_get_string()' - Get a string value.
   1372  */
   1373 
   1374 static char *				/* O  - String */
   1375 asn1_get_string(
   1376     unsigned char **buffer,		/* IO - Pointer in buffer */
   1377     unsigned char *bufend,		/* I  - End of buffer */
   1378     unsigned      length,		/* I  - Value length */
   1379     char          *string,		/* I  - String buffer */
   1380     size_t        strsize)		/* I  - String buffer size */
   1381 {
   1382   if (length > (unsigned)(bufend - *buffer))
   1383     length = (unsigned)(bufend - *buffer);
   1384 
   1385   if (length < strsize)
   1386   {
   1387    /*
   1388     * String is smaller than the buffer...
   1389     */
   1390 
   1391     if (length > 0)
   1392       memcpy(string, *buffer, length);
   1393 
   1394     string[length] = '\0';
   1395   }
   1396   else
   1397   {
   1398    /*
   1399     * String is larger than the buffer...
   1400     */
   1401 
   1402     memcpy(string, *buffer, strsize - 1);
   1403     string[strsize - 1] = '\0';
   1404   }
   1405 
   1406   if (length > 0)
   1407     (*buffer) += length;
   1408 
   1409   return (string);
   1410 }
   1411 
   1412 
   1413 /*
   1414  * 'asn1_get_type()' - Get a value type.
   1415  */
   1416 
   1417 static int				/* O  - Type */
   1418 asn1_get_type(unsigned char **buffer,	/* IO - Pointer in buffer */
   1419 	      unsigned char *bufend)	/* I  - End of buffer */
   1420 {
   1421   int	type;				/* Type */
   1422 
   1423 
   1424   type = **buffer;
   1425   (*buffer) ++;
   1426 
   1427   if ((type & 31) == 31)
   1428     type = asn1_get_packed(buffer, bufend);
   1429 
   1430   return (type);
   1431 }
   1432 
   1433 
   1434 /*
   1435  * 'asn1_set_integer()' - Set an integer value.
   1436  */
   1437 
   1438 static void
   1439 asn1_set_integer(unsigned char **buffer,/* IO - Pointer in buffer */
   1440                  int           integer)	/* I  - Integer value */
   1441 {
   1442   **buffer = CUPS_ASN1_INTEGER;
   1443   (*buffer) ++;
   1444 
   1445   if (integer > 0x7fffff || integer < -0x800000)
   1446   {
   1447     **buffer = 4;
   1448     (*buffer) ++;
   1449     **buffer = (unsigned char)(integer >> 24);
   1450     (*buffer) ++;
   1451     **buffer = (unsigned char)(integer >> 16);
   1452     (*buffer) ++;
   1453     **buffer = (unsigned char)(integer >> 8);
   1454     (*buffer) ++;
   1455     **buffer = (unsigned char)integer;
   1456     (*buffer) ++;
   1457   }
   1458   else if (integer > 0x7fff || integer < -0x8000)
   1459   {
   1460     **buffer = 3;
   1461     (*buffer) ++;
   1462     **buffer = (unsigned char)(integer >> 16);
   1463     (*buffer) ++;
   1464     **buffer = (unsigned char)(integer >> 8);
   1465     (*buffer) ++;
   1466     **buffer = (unsigned char)integer;
   1467     (*buffer) ++;
   1468   }
   1469   else if (integer > 0x7f || integer < -0x80)
   1470   {
   1471     **buffer = 2;
   1472     (*buffer) ++;
   1473     **buffer = (unsigned char)(integer >> 8);
   1474     (*buffer) ++;
   1475     **buffer = (unsigned char)integer;
   1476     (*buffer) ++;
   1477   }
   1478   else
   1479   {
   1480     **buffer = 1;
   1481     (*buffer) ++;
   1482     **buffer = (unsigned char)integer;
   1483     (*buffer) ++;
   1484   }
   1485 }
   1486 
   1487 
   1488 /*
   1489  * 'asn1_set_length()' - Set a value length.
   1490  */
   1491 
   1492 static void
   1493 asn1_set_length(unsigned char **buffer,	/* IO - Pointer in buffer */
   1494 		unsigned      length)	/* I  - Length value */
   1495 {
   1496   if (length > 255)
   1497   {
   1498     **buffer = 0x82;			/* 2-byte length */
   1499     (*buffer) ++;
   1500     **buffer = (unsigned char)(length >> 8);
   1501     (*buffer) ++;
   1502     **buffer = (unsigned char)length;
   1503     (*buffer) ++;
   1504   }
   1505   else if (length > 127)
   1506   {
   1507     **buffer = 0x81;			/* 1-byte length */
   1508     (*buffer) ++;
   1509     **buffer = (unsigned char)length;
   1510     (*buffer) ++;
   1511   }
   1512   else
   1513   {
   1514     **buffer = (unsigned char)length;	/* Length */
   1515     (*buffer) ++;
   1516   }
   1517 }
   1518 
   1519 
   1520 /*
   1521  * 'asn1_set_oid()' - Set an OID value.
   1522  */
   1523 
   1524 static void
   1525 asn1_set_oid(unsigned char **buffer,	/* IO - Pointer in buffer */
   1526              const int     *oid)	/* I  - OID value */
   1527 {
   1528   **buffer = CUPS_ASN1_OID;
   1529   (*buffer) ++;
   1530 
   1531   asn1_set_length(buffer, asn1_size_oid(oid));
   1532 
   1533   if (oid[1] < 0)
   1534   {
   1535     asn1_set_packed(buffer, oid[0] * 40);
   1536     return;
   1537   }
   1538 
   1539   asn1_set_packed(buffer, oid[0] * 40 + oid[1]);
   1540 
   1541   for (oid += 2; *oid >= 0; oid ++)
   1542     asn1_set_packed(buffer, *oid);
   1543 }
   1544 
   1545 
   1546 /*
   1547  * 'asn1_set_packed()' - Set a packed integer value.
   1548  */
   1549 
   1550 static void
   1551 asn1_set_packed(unsigned char **buffer,	/* IO - Pointer in buffer */
   1552 		int           integer)	/* I  - Integer value */
   1553 {
   1554   if (integer > 0xfffffff)
   1555   {
   1556     **buffer = ((integer >> 28) & 0x7f) | 0x80;
   1557     (*buffer) ++;
   1558   }
   1559 
   1560   if (integer > 0x1fffff)
   1561   {
   1562     **buffer = ((integer >> 21) & 0x7f) | 0x80;
   1563     (*buffer) ++;
   1564   }
   1565 
   1566   if (integer > 0x3fff)
   1567   {
   1568     **buffer = ((integer >> 14) & 0x7f) | 0x80;
   1569     (*buffer) ++;
   1570   }
   1571 
   1572   if (integer > 0x7f)
   1573   {
   1574     **buffer = ((integer >> 7) & 0x7f) | 0x80;
   1575     (*buffer) ++;
   1576   }
   1577 
   1578   **buffer = integer & 0x7f;
   1579   (*buffer) ++;
   1580 }
   1581 
   1582 
   1583 /*
   1584  * 'asn1_size_integer()' - Figure out the number of bytes needed for an
   1585  *                         integer value.
   1586  */
   1587 
   1588 static unsigned				/* O - Size in bytes */
   1589 asn1_size_integer(int integer)		/* I - Integer value */
   1590 {
   1591   if (integer > 0x7fffff || integer < -0x800000)
   1592     return (4);
   1593   else if (integer > 0x7fff || integer < -0x8000)
   1594     return (3);
   1595   else if (integer > 0x7f || integer < -0x80)
   1596     return (2);
   1597   else
   1598     return (1);
   1599 }
   1600 
   1601 
   1602 /*
   1603  * 'asn1_size_length()' - Figure out the number of bytes needed for a
   1604  *                        length value.
   1605  */
   1606 
   1607 static unsigned				/* O - Size in bytes */
   1608 asn1_size_length(unsigned length)	/* I - Length value */
   1609 {
   1610   if (length > 0xff)
   1611     return (3);
   1612   else if (length > 0x7f)
   1613     return (2);
   1614   else
   1615     return (1);
   1616 }
   1617 
   1618 
   1619 /*
   1620  * 'asn1_size_oid()' - Figure out the numebr of bytes needed for an
   1621  *                     OID value.
   1622  */
   1623 
   1624 static unsigned				/* O - Size in bytes */
   1625 asn1_size_oid(const int *oid)		/* I - OID value */
   1626 {
   1627   unsigned	length;			/* Length of value */
   1628 
   1629 
   1630   if (oid[1] < 0)
   1631     return (asn1_size_packed(oid[0] * 40));
   1632 
   1633   for (length = asn1_size_packed(oid[0] * 40 + oid[1]), oid += 2;
   1634        *oid >= 0;
   1635        oid ++)
   1636     length += asn1_size_packed(*oid);
   1637 
   1638   return (length);
   1639 }
   1640 
   1641 
   1642 /*
   1643  * 'asn1_size_packed()' - Figure out the number of bytes needed for a
   1644  *                        packed integer value.
   1645  */
   1646 
   1647 static unsigned				/* O - Size in bytes */
   1648 asn1_size_packed(int integer)		/* I - Integer value */
   1649 {
   1650   if (integer > 0xfffffff)
   1651     return (5);
   1652   else if (integer > 0x1fffff)
   1653     return (4);
   1654   else if (integer > 0x3fff)
   1655     return (3);
   1656   else if (integer > 0x7f)
   1657     return (2);
   1658   else
   1659     return (1);
   1660 }
   1661 
   1662 
   1663 /*
   1664  * 'snmp_set_error()' - Set the localized error for a packet.
   1665  */
   1666 
   1667 static void
   1668 snmp_set_error(cups_snmp_t *packet,	/* I - Packet */
   1669                const char *message)	/* I - Error message */
   1670 {
   1671   _cups_globals_t *cg = _cupsGlobals();	/* Global data */
   1672 
   1673 
   1674   if (!cg->lang_default)
   1675     cg->lang_default = cupsLangDefault();
   1676 
   1677   packet->error = _cupsLangString(cg->lang_default, message);
   1678 }
   1679