Right now if we receive a malformed reply (apart from potentially
crashing[0]) we return a rather unsightly and uninformative error
message:
$ LD_PRELOAD=/usr/src/lib/libutil/obj/libutil.so.13.1 snmp getnext -v2c 
-cpublic 127.0.0.1 ifInDiscards.0      
snmp: getnext: Undefined error: 0

This diff always sets errno to EPROTO if we can't parse the packet.
$ LD_PRELOAD=/usr/src/lib/libutil/obj/libutil.so.13.1 ./obj/snmp getnext -v2c 
-cpublic 127.0.0.1 ifInDiscards.0
snmp: getnext: Protocol error

(note that I malformed snmpd to return "{o}" for this case).

This diff also retries the packet if a malformed reply is received.
This is similar to what netsnmp does.

OK?

martijn@

[0] https://marc.info/?l=openbsd-tech&m=156570856731073&w=2

Index: snmp.c
===================================================================
RCS file: /cvs/src/usr.bin/snmp/snmp.c,v
retrieving revision 1.1
diff -u -p -r1.1 snmp.c
--- snmp.c      9 Aug 2019 06:17:59 -0000       1.1
+++ snmp.c      13 Aug 2019 17:25:28 -0000
@@ -254,26 +254,51 @@ snmp_resolve(struct snmp_agent *agent, s
                if (ret <= 0)
                        goto fail;
                ber_set_readbuf(&ber, buf, ret);
-               if ((message = ber_read_elements(&ber, NULL)) == NULL)
-                       goto fail;
+               if ((message = ber_read_elements(&ber, NULL)) == NULL) {
+                       direction = POLLOUT;
+                       tries--;
+                       continue;
+               }
                if (ber_scanf_elements(message, "{ise", &version, &community,
-                   &pdu) != 0)
-                       goto fail;
+                   &pdu) != 0) {
+                       errno = EPROTO;
+                       direction = POLLOUT;
+                       tries--;
+                       continue;
+               }
                /* Skip invalid packets; should not happen */
                if (version != agent->version ||
-                   strcmp(community, agent->community) != 0)
+                   strcmp(community, agent->community) != 0) {
+                       errno = EPROTO;
+                       direction = POLLOUT;
+                       tries--;
                        continue;
+               }
                /* Validate pdu format and check request id */
                if (ber_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
-                   varbind->be_encoding != BER_TYPE_SEQUENCE)
-                       goto fail;
-               if (rreqid != reqid)
+                   varbind->be_encoding != BER_TYPE_SEQUENCE) {
+                       errno = EPROTO;
+                       direction = POLLOUT;
+                       tries--;
                        continue;
+               }
+               if (rreqid != reqid) {
+                       errno = EPROTO;
+                       direction = POLLOUT;
+                       tries--;
+                       continue;
+               }
                for (varbind = varbind->be_sub; varbind != NULL;
                    varbind = varbind->be_next) {
-                       if (ber_scanf_elements(varbind, "{oS}", &oid) != 0)
-                               goto fail;
+                       if (ber_scanf_elements(varbind, "{oS}", &oid) != 0) {
+                               errno = EPROTO;
+                               direction = POLLOUT;
+                               tries--;
+                               break;
+                       }
                }
+               if (varbind != NULL)
+                       continue;
 
                ber_unlink_elements(message->be_sub->be_next);
                ber_free_elements(message);

Reply via email to