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