I would like to move snmpd(8) to a more modular dispatch/transport map
system, but that's going to be quite the undertaking. To make things a
little better to grok I'd like to split of the trap receiver components
out of struct address as a first step. While doing this I can also
greatly simplify the code for resolving hosts.
According parse.y's cvs log:
revision 1.15
date: 2008/07/18 12:35:27;  author: reyk;  state: Exp;  lines: +22 -14;
merge host_v6 with relayd's version to use getaddrinfo instead of
inet_pton.  host_v4, host_v6, and host_dns could be merged into one
function using getaddrinfo but i keep it in multiple functions to keep
it in sync with the other daemons using this common code.

Since I intent to completely overhaul this part of the code I see no
reason to keep this in sync.

No functional change intended, except that trap receiver's
source-address might now also be a resolvable hostname.

OK?

martijn@

Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/parse.y,v
retrieving revision 1.59
diff -u -p -r1.59 parse.y
--- parse.y     23 Aug 2020 07:39:57 -0000      1.59
+++ parse.y     25 Aug 2020 19:52:01 -0000
@@ -91,16 +91,11 @@ char                *symget(const char *);
 
 struct snmpd                   *conf = NULL;
 static int                      errors = 0;
-static struct addresslist      *hlist;
 static struct usmuser          *user = NULL;
 
