So according to RFC2579 an octetstring can contain UTF-8 characters if
so described in the DISPLAY-HINT. One of the main consumers of this is
SnmpAdminString, which is used quite a lot.
Now that we trimmed a little fat from snmp's oid, I wanted to fill it
up again and implemented the bare minimum DISPLAY-HINT parsing that is
required for SnmpAdminString. Other parts of the syntax can be atter
at a later state if desirable.
Now I decided to implement this a little different from net-snmp,
because they throw incomplete sequences directly to the terminal, which
I recon is not a sane approach. I choose to take the approach taken by
the likes of wall(1) and replace invalid and incomplete sequences with
a '?'. This because DISPLAY-HINT already states that too long sequences
strings should be cut short at the final multibyte character fitting
inside the specified length, meaning that output can already get
mangled.
This also brings me to the question how we should handle DISPLAY-HINT
in the case of -Oa and -Ox. Net-snmp prioritizes DISPLAY-HINT over
these, but it has the option to disable this by disabling MIB-loading
via -m''; This implementation doesn't give us the that option (yet?).
The current diff follows net-snmp, but there is something to say to
prioritize -Ox over DISPLAY-HINT, so an admin can use it to debug
snmp-output without it being mangled. Any feedback here is welcome.
Once it's clear if this is the right approach I'll do a thorough search
through mib.h on which objects actually can use this new definition.
Example choosen because his copyright was in front of me:
$ snmp walk 127.0.0.1 sysContact
sysContact.0 = Hex-STRING: 52 65 79 6B 20 46 6C C3 B6 74 65 72
$ ./obj/snmp walk 127.0.0.1 sysContact
sysContact.0 = Reyk Flöter
$ LC_ALL=C
$ ./obj/snmp walk 127.0.0.1 sysContact
sysContact.0 = Reyk Fl?ter
Thoughts?
martijn@
Index: mib.c
===================================================================
RCS file: /cvs/src/usr.bin/snmp/mib.c,v
retrieving revision 1.2
diff -u -p -r1.2 mib.c
--- mib.c 19 May 2020 13:41:01 -0000 1.2
+++ mib.c 19 May 2020 20:21:42 -0000
@@ -27,9 +27,11 @@
#include "smi.h"
static struct oid mib_tree[] = MIB_TREE;
+static struct textconv textconv_tree[] = TEXTCONV_TREE;
void
mib_init(void)
{
smi_mibtree(mib_tree);
+ smi_textconvtree(textconv_tree);
}
Index: mib.h
===================================================================
RCS file: /cvs/src/usr.bin/snmp/mib.h,v
retrieving revision 1.1
diff -u -p -r1.1 mib.h
--- mib.h 9 Aug 2019 06:17:59 -0000 1.1
+++ mib.h 19 May 2020 20:21:42 -0000
@@ -751,7 +751,7 @@
{ MIBDECL(sysDescr) }, \
{ MIBDECL(sysOID) }, \
{ MIBDECL(sysUpTime) }, \
- { MIBDECL(sysContact) }, \
+ { MIBDECL(sysContact), "SnmpAdminString" }, \
{ MIBDECL(sysName) }, \
{ MIBDECL(sysLocation) }, \
{ MIBDECL(sysServices) }, \
@@ -1345,6 +1345,11 @@
{ MIBDECL(ipfRouteEntRouteMetric5) }, \
{ MIBDECL(ipfRouteEntStatus) }, \
{ MIBEND } \
+}
+
+#define TEXTCONV_TREE { \
+ { "SnmpAdminString", "255t", BER_TYPE_OCTETSTRING }, \
+ { NULL, NULL } \
}
void mib_init(void);
Index: smi.c
===================================================================
RCS file: /cvs/src/usr.bin/snmp/smi.c,v
retrieving revision 1.8
diff -u -p -r1.8 smi.c
--- smi.c 19 May 2020 13:41:01 -0000 1.8
+++ smi.c 19 May 2020 20:21:42 -0000
@@ -24,10 +24,13 @@
#include <arpa/inet.h>
#include <ctype.h>
+#include <errno.h>
+#include <langinfo.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
+#include <wchar.h>
#include "ber.h"
#include "mib.h"
@@ -36,8 +39,12 @@
#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
+int isu8cont(unsigned char);
+char *smi_displayhint_os(struct textconv *, const char *, size_t);
+
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);
@@ -48,6 +55,10 @@ 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)
{
@@ -181,7 +192,7 @@ smi_debug_elements(struct ber_element *r
fprintf(stderr, "(%u) encoding %u ",
root->be_type, root->be_encoding);
- if ((value = smi_print_element(root, 1, smi_os_default,
+ if ((value = smi_print_element(NULL, root, 1, smi_os_default,
smi_oidl_numeric)) == NULL)
goto invalid;
@@ -236,10 +247,13 @@ smi_debug_elements(struct ber_element *r
}
char *
-smi_print_element(struct ber_element *root, int print_hint,
+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)
{
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 d;
@@ -249,6 +263,19 @@ smi_print_element(struct ber_element *ro
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->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_BOOLEAN:
if (ober_get_boolean(root, &d) == -1)
@@ -379,6 +406,10 @@ smi_print_element(struct ber_element *ro
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,
+ buf, root->be_len);
for (i = 0; i < root->be_len; i++) {
if (!isprint(buf[i])) {
if (output_string == smi_os_default)
@@ -575,6 +606,15 @@ smi_mibtree(struct oid *oids)
}
}
+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)
{
@@ -598,6 +638,60 @@ smi_foreach(struct oid *oid)
}
int
+isu8cont(unsigned char c)
+{
+ return (c & (0x80 | 0x40)) == 0x80;
+}
+
+char *
+smi_displayhint_os(struct textconv *tc, const char *buf, size_t buflen)
+{
+ size_t octetlength, i, j, start;
+ char *displayformat;
+ char *rbuf;
+ mbstate_t mbs;
+
+ errno = 0;
+ octetlength = (size_t) strtol(tc->tc_display_hint, &displayformat, 10);
+ if (octetlength == 0 && errno != 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (displayformat[0] == 't') {
+ rbuf = malloc(octetlength + 1);
+ if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) {
+ bzero(&mbs, sizeof(mbs));
+ for (start = j = i = 0; i < octetlength && i < buflen;
i++) {
+ switch (mbrtowc(NULL, &(buf[i]), 1, &mbs)) {
+ case 0:
+ rbuf[j++] = '\0';
+ break;
+ case -1:
+ rbuf[j++] = '?';
+ bzero(&mbs, sizeof(mbs));
+ break;
+ case -2:
+ break;
+ default:
+ memcpy(&(rbuf[j]), &(buf[start]), i -
start + 1);
+ j += i - start + 1;
+ start = i + 1;
+ break;
+ }
+ }
+ } else {
+ for (j = i = 0; i < octetlength && i < buflen; i++) {
+ if (isu8cont(buf[i]))
+ continue;
+ rbuf[j++] = isprint(buf[i]) ? buf[i] : '?';
+ }
+ }
+ return rbuf;
+ }
+ return NULL;
+}
+
+int
smi_oid_cmp(struct oid *a, struct oid *b)
{
size_t i;
@@ -618,5 +712,12 @@ smi_key_cmp(struct oid *a, struct oid *b
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);
Index: smi.h
===================================================================
RCS file: /cvs/src/usr.bin/snmp/smi.h,v
retrieving revision 1.2
diff -u -p -r1.2 smi.h
--- smi.h 19 May 2020 13:41:01 -0000 1.2
+++ smi.h 19 May 2020 20:21:42 -0000
@@ -59,18 +59,28 @@ struct oid {
#define o_oid o_id.bo_id
#define o_oidlen o_id.bo_n
- char *o_name;
+ const char *o_name;
+ const char *o_tcname;
+ struct textconv *o_textconv;
RB_ENTRY(oid) o_element;
RB_ENTRY(oid) o_keyword;
};
+struct textconv {
+ const char *tc_name;
+ const char *tc_display_hint;
+ unsigned int tc_syntax;
+ RB_ENTRY(textconv) tc_entry;
+};
+
int smi_init(void);
unsigned int smi_application(struct ber_element *);
int smi_string2oid(const char *, struct ber_oid *);
char *smi_oid2string(struct ber_oid *, char *, size_t, enum smi_oid_lookup);
void smi_mibtree(struct oid *);
+void smi_textconvtree(struct textconv *);
struct oid *smi_foreach(struct oid *);
void smi_debug_elements(struct ber_element *);
-char *smi_print_element(struct ber_element *, int, enum smi_output_string,
- enum smi_oid_lookup);
+char *smi_print_element(struct ber_oid *, struct ber_element *, int,
+ enum smi_output_string, enum smi_oid_lookup);
Index: snmpc.c
===================================================================
RCS file: /cvs/src/usr.bin/snmp/snmpc.c,v
retrieving revision 1.25
diff -u -p -r1.25 snmpc.c
--- snmpc.c 19 May 2020 13:41:01 -0000 1.25
+++ snmpc.c 19 May 2020 20:21:42 -0000
@@ -29,6 +29,7 @@
#include <ctype.h>
#include <err.h>
#include <errno.h>
+#include <locale.h>
#include <netdb.h>
#include <poll.h>
#include <stdio.h>
@@ -129,6 +130,8 @@ main(int argc, char *argv[])
int ch;
size_t i;
+ setlocale(LC_CTYPE, "");
+
if (pledge("stdio inet dns unix", NULL) == -1)
err(1, "pledge");
@@ -1069,7 +1072,8 @@ snmpc_print(struct ber_element *elm)
}
elm = elm->be_next;
- value = smi_print_element(elm, smi_print_hint, output_string,
oid_lookup);
+ value = smi_print_element(&oid, elm, smi_print_hint, output_string,
+ oid_lookup);
if (value == NULL)
return 0;