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: