752 lines
18 KiB
C
752 lines
18 KiB
C
/* $OpenBSD: smi.c,v 1.15 2021/10/21 08:17:34 martijn Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
|
|
* Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include <sys/limits.h>
|
|
#include <sys/tree.h>
|
|
#include <sys/queue.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <wctype.h>
|
|
|
|
#include "ber.h"
|
|
#include "mib.h"
|
|
#include "snmp.h"
|
|
#include "smi.h"
|
|
|
|
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
|
|
|
|
char *smi_displayhint_os(struct textconv *, int, const char *, size_t, int);
|
|
char *smi_displayhint_int(struct textconv*, int, long long);
|
|
|
|
int smi_oid_cmp(struct oid *, struct oid *);
|
|
int smi_key_cmp(struct oid *, struct oid *);
|
|
int smi_textconv_cmp(struct textconv *, struct textconv *);
|
|
struct oid * smi_findkey(char *);
|
|
|
|
RB_HEAD(oidtree, oid);
|
|
RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp)
|
|
struct oidtree smi_oidtree;
|
|
|
|
RB_HEAD(keytree, oid);
|
|
RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp)
|
|
struct keytree smi_keytree;
|
|
|
|
RB_HEAD(textconvtree, textconv);
|
|
RB_PROTOTYPE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
|
|
struct textconvtree smi_tctree;
|
|
|
|
int
|
|
smi_init(void)
|
|
{
|
|
/* Initialize the Structure of Managed Information (SMI) */
|
|
RB_INIT(&smi_oidtree);
|
|
mib_init();
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
smi_debug_elements(struct ber_element *root, int utf8)
|
|
{
|
|
static int indent = 0;
|
|
char *value;
|
|
int constructed;
|
|
|
|
/* calculate lengths */
|
|
ober_calc_len(root);
|
|
|
|
switch (root->be_encoding) {
|
|
case BER_TYPE_SEQUENCE:
|
|
case BER_TYPE_SET:
|
|
constructed = root->be_encoding;
|
|
break;
|
|
default:
|
|
constructed = 0;
|
|
break;
|
|
}
|
|
|
|
fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
|
|
switch (root->be_class) {
|
|
case BER_CLASS_UNIVERSAL:
|
|
fprintf(stderr, "class: universal(%u) type: ", root->be_class);
|
|
switch (root->be_type) {
|
|
case BER_TYPE_EOC:
|
|
fprintf(stderr, "end-of-content");
|
|
break;
|
|
case BER_TYPE_INTEGER:
|
|
fprintf(stderr, "integer");
|
|
break;
|
|
case BER_TYPE_BITSTRING:
|
|
fprintf(stderr, "bit-string");
|
|
break;
|
|
case BER_TYPE_OCTETSTRING:
|
|
fprintf(stderr, "octet-string");
|
|
break;
|
|
case BER_TYPE_NULL:
|
|
fprintf(stderr, "null");
|
|
break;
|
|
case BER_TYPE_OBJECT:
|
|
fprintf(stderr, "object");
|
|
break;
|
|
case BER_TYPE_ENUMERATED:
|
|
fprintf(stderr, "enumerated");
|
|
break;
|
|
case BER_TYPE_SEQUENCE:
|
|
fprintf(stderr, "sequence");
|
|
break;
|
|
case BER_TYPE_SET:
|
|
fprintf(stderr, "set");
|
|
break;
|
|
}
|
|
break;
|
|
case BER_CLASS_APPLICATION:
|
|
fprintf(stderr, "class: application(%u) type: ",
|
|
root->be_class);
|
|
switch (root->be_type) {
|
|
case SNMP_T_IPADDR:
|
|
fprintf(stderr, "ipaddr");
|
|
break;
|
|
case SNMP_T_COUNTER32:
|
|
fprintf(stderr, "counter32");
|
|
break;
|
|
case SNMP_T_GAUGE32:
|
|
fprintf(stderr, "gauge32");
|
|
break;
|
|
case SNMP_T_TIMETICKS:
|
|
fprintf(stderr, "timeticks");
|
|
break;
|
|
case SNMP_T_OPAQUE:
|
|
fprintf(stderr, "opaque");
|
|
break;
|
|
case SNMP_T_COUNTER64:
|
|
fprintf(stderr, "counter64");
|
|
break;
|
|
}
|
|
break;
|
|
case BER_CLASS_CONTEXT:
|
|
fprintf(stderr, "class: context(%u) type: ",
|
|
root->be_class);
|
|
switch (root->be_type) {
|
|
case SNMP_C_GETREQ:
|
|
fprintf(stderr, "getreq");
|
|
break;
|
|
case SNMP_C_GETNEXTREQ:
|
|
fprintf(stderr, "nextreq");
|
|
break;
|
|
case SNMP_C_GETRESP:
|
|
fprintf(stderr, "getresp");
|
|
break;
|
|
case SNMP_C_SETREQ:
|
|
fprintf(stderr, "setreq");
|
|
break;
|
|
case SNMP_C_TRAP:
|
|
fprintf(stderr, "trap");
|
|
break;
|
|
case SNMP_C_GETBULKREQ:
|
|
fprintf(stderr, "getbulkreq");
|
|
break;
|
|
case SNMP_C_INFORMREQ:
|
|
fprintf(stderr, "informreq");
|
|
break;
|
|
case SNMP_C_TRAPV2:
|
|
fprintf(stderr, "trapv2");
|
|
break;
|
|
case SNMP_C_REPORT:
|
|
fprintf(stderr, "report");
|
|
break;
|
|
}
|
|
break;
|
|
case BER_CLASS_PRIVATE:
|
|
fprintf(stderr, "class: private(%u) type: ", root->be_class);
|
|
break;
|
|
default:
|
|
fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
|
|
break;
|
|
}
|
|
fprintf(stderr, "(%u) encoding %u ",
|
|
root->be_type, root->be_encoding);
|
|
|
|
if ((value = smi_print_element(NULL, root, 1, smi_os_default,
|
|
smi_oidl_numeric, utf8)) == NULL)
|
|
goto invalid;
|
|
|
|
switch (root->be_encoding) {
|
|
case BER_TYPE_INTEGER:
|
|
case BER_TYPE_ENUMERATED:
|
|
fprintf(stderr, "value %s", value);
|
|
break;
|
|
case BER_TYPE_BITSTRING:
|
|
fprintf(stderr, "hexdump %s", value);
|
|
break;
|
|
case BER_TYPE_OBJECT:
|
|
fprintf(stderr, "oid %s", value);
|
|
break;
|
|
case BER_TYPE_OCTETSTRING:
|
|
if (root->be_class == BER_CLASS_APPLICATION &&
|
|
root->be_type == SNMP_T_IPADDR) {
|
|
fprintf(stderr, "addr %s", value);
|
|
} else {
|
|
fprintf(stderr, "string %s", value);
|
|
}
|
|
break;
|
|
case BER_TYPE_NULL: /* no payload */
|
|
case BER_TYPE_EOC:
|
|
case BER_TYPE_SEQUENCE:
|
|
case BER_TYPE_SET:
|
|
default:
|
|
fprintf(stderr, "%s", value);
|
|
break;
|
|
}
|
|
|
|
invalid:
|
|
if (value == NULL)
|
|
fprintf(stderr, "<INVALID>");
|
|
else
|
|
free(value);
|
|
fprintf(stderr, "\n");
|
|
|
|
if (constructed)
|
|
root->be_encoding = constructed;
|
|
|
|
if (constructed && root->be_sub) {
|
|
indent += 2;
|
|
smi_debug_elements(root->be_sub, utf8);
|
|
indent -= 2;
|
|
}
|
|
if (root->be_next)
|
|
smi_debug_elements(root->be_next, utf8);
|
|
}
|
|
|
|
char *
|
|
smi_print_element(struct ber_oid *oid, struct ber_element *root, int print_hint,
|
|
enum smi_output_string output_string, enum smi_oid_lookup lookup, int utf8)
|
|
{
|
|
char *str = NULL, *buf, *p;
|
|
struct oid okey;
|
|
struct oid *object = NULL;
|
|
struct textconv tckey;
|
|
size_t len, i, slen;
|
|
long long v, ticks;
|
|
int is_hex = 0, ret;
|
|
struct ber_oid o;
|
|
char strbuf[BUFSIZ];
|
|
char *hint;
|
|
int days, hours, min, sec, csec;
|
|
|
|
if (oid != NULL) {
|
|
bcopy(oid, &(okey.o_id), sizeof(okey));
|
|
do {
|
|
object = RB_FIND(oidtree, &smi_oidtree, &okey);
|
|
okey.o_id.bo_n--;
|
|
} while (object == NULL && okey.o_id.bo_n > 0);
|
|
if (object != NULL && object->o_textconv == NULL &&
|
|
object->o_tcname != NULL) {
|
|
tckey.tc_name = object->o_tcname;
|
|
object->o_textconv = RB_FIND(textconvtree, &smi_tctree,
|
|
&tckey);
|
|
}
|
|
}
|
|
|
|
switch (root->be_encoding) {
|
|
case BER_TYPE_INTEGER:
|
|
case BER_TYPE_ENUMERATED:
|
|
if (ober_get_integer(root, &v) == -1)
|
|
goto fail;
|
|
if (root->be_class == BER_CLASS_APPLICATION &&
|
|
root->be_type == SNMP_T_TIMETICKS) {
|
|
ticks = v;
|
|
days = ticks / (60 * 60 * 24 * 100);
|
|
ticks %= (60 * 60 * 24 * 100);
|
|
hours = ticks / (60 * 60 * 100);
|
|
ticks %= (60 * 60 * 100);
|
|
min = ticks / (60 * 100);
|
|
ticks %= (60 * 100);
|
|
sec = ticks / 100;
|
|
ticks %= 100;
|
|
csec = ticks;
|
|
|
|
if (print_hint) {
|
|
if (days == 0) {
|
|
if (asprintf(&str,
|
|
"Timeticks: (%lld) "
|
|
"%d:%02d:%02d.%02d",
|
|
v, hours, min, sec, csec) == -1)
|
|
goto fail;
|
|
} else if (days == 1) {
|
|
if (asprintf(&str,
|
|
"Timeticks: (%lld) "
|
|
"1 day %d:%02d:%02d.%02d",
|
|
v, hours, min, sec, csec) == -1)
|
|
goto fail;
|
|
} else {
|
|
if (asprintf(&str,
|
|
"Timeticks: (%lld) "
|
|
"%d day %d:%02d:%02d.%02d",
|
|
v, days, hours, min, sec, csec) ==
|
|
-1)
|
|
goto fail;
|
|
}
|
|
} else {
|
|
if (days == 0) {
|
|
if (asprintf(&str, "%d:%02d:%02d.%02d",
|
|
hours, min, sec, csec) == -1)
|
|
goto fail;
|
|
} else if (days == 1) {
|
|
if (asprintf(&str,
|
|
"1 day %d:%02d:%02d.%02d",
|
|
hours, min, sec, csec) == -1)
|
|
goto fail;
|
|
} else {
|
|
if (asprintf(&str,
|
|
"%d day %d:%02d:%02d.%02d",
|
|
days, hours, min, sec, csec) == -1)
|
|
goto fail;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
hint = "INTEGER: ";
|
|
if (object != NULL && object->o_textconv != NULL &&
|
|
object->o_textconv->tc_syntax == root->be_encoding)
|
|
return smi_displayhint_int(object->o_textconv,
|
|
print_hint, v);
|
|
if (root->be_class == BER_CLASS_APPLICATION) {
|
|
if (root->be_type == SNMP_T_COUNTER32)
|
|
hint = "Counter32: ";
|
|
else if (root->be_type == SNMP_T_GAUGE32)
|
|
hint = "Gauge32: ";
|
|
else if (root->be_type == SNMP_T_OPAQUE)
|
|
hint = "Opaque: ";
|
|
else if (root->be_type == SNMP_T_COUNTER64)
|
|
hint = "Counter64: ";
|
|
}
|
|
if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) == -1)
|
|
goto fail;
|
|
break;
|
|
case BER_TYPE_BITSTRING:
|
|
if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
|
|
goto fail;
|
|
slen = len * 2 + 1 + sizeof("BITS: ");
|
|
if ((str = calloc(1, slen)) == NULL)
|
|
goto fail;
|
|
p = str;
|
|
if (print_hint) {
|
|
strlcpy(str, "BITS: ", slen);
|
|
p += sizeof("BITS: ");
|
|
}
|
|
for (i = 0; i < len; i++) {
|
|
snprintf(p, 3, "%02x", buf[i]);
|
|
p += 2;
|
|
}
|
|
break;
|
|
case BER_TYPE_OBJECT:
|
|
if (ober_get_oid(root, &o) == -1)
|
|
goto fail;
|
|
if (asprintf(&str, "%s%s",
|
|
print_hint ? "OID: " : "",
|
|
smi_oid2string(&o, strbuf, sizeof(strbuf), lookup)) == -1)
|
|
goto fail;
|
|
break;
|
|
case BER_TYPE_OCTETSTRING:
|
|
if (ober_get_string(root, &buf) == -1)
|
|
goto fail;
|
|
if (root->be_class == BER_CLASS_APPLICATION &&
|
|
root->be_type == SNMP_T_IPADDR) {
|
|
if (asprintf(&str, "%s%s",
|
|
print_hint ? "IpAddress: " : "",
|
|
inet_ntoa(*(struct in_addr *)buf)) == -1)
|
|
goto fail;
|
|
} else if (root->be_class == BER_CLASS_CONTEXT) {
|
|
if (root->be_type == SNMP_E_NOSUCHOBJECT)
|
|
str = strdup("No Such Object available on this "
|
|
"agent at this OID");
|
|
else if (root->be_type == SNMP_E_NOSUCHINSTANCE)
|
|
str = strdup("No Such Instance currently "
|
|
"exists at this OID");
|
|
else if (root->be_type == SNMP_E_ENDOFMIB)
|
|
str = strdup("No more variables left in this "
|
|
"MIB View (It is past the end of the MIB "
|
|
"tree)");
|
|
else
|
|
str = strdup("Unknown status at this OID");
|
|
} else {
|
|
if (object != NULL && object->o_textconv != NULL &&
|
|
object->o_textconv->tc_syntax == root->be_encoding)
|
|
return smi_displayhint_os(object->o_textconv,
|
|
print_hint, buf, root->be_len, utf8);
|
|
for (i = 0; i < root->be_len; i++) {
|
|
if (!isprint(buf[i])) {
|
|
if (output_string == smi_os_default)
|
|
output_string = smi_os_hex;
|
|
else if (output_string == smi_os_ascii)
|
|
is_hex = 1;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* hex is 3 * n (2 digits + n - 1 spaces + NUL-byte)
|
|
* ascii can be max (2 * n) + 2 quotes + NUL-byte
|
|
*/
|
|
len = output_string == smi_os_hex ? 3 : 2;
|
|
p = str = reallocarray(NULL, root->be_len + 2, len);
|
|
if (p == NULL)
|
|
goto fail;
|
|
len *= root->be_len + 2;
|
|
if (is_hex) {
|
|
*str++ = '"';
|
|
len--;
|
|
}
|
|
for (i = 0; i < root->be_len; i++) {
|
|
switch (output_string) {
|
|
case smi_os_default:
|
|
/* FALLTHROUGH */
|
|
case smi_os_ascii:
|
|
/*
|
|
* There's probably more edgecases here,
|
|
* not fully investigated
|
|
*/
|
|
if (len < 2)
|
|
goto fail;
|
|
if (is_hex && buf[i] == '\\') {
|
|
*str++ = '\\';
|
|
len--;
|
|
}
|
|
*str++ = isprint(buf[i]) ? buf[i] : '.';
|
|
len--;
|
|
break;
|
|
case smi_os_hex:
|
|
ret = snprintf(str, len, "%s%02hhX",
|
|
i == 0 ? "" :
|
|
i % 16 == 0 ? "\n" : " ", buf[i]);
|
|
if (ret == -1 || ret > (int) len)
|
|
goto fail;
|
|
len -= ret;
|
|
str += ret;
|
|
break;
|
|
}
|
|
}
|
|
if (is_hex) {
|
|
if (len < 2)
|
|
goto fail;
|
|
*str++ = '"';
|
|
len--;
|
|
}
|
|
if (len == 0)
|
|
goto fail;
|
|
*str = '\0';
|
|
str = NULL;
|
|
if (asprintf(&str, "%s%s",
|
|
print_hint ?
|
|
output_string == smi_os_hex ? "Hex-STRING: " :
|
|
"STRING: " :
|
|
"", p) == -1) {
|
|
free(p);
|
|
goto fail;
|
|
}
|
|
free(p);
|
|
}
|
|
break;
|
|
case BER_TYPE_NULL: /* no payload */
|
|
case BER_TYPE_EOC:
|
|
case BER_TYPE_SEQUENCE:
|
|
case BER_TYPE_SET:
|
|
default:
|
|
str = strdup("");
|
|
break;
|
|
}
|
|
|
|
return (str);
|
|
|
|
fail:
|
|
free(str);
|
|
return (NULL);
|
|
}
|
|
|
|
int
|
|
smi_string2oid(const char *oidstr, struct ber_oid *o)
|
|
{
|
|
char *sp, *p, str[BUFSIZ];
|
|
const char *errstr;
|
|
struct oid *oid;
|
|
struct ber_oid ko;
|
|
|
|
if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
|
|
return (-1);
|
|
bzero(o, sizeof(*o));
|
|
|
|
/*
|
|
* Parse OID strings in the common form n.n.n or n-n-n.
|
|
* Based on ober_string2oid with additional support for symbolic names.
|
|
*/
|
|
p = sp = str[0] == '.' ? str + 1 : str;
|
|
for (; p != NULL; sp = p) {
|
|
if ((p = strpbrk(p, ".-")) != NULL)
|
|
*p++ = '\0';
|
|
if ((oid = smi_findkey(sp)) != NULL) {
|
|
bcopy(&oid->o_id, &ko, sizeof(ko));
|
|
if (o->bo_n && ober_oid_cmp(&ko, o) != 2)
|
|
return (-1);
|
|
bcopy(&ko, o, sizeof(*o));
|
|
errstr = NULL;
|
|
} else {
|
|
o->bo_id[o->bo_n++] =
|
|
strtonum(sp, 0, UINT_MAX, &errstr);
|
|
}
|
|
if (errstr || o->bo_n > BER_MAX_OID_LEN)
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
unsigned int
|
|
smi_application(struct ber_element *elm)
|
|
{
|
|
if (elm->be_class != BER_CLASS_APPLICATION)
|
|
return (BER_TYPE_OCTETSTRING);
|
|
|
|
switch (elm->be_type) {
|
|
case SNMP_T_IPADDR:
|
|
return (BER_TYPE_OCTETSTRING);
|
|
case SNMP_T_COUNTER32:
|
|
case SNMP_T_GAUGE32:
|
|
case SNMP_T_TIMETICKS:
|
|
case SNMP_T_OPAQUE:
|
|
case SNMP_T_COUNTER64:
|
|
return (BER_TYPE_INTEGER);
|
|
default:
|
|
break;
|
|
}
|
|
return (BER_TYPE_OCTETSTRING);
|
|
|
|
}
|
|
|
|
char *
|
|
smi_oid2string(struct ber_oid *o, char *buf, size_t len,
|
|
enum smi_oid_lookup lookup)
|
|
{
|
|
char str[256];
|
|
struct oid *value, key;
|
|
size_t i;
|
|
|
|
bzero(buf, len);
|
|
bzero(&key, sizeof(key));
|
|
bcopy(o, &key.o_id, sizeof(struct ber_oid));
|
|
|
|
for (i = 0; i < o->bo_n; i++) {
|
|
key.o_oidlen = i + 1;
|
|
if (lookup != smi_oidl_numeric &&
|
|
(value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) {
|
|
snprintf(str, sizeof(str), "%s", value->o_name);
|
|
if (lookup == smi_oidl_short && i + 1 < o->bo_n) {
|
|
key.o_oidlen = i + 2;
|
|
if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL)
|
|
continue;
|
|
}
|
|
} else
|
|
snprintf(str, sizeof(str), "%u", key.o_oid[i]);
|
|
if (*buf != '\0' || i == 0)
|
|
strlcat(buf, ".", len);
|
|
strlcat(buf, str, len);
|
|
}
|
|
|
|
return (buf);
|
|
}
|
|
|
|
void
|
|
smi_mibtree(struct oid *oids)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; oids[i].o_name != NULL; i++) {
|
|
RB_INSERT(oidtree, &smi_oidtree, &(oids[i]));
|
|
RB_INSERT(keytree, &smi_keytree, &(oids[i]));
|
|
}
|
|
}
|
|
|
|
void
|
|
smi_textconvtree(struct textconv *textconvs)
|
|
{
|
|
size_t i = 0;
|
|
|
|
for (i = 0; textconvs[i].tc_name != NULL; i++)
|
|
RB_INSERT(textconvtree, &smi_tctree, &(textconvs[i]));
|
|
}
|
|
|
|
struct oid *
|
|
smi_findkey(char *name)
|
|
{
|
|
struct oid oid;
|
|
if (name == NULL)
|
|
return (NULL);
|
|
oid.o_name = name;
|
|
return (RB_FIND(keytree, &smi_keytree, &oid));
|
|
}
|
|
|
|
struct oid *
|
|
smi_foreach(struct oid *oid)
|
|
{
|
|
/*
|
|
* Traverse the tree of MIBs with the option to check
|
|
* for specific OID flags.
|
|
*/
|
|
if (oid == NULL)
|
|
return RB_MIN(oidtree, &smi_oidtree);
|
|
return RB_NEXT(oidtree, &smi_oidtree, oid);
|
|
}
|
|
|
|
char *
|
|
smi_displayhint_int(struct textconv *tc, int print_hint, long long v)
|
|
{
|
|
size_t i;
|
|
char *rbuf;
|
|
|
|
for (i = 0; tc->tc_enum[i].tce_name != NULL; i++) {
|
|
if (tc->tc_enum[i].tce_number == v) {
|
|
if (print_hint) {
|
|
if (asprintf(&rbuf, "INTEGER: %s(%lld)",
|
|
tc->tc_enum[i].tce_name, v) == -1)
|
|
return NULL;
|
|
} else {
|
|
if (asprintf(&rbuf, "%s",
|
|
tc->tc_enum[i].tce_name) == -1)
|
|
return NULL;
|
|
}
|
|
return rbuf;
|
|
}
|
|
}
|
|
if (asprintf(&rbuf, "%s%lld", print_hint ? "INTEGER: " : "", v) == -1)
|
|
return NULL;
|
|
return rbuf;
|
|
}
|
|
|
|
#define REPLACEMENT "\357\277\275"
|
|
char *
|
|
smi_displayhint_os(struct textconv *tc, int print_hint, const char *src,
|
|
size_t srclen, int utf8)
|
|
{
|
|
size_t octetlength, i = 0, j = 0;
|
|
size_t prefixlen;
|
|
unsigned long ulval;
|
|
int clen;
|
|
char *displayformat;
|
|
const char *prefix;
|
|
char *rbuf, *dst;
|
|
wchar_t wc;
|
|
|
|
prefix = print_hint ? "STRING: " : "";
|
|
prefixlen = strlen(prefix);
|
|
|
|
errno = 0;
|
|
ulval = strtoul(tc->tc_display_hint, &displayformat, 10);
|
|
octetlength = ulval;
|
|
if (!isdigit(tc->tc_display_hint[0]) ||
|
|
(errno != 0 && (ulval == 0 || ulval == ULONG_MAX)) ||
|
|
(unsigned long) octetlength != ulval) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
if (displayformat[0] == 't' || displayformat[0] == 'a') {
|
|
if ((rbuf = malloc(prefixlen + octetlength + 1)) == NULL)
|
|
return NULL;
|
|
(void)strlcpy(rbuf, prefix, prefixlen + octetlength + 1);
|
|
dst = rbuf + prefixlen;
|
|
while (j < octetlength && i < srclen) {
|
|
clen = mbtowc(&wc, &(src[i]), srclen - i);
|
|
if (displayformat[0] == 'a' && clen > 1)
|
|
clen = -1;
|
|
switch (clen) {
|
|
case 0:
|
|
dst[j++] = '.';
|
|
i++;
|
|
break;
|
|
case -1:
|
|
mbtowc(NULL, NULL, MB_CUR_MAX);
|
|
if (utf8) {
|
|
if (octetlength - j <
|
|
sizeof(REPLACEMENT) - 1) {
|
|
dst[j] = '\0';
|
|
return rbuf;
|
|
}
|
|
memcpy(&(dst[j]), REPLACEMENT,
|
|
sizeof(REPLACEMENT) - 1);
|
|
j += sizeof(REPLACEMENT) - 1;
|
|
} else
|
|
dst[j++] = '?';
|
|
i++;
|
|
break;
|
|
default:
|
|
if (!iswprint(wc) || (!utf8 && clen > 1))
|
|
dst[j++] = '.';
|
|
else if (octetlength - j < (size_t)clen) {
|
|
dst[j] = '\0';
|
|
return rbuf;
|
|
} else {
|
|
memcpy(&(dst[j]), &(src[i]), clen);
|
|
j += clen;
|
|
}
|
|
i += clen;
|
|
break;
|
|
}
|
|
}
|
|
dst[j] = '\0';
|
|
return rbuf;
|
|
}
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
smi_oid_cmp(struct oid *a, struct oid *b)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) {
|
|
if (a->o_oid[i] != b->o_oid[i])
|
|
return (a->o_oid[i] - b->o_oid[i]);
|
|
}
|
|
|
|
return (a->o_oidlen - b->o_oidlen);
|
|
}
|
|
|
|
int
|
|
smi_key_cmp(struct oid *a, struct oid *b)
|
|
{
|
|
if (a->o_name == NULL || b->o_name == NULL)
|
|
return (-1);
|
|
return (strcasecmp(a->o_name, b->o_name));
|
|
}
|
|
|
|
int
|
|
smi_textconv_cmp(struct textconv *a, struct textconv *b)
|
|
{
|
|
return strcmp(a->tc_name, b->tc_name);
|
|
}
|
|
|
|
RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp)
|
|
RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp)
|
|
RB_GENERATE(textconvtree, textconv, tc_entry, smi_textconv_cmp);
|