Hello tech@,

Long story short: the traphandler process in snmpd annoys me a great
deal and is in the way for overhauling the transport mapping section
of snmpe, which is needed for implementing new agentx master support.

The current traphandler process is also a joke, since all it does is
receive a message, does a minimal parsing validation and then pass
then entire buffer to the parent process, which forks the actual
handlers. This means that the current pledgeset is also way too wide
for what traphandler does.

Since snmpe already does a similar packet parsing I see no reason to
keep this initial verification in the traphandler process. The diff
below drops the traphandler process and moves the receiving of the
packets to snmpe, which then forwards the pdu (instead of the
current whole packet) to the parent. I also extended the checking, so
it should be a little more resilient to malformed packets.

While here I also didn't like the fact that listening on a trap-port
always implies that snmp itself is also listening. This diff adds the
trap keyword (for udp) on the listen on line, so that we can setup a
traphandler-only setup. This gives the added bonus that we can now
also choose the port we listen on for traps. Adding a listen trap
statement requires a trap handle and vice versa.

It's not the pretties code, but it's a stopgap that allows me to move
forward without creating even larger diffs and without (hopefully)
breaking existing setups (apart from the keyword change).

OK?

martijn@

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.62
diff -u -p -r1.62 parse.y
--- parse.y     30 Oct 2020 07:43:48 -0000      1.62
+++ parse.y     1 Dec 2020 16:12:20 -0000
@@ -94,11 +94,9 @@ char         *symget(const char *);
 struct snmpd                   *conf = NULL;
 static int                      errors = 0;
 static struct usmuser          *user = NULL;
-static char                    *snmpd_port = SNMPD_PORT;
 
 int             host(const char *, const char *, int,
                    struct sockaddr_storage *, int);
