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