-struct address *host_v4(const char *);
-struct address *host_v6(const char *);
-int             host_dns(const char *, struct addresslist *,
-                   int, in_port_t, struct ber_oid *, char *,
-                   struct address *, int);
-int             host(const char *, struct addresslist *,
-                   int, in_port_t, struct ber_oid *, char *, char *, int);
+int             host(const char *, const char *, int,
+                   struct sockaddr_storage *, int);
+int             listen_add(struct sockaddr_storage *, int);
 
 typedef struct {
        union {
@@ -120,6 +115,8 @@ typedef struct {
        int lineno;
 } YYSTYPE;
 
+#define NELEM(a) (sizeof(a) / sizeof((a)[0]))
+
 %}
 
 %token INCLUDE
@@ -199,13 +196,26 @@ yesno             :  STRING                       {
                ;
 
 main           : LISTEN ON STRING proto        {
-                       if (host($3, &conf->sc_addresses, 16, SNMPD_PORT, NULL,
-                           NULL, NULL, $4) <= 0) {
-                               yyerror("invalid ip address: %s", $3);
+                       struct sockaddr_storage ss[16];
+                       int nhosts, i;
+
+                       nhosts = host($3, SNMPD_PORT, $4, ss, NELEM(ss));
+                       if (nhosts < 1) {
+                               yyerror("invalid address: %s", $3);
                                free($3);
                                YYERROR;
                        }
+                       if (nhosts > (int)NELEM(ss))
+                               log_warn("%s resolves to more than %zu hosts",
+                                   $3, NELEM(ss));
                        free($3);
+
+                       for (i = 0; i < nhosts; i++) {
+                               if (listen_add(&(ss[i]), $4) == -1) {
+                                       yyerror("calloc");
+                                       YYERROR;
+                               }
+                       }
                }
                | READONLY COMMUNITY STRING     {
                        if (strlcpy(conf->sc_rdcommunity, $3,
@@ -240,11 +250,7 @@ main               : LISTEN ON STRING proto        {
                        }
                        free($3);
                }
-               | TRAP RECEIVER                 {
-                       hlist = &conf->sc_trapreceivers;
-               } host                          {
-                       hlist = NULL;
-               }
+               | TRAP RECEIVER host
                | TRAP HANDLE hostcmn trapoid cmd {
                        struct trapcmd *cmd = $5.data;
 
@@ -420,13 +426,40 @@ srcaddr           : /* empty */                           
{ $$ = NULL; }
                ;
 
 hostdef                : STRING hostoid hostcmn srcaddr        {
-                       if (host($1, hlist, 1,
-                           SNMPD_TRAPPORT, $2, $3, $4, IPPROTO_UDP) <= 0) {
+                       struct sockaddr_storage ss;
+                       struct trap_address *tr;
+
+                       if ((tr = calloc(1, sizeof(*tr))) == NULL) {
+                               yyerror("calloc");
+                               YYERROR;
+                       }
+
+                       if (host($1, SNMPD_TRAPPORT, SOCK_DGRAM, &ss, 1) <= 0) {
                                yyerror("invalid host: %s", $1);
                                free($1);
+                               free($2);
+                               free($3);
+                               free($4);
+                               free(tr);
                                YYERROR;
                        }
                        free($1);
+                       bcopy(&ss, &(tr->ss), sizeof(ss));
+                       if ($4 != NULL) {
+                               if (host($1, "0", SOCK_DGRAM, &ss, 1) <= 0) {
+                                       yyerror("invalid host: %s", $1);
+                                       free($2);
+                                       free($3);
+                                       free($4);
+                                       free(tr);
+                                       YYERROR;
+                               }
+                               free($4);
+                               bcopy(&ss, &(tr->ss_local), sizeof(ss));
+                       }
+                       tr->sa_oid = $2;
+                       tr->sa_community = $3;
+                       TAILQ_INSERT_TAIL(&(conf->sc_trapreceivers), tr, entry);
                }
                ;
 
@@ -510,9 +543,9 @@ enc         : STRING                        {
                }
                ;
 
-proto          : /* empty */                   { $$ = IPPROTO_UDP; }
-               | TCP                           { $$ = IPPROTO_TCP; }
-               | UDP                           { $$ = IPPROTO_UDP; }
+proto          : /* empty */                   { $$ = SOCK_DGRAM; }
+               | TCP                           { $$ = SOCK_STREAM; }
+               | UDP                           { $$ = SOCK_DGRAM; }
                ;
 
 cmd            : STRING                {
@@ -985,6 +1018,7 @@ popfile(void)
 struct snmpd *
 parse_config(const char *filename, u_int flags)
 {
+       struct sockaddr_storage ss;
        struct sym      *sym, *next;
        struct address  *h;
        int found;
@@ -1017,15 +1051,19 @@ parse_config(const char *filename, u_int
 
        /* Setup default listen addresses */
        if (TAILQ_EMPTY(&conf->sc_addresses)) {
-               host("0.0.0.0", &conf->sc_addresses, 1, SNMPD_PORT,
-                   NULL, NULL, NULL, IPPROTO_UDP);
-               host("::", &conf->sc_addresses, 1, SNMPD_PORT,
-                   NULL, NULL, NULL, IPPROTO_UDP);
+               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)
+                       fatal("calloc");
+               if (host("::", SNMPD_PORT, SOCK_DGRAM, &ss, 1) != 1)
+                       fatal("Unexpected resolving of ::");
+               if (listen_add(&ss, SOCK_DGRAM) == -1)
+                       fatal("calloc");
        }
        if (conf->sc_traphandler) {
                found = 0;
                TAILQ_FOREACH(h, &conf->sc_addresses, entry) {
-                       if (h->ipproto == IPPROTO_UDP)
+                       if (h->type == SOCK_DGRAM)
                                found = 1;
                }
                if (!found) {
@@ -1128,167 +1166,55 @@ symget(const char *nam)
        return (NULL);
 }
 
-struct address *
-host_v4(const char *s)
-{
-       struct in_addr           ina;
-       struct sockaddr_in      *sain;
-       struct address          *h;
-
-       bzero(&ina, sizeof(ina));
-       if (inet_pton(AF_INET, s, &ina) != 1)
-               return (NULL);
-
-       if ((h = calloc(1, sizeof(*h))) == NULL)
-               fatal(__func__);
-       sain = (struct sockaddr_in *)&h->ss;
-       sain->sin_len = sizeof(struct sockaddr_in);
-       sain->sin_family = AF_INET;
-       sain->sin_addr.s_addr = ina.s_addr;
-
-       return (h);
-}
-
-struct address *
-host_v6(const char *s)
-{
-       struct addrinfo          hints, *res;
-       struct sockaddr_in6     *sa_in6;
-       struct address          *h = NULL;
-
-       bzero(&hints, sizeof(hints));
-       hints.ai_family = AF_INET6;
-       hints.ai_socktype = SOCK_DGRAM; /* dummy */
-       hints.ai_flags = AI_NUMERICHOST;
-       if (getaddrinfo(s, "0", &hints, &res) == 0) {
-               if ((h = calloc(1, sizeof(*h))) == NULL)
-                       fatal(__func__);
-               sa_in6 = (struct sockaddr_in6 *)&h->ss;
-               sa_in6->sin6_len = sizeof(struct sockaddr_in6);
-               sa_in6->sin6_family = AF_INET6;
-               memcpy(&sa_in6->sin6_addr,
-                   &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
-                   sizeof(sa_in6->sin6_addr));
-               sa_in6->sin6_scope_id =
-                   ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
-
-               freeaddrinfo(res);
-       }
-
-       return (h);
-}
-
 int
-host_dns(const char *s, struct addresslist *al, int max,
-    in_port_t port, struct ber_oid *oid, char *cmn,
-    struct address *src, int ipproto)
+host(const char *s, const char *port, int type, struct sockaddr_storage *ss,
+    int max)
 {
        struct addrinfo          hints, *res0, *res;
-       int                      error, cnt = 0;
-       struct sockaddr_in      *sain;
-       struct sockaddr_in6     *sin6;
-       struct address          *h;
+       int                      error, i;
 
        bzero(&hints, sizeof(hints));
        hints.ai_family = PF_UNSPEC;
-       hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
-       hints.ai_flags = AI_ADDRCONFIG;
-       error = getaddrinfo(s, NULL, &hints, &res0);
+       hints.ai_socktype = type;
+       error = getaddrinfo(s, port, &hints, &res0);
        if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
-               return (0);
+               return 0;
        if (error) {
-               log_warnx("host_dns: could not parse \"%s\": %s", s,
-                   gai_strerror(error));
-               return (-1);
+               log_warnx("Could not parse \"%s\": %s", s, gai_strerror(error));
+               return -1;
        }
 
-       for (res = res0; res && cnt < max; res = res->ai_next) {
+       for (i = 0, res = res0; res; res = res->ai_next, i++) {
                if (res->ai_family != AF_INET &&
                    res->ai_family != AF_INET6)
                        continue;
-               if (src != NULL && src->ss.ss_family != res->ai_family)
+               if (i >= max)
                        continue;
-               if ((h = calloc(1, sizeof(*h))) == NULL)
-                       fatal(__func__);
-
-               h->port = port;
-               h->ipproto = ipproto;
-               if (oid != NULL) {
-                       if ((h->sa_oid = calloc(1, sizeof(*oid))) == NULL)
-                               fatal(__func__);
-                       bcopy(oid, h->sa_oid, sizeof(*oid));
-               }
-               if (cmn != NULL) {
-                       if ((h->sa_community = strdup(cmn)) == NULL)
-                               fatal(__func__);
-               }
-
-               h->ss.ss_family = res->ai_family;
-               if (res->ai_family == AF_INET) {
-                       sain = (struct sockaddr_in *)&h->ss;
-                       sain->sin_len = sizeof(struct sockaddr_in);
-                       sain->sin_addr.s_addr = ((struct sockaddr_in *)
-                           res->ai_addr)->sin_addr.s_addr;
-               } else {
-                       sin6 = (struct sockaddr_in6 *)&h->ss;
-                       sin6->sin6_len = sizeof(struct sockaddr_in6);
-                       memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
-                           res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
-               }
-
-               h->sa_srcaddr = src;
 
-               TAILQ_INSERT_TAIL(al, h, entry);
-               cnt++;
-       }
-       if (cnt == max && res) {
-               log_warnx("host_dns: %s resolves to more than %d hosts",
-                   s, max);
+               bcopy(res->ai_addr, &(ss[i]), res->ai_addrlen);
        }
        freeaddrinfo(res0);
-       if (oid != NULL)
-               free(oid);
-       if (cmn != NULL)
-               free(cmn);
-       return (cnt);
+       return i;
 }
 
 int
-host(const char *s, struct addresslist *al, int max,
-    in_port_t port, struct ber_oid *oid, char *cmn, char *srcaddr, int ipproto)
+listen_add(struct sockaddr_storage *ss, int type)
 {
-       struct address  *h, *src = NULL;
-
-       if (srcaddr != NULL) {
-               src = host_v4(srcaddr);
-               if (src == NULL)
-                       src = host_v6(srcaddr);
-               if (src == NULL) {
-                       log_warnx("invalid source-address %s", srcaddr);
-                       return (-1);
-               }
-       }
-
-       h = host_v4(s);
+       struct sockaddr_in *sin;
+       struct sockaddr_in6 *sin6;
+       struct address *h;
 
-       /* IPv6 address? */
-       if (h == NULL)
-               h = host_v6(s);
-
-       if (h != NULL) {
-               h->port = port;
-               h->sa_oid = oid;
-               h->sa_community = cmn;
-               h->ipproto = ipproto;
-               if (src != NULL && h->ss.ss_family != src->ss.ss_family) {
-                       log_warnx("host and source-address family mismatch");
-                       return (-1);
-               }
-               h->sa_srcaddr = src;
-
-               TAILQ_INSERT_TAIL(al, h, entry);
-               return (1);
-       }
-
-       return (host_dns(s, al, max, port, oid, cmn, src, ipproto));
+       if ((h = calloc(1, sizeof(*h))) == NULL)
+               return -1;
+       bcopy(ss, &(h->ss), sizeof(*ss));
+       if (ss->ss_family == AF_INET) {
+               sin = (struct sockaddr_in *)ss;
+               h->port = ntohs(sin->sin_port);
+       } else {
+               sin6 = (struct sockaddr_in6*)ss;
+               h->port = ntohs(sin6->sin6_port);
+       }
+       h->type = type;
+       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.41
diff -u -p -r1.41 snmpd.c
--- snmpd.c     8 Jan 2019 15:38:36 -0000       1.41
+++ snmpd.c     25 Aug 2020 19:52:01 -0000
@@ -310,33 +310,13 @@ snmpd_dispatch_snmpe(int fd, struct priv
 }
 
 int
-snmpd_socket_af(struct sockaddr_storage *ss, in_port_t port, int ipproto)
+snmpd_socket_af(struct sockaddr_storage *ss, int type)
 {
-       int      s;
-
-       switch (ss->ss_family) {
-       case AF_INET:
-               ((struct sockaddr_in *)ss)->sin_port = port;
-               ((struct sockaddr_in *)ss)->sin_len =
-                   sizeof(struct sockaddr_in);
-               break;
-       case AF_INET6:
-               ((struct sockaddr_in6 *)ss)->sin6_port = port;
-               ((struct sockaddr_in6 *)ss)->sin6_len =
-                   sizeof(struct sockaddr_in6);
-               break;
-       default:
-               return (-1);
-       }
-
-       if (ipproto == IPPROTO_TCP)
-               s = socket(ss->ss_family,
-                   SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
+       if (type == SOCK_STREAM)
+               return socket(ss->ss_family,
+                   SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
        else
-               s = socket(ss->ss_family,
-                   SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
-
-       return (s);
+               return socket(ss->ss_family, SOCK_DGRAM | SOCK_CLOEXEC, 0);
 }
 
 void
Index: snmpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.89
diff -u -p -r1.89 snmpd.h
--- snmpd.h     23 Aug 2020 07:39:57 -0000      1.89
+++ snmpd.h     25 Aug 2020 19:52:01 -0000
@@ -48,8 +48,8 @@
 #define CONF_FILE              "/etc/snmpd.conf"
 #define SNMPD_SOCKET           "/var/run/snmpd.sock"
 #define SNMPD_USER             "_snmpd"
-#define SNMPD_PORT             161
-#define SNMPD_TRAPPORT         162
+#define SNMPD_PORT             "161"
+#define SNMPD_TRAPPORT         "162"
 
 #define SNMPD_MAXSTRLEN                484
 #define SNMPD_MAXCOMMUNITYLEN  SNMPD_MAXSTRLEN
@@ -481,19 +481,24 @@ struct snmp_stats {
 struct address {
        struct sockaddr_storage  ss;
        in_port_t                port;
-       int                      ipproto;
+       int                      type;
        int                      fd;
        struct event             ev;
        struct event             evt;
 
        TAILQ_ENTRY(address)     entry;
+};
+TAILQ_HEAD(addresslist, address);
 
-       /* For SNMP trap receivers etc. */
+struct trap_address {
+       struct sockaddr_storage  ss;
+       struct sockaddr_storage  ss_local;
        char                    *sa_community;
        struct ber_oid          *sa_oid;
-       struct address          *sa_srcaddr;
+
+       TAILQ_ENTRY(trap_address) entry;
 };
-TAILQ_HEAD(addresslist, address);
+TAILQ_HEAD(trap_addresslist, trap_address);
 
 enum usmauth {
        AUTH_NONE = 0,
@@ -551,7 +556,7 @@ struct snmpd {
 
        struct snmp_stats        sc_stats;
 
-       struct addresslist       sc_trapreceivers;
+       struct trap_addresslist  sc_trapreceivers;
 
        int                      sc_ncpu;
        int64_t                 *sc_cpustates;
@@ -706,7 +711,7 @@ char                *smi_print_element(struct ber_elem
 void            timer_init(void);
 
 /* snmpd.c */
-int             snmpd_socket_af(struct sockaddr_storage *, in_port_t, int);
+int             snmpd_socket_af(struct sockaddr_storage *, int);
 u_long          snmpd_engine_time(void);
 char           *tohexstr(u_int8_t *, int);
 
Index: snmpe.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.65
diff -u -p -r1.65 snmpe.c
--- snmpe.c     23 Aug 2020 07:39:57 -0000      1.65
+++ snmpe.c     25 Aug 2020 19:52:01 -0000
@@ -102,7 +102,7 @@ snmpe_init(struct privsep *ps, struct pr
 
        /* listen for incoming SNMP UDP/TCP messages */
        TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-               if (h->ipproto == IPPROTO_TCP) {
+               if (h->type == SOCK_STREAM) {
                        if (listen(h->fd, 5) < 0)
                                fatalx("snmpe: failed to listen on socket");
                        event_set(&h->ev, h->fd, EV_READ, snmpe_acceptcb, h);
@@ -128,7 +128,7 @@ snmpe_shutdown(void)
 
        TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) {
                event_del(&h->ev);
-               if (h->ipproto == IPPROTO_TCP)
+               if (h->type == SOCK_STREAM)
                        event_del(&h->evt);
                close(h->fd);
        }
@@ -152,8 +152,7 @@ snmpe_bind(struct address *addr)
        char     buf[512];
        int      val, s;
 
-       if ((s = snmpd_socket_af(&addr->ss, htons(addr->port),
-           addr->ipproto)) == -1)
+       if ((s = snmpd_socket_af(&addr->ss, addr->type)) == -1)
                return (-1);
 
        /*
@@ -162,7 +161,7 @@ snmpe_bind(struct address *addr)
        if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
                goto bad;
 
-       if (addr->ipproto == IPPROTO_TCP) {
+       if (addr->type == SOCK_STREAM) {
                val = 1;
                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
                    &val, sizeof(val)) == -1)
@@ -196,7 +195,7 @@ snmpe_bind(struct address *addr)
                goto bad;
 
        log_info("snmpe: listening on %s %s:%d",
-           (addr->ipproto == IPPROTO_TCP) ? "tcp" : "udp", buf, addr->port);
+           (addr->type == SOCK_STREAM) ? "tcp" : "udp", buf, addr->port);
 
        return (s);
 
Index: trap.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/trap.c,v
retrieving revision 1.35
diff -u -p -r1.35 trap.c
--- trap.c      30 Jun 2020 17:11:49 -0000      1.35
+++ trap.c      25 Aug 2020 19:52:01 -0000
@@ -55,7 +55,7 @@ int
 trap_send(struct ber_oid *oid, struct ber_element *elm)
 {
        int                      ret = 0, s;
-       struct address          *tr;
+       struct trap_address     *tr;
        struct ber_element      *root, *b, *c, *trap;
        struct ber               ber;
        char                    *cmn;
@@ -104,14 +104,13 @@ trap_send(struct ber_oid *oid, struct be
                                continue;
                }
 
-               if ((s = snmpd_socket_af(&tr->ss, htons(tr->port),
-                   IPPROTO_UDP)) == -1) {
+               if ((s = snmpd_socket_af(&tr->ss, SOCK_DGRAM)) == -1) {
                        ret = -1;
                        goto done;
                }
-               if (tr->sa_srcaddr != NULL) {
-                       if (bind(s, (struct sockaddr *)&tr->sa_srcaddr->ss,
-                           tr->sa_srcaddr->ss.ss_len) == -1) {
+               if (tr->ss_local.ss_family != 0) {
+                       if (bind(s, (struct sockaddr *)&(tr->ss_local),
+                           tr->ss_local.ss_len) == -1) {
                                ret = -1;
                                goto done;
                        }
Index: traphandler.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/traphandler.c,v
retrieving revision 1.17
diff -u -p -r1.17 traphandler.c
--- traphandler.c       23 Aug 2020 07:39:57 -0000      1.17
+++ traphandler.c       25 Aug 2020 19:52:01 -0000
@@ -77,7 +77,7 @@ traphandler(struct privsep *ps, struct p
 
        if (env->sc_traphandler) {
                TAILQ_FOREACH(h, &env->sc_addresses, entry) {
-                       if (h->ipproto != IPPROTO_UDP)
+                       if (h->type != SOCK_DGRAM)
                                continue;
                        if ((h->fd = traphandler_bind(h)) == -1)
                                fatal("could not create trap listener socket");
@@ -112,9 +112,17 @@ traphandler_bind(struct address *addr)
 {
        int                      s;
        char                     buf[512];
+       struct sockaddr_in      *sin;
+       struct sockaddr_in6     *sin6;
 
-       if ((s = snmpd_socket_af(&addr->ss, htons(SNMPD_TRAPPORT),
-           IPPROTO_UDP)) == -1)
+       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)
@@ -126,7 +134,7 @@ traphandler_bind(struct address *addr)
        if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
                goto bad;
 
-       log_info("traphandler: listening on %s:%d", buf, SNMPD_TRAPPORT);
+       log_info("traphandler: listening on %s:%s", buf, SNMPD_TRAPPORT);
 
        return (s);
  bad:

Reply via email to