-int             listen_add(struct sockaddr_storage *, int);
 
 typedef struct {
        union {
@@ -284,13 +282,16 @@ listenproto       : UDP listen_udp
 listen_udp     : STRING port                   {
                        struct sockaddr_storage ss[16];
                        int nhosts, i;
+                       char *port = $2;
 
-                       nhosts = host($1, $2, SOCK_DGRAM, ss, nitems(ss));
+                       if (port == NULL)
+                               port = SNMPD_PORT;
+
+                       nhosts = host($1, port, SOCK_DGRAM, ss, nitems(ss));
                        if (nhosts < 1) {
                                yyerror("invalid address: %s", $1);
                                free($1);
-                               if ($2 != snmpd_port)
-                                       free($2);
+                               free($2);
                                YYERROR;
                        }
                        if (nhosts > (int)nitems(ss))
@@ -298,10 +299,38 @@ listen_udp        : STRING port                   {
                                    $1, $2, nitems(ss));
 
                        free($1);
-                       if ($2 != snmpd_port)
+                       free($2);
+                       for (i = 0; i < nhosts; i++) {
+                               if (listen_add(&(ss[i]), SOCK_DGRAM, 0) == -1) {
+                                       yyerror("calloc");
+                                       YYERROR;
+                               }
+                       }
+               }
+               | STRING port TRAP              {
+                       struct sockaddr_storage ss[16];
+                       int nhosts, i;
+                       char *port = $2;
+
+                       if (port == NULL)
+                               port = SNMPD_TRAPPORT;
+
+                       nhosts = host($1, port, SOCK_DGRAM, ss, nitems(ss));
+                       if (nhosts < 1) {
+                               yyerror("invalid address: %s", $1);
+                               free($1);
                                free($2);
+                               YYERROR;
+                       }
+                       if (nhosts > (int)nitems(ss))
+                               log_warn("%s:%s resolves to more than %zu 
hosts",
+                                   $1, $2, nitems(ss));
+
+                       free($1);
+                       free($2);
                        for (i = 0; i < nhosts; i++) {
-                               if (listen_add(&(ss[i]), SOCK_DGRAM) == -1) {
+                               if (listen_add(&(ss[i]), SOCK_DGRAM,
+                                   ADDRESS_FLAG_TRAP) == -1) {
                                        yyerror("calloc");
                                        YYERROR;
                                }
@@ -311,13 +340,16 @@ listen_udp        : STRING port                   {
 listen_tcp     : STRING port                   {
                        struct sockaddr_storage ss[16];
                        int nhosts, i;
+                       char *port = $2;
+
+                       if (port == NULL)
+                               port = SNMPD_PORT;
 
-                       nhosts = host($1, $2, SOCK_STREAM, ss, nitems(ss));
+                       nhosts = host($1, port, SOCK_STREAM, ss, nitems(ss));
                        if (nhosts < 1) {
                                yyerror("invalid address: %s", $1);
                                free($1);
-                               if ($2 != snmpd_port)
-                                       free($2);
+                               free($2);
                                YYERROR;
                        }
                        if (nhosts > (int)nitems(ss))
@@ -325,10 +357,10 @@ listen_tcp        : STRING port                   {
                                    $1, $2, nitems(ss));
 
                        free($1);
-                       if ($2 != snmpd_port)
-                               free($2);
+                       free($2);
                        for (i = 0; i < nhosts; i++) {
-                               if (listen_add(&(ss[i]), SOCK_STREAM) == -1) {
+                               if (listen_add(&(ss[i]), SOCK_STREAM,
+                                   0) == -1) {
                                        yyerror("calloc");
                                        YYERROR;
                                }
@@ -336,7 +368,7 @@ listen_tcp  : STRING port                   {
                }
 
 port           : /* empty */                   {
-                       $$ = snmpd_port;
+                       $$ = NULL;
                }
                | PORT STRING                   {
                        $$ = $2;
@@ -1079,7 +1111,7 @@ parse_config(const char *filename, u_int
 {
        struct sockaddr_storage ss;
        struct sym      *sym, *next;
-       struct address  *h;
+       struct address  *h, *htmp;
        int found;
 
        if ((conf = calloc(1, sizeof(*conf))) == NULL) {
@@ -1108,15 +1140,28 @@ parse_config(const char *filename, u_int
 
        endservent();
 
+       found = 0;
+       TAILQ_FOREACH_SAFE(h, &conf->sc_addresses, entry, htmp) {
+               if (h->flags & ADDRESS_FLAG_TRAP) {
+                       if (!conf->sc_traphandler) {
+                               if (!found)
+                                       fatalx("No trap handle configured");
+                       }
+                       found = 1;
+               }
+       }
+       if (conf->sc_traphandler && !found)
+               fatalx("Trap handle configured without trap listener");
+
        /* Setup default listen addresses */
        if (TAILQ_EMPTY(&conf->sc_addresses)) {
                if (host("0.0.0.0", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
                        fatal("Unexpected resolving of 0.0.0.0");
-               if (listen_add(&ss, SOCK_DGRAM) == -1)
+               if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
                        fatal("calloc");
                if (host("::", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
                        fatal("Unexpected resolving of ::");
-               if (listen_add(&ss, SOCK_DGRAM) == -1)
+               if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
                        fatal("calloc");
        }
        if (conf->sc_traphandler) {
@@ -1258,7 +1303,7 @@ host(const char *s, const char *port, in
 }
 
 int
-listen_add(struct sockaddr_storage *ss, int type)
+listen_add(struct sockaddr_storage *ss, int type, int flags)
 {
        struct sockaddr_in *sin;
        struct sockaddr_in6 *sin6;
@@ -1275,6 +1320,7 @@ listen_add(struct sockaddr_storage *ss, 
                h->port = ntohs(sin6->sin6_port);
        }
        h->type = type;
+       h->flags = flags;
        TAILQ_INSERT_TAIL(&(conf->sc_addresses), h, entry);
 
        return 0;
Index: snmpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.c,v
retrieving revision 1.42
diff -u -p -r1.42 snmpd.c
--- snmpd.c     6 Sep 2020 15:51:28 -0000       1.42
+++ snmpd.c     1 Dec 2020 16:12:20 -0000
@@ -51,9 +51,7 @@ int    check_child(pid_t, const char *);
 struct snmpd   *snmpd_env;
 
 static struct privsep_proc procs[] = {
-       { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown },
-       { "traphandler", PROC_TRAP, snmpd_dispatch_traphandler, traphandler,
-           traphandler_shutdown }
+       { "snmpe", PROC_SNMPE, snmpd_dispatch_snmpe, snmpe, snmpe_shutdown }
 };
 
 void
@@ -300,6 +298,8 @@ int
 snmpd_dispatch_snmpe(int fd, struct privsep_proc *p, struct imsg *imsg)
 {
        switch (imsg->hdr.type) {
+       case IMSG_ALERT:
+               return (traphandler_priv_recvmsg(p, imsg));
        case IMSG_CTL_RELOAD:
                /* XXX notyet */
        default:
Index: snmpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.conf.5,v
retrieving revision 1.45
diff -u -p -r1.45 snmpd.conf.5
--- snmpd.conf.5        24 Oct 2020 14:52:11 -0000      1.45
+++ snmpd.conf.5        1 Dec 2020 16:12:20 -0000
@@ -95,13 +95,33 @@ Routing table information will not be av
 reduced during bulk updates.
 The default is
 .Ic no .
-.It Ic listen on Oo Ic tcp | udp Oc Ar address Op Ic port Ar port
+.It Ic listen on Oo Ic tcp | udp Oc Ar address Oo Ic port Ar port Oc Op Ic trap
 Specify the local address
 .Xr snmpd 8
 should listen on for incoming SNMP messages.
 Multiple
 .Ic listen on
-statements are supported, the default is UDP.
+statements are supported, the default is
+.Ic udp .
+The default
+.Ar port
+is 161.
+.Pp
+The
+.Ic trap
+keyword disables support for get and set queries and enables support for
+receiving traps.
+Currently only
+.Ic udp
+listeners support
+.Ic trap .
+At least one
+.Ic trap handle
+directive must be set.
+The default
+.Ic trap
+.Ar port
+port is 162.
 .It Ic read-only community Ar string
 Specify the name of the read-only community.
 The default value is
@@ -193,9 +213,11 @@ the resolved hostname of the host sendin
 the IP address of the host sending the trap,
 and any variable bindings contained in the trap
 (the OID followed by the value, separated by a single space).
-Traps will be accepted on all
-.Ic listen on
-UDP addresses.
+Setting
+.Ic trap handle
+requires that at least one
+.Ic listen on trap
+command is set.
 .It Xo
 .Ic trap receiver Ar string
 .Op Ic oid Ar oid-string
Index: snmpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.90
diff -u -p -r1.90 snmpd.h
--- snmpd.h     6 Sep 2020 15:51:28 -0000       1.90
+++ snmpd.h     1 Dec 2020 16:12:20 -0000
@@ -110,7 +110,6 @@ struct imsgev {
 enum privsep_procid {
        PROC_PARENT,    /* Parent process and application interface */
        PROC_SNMPE,     /* SNMP engine */
-       PROC_TRAP,      /* SNMP trap receiver */
        PROC_MAX
 };
 
@@ -386,6 +385,7 @@ struct snmp_message {
        int                      sm_sock_tcp;
        struct event             sm_sockev;
        char                     sm_host[HOST_NAME_MAX+1];
+       int                      sm_addressflags;
 
        struct sockaddr_storage  sm_local_ss;
        socklen_t                sm_local_slen;
@@ -483,11 +483,15 @@ struct address {
        in_port_t                port;
        int                      type;
        int                      fd;
+       int                      flags;
        struct event             ev;
        struct event             evt;
 
        TAILQ_ENTRY(address)     entry;
 };
+
+#define ADDRESS_FLAG_TRAP 0x01
+
 TAILQ_HEAD(addresslist, address);
 
 struct trap_address {
@@ -588,6 +592,7 @@ extern struct snmpd *snmpd_env;
 /* parse.y */
 struct snmpd   *parse_config(const char *, u_int);
 int             cmdline_symset(char *);
+int             listen_add(struct sockaddr_storage *, int, int);
 
 /* log.c */
 void   log_init(int, int);
@@ -761,9 +766,8 @@ struct imsgev *
 int     proc_flush_imsg(struct privsep *, enum privsep_procid, int);
 
 /* traphandler.c */
-void    traphandler(struct privsep *, struct privsep_proc *);
-void    traphandler_shutdown(void);
-int     snmpd_dispatch_traphandler(int, struct privsep_proc *, struct imsg *);
+int     traphandler_parse(struct snmp_message *);
+int     traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
 void    trapcmd_free(struct trapcmd *);
 int     trapcmd_add(struct trapcmd *);
 struct trapcmd *
Index: snmpe.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.67
diff -u -p -r1.67 snmpe.c
--- snmpe.c     6 Sep 2020 17:29:35 -0000       1.67
+++ snmpe.c     1 Dec 2020 16:12:20 -0000
@@ -108,7 +108,7 @@ snmpe_init(struct privsep *ps, struct pr
                        evtimer_set(&h->evt, snmpe_acceptcb, h);
                } else {
                        event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                           snmpe_recvmsg, env);
+                           snmpe_recvmsg, h);
                }
                event_add(&h->ev, NULL);
        }
@@ -271,6 +271,12 @@ snmpe_parse(struct snmp_message *msg)
        if (class != BER_CLASS_CONTEXT)
                goto parsefail;
 
+       if (msg->sm_addressflags & ADDRESS_FLAG_TRAP &&
+           type != SNMP_C_TRAP && type != SNMP_C_TRAPV2) {
+               msg->sm_errstr = "invalid request on trap port";
+               goto fail;
+       }
+
        switch (type) {
        case SNMP_C_GETBULKREQ:
                if (msg->sm_version == SNMP_V1) {
@@ -320,7 +326,18 @@ snmpe_parse(struct snmp_message *msg)
                goto parsefail;
 
        case SNMP_C_TRAP:
+               if (msg->sm_version != SNMP_V1) {
+                       stats->snmp_inbadversions++;
+                       msg->sm_errstr = "SNMPv1-trap on new protocol";
+                       goto fail;
+               }
        case SNMP_C_TRAPV2:
+               if (type == SNMP_C_TRAPV2 &&
+                   msg->sm_version != SNMP_V2 && msg->sm_version != SNMP_V3) {
+                       stats->snmp_inbadversions++;
+                       msg->sm_errstr = "SNMPv2-trap on wrong protocol";
+                       goto fail;
+               }
                if (msg->sm_version != SNMP_V3 &&
                    strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
                        stats->snmp_inbadcommunitynames++;
@@ -328,8 +345,13 @@ snmpe_parse(struct snmp_message *msg)
                        goto fail;
                }
                stats->snmp_intraps++;
-               msg->sm_errstr = "received trap";
-               goto fail;
+               if ((msg->sm_addressflags & ADDRESS_FLAG_TRAP) == 0) {
+                       msg->sm_errstr = "received trap";
+                       goto fail;
+               }
+               if (traphandler_parse(msg) == -1)
+                       goto fail;
+               return (0);
 
        default:
                msg->sm_errstr = "invalid context";
@@ -662,8 +684,8 @@ snmpe_writecb(int fd, short type, void *
 void
 snmpe_recvmsg(int fd, short sig, void *arg)
 {
-       struct snmpd            *env = arg;
-       struct snmp_stats       *stats = &env->sc_stats;
+       struct address          *h = arg;
+       struct snmp_stats       *stats = &(snmpd_env->sc_stats);
        ssize_t                  len;
        struct snmp_message     *msg;
 
@@ -672,6 +694,7 @@ snmpe_recvmsg(int fd, short sig, void *a
 
        msg->sm_sock = fd;
        msg->sm_slen = sizeof(msg->sm_ss);
+       msg->sm_addressflags = h->flags;
        if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
            (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
            (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
Index: traphandler.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/traphandler.c,v
retrieving revision 1.18
diff -u -p -r1.18 traphandler.c
--- traphandler.c       6 Sep 2020 15:51:28 -0000       1.18
+++ traphandler.c       1 Dec 2020 16:12:20 -0000
@@ -42,16 +42,10 @@
 #include "snmpd.h"
 #include "mib.h"
 
-char    trap_path[PATH_MAX];
-
-void    traphandler_init(struct privsep *, struct privsep_proc *, void *arg);
-int     traphandler_dispatch_parent(int, struct privsep_proc *, struct imsg *);
-int     traphandler_bind(struct address *);
-void    traphandler_recvmsg(int, short, void *);
+int     traphandler_extract(struct snmp_message *, struct ber_element *,
+           u_int *, struct ber_oid *, struct ber_element **);
 int     traphandler_priv_recvmsg(struct privsep_proc *, struct imsg *);
 int     traphandler_fork_handler(struct privsep_proc *, struct imsg *);
-int     traphandler_parse(char *, size_t, struct ber_element **,
-           struct ber_element **, u_int *, struct ber_oid *);
 void    traphandler_v1translate(struct ber_oid *, u_int, u_int);
 
 int     trapcmd_cmp(struct trapcmd *, struct trapcmd *);
@@ -65,208 +59,105 @@ RB_GENERATE(trapcmd_tree, trapcmd, cmd_e
 
 struct trapcmd_tree trapcmd_tree = RB_INITIALIZER(&trapcmd_tree);
 
-static struct privsep_proc procs[] = {
-       { "parent",     PROC_PARENT,    traphandler_dispatch_parent }
-};
-
-void
-traphandler(struct privsep *ps, struct privsep_proc *p)
-{
-       struct snmpd            *env = ps->ps_env;
-       struct address          *h;
-
-       if (env->sc_traphandler) {
-               TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-                       if (h->type != SOCK_DGRAM)
-                               continue;
-                       if ((h->fd = traphandler_bind(h)) == -1)
-                               fatal("could not create trap listener socket");
-               }
-       }
-
-       proc_run(ps, p, procs, nitems(procs), traphandler_init, NULL);
-}
-
-void
-traphandler_init(struct privsep *ps, struct privsep_proc *p, void *arg)
-{
-       struct snmpd            *env = ps->ps_env;
-       struct address          *h;
-
-       if (pledge("stdio id proc recvfd exec", NULL) == -1)
-               fatal("pledge");
-
-       if (!env->sc_traphandler)
-               return;
-
-       /* listen for SNMP trap messages */
-       TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-               event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
-                   traphandler_recvmsg, ps);
-               event_add(&h->ev, NULL);
-       }
-}
-
-int
-traphandler_bind(struct address *addr)
-{
-       int                      s;
-       char                     buf[512];
-       struct sockaddr_in      *sin;
-       struct sockaddr_in6     *sin6;
-
-       if (addr->ss.ss_family == AF_INET) {
-               sin = (struct sockaddr_in *)&(addr->ss);
-               sin->sin_port = htons(162);
-       } else {
-               sin6 = (struct sockaddr_in6 *)&(addr->ss);
-               sin6->sin6_port = htons(162);
-       }
-       if ((s = snmpd_socket_af(&addr->ss, SOCK_DGRAM)) == -1)
-               return (-1);
-
-       if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
-               goto bad;
-
-       if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
-               goto bad;
-
-       if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
-               goto bad;
-
-       log_info("traphandler: listening on %s:%s", buf, SNMPD_TRAPPORT);
-
-       return (s);
- bad:
-       close (s);
-       return (-1);
-}
-
-void
-traphandler_shutdown(void)
-{
-       struct address          *h;
-
-       TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) {
-               event_del(&h->ev);
-               close(h->fd);
-       }
-}
-
-int
-traphandler_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
-{
-       switch (imsg->hdr.type) {
-       default:
-               break;
-       }
-
-       return (-1);
-}
-
-int
-snmpd_dispatch_traphandler(int fd, struct privsep_proc *p, struct imsg *imsg)
-{
-       switch (imsg->hdr.type) {
-       case IMSG_ALERT:
-               return (traphandler_priv_recvmsg(p, imsg));
-       default:
-               break;
-       }
-
-       return (-1);
-}
-
-void
-traphandler_recvmsg(int fd, short events, void *arg)
-{
-       struct privsep          *ps = arg;
-       char                     buf[8196];
-       struct iovec             iov[2];
-       struct sockaddr_storage  ss;
-       socklen_t                slen;
-       ssize_t                  n;
-       struct ber_element      *req, *iter;
-       struct ber_oid           trapoid;
-       u_int                    uptime;
-
-       slen = sizeof(ss);
-       if ((n = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *)&ss,
-           &slen)) == -1)
-               return;
-
-       if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
-               goto done;
-
-       iov[0].iov_base = &ss;
-       iov[0].iov_len = ss.ss_len;
-       iov[1].iov_base = buf;
-       iov[1].iov_len = n;
-
-       /* Forward it to the parent process */
-       if (proc_composev(ps, PROC_PARENT, IMSG_ALERT, iov, 2) == -1)
-               goto done;
-
- done:
-       if (req != NULL)
-               ober_free_elements(req);
-       return;
-}
-
 /*
- * Validate received message
+ * Validate received message let parent create a handler process.
  */
 int
-traphandler_parse(char *buf, size_t n, struct ber_element **req,
-    struct ber_element **vbinds, u_int *uptime, struct ber_oid *trapoid)
+traphandler_parse(struct snmp_message *msg)
 {
        struct ber               ber;
-       struct ber_element      *elm;
-       u_int                    vers, gtype, etype;
+       struct ber_oid           oid;
+       struct ber_element      *vb;
+       struct iovec             iov[2];
+       u_int                    uptime;
+       ssize_t                  len;
+       char                    *pdu;
+
+       if (traphandler_extract(msg, msg->sm_pdu, &uptime, &oid, &vb) == -1)
+               return -1;
 
        bzero(&ber, sizeof(ber));
        ober_set_application(&ber, smi_application);
-       ober_set_readbuf(&ber, buf, n);
 
-       if ((*req = ober_read_elements(&ber, NULL)) == NULL)
-               goto done;
+       if ((len = ober_write_elements(&ber, msg->sm_pdu)) == -1) {
+               /* ober_write_elements can only return ENOMEM on error */
+               msg->sm_errstr = "failed to handle trap: Cannot allocate 
memory";
+               ober_free(&ber);
+               return -1;
+       }
+       ober_get_writebuf(&ber, (void **)&pdu);
+
+       iov[0].iov_base = &(msg->sm_ss);
+       iov[0].iov_len = msg->sm_ss.ss_len;
+       iov[1].iov_base = pdu;
+       iov[1].iov_len = (size_t)len;
+
+       if (proc_composev(&(snmpd_env->sc_ps), PROC_PARENT, IMSG_ALERT, iov,
+           2) == -1) {
+               ober_free(&ber);
+               /* We need a better error reporting system, no errno for now */
+               msg->sm_errstr = "Failed to handle trap";
+               return -1;
+       }
 
-       if (ober_scanf_elements(*req, "{dSe", &vers, &elm) == -1)
-               goto done;
+       ober_free(&ber);
+       return (0);
+}
 
-       switch (vers) {
-       case SNMP_V1:
-               if (ober_scanf_elements(elm, "{oSddde",
-                   trapoid, &gtype, &etype, uptime, &elm) == -1)
-                       goto done;
-               traphandler_v1translate(trapoid, gtype, etype);
-               if (elm->be_type != BER_TYPE_SEQUENCE)
-                       goto done;
-               *vbinds = elm->be_sub;
+int
+traphandler_extract(struct snmp_message *msg, struct ber_element *pdu,
+    u_int *uptime, struct ber_oid *trapoid, struct ber_element **vb0)
+{
+       struct ber_oid           oid;
+       u_int                    class, gtype, etype;
+       unsigned int             type;
+       struct ber_element      *vb, *tmp;
+       /* OK to set stats twice, they only count in the snmpe process */
+       struct snmp_stats       *stats = &(snmpd_env->sc_stats);
+       char *buf;
+
+       if (ober_scanf_elements(pdu, "t", &class, &type) != 0) {
+               msg->sm_errstr = "Invalid trap format";
+               stats->snmp_inasnparseerrs++;
+               return -1;
+       }
+
+       switch (type) {
+       case SNMP_C_TRAP:
+               if (ober_scanf_elements(pdu, "{oSddd{e", trapoid, &gtype, 
&etype,
+                   &uptime, vb0) == -1) {
+                       msg->sm_errstr = "Invalid trap format";
+                       stats->snmp_inasnparseerrs++;
+                       return -1;
+               }
                break;
-
-       case SNMP_V2:
-               if (ober_scanf_elements(elm, "{SSS{e}}", &elm) == -1 ||
-                   ober_scanf_elements(elm, "{Sd}{So}",
-                   uptime, trapoid) == -1)
-                       goto done;
-               *vbinds = elm->be_next->be_next;
+       case SNMP_C_TRAPV2:
+               if (ober_scanf_elements(pdu, "{SSS{e", &vb) == -1 ||
+                   ober_scanf_elements(vb, "{Sd}{So}",
+                   &uptime, trapoid) == -1) {
+                       msg->sm_errstr = "Invalid trapv2 format";
+                       stats->snmp_inasnparseerrs++;
+                       return -1;
+               }
+               *vb0 = vb->be_next->be_next;
                break;
-
        default:
-               log_warnx("unsupported SNMP trap version '%d'", vers);
-               goto done;
+               msg->sm_errstr = "invalid pdu type";
+               stats->snmp_inasnparseerrs++;
+               return -1;
        }
 
-       ober_free(&ber);
-       return (0);
+       for (vb = *vb0; vb != NULL; vb = vb->be_next) {
+               if (ober_scanf_elements(vb, "{oe}", &oid, &tmp) == -1 ||
+                   (buf = smi_print_element(tmp)) == NULL) {
+                       msg->sm_errstr = "invalid varbind in trap";
+                       stats->snmp_inasnparseerrs++;
+                       return -1;
+               }
+               free(buf);
+       }
 
- done:
-       ober_free(&ber);
-       if (*req)
-               ober_free_elements(*req);
-       *req = NULL;
-       return (-1);
+       return 0;
 }
 
 void
@@ -312,7 +203,8 @@ traphandler_fork_handler(struct privsep_
        struct sockaddr         *sa;
        char                    *buf;
        ssize_t                  n;
-       struct ber_element      *req, *iter;
+       struct ber               ber;
+       struct ber_element      *iter, *pdu;
        struct trapcmd          *cmd;
        struct ber_oid           trapoid;
        u_int                    uptime;
@@ -339,15 +231,21 @@ traphandler_fork_handler(struct privsep_
        n -= sa->sa_len;
        buf = (char *)imsg->data + sa->sa_len;
 
-       if (traphandler_parse(buf, n, &req, &iter, &uptime, &trapoid) == -1)
+       bzero(&ber, sizeof(ber));
+       ober_set_application(&ber, smi_application);
+       ober_set_readbuf(&ber, buf, n);
+       if ((pdu = ober_read_elements(&ber, NULL)) == NULL)
+               fatal("traphandler_fork_handler: ober_read_elements");
+
+       if (traphandler_extract(NULL, pdu, &uptime, &trapoid, &iter) == -1)
                fatalx("couldn't parse SNMP trap message");
 
        smi_oid2string(&trapoid, oidbuf, sizeof(oidbuf), 0);
        if ((cmd = trapcmd_lookup(&trapoid)) != NULL)
                trapcmd_exec(cmd, sa, iter, oidbuf, uptime);
 
-       if (req != NULL)
-               ober_free_elements(req);
+       ober_free_elements(pdu);
+       ober_free(&ber);
 
        exit(0);
 }


Reply via email to