/* * Copyright (c) 2018 * Hartmut Brandt. * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $ * * Internet transport */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "snmpd.h" #define SNMPTREE_TYPES #define SNMPENUM_FUNCS #include "tree.h" #include "oid.h" extern const struct transport_def inet_trans; struct inet_port; struct inet_port_params; struct port_sock; typedef int create_func(struct inet_port *, struct inet_port_params *); typedef void input_func(int, void *); typedef int activate_func(struct inet_port *); typedef void deactivate_func(struct inet_port *); typedef void parse_ctrl_func(struct port_sock *, const struct msghdr *); typedef void setsrc_func(struct port_sock *, struct msghdr *, char *); static create_func ipv4_create; static input_func ipv4_input; static activate_func ipv4_activate; static deactivate_func ipv4_deactivate; static parse_ctrl_func ipv4_parse_ctrl; static setsrc_func ipv4_setsrc; static create_func ipv6_create; static input_func ipv6_input; static activate_func ipv6_activate; static deactivate_func ipv6_deactivate; static parse_ctrl_func ipv6_parse_ctrl; static setsrc_func ipv6_setsrc; static create_func ipv6z_create; static create_func dns_create; static activate_func dns_activate; static deactivate_func dns_deactivate; struct port_sock { /* common input stuff; must be first */ struct port_input input; /** link field */ TAILQ_ENTRY(port_sock) link; /** pointer to parent */ struct inet_port *port; /** bind address */ struct sockaddr_storage bind_addr; /** reply destination */ struct sockaddr_storage ret_dest; /** need to set source address in reply; set for INADDR_ANY */ bool set_ret_source; /** address of the receive interface */ union { /** IPv4 case */ struct in_addr a4; /** IPv6 case */ struct in6_pktinfo a6; } ret_source; /** parse control message */ parse_ctrl_func *parse_ctrl; /** set source address for a send() */ setsrc_func *setsrc; }; static_assert(offsetof(struct port_sock, input) == 0, "input not first in port_sock"); /** * Table row for the inet ports. * * When actived each row can have one or several open sockets. For ipv6, * ipv4 and ipv6z addresses it is always one, for dns addresses more than * one socket can be open. */ struct inet_port { /** common i/o port stuff (must be first) */ struct tport tport; /** transport protocol */ enum BegemotSnmpdTransportProto proto; /** row status */ enum RowStatus row_status; /** socket list */ TAILQ_HEAD(, port_sock) socks; /** value for InetAddressType::dns */ char *dns_addr; /** port number in dns case; network byte order */ uint16_t dns_port; /** create a port */ create_func *create; /** activate a port */ activate_func *activate; /** deactivate port */ deactivate_func *deactivate; }; static_assert(offsetof(struct inet_port, tport) == 0, "tport not first in inet_port"); /** to be used in bind_addr field */ #define AF_DNS AF_VENDOR00 /** registered transport */ static struct transport *my_trans; /** set operation */ enum { SET_CREATED, SET_ACTIVATED, SET_DEACTIVATE, SET_DESTROY, }; /** length of the control data buffer */ static const size_t RECV_CBUF_SIZE = MAX(CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) + CMSG_SPACE(sizeof(struct in6_pktinfo))); /** length of the control data buffer */ static const size_t XMIT_CBUF_SIZE = MAX(CMSG_SPACE(sizeof(struct in_addr)), CMSG_SPACE(sizeof(struct in6_pktinfo))); /** * Start the transport. This registers the transport with the * transport table. * * \return SNMP error code */ static int inet_start(void) { return (trans_register(&inet_trans, &my_trans)); } /** * Stop the transport. This tries to unregister the transport which * in turn fails if the list of ports is not empty. * * \return SNMP error code */ static int inet_stop(int force __unused) { if (my_trans != NULL) if (trans_unregister(my_trans) != 0) return (SNMP_ERR_GENERR); return (SNMP_ERR_NOERROR); } /** * Deactivate SNMP port. * * \param tp port to close */ static void deactivate_port(struct inet_port *p) { p->deactivate(p); } /* * This function activates a port. For ports opened via the config files * this is called just before entering the event loop. For ports create * during runtime this is called when the RowStatus is set to Active or * as second step for CreateAndGo. * * \param tp transport port * * \return SNMP error code */ static int inet_activate(struct tport *tp) { struct inet_port *port = (struct inet_port *)tp; return (port->activate(port)); } /* * Close the SNMP port if it is open and destroy it. * * \param tp port to close */ static void inet_destroy_port(struct tport *tp) { struct inet_port *port = (struct inet_port *)tp; deactivate_port(port); trans_remove_port(tp); free(port->dns_addr); free(port); } /** * If the input struct has no buffer allocated yet, do it now. If allocation * fails, read the data into a local buffer and drop it. * * \param pi input struct * * \return -1 if allocation fails, 0 otherwise */ static int inet_alloc_buf(struct port_input *pi) { char drop_buf[2000]; if (pi->buf == NULL) { if ((pi->buf = buf_alloc(0)) == NULL) { (void)recvfrom(pi->fd, drop_buf, sizeof(drop_buf), 0, NULL, NULL); return (-1); } pi->buflen = buf_size(0); } return (0); } /** * Read message into input buffer. Get also the source address and any * control data that is available. If the message is truncated, increment * corresponding statistics. * * \param pi input object * \param msg message object to fill * \param cbuf control data buffer * * \return -1 when something goes wrong, 0 othersise */ static int inet_read_msg(struct port_input *pi, struct msghdr *msg, char *cbuf) { struct iovec iov[1]; iov[0].iov_base = pi->buf; iov[0].iov_len = pi->buflen; msg->msg_name = pi->peer; msg->msg_namelen = pi->peerlen; msg->msg_iov = iov; msg->msg_iovlen = 1; msg->msg_control = cbuf; msg->msg_controllen = RECV_CBUF_SIZE; msg->msg_flags = 0; memset(cbuf, 0, RECV_CBUF_SIZE); const ssize_t len = recvmsg(pi->fd, msg, 0); if (len == -1 || len == 0) /* receive error */ return (-1); if (msg->msg_flags & MSG_TRUNC) { /* truncated - drop */ snmpd_stats.silentDrops++; snmpd_stats.inTooLong++; return (-1); } pi->length = (size_t)len; return (0); } /* * Input available on socket. * * \param tp transport port * \param pi input struct * * \return number of bytes received */ static ssize_t inet_recv(struct tport *tp, struct port_input *pi) { struct inet_port *port = __containerof(tp, struct inet_port, tport); struct port_sock *sock = __containerof(pi, struct port_sock, input); assert(port->proto == BegemotSnmpdTransportProto_udp); if (inet_alloc_buf(pi) == -1) return (-1); char cbuf[RECV_CBUF_SIZE]; struct msghdr msg; if (inet_read_msg(pi, &msg, cbuf) == -1) return (-1); sock->parse_ctrl(sock, &msg); return (0); } /* * Send message. * * \param tp port * \param buf data to send * \param len number of bytes to send * \param addr destination address * \param addlen destination address length * * \return number of bytes sent */ static ssize_t inet_send2(struct tport *tp, const u_char *buf, size_t len, struct port_input *pi) { struct inet_port *p = __containerof(tp, struct inet_port, tport); struct port_sock *s = (pi == NULL) ? TAILQ_FIRST(&p->socks) : __containerof(pi, struct port_sock, input); struct iovec iov; iov.iov_base = __DECONST(void*, buf); iov.iov_len = len; struct msghdr msg; msg.msg_flags = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_name = (void *)pi->peer; msg.msg_namelen = pi->peerlen; char cbuf[XMIT_CBUF_SIZE]; if (s->set_ret_source) { s->setsrc(s, &msg, cbuf); } else { msg.msg_control = NULL; msg.msg_controllen = 0; } return (sendmsg(s->input.fd, &msg, 0)); } /** exported to daemon */ const struct transport_def inet_trans = { "inet", OIDX_begemotSnmpdTransInet, inet_start, inet_stop, inet_destroy_port, inet_activate, NULL, inet_recv, inet_send2, }; struct inet_port_params { /** index oid */ struct asn_oid index; /** internet address type */ enum InetAddressType type; /** internet address */ u_char *addr; /** length of address */ size_t addr_len; /** port number */ uint32_t port; /** protocol */ enum BegemotSnmpdTransportProto proto; }; /** * IPv4 creation stuff. Parse the index, fill socket address and creates * a port_sock. * * \param port the port to create * \param params parameters from the SNMP SET * * \return SNMP error */ static int ipv4_create(struct inet_port *port, struct inet_port_params *params) { if (params->addr_len != 4) return (SNMP_ERR_INCONS_VALUE); struct port_sock *sock = calloc(1, sizeof(struct port_sock)); if (sock == NULL) return (SNMP_ERR_GENERR); snmpd_input_init(&sock->input); TAILQ_INSERT_HEAD(&port->socks, sock, link); struct sockaddr_in *sin = (struct sockaddr_in *)&sock->bind_addr; sin->sin_len = sizeof(struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = htons(params->port); memcpy(&sin->sin_addr, params->addr, 4); /* network byte order */ sock->port = port; return (SNMP_ERR_NOERROR); } /* * An IPv4 inet port is ready. Delegate to the generic code to read the data * and react. * * \param fd file descriptor that is ready * \param udata inet_port pointer */ static void ipv4_input(int fd __unused, void *udata) { struct port_sock *sock = udata; sock->input.peerlen = sizeof(struct sockaddr_in); snmpd_input(&sock->input, &sock->port->tport); } /** * Activate an IPv4 socket. * * \param sock thhe socket to activate * * \return error code */ static int ipv4_activate_sock(struct port_sock *sock) { if ((sock->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "creating UDP4 socket: %m"); return (SNMP_ERR_RES_UNAVAIL); } const struct sockaddr_in *sin = (const struct sockaddr_in *)&sock->bind_addr; if (sin->sin_addr.s_addr == INADDR_ANY) { /* need to know from which address to return */ static const int on = 1; if (setsockopt(sock->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on)) == -1) { syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m"); (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_GENERR); } sock->set_ret_source = true; } if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) { if (errno == EADDRNOTAVAIL) { (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_INCONS_NAME); } syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_GENERR); } if ((sock->input.id = fd_select(sock->input.fd, ipv4_input, sock, NULL)) == NULL) { (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_GENERR); } sock->input.peer = (struct sockaddr *)&sock->ret_dest; sock->parse_ctrl = ipv4_parse_ctrl; sock->setsrc = ipv4_setsrc; return (SNMP_ERR_NOERROR); } /** * Open an IPv4 socket. Make the socket, bind it and put it on the select * list. The socket struct has already been created during creation. * * \param p inet port * * \return SNMP error code */ static int ipv4_activate(struct inet_port *p) { struct port_sock *sock = TAILQ_FIRST(&p->socks); assert(sock); assert(!TAILQ_NEXT(sock, link)); const int ret = ipv4_activate_sock(sock); if (ret == SNMP_ERR_NOERROR) p->row_status = RowStatus_active; return (ret); } /** * Close an IPv4 socket. Keep the sock object. * * \param p inet port */ static void ipv4_deactivate(struct inet_port *p) { struct port_sock *sock = TAILQ_FIRST(&p->socks); assert(sock); assert(!TAILQ_NEXT(sock, link)); snmpd_input_close(&sock->input); p->row_status = RowStatus_notInService; } /** * Parse the control data received with a UDPv4 packet. This may contain * credentials (for a local connection) and the address of the interface * the message was received on. If there are credentials set the priv flag * if the effective UID is zero. * * \param sock the sock object * \param msg the received message */ static void ipv4_parse_ctrl(struct port_sock *sock, const struct msghdr *msg) { struct sockcred *cred = NULL; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_RECVDSTADDR) { memcpy(&sock->ret_source.a4, CMSG_DATA(cmsg), sizeof(struct in_addr)); } else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) { cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); } } sock->input.priv = 0; if (sock->input.cred && cred) /* remote end has sent credentials */ sock->input.priv = (cred->sc_euid == 0); } /** * Set the source address option for IPv4 sockets. * * \param sock socket object * \param msg message */ static void ipv4_setsrc(struct port_sock *sock, struct msghdr *msg, char *cbuf) { struct cmsghdr *cmsg; msg->msg_control = cbuf; msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr)); /* select outgoing interface by setting source address */ cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = IPPROTO_IP; cmsg->cmsg_type = IP_SENDSRCADDR; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr)); memcpy(CMSG_DATA(cmsg), &sock->ret_source.a4, sizeof(struct in_addr)); } /** * Common part of IPv6 creation. This is used by both ipv6_create() and * ipv6z_create(). * * \param port the table row * \param params creation parameters * \param scope_id scope_id (0 or from index) * * \return SNMP_ERR_NOERROR if port has been created, error code otherwise */ static int ipv6_create_common(struct inet_port *port, struct inet_port_params *params, u_int scope_id) { struct port_sock *sock = calloc(1, sizeof(struct port_sock)); if (sock == NULL) return (SNMP_ERR_GENERR); struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&sock->bind_addr; sin->sin6_len = sizeof(struct sockaddr_in6); sin->sin6_family = AF_INET6; sin->sin6_port = htons(params->port); sin->sin6_flowinfo = 0; sin->sin6_scope_id = scope_id; memcpy(sin->sin6_addr.s6_addr, params->addr, 16); if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && scope_id == 0) { char buf[INET6_ADDRSTRLEN]; syslog(LOG_INFO, "%s: link local address used without scope " "index: %s", __func__, inet_ntop(AF_INET6, &sin->sin6_addr, buf, sizeof(buf))); free(sock); return (SNMP_ERR_NO_CREATION); } sock->port = port; snmpd_input_init(&sock->input); TAILQ_INSERT_HEAD(&port->socks, sock, link); return (SNMP_ERR_NOERROR); } /** * IPv6 creation stuff. Parse the index, fill socket address and creates * a port_sock. * * \param port the port to create * \param params parameters from the SNMP SET * * \return SNMP error */ static int ipv6_create(struct inet_port *port, struct inet_port_params *params) { if (params->addr_len != 16) return (SNMP_ERR_INCONS_VALUE); const int ret = ipv6_create_common(port, params, 0); if (ret != SNMP_ERR_NOERROR) return (ret); return (SNMP_ERR_NOERROR); } /* * An IPv6 inet port is ready. Delegate to the generic code to read the data * and react. * * \param fd file descriptor that is ready * \param udata inet_port pointer */ static void ipv6_input(int fd __unused, void *udata) { struct port_sock *sock = udata; sock->input.peerlen = sizeof(struct sockaddr_in6); snmpd_input(&sock->input, &sock->port->tport); } /** * Activate an IPv6 socket. * * \param sock thhe socket to activate * * \return error code */ static int ipv6_activate_sock(struct port_sock *sock) { if ((sock->input.fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) { syslog(LOG_ERR, "creating UDP6 socket: %m"); return (SNMP_ERR_RES_UNAVAIL); } const struct sockaddr_in6 *sin = (const struct sockaddr_in6 *)&sock->bind_addr; if (memcmp(&sin->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) { /* need to know from which address to return */ static const int on = 1; if (setsockopt(sock->input.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) == -1) { syslog(LOG_ERR, "setsockopt(IP6_PKTINFO): %m"); (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_GENERR); } sock->set_ret_source = true; } if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) { if (community != COMM_INITIALIZE && errno == EADDRNOTAVAIL) { (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_INCONS_NAME); } char buf[INET6_ADDRSTRLEN]; syslog(LOG_ERR, "bind: %s:%u:%u %m", inet_ntop(AF_INET6, &sin->sin6_addr, buf, sizeof(buf)), sin->sin6_scope_id, ntohs(sin->sin6_port)); (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_GENERR); } if ((sock->input.id = fd_select(sock->input.fd, ipv6_input, sock, NULL)) == NULL) { (void)close(sock->input.fd); sock->input.fd = -1; return (SNMP_ERR_GENERR); } sock->input.peer = (struct sockaddr *)&sock->ret_dest; sock->parse_ctrl = ipv6_parse_ctrl; sock->setsrc = ipv6_setsrc; return (SNMP_ERR_NOERROR); } /** * Open an IPv6 socket. * * \param port inet port * * \return SNMP error code */ static int ipv6_activate(struct inet_port *p) { struct port_sock *sock = TAILQ_FIRST(&p->socks); assert(sock); const int ret = ipv6_activate_sock(sock); if (ret == SNMP_ERR_NOERROR) p->row_status = RowStatus_active; return (ret); } /** * Close an IPv6 socket. Keep the sock object. * * \param p inet port */ static void ipv6_deactivate(struct inet_port *p) { struct port_sock *sock = TAILQ_FIRST(&p->socks); assert(sock); assert(!TAILQ_NEXT(sock, link)); snmpd_input_close(&sock->input); p->row_status = RowStatus_notInService; } /** * IPv6 specific part of message processing. The control data may contain * credentials and packet info that contains the destination address of * the packet. * * \param sock the sock object * \param msg the received message */ static void ipv6_parse_ctrl(struct port_sock *sock, const struct msghdr *msg) { struct sockcred *cred = NULL; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { if (cmsg->cmsg_level == IPPROTO_IPV6 && cmsg->cmsg_type == IPV6_PKTINFO) { const struct in6_pktinfo *info = (const struct in6_pktinfo *)(const void *) CMSG_DATA(cmsg); sock->ret_source.a6.ipi6_addr = info->ipi6_addr; sock->ret_source.a6.ipi6_ifindex = !IN6_IS_ADDR_LINKLOCAL(&info->ipi6_addr) ? 0: info->ipi6_ifindex; } else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS) { cred = (struct sockcred *)(void *)CMSG_DATA(cmsg); } } sock->input.priv = 0; if (sock->input.cred && cred) /* remote end has sent credentials */ sock->input.priv = (cred->sc_euid == 0); } /** * Set the source address option for IPv4 sockets. * * \param sock socket object * \param msg message */ static void ipv6_setsrc(struct port_sock *sock, struct msghdr *msg, char *cbuf) { struct cmsghdr *cmsg; msg->msg_control = cbuf; msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo)); /* select outgoing interface by setting source address */ cmsg = CMSG_FIRSTHDR(msg); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo)); memcpy(CMSG_DATA(cmsg), &sock->ret_source.a6, sizeof(struct in6_pktinfo)); } /** * IPv6z creation stuff. Parse the index, fill socket address and creates * a port_sock. * * \param port the port to create * \param params parameters from the SNMP SET * * \return SNMP error */ static int ipv6z_create(struct inet_port *port, struct inet_port_params *params) { if (params->addr_len != 20) return (SNMP_ERR_INCONS_VALUE); u_int scope_id = 0; for (u_int i = 16; i < 20; i++) { scope_id <<= 8; scope_id |= params->addr[i]; } const int ret = ipv6_create_common(port, params, scope_id); if (ret != SNMP_ERR_NOERROR) return (ret); return (SNMP_ERR_NOERROR); } /** * DNS name creation stuff. Parse the index and save the string. * This does not create a socket struct. * * \param port the port to create * \param params parameters from the SNMP SET * * \return SNMP error */ static int dns_create(struct inet_port *port, struct inet_port_params *params) { if (params->addr_len > 64) return (SNMP_ERR_INCONS_VALUE); if (strnlen(params->addr, params->addr_len) != params->addr_len) return (SNMP_ERR_INCONS_VALUE); if ((port->dns_addr = realloc(params->addr, params->addr_len + 1)) == NULL) return (SNMP_ERR_GENERR); port->dns_addr[params->addr_len] = '\0'; params->addr = NULL; port->dns_port = htons(params->port); return (SNMP_ERR_NOERROR); } /** * Open sockets. This loops through all the addresses returned by getaddrinfo * and opens a socket for each of them. * * \param port inet port * * \return SNMP error code */ static int dns_activate(struct inet_port *port) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; // XXX udp-only hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV; char portbuf[8]; sprintf(portbuf, "%hu", ntohs(port->dns_port)); struct addrinfo *res0; int error = getaddrinfo(port->dns_addr[0] == '\0' ? NULL : port->dns_addr, portbuf, &hints, &res0); if (error) { syslog(LOG_ERR, "cannot resolve address '%s': %s", port->dns_addr, gai_strerror(error)); return (SNMP_ERR_GENERR); } for (struct addrinfo *res = res0; res != NULL; res = res->ai_next) { if (res->ai_family != AF_INET && res->ai_family != AF_INET6) continue; struct port_sock *sock = calloc(1, sizeof(struct port_sock)); if (sock == NULL) return (SNMP_ERR_GENERR); snmpd_input_init(&sock->input); sock->port = port; int ret = SNMP_ERR_NOERROR; if (res->ai_family == AF_INET) { *(struct sockaddr_in *)&sock->bind_addr = *(struct sockaddr_in *)(void *)res->ai_addr; ret = ipv4_activate_sock(sock); } else { *(struct sockaddr_in6 *)&sock->bind_addr = *(struct sockaddr_in6 *)(void *)res->ai_addr; ret = ipv6_activate_sock(sock); } if (ret != SNMP_ERR_NOERROR) free(sock); else TAILQ_INSERT_HEAD(&port->socks, sock, link); } if (!TAILQ_EMPTY(&port->socks)) port->row_status = RowStatus_active; freeaddrinfo(res0); return (SNMP_ERR_GENERR); } /** * Deactive the socket. Close all open sockets and delete all sock objects. * * \param port inet port */ static void dns_deactivate(struct inet_port *port) { while (!TAILQ_EMPTY(&port->socks)) { struct port_sock *sock = TAILQ_FIRST(&port->socks); TAILQ_REMOVE(&port->socks, sock, link); snmpd_input_close(&sock->input); free(sock); } port->row_status = RowStatus_notInService; } static int inet_create(struct inet_port_params *params, struct inet_port **pp) { int err = SNMP_ERR_NOERROR; struct inet_port *port = NULL; if (params->port > 0xffff) { err = SNMP_ERR_NO_CREATION; goto fail; } if ((port = malloc(sizeof(*port))) == NULL) { err = SNMP_ERR_GENERR; goto fail; } memset(port, 0, sizeof(*port)); TAILQ_INIT(&port->socks); port->proto = params->proto; port->tport.index = params->index; switch (params->type) { case InetAddressType_ipv4: port->create = ipv4_create; port->activate = ipv4_activate; port->deactivate = ipv4_deactivate; break; case InetAddressType_ipv6: port->create = ipv6_create; port->activate = ipv6_activate; port->deactivate = ipv6_deactivate; break; case InetAddressType_ipv6z: port->create = ipv6z_create; port->activate = ipv6_activate; port->deactivate = ipv6_deactivate; break; case InetAddressType_dns: port->create = dns_create; port->activate = dns_activate; port->deactivate = dns_deactivate; break; default: err = SNMP_ERR_NO_CREATION; goto fail; } if ((err = port->create(port, params)) != SNMP_ERR_NOERROR) goto fail; *pp = port; trans_insert_port(my_trans, &port->tport); return (err); fail: free(port->dns_addr); free(port); return (err); } static int create_and_go(struct snmp_context *ctx, struct inet_port_params *params) { int err; struct inet_port *port; if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR) return (err); port->row_status = RowStatus_createAndGo; ctx->scratch->ptr1 = port; if (community == COMM_INITIALIZE) return (err); return (inet_activate(&port->tport)); } static int create_and_wait(struct snmp_context *ctx, struct inet_port_params *params) { int err; struct inet_port *port; if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR) return (err); port->row_status = RowStatus_createAndWait; ctx->scratch->ptr1 = port; return (err); } /** * This is called to set a RowStatus value in the port table during * SET processing. * * When this is called during initialization time and the RowStatus is set * to CreateAndGo, the port is actually not activated. This is done when * the main code calls the init() for all ports later. */ static int inet_port_set(struct snmp_context *ctx, struct inet_port *port, struct inet_port_params *params, enum RowStatus nstatus) { switch (nstatus) { case RowStatus_createAndGo: if (port != NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = SET_CREATED; return (create_and_go(ctx, params)); case RowStatus_createAndWait: if (port != NULL) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = SET_CREATED; return (create_and_wait(ctx, params)); case RowStatus_active: if (port == NULL) return (SNMP_ERR_INCONS_VALUE); switch (port->row_status) { case RowStatus_notReady: /* this can not happend */ abort(); case RowStatus_notInService: ctx->scratch->int1 = SET_ACTIVATED; return (inet_activate(&port->tport)); case RowStatus_active: return (SNMP_ERR_NOERROR); case RowStatus_createAndGo: case RowStatus_createAndWait: case RowStatus_destroy: abort(); } break; case RowStatus_notInService: if (port == NULL) return (SNMP_ERR_INCONS_VALUE); switch (port->row_status) { case RowStatus_notReady: /* this can not happend */ abort(); case RowStatus_notInService: return (SNMP_ERR_NOERROR); case RowStatus_active: /* this is done during commit */ ctx->scratch->int1 = SET_DEACTIVATE; return (SNMP_ERR_NOERROR); case RowStatus_createAndGo: case RowStatus_createAndWait: case RowStatus_destroy: abort(); } break; case RowStatus_destroy: /* this is done during commit */ ctx->scratch->int1 = SET_DESTROY; return (SNMP_ERR_NOERROR); case RowStatus_notReady: return (SNMP_ERR_WRONG_VALUE); } abort(); } /* * Port table */ int op_snmp_trans_inet(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int iidx __unused, enum snmp_op op) { asn_subid_t which = value->var.subs[sub - 1]; struct inet_port *port; struct inet_port_params params; int ret; switch (op) { case SNMP_OP_GETNEXT: if ((port = (struct inet_port *)trans_next_port(my_trans, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); index_append(&value->var, sub, &port->tport.index); goto fetch; case SNMP_OP_GET: if ((port = (struct inet_port *)trans_find_port(my_trans, &value->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); goto fetch; case SNMP_OP_SET: port = (struct inet_port *)trans_find_port(my_trans, &value->var, sub); if (which != LEAF_begemotSnmpdTransInetStatus) abort(); if (!isok_RowStatus(value->v.integer)) return (SNMP_ERR_WRONG_VALUE); if (index_decode(&value->var, sub, iidx, ¶ms.type, ¶ms.addr, ¶ms.addr_len, ¶ms.port, ¶ms.proto)) return (SNMP_ERR_NO_CREATION); asn_slice_oid(¶ms.index, &value->var, sub, value->var.len); ret = inet_port_set(ctx, port, ¶ms, (enum RowStatus)value->v.integer); free(params.addr); return (ret); case SNMP_OP_ROLLBACK: if ((port = (struct inet_port *)trans_find_port(my_trans, &value->var, sub)) == NULL) /* cannot happen */ abort(); switch (ctx->scratch->int1) { case SET_CREATED: /* newly created */ assert(port != NULL); inet_destroy_port(&port->tport); return (SNMP_ERR_NOERROR); case SET_DESTROY: /* do it now */ assert(port != NULL); return (SNMP_ERR_NOERROR); case SET_ACTIVATED: deactivate_port(port); return (SNMP_ERR_NOERROR); case SET_DEACTIVATE: return (SNMP_ERR_NOERROR); } abort(); case SNMP_OP_COMMIT: if ((port = (struct inet_port *)trans_find_port(my_trans, &value->var, sub)) == NULL) /* cannot happen */ abort(); switch (ctx->scratch->int1) { case SET_CREATED: /* newly created */ assert(port != NULL); return (SNMP_ERR_NOERROR); case SET_DESTROY: /* do it now */ assert(port != NULL); inet_destroy_port(&port->tport); return (SNMP_ERR_NOERROR); case SET_ACTIVATED: return (SNMP_ERR_NOERROR); case SET_DEACTIVATE: deactivate_port(port); return (SNMP_ERR_NOERROR); } abort(); } abort(); fetch: switch (which) { case LEAF_begemotSnmpdTransInetStatus: value->v.integer = port->row_status; break; default: abort(); } return (SNMP_ERR_NOERROR); }