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;
 

Reply via email to