17f23d61harti/*
27f23d61harti * Copyright (c) 2018
37f23d61harti *	Hartmut Brandt.
47f23d61harti *	All rights reserved.
57f23d61harti *
67f23d61harti * Author: Harti Brandt <harti@freebsd.org>
77f23d61harti *
87f23d61harti * Redistribution and use in source and binary forms, with or without
97f23d61harti * modification, are permitted provided that the following conditions
107f23d61harti * are met:
117f23d61harti * 1. Redistributions of source code must retain the above copyright
127f23d61harti *    notice, this list of conditions and the following disclaimer.
137f23d61harti * 2. Redistributions in binary form must reproduce the above copyright
147f23d61harti *    notice, this list of conditions and the following disclaimer in the
157f23d61harti *    documentation and/or other materials provided with the distribution.
167f23d61harti *
177f23d61harti * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
187f23d61harti * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
197f23d61harti * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
207f23d61harti * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
217f23d61harti * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
227f23d61harti * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
237f23d61harti * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
247f23d61harti * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
257f23d61harti * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
267f23d61harti * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
277f23d61harti * SUCH DAMAGE.
287f23d61harti *
297f23d61harti * $Begemot: bsnmp/snmpd/trans_udp.c,v 1.5 2005/10/04 08:46:56 brandt_h Exp $
307f23d61harti *
317f23d61harti * Internet transport
327f23d61harti */
337f23d61harti
347f23d61harti#include <sys/param.h>
357f23d61harti#include <sys/socket.h>
367f23d61harti#include <sys/types.h>
377f23d61harti
387f23d61harti#include <assert.h>
397f23d61harti#include <errno.h>
407f23d61harti#include <netdb.h>
417f23d61harti#include <stdbool.h>
427f23d61harti#include <stddef.h>
437f23d61harti#include <stdlib.h>
447f23d61harti#include <string.h>
457f23d61harti#include <syslog.h>
467f23d61harti#include <unistd.h>
477f23d61harti
487f23d61harti#include <stdio.h>
497f23d61harti
507f23d61harti#include <arpa/inet.h>
517f23d61harti
527f23d61harti#include "asn1.h"
537f23d61harti#include "snmp.h"
547f23d61harti#include "snmpmod.h"
557f23d61harti
567f23d61harti#include "snmpd.h"
577f23d61harti
587f23d61harti#define	SNMPTREE_TYPES
597f23d61harti#define	SNMPENUM_FUNCS
607f23d61harti#include "tree.h"
617f23d61harti#include "oid.h"
627f23d61harti
637f23d61hartiextern const struct transport_def inet_trans;
647f23d61harti
657f23d61hartistruct inet_port;
667f23d61hartistruct inet_port_params;
677f23d61hartistruct port_sock;
687f23d61harti
697f23d61hartitypedef int create_func(struct inet_port *, struct inet_port_params *);
707f23d61hartitypedef void input_func(int, void *);
717f23d61hartitypedef int activate_func(struct inet_port *);
727f23d61hartitypedef void deactivate_func(struct inet_port *);
737f23d61hartitypedef void parse_ctrl_func(struct port_sock *, const struct msghdr *);
74a72ba8cglebiustypedef void setsrc_func(struct port_sock *, struct msghdr *, char *);
757f23d61harti
767f23d61hartistatic create_func ipv4_create;
777f23d61hartistatic input_func ipv4_input;
787f23d61hartistatic activate_func ipv4_activate;
797f23d61hartistatic deactivate_func ipv4_deactivate;
807f23d61hartistatic parse_ctrl_func ipv4_parse_ctrl;
817f23d61hartistatic setsrc_func ipv4_setsrc;
827f23d61harti
837f23d61hartistatic create_func ipv6_create;
847f23d61hartistatic input_func ipv6_input;
857f23d61hartistatic activate_func ipv6_activate;
867f23d61hartistatic deactivate_func ipv6_deactivate;
877f23d61hartistatic parse_ctrl_func ipv6_parse_ctrl;
887f23d61hartistatic setsrc_func ipv6_setsrc;
897f23d61harti
907f23d61hartistatic create_func ipv6z_create;
917f23d61harti
927f23d61hartistatic create_func dns_create;
937f23d61hartistatic activate_func dns_activate;
947f23d61hartistatic deactivate_func dns_deactivate;
957f23d61harti
967f23d61hartistruct port_sock {
977f23d61harti	/* common input stuff; must be first */
987f23d61harti	struct port_input input;
997f23d61harti
1007f23d61harti	/** link field */
1017f23d61harti	TAILQ_ENTRY(port_sock) link;
1027f23d61harti
1037f23d61harti	/** pointer to parent */
1047f23d61harti	struct inet_port *port;
1057f23d61harti
1067f23d61harti	/** bind address */
1077f23d61harti	struct sockaddr_storage bind_addr;
1087f23d61harti
1097f23d61harti	/** reply destination */
1107f23d61harti	struct sockaddr_storage ret_dest;
1117f23d61harti
1127f23d61harti	/** need to set source address in reply; set for INADDR_ANY */
1137f23d61harti	bool set_ret_source;
1147f23d61harti
1157f23d61harti	/** address of the receive interface */
1167f23d61harti	union {
1177f23d61harti		/** IPv4 case */
1187f23d61harti		struct in_addr	a4;
1197f23d61harti
1207f23d61harti		/** IPv6 case */
1217f23d61harti		struct in6_pktinfo a6;
1227f23d61harti	} ret_source;
1237f23d61harti
1247f23d61harti	/** parse control message */
1257f23d61harti	parse_ctrl_func *parse_ctrl;
1267f23d61harti
1277f23d61harti	/** set source address for a send() */
1287f23d61harti	setsrc_func *setsrc;
1297f23d61harti};
1307f23d61hartistatic_assert(offsetof(struct port_sock, input) == 0,
1317f23d61harti    "input not first in port_sock");
1327f23d61harti
1337f23d61harti/**
1347f23d61harti * Table row for the inet ports.
1357f23d61harti *
1367f23d61harti * When actived each row can have one or several open sockets. For ipv6,
1377f23d61harti * ipv4 and ipv6z addresses it is always one, for dns addresses more than
1387f23d61harti * one socket can be open.
1397f23d61harti */
1407f23d61hartistruct inet_port {
1417f23d61harti	/** common i/o port stuff (must be first) */
1427f23d61harti	struct tport tport;
1437f23d61harti
1447f23d61harti	/** transport protocol */
1457f23d61harti	enum BegemotSnmpdTransportProto proto;
1467f23d61harti
1477f23d61harti	/** row status */
1487f23d61harti	enum RowStatus row_status;
1497f23d61harti
1507f23d61harti	/** socket list */
1517f23d61harti	TAILQ_HEAD(, port_sock) socks;
1527f23d61harti
1537f23d61harti	/** value for InetAddressType::dns */
1547f23d61harti	char *dns_addr;
1557f23d61harti
1567f23d61harti	/** port number in dns case; network byte order */
1577f23d61harti	uint16_t dns_port;
1587f23d61harti
1597f23d61harti	/** create a port */
1607f23d61harti	create_func *create;
1617f23d61harti
1627f23d61harti	/** activate a port */
1637f23d61harti	activate_func *activate;
1647f23d61harti
1657f23d61harti	/** deactivate port */
1667f23d61harti	deactivate_func *deactivate;
1677f23d61harti};
1687f23d61hartistatic_assert(offsetof(struct inet_port, tport) == 0,
1697f23d61harti    "tport not first in inet_port");
1707f23d61harti
1717f23d61harti/** to be used in bind_addr field */
1727f23d61harti#define	AF_DNS	AF_VENDOR00
1737f23d61harti
1747f23d61harti/** registered transport */
1757f23d61hartistatic struct transport *my_trans;
1767f23d61harti
1777f23d61harti/** set operation */
1787f23d61hartienum {
1797f23d61harti	SET_CREATED,
1807f23d61harti	SET_ACTIVATED,
1817f23d61harti	SET_DEACTIVATE,
1827f23d61harti	SET_DESTROY,
1837f23d61harti};
1847f23d61harti
1857f23d61harti/** length of the control data buffer */
1867f23d61hartistatic const size_t RECV_CBUF_SIZE =
1877f23d61harti    MAX(CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
1887f23d61harti	CMSG_SPACE(sizeof(struct in_addr)),
1897f23d61harti	CMSG_SPACE(SOCKCREDSIZE(CMGROUP_MAX)) +
1907f23d61harti	CMSG_SPACE(sizeof(struct in6_pktinfo)));
1917f23d61harti
1927f23d61harti/** length of the control data buffer */
1937f23d61hartistatic const size_t XMIT_CBUF_SIZE =
1947f23d61harti    MAX(CMSG_SPACE(sizeof(struct in_addr)),
1957f23d61harti	CMSG_SPACE(sizeof(struct in6_pktinfo)));
1967f23d61harti
1977f23d61harti/**
1987f23d61harti * Start the transport. This registers the transport with the
1997f23d61harti * transport table.
2007f23d61harti *
2017f23d61harti * \return SNMP error code
2027f23d61harti */
2037f23d61hartistatic int
2047f23d61hartiinet_start(void)
2057f23d61harti{
2067f23d61harti	return (trans_register(&inet_trans, &my_trans));
2077f23d61harti}
2087f23d61harti
2097f23d61harti/**
2107f23d61harti * Stop the transport. This tries to unregister the transport which
2117f23d61harti * in turn fails if the list of ports is not empty.
2127f23d61harti *
2137f23d61harti * \return SNMP error code
2147f23d61harti */
2157f23d61hartistatic int
2167f23d61hartiinet_stop(int force __unused)
2177f23d61harti{
2187f23d61harti	if (my_trans != NULL)
2197f23d61harti		if (trans_unregister(my_trans) != 0)
2207f23d61harti			return (SNMP_ERR_GENERR);
2217f23d61harti	return (SNMP_ERR_NOERROR);
2227f23d61harti}
2237f23d61harti
2247f23d61harti/**
2257f23d61harti * Deactivate SNMP port.
2267f23d61harti *
2277f23d61harti * \param tp	port to close
2287f23d61harti */
2297f23d61hartistatic void
2307f23d61hartideactivate_port(struct inet_port *p)
2317f23d61harti{
2327f23d61harti	p->deactivate(p);
2337f23d61harti}
2347f23d61harti
2357f23d61harti/*
2367f23d61harti * This function activates a port. For ports opened via the config files
2377f23d61harti * this is called just before entering the event loop. For ports create
2387f23d61harti * during runtime this is called when the RowStatus is set to Active or
2397f23d61harti * as second step for CreateAndGo.
2407f23d61harti *
2417f23d61harti * \param tp	transport port
2427f23d61harti *
2437f23d61harti * \return SNMP error code
2447f23d61harti */
2457f23d61hartistatic int
2467f23d61hartiinet_activate(struct tport *tp)
2477f23d61harti{
2487f23d61harti	struct inet_port *port = (struct inet_port *)tp;
2497f23d61harti
2507f23d61harti	return (port->activate(port));
2517f23d61harti}
2527f23d61harti
2537f23d61harti/*
2547f23d61harti * Close the SNMP port if it is open and destroy it.
2557f23d61harti *
2567f23d61harti * \param tp	port to close
2577f23d61harti */
2587f23d61hartistatic void
2597f23d61hartiinet_destroy_port(struct tport *tp)
2607f23d61harti{
2617f23d61harti	struct inet_port *port = (struct inet_port *)tp;
2627f23d61harti
2637f23d61harti	deactivate_port(port);
2647f23d61harti
2657f23d61harti	trans_remove_port(tp);
2667f23d61harti
2677f23d61harti	free(port->dns_addr);
2687f23d61harti	free(port);
2697f23d61harti}
2707f23d61harti
2717f23d61harti/**
2727f23d61harti * If the input struct has no buffer allocated yet, do it now. If allocation
2737f23d61harti * fails, read the data into a local buffer and drop it.
2747f23d61harti *
2757f23d61harti * \param pi	input struct
2767f23d61harti *
2777f23d61harti * \return -1 if allocation fails, 0 otherwise
2787f23d61harti */
2797f23d61hartistatic int
2807f23d61hartiinet_alloc_buf(struct port_input *pi)
2817f23d61harti{
2827f23d61harti	char drop_buf[2000];
2837f23d61harti
2847f23d61harti	if (pi->buf == NULL) {
2857f23d61harti		if ((pi->buf = buf_alloc(0)) == NULL) {
2867f23d61harti			(void)recvfrom(pi->fd, drop_buf, sizeof(drop_buf),
2877f23d61harti			    0, NULL, NULL);
2887f23d61harti			return (-1);
2897f23d61harti		}
2907f23d61harti		pi->buflen = buf_size(0);
2917f23d61harti	}
2927f23d61harti	return (0);
2937f23d61harti}
2947f23d61harti
2957f23d61harti/**
2967f23d61harti * Read message into input buffer. Get also the source address and any
2977f23d61harti * control data that is available. If the message is truncated, increment
2987f23d61harti * corresponding statistics.
2997f23d61harti *
3007f23d61harti * \param pi	input object
3017f23d61harti * \param msg	message object to fill
3027f23d61harti * \param cbuf	control data buffer
3037f23d61harti *
3047f23d61harti * \return -1 when something goes wrong, 0 othersise
3057f23d61harti */
3067f23d61hartistatic int
3077f23d61hartiinet_read_msg(struct port_input *pi, struct msghdr *msg, char *cbuf)
3087f23d61harti{
3097f23d61harti	struct iovec iov[1];
3107f23d61harti
3117f23d61harti	iov[0].iov_base = pi->buf;
3127f23d61harti	iov[0].iov_len = pi->buflen;
3137f23d61harti
3147f23d61harti	msg->msg_name = pi->peer;
3157f23d61harti	msg->msg_namelen = pi->peerlen;
3167f23d61harti	msg->msg_iov = iov;
3177f23d61harti	msg->msg_iovlen = 1;
3187f23d61harti	msg->msg_control = cbuf;
3197f23d61harti	msg->msg_controllen = RECV_CBUF_SIZE;
3207f23d61harti	msg->msg_flags = 0;
3217f23d61harti
3227f23d61harti	memset(cbuf, 0, RECV_CBUF_SIZE);
3237f23d61harti
3247f23d61harti	const ssize_t len = recvmsg(pi->fd, msg, 0);
3257f23d61harti
3267f23d61harti	if (len == -1 || len == 0)
3277f23d61harti		/* receive error */
3287f23d61harti		return (-1);
3297f23d61harti
3307f23d61harti	if (msg->msg_flags & MSG_TRUNC) {
3317f23d61harti		/* truncated - drop */
3327f23d61harti		snmpd_stats.silentDrops++;
3337f23d61harti		snmpd_stats.inTooLong++;
3347f23d61harti		return (-1);
3357f23d61harti	}
3367f23d61harti
3377f23d61harti	pi->length = (size_t)len;
3387f23d61harti
3397f23d61harti	return (0);
3407f23d61harti}
3417f23d61harti
3427f23d61harti/*
3437f23d61harti * Input available on socket.
3447f23d61harti *
3457f23d61harti * \param tp	transport port
3467f23d61harti * \param pi	input struct
3477f23d61harti *
3487f23d61harti * \return number of bytes received
3497f23d61harti */
3507f23d61hartistatic ssize_t
3517f23d61hartiinet_recv(struct tport *tp, struct port_input *pi)
3527f23d61harti{
3537f23d61harti	struct inet_port *port = __containerof(tp, struct inet_port, tport);
3547f23d61harti	struct port_sock *sock = __containerof(pi, struct port_sock, input);
3557f23d61harti
3567f23d61harti	assert(port->proto == BegemotSnmpdTransportProto_udp);
3577f23d61harti
3587f23d61harti	if (inet_alloc_buf(pi) == -1)
3597f23d61harti		return (-1);
3607f23d61harti
3617f23d61harti	char cbuf[RECV_CBUF_SIZE];
3627f23d61harti	struct msghdr msg;
3637f23d61harti
3647f23d61harti	if (inet_read_msg(pi, &msg, cbuf) == -1)
3657f23d61harti		return (-1);
3667f23d61harti
3677f23d61harti	sock->parse_ctrl(sock, &msg);
3687f23d61harti
3697f23d61harti	return (0);
3707f23d61harti}
3717f23d61harti
3727f23d61harti/*
3737f23d61harti * Send message.
3747f23d61harti *
3757f23d61harti * \param tp		port
3767f23d61harti * \param buf		data to send
3777f23d61harti * \param len		number of bytes to send
3787f23d61harti * \param addr		destination address
3797f23d61harti * \param addlen	destination address length
3807f23d61harti *
3817f23d61harti * \return number of bytes sent
3827f23d61harti */
3837f23d61hartistatic ssize_t
3847f23d61hartiinet_send2(struct tport *tp, const u_char *buf, size_t len,
3857f23d61harti    struct port_input *pi)
3867f23d61harti{
3877f23d61harti	struct inet_port *p = __containerof(tp, struct inet_port, tport);
3887f23d61harti	struct port_sock *s = (pi == NULL) ?  TAILQ_FIRST(&p->socks) :
3897f23d61harti	    __containerof(pi, struct port_sock, input);
3907f23d61harti
3917f23d61harti	struct iovec iov;
3927f23d61harti
3937f23d61harti	iov.iov_base = __DECONST(void*, buf);
3947f23d61harti	iov.iov_len = len;
3957f23d61harti
3967f23d61harti	struct msghdr msg;
3977f23d61harti
3987f23d61harti	msg.msg_flags = 0;
3997f23d61harti	msg.msg_iov = &iov;
4007f23d61harti	msg.msg_iovlen = 1;
4017f23d61harti	msg.msg_name = (void *)pi->peer;
4027f23d61harti	msg.msg_namelen = pi->peerlen;
4037f23d61harti
4047f23d61harti	char cbuf[XMIT_CBUF_SIZE];
4057f23d61harti	if (s->set_ret_source) {
406a72ba8cglebius		s->setsrc(s, &msg, cbuf);
407a72ba8cglebius	} else {
408a72ba8cglebius		msg.msg_control = NULL;
409a72ba8cglebius		msg.msg_controllen = 0;
4107f23d61harti	}
4117f23d61harti
4127f23d61harti	return (sendmsg(s->input.fd, &msg, 0));
4137f23d61harti}
4147f23d61harti
4157f23d61harti/** exported to daemon */
4167f23d61harticonst struct transport_def inet_trans = {
4177f23d61harti	"inet",
4187f23d61harti	OIDX_begemotSnmpdTransInet,
4197f23d61harti	inet_start,
4207f23d61harti	inet_stop,
4217f23d61harti	inet_destroy_port,
4227f23d61harti	inet_activate,
4237f23d61harti	NULL,
4247f23d61harti	inet_recv,
4257f23d61harti	inet_send2,
4267f23d61harti};
4277f23d61harti
4287f23d61hartistruct inet_port_params {
4297f23d61harti	/** index oid */
4307f23d61harti	struct asn_oid index;
4317f23d61harti
4327f23d61harti	/** internet address type */
4337f23d61harti	enum InetAddressType type;
4347f23d61harti
4357f23d61harti	/** internet address */
4367f23d61harti	u_char *addr;
4377f23d61harti
4387f23d61harti	/** length of address */
4397f23d61harti	size_t addr_len;
4407f23d61harti
4417f23d61harti	/** port number */
4427f23d61harti	uint32_t port;
4437f23d61harti
4447f23d61harti	/** protocol */
4457f23d61harti	enum BegemotSnmpdTransportProto proto;
4467f23d61harti};
4477f23d61harti
4487f23d61harti/**
4497f23d61harti * IPv4 creation stuff. Parse the index, fill socket address and creates
4507f23d61harti * a port_sock.
4517f23d61harti *
4527f23d61harti * \param port		the port to create
4537f23d61harti * \param params	parameters from the SNMP SET
4547f23d61harti *
4557f23d61harti * \return SNMP error
4567f23d61harti */
4577f23d61hartistatic int
4587f23d61hartiipv4_create(struct inet_port *port, struct inet_port_params *params)
4597f23d61harti{
4607f23d61harti
4617f23d61harti	if (params->addr_len != 4)
4627f23d61harti		return (SNMP_ERR_INCONS_VALUE);
4637f23d61harti
4647f23d61harti	struct port_sock *sock = calloc(1, sizeof(struct port_sock));
4657f23d61harti	if (sock == NULL)
4667f23d61harti		return (SNMP_ERR_GENERR);
4677f23d61harti
4687f23d61harti	snmpd_input_init(&sock->input);
4697f23d61harti
4707f23d61harti	TAILQ_INSERT_HEAD(&port->socks, sock, link);
4717f23d61harti
4727f23d61harti	struct sockaddr_in *sin =
4737f23d61harti	    (struct sockaddr_in *)&sock->bind_addr;
4747f23d61harti
4757f23d61harti	sin->sin_len = sizeof(struct sockaddr_in);
4767f23d61harti	sin->sin_family = AF_INET;
4777f23d61harti	sin->sin_port = htons(params->port);
4787f23d61harti	memcpy(&sin->sin_addr, params->addr, 4); /* network byte order */
4797f23d61harti
4807f23d61harti	sock->port = port;
4817f23d61harti
4827f23d61harti	return (SNMP_ERR_NOERROR);
4837f23d61harti}
4847f23d61harti
4857f23d61harti/*
4867f23d61harti * An IPv4 inet port is ready. Delegate to the generic code to read the data
4877f23d61harti * and react.
4887f23d61harti *
4897f23d61harti * \param fd	file descriptor that is ready
4907f23d61harti * \param udata	inet_port pointer
4917f23d61harti */
4927f23d61hartistatic void
4937f23d61hartiipv4_input(int fd __unused, void *udata)
4947f23d61harti{
4957f23d61harti	struct port_sock *sock = udata;
4967f23d61harti
4977f23d61harti	sock->input.peerlen = sizeof(struct sockaddr_in);
4987f23d61harti	snmpd_input(&sock->input, &sock->port->tport);
4997f23d61harti}
5007f23d61harti
5017f23d61harti/**
5027f23d61harti * Activate an IPv4 socket.
5037f23d61harti *
5047f23d61harti * \param sock	thhe socket to activate
5057f23d61harti *
5067f23d61harti * \return error code
5077f23d61harti */
5087f23d61hartistatic int
5097f23d61hartiipv4_activate_sock(struct port_sock *sock)
5107f23d61harti{
5117f23d61harti	if ((sock->input.fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1) {
5127f23d61harti		syslog(LOG_ERR, "creating UDP4 socket: %m");
5137f23d61harti		return (SNMP_ERR_RES_UNAVAIL);
5147f23d61harti	}
5157f23d61harti
5167f23d61harti	const struct sockaddr_in *sin =
5177f23d61harti	    (const struct sockaddr_in *)&sock->bind_addr;
5187f23d61harti
5197f23d61harti	if (sin->sin_addr.s_addr == INADDR_ANY) {
5207f23d61harti		/* need to know from which address to return */
5217f23d61harti		static const int on = 1;
5227f23d61harti
5237f23d61harti		if (setsockopt(sock->input.fd, IPPROTO_IP, IP_RECVDSTADDR, &on,
5247f23d61harti		    sizeof(on)) == -1) {
5257f23d61harti			syslog(LOG_ERR, "setsockopt(IP_RECVDSTADDR): %m");
5267f23d61harti			(void)close(sock->input.fd);
5277f23d61harti			sock->input.fd = -1;
5287f23d61harti			return (SNMP_ERR_GENERR);
5297f23d61harti		}
5307f23d61harti		sock->set_ret_source = true;
5317f23d61harti	}
5327f23d61harti
5337f23d61harti	if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) {
5347f23d61harti		if (errno == EADDRNOTAVAIL) {
5357f23d61harti			(void)close(sock->input.fd);
5367f23d61harti			sock->input.fd = -1;
5377f23d61harti			return (SNMP_ERR_INCONS_NAME);
5387f23d61harti		}
5397f23d61harti		syslog(LOG_ERR, "bind: %s:%u %m", inet_ntoa(sin->sin_addr),
5407f23d61harti		    ntohs(sin->sin_port));
5417f23d61harti		(void)close(sock->input.fd);
5427f23d61harti		sock->input.fd = -1;
5437f23d61harti		return (SNMP_ERR_GENERR);
5447f23d61harti	}
5457f23d61harti
5467f23d61harti	if ((sock->input.id = fd_select(sock->input.fd, ipv4_input,
5477f23d61harti	    sock, NULL)) == NULL) {
5487f23d61harti		(void)close(sock->input.fd);
5497f23d61harti		sock->input.fd = -1;
5507f23d61harti		return (SNMP_ERR_GENERR);
5517f23d61harti	}
5527f23d61harti	sock->input.peer = (struct sockaddr *)&sock->ret_dest;
5537f23d61harti
5547f23d61harti	sock->parse_ctrl = ipv4_parse_ctrl;
5557f23d61harti	sock->setsrc = ipv4_setsrc;
5567f23d61harti
5577f23d61harti	return (SNMP_ERR_NOERROR);
5587f23d61harti}
5597f23d61harti
5607f23d61harti/**
5617f23d61harti * Open an IPv4 socket. Make the socket, bind it and put it on the select
5627f23d61harti * list. The socket struct has already been created during creation.
5637f23d61harti *
5647f23d61harti * \param p	inet port
5657f23d61harti *
5667f23d61harti * \return SNMP error code
5677f23d61harti */
5687f23d61hartistatic int
5697f23d61hartiipv4_activate(struct inet_port *p)
5707f23d61harti{
5717f23d61harti	struct port_sock *sock = TAILQ_FIRST(&p->socks);
5727f23d61harti	assert(sock);
5737f23d61harti	assert(!TAILQ_NEXT(sock, link));
5747f23d61harti
5757f23d61harti	const int ret = ipv4_activate_sock(sock);
5767f23d61harti	if (ret == SNMP_ERR_NOERROR)
5777f23d61harti		p->row_status = RowStatus_active;
5787f23d61harti
5797f23d61harti	return (ret);
5807f23d61harti}
5817f23d61harti
5827f23d61harti/**
5837f23d61harti * Close an IPv4 socket. Keep the sock object.
5847f23d61harti *
5857f23d61harti * \param p	inet port
5867f23d61harti */
5877f23d61hartistatic void
5887f23d61hartiipv4_deactivate(struct inet_port *p)
5897f23d61harti{
5907f23d61harti	struct port_sock *sock = TAILQ_FIRST(&p->socks);
5917f23d61harti	assert(sock);
5927f23d61harti	assert(!TAILQ_NEXT(sock, link));
5937f23d61harti
5947f23d61harti	snmpd_input_close(&sock->input);
5957f23d61harti
5967f23d61harti	p->row_status = RowStatus_notInService;
5977f23d61harti}
5987f23d61harti
5997f23d61harti/**
6007f23d61harti * Parse the control data received with a UDPv4 packet. This may contain
6017f23d61harti * credentials (for a local connection) and the address of the interface
6027f23d61harti * the message was received on. If there are credentials set the priv flag
6037f23d61harti * if the effective UID is zero.
6047f23d61harti *
6057f23d61harti * \param sock	the sock object
6067f23d61harti * \param msg	the received message
6077f23d61harti */
6087f23d61hartistatic void
6097f23d61hartiipv4_parse_ctrl(struct port_sock *sock, const struct msghdr *msg)
6107f23d61harti{
6117f23d61harti	struct sockcred *cred = NULL;
6127f23d61harti
6137f23d61harti	for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
6147f23d61harti	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
6157f23d61harti
6167f23d61harti		if (cmsg->cmsg_level == IPPROTO_IP &&
6177f23d61harti		    cmsg->cmsg_type == IP_RECVDSTADDR) {
6187f23d61harti			memcpy(&sock->ret_source.a4, CMSG_DATA(cmsg),
6197f23d61harti			    sizeof(struct in_addr));
6207f23d61harti
6217f23d61harti		} else if (cmsg->cmsg_level == SOL_SOCKET &&
6227f23d61harti		    cmsg->cmsg_type == SCM_CREDS) {
6237f23d61harti			cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
6247f23d61harti		}
6257f23d61harti	}
6267f23d61harti
6277f23d61harti	sock->input.priv = 0;
6287f23d61harti	if (sock->input.cred && cred)
6297f23d61harti		/* remote end has sent credentials */
6307f23d61harti		sock->input.priv = (cred->sc_euid == 0);
6317f23d61harti}
6327f23d61harti
6337f23d61harti/**
6347f23d61harti * Set the source address option for IPv4 sockets.
6357f23d61harti *
6367f23d61harti * \param sock	socket object
6377f23d61harti * \param msg	message
6387f23d61harti */
6397f23d61hartistatic void
640a72ba8cglebiusipv4_setsrc(struct port_sock *sock, struct msghdr *msg, char *cbuf)
6417f23d61harti{
642a72ba8cglebius	struct cmsghdr *cmsg;
643a72ba8cglebius
644a72ba8cglebius	msg->msg_control = cbuf;
645a72ba8cglebius	msg->msg_controllen = CMSG_SPACE(sizeof(struct in_addr));
6467f23d61harti
6477f23d61harti	/* select outgoing interface by setting source address */
648a72ba8cglebius	cmsg = CMSG_FIRSTHDR(msg);
6497f23d61harti	cmsg->cmsg_level = IPPROTO_IP;
6507f23d61harti	cmsg->cmsg_type = IP_SENDSRCADDR;
6517f23d61harti	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));
6527f23d61harti	memcpy(CMSG_DATA(cmsg), &sock->ret_source.a4,
6537f23d61harti	    sizeof(struct in_addr));
6547f23d61harti}
6557f23d61harti
6567f23d61harti/**
6577f23d61harti * Common part of IPv6 creation. This is used by both ipv6_create() and
6587f23d61harti * ipv6z_create().
6597f23d61harti *
6607f23d61harti * \param port		the table row
6617f23d61harti * \param params	creation parameters
6627f23d61harti * \param scope_id	scope_id (0 or from index)
6637f23d61harti *
6647f23d61harti * \return SNMP_ERR_NOERROR if port has been created, error code otherwise
6657f23d61harti */
6667f23d61hartistatic int
6677f23d61hartiipv6_create_common(struct inet_port *port, struct inet_port_params *params,
6687f23d61harti    u_int scope_id)
6697f23d61harti{
6707f23d61harti	struct port_sock *sock = calloc(1, sizeof(struct port_sock));
6717f23d61harti
6727f23d61harti	if (sock == NULL)
6737f23d61harti		return (SNMP_ERR_GENERR);
6747f23d61harti
6757f23d61harti	struct sockaddr_in6 *sin = (struct sockaddr_in6 *)&sock->bind_addr;
6767f23d61harti
6777f23d61harti	sin->sin6_len = sizeof(struct sockaddr_in6);
6787f23d61harti	sin->sin6_family = AF_INET6;
6797f23d61harti	sin->sin6_port = htons(params->port);
6807f23d61harti	sin->sin6_flowinfo = 0;
6817f23d61harti	sin->sin6_scope_id = scope_id;
6827f23d61harti
6837f23d61harti	memcpy(sin->sin6_addr.s6_addr, params->addr, 16);
6847f23d61harti
6857f23d61harti	if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) && scope_id == 0) {
6867f23d61harti		char buf[INET6_ADDRSTRLEN];
6877f23d61harti		syslog(LOG_INFO, "%s: link local address used without scope "
6887f23d61harti		    "index: %s", __func__, inet_ntop(AF_INET6,
6897f23d61harti		    &sin->sin6_addr, buf, sizeof(buf)));
6907f23d61harti		free(sock);
6917f23d61harti		return (SNMP_ERR_NO_CREATION);
6927f23d61harti	}
6937f23d61harti
6947f23d61harti	sock->port = port;
6957f23d61harti
6967f23d61harti	snmpd_input_init(&sock->input);
6977f23d61harti	TAILQ_INSERT_HEAD(&port->socks, sock, link);
6987f23d61harti
6997f23d61harti	return (SNMP_ERR_NOERROR);
7007f23d61harti}
7017f23d61harti
7027f23d61harti/**
7037f23d61harti * IPv6 creation stuff. Parse the index, fill socket address and creates
7047f23d61harti * a port_sock.
7057f23d61harti *
7067f23d61harti * \param port		the port to create
7077f23d61harti * \param params	parameters from the SNMP SET
7087f23d61harti *
7097f23d61harti * \return SNMP error
7107f23d61harti */
7117f23d61hartistatic int
7127f23d61hartiipv6_create(struct inet_port *port, struct inet_port_params *params)
7137f23d61harti{
7147f23d61harti	if (params->addr_len != 16)
7157f23d61harti		return (SNMP_ERR_INCONS_VALUE);
7167f23d61harti
7177f23d61harti	const int ret = ipv6_create_common(port, params, 0);
7187f23d61harti	if (ret != SNMP_ERR_NOERROR)
7197f23d61harti		return (ret);
7207f23d61harti
7217f23d61harti	return (SNMP_ERR_NOERROR);
7227f23d61harti}
7237f23d61harti
7247f23d61harti/*
7257f23d61harti * An IPv6 inet port is ready. Delegate to the generic code to read the data
7267f23d61harti * and react.
7277f23d61harti *
7287f23d61harti * \param fd	file descriptor that is ready
7297f23d61harti * \param udata	inet_port pointer
7307f23d61harti */
7317f23d61hartistatic void
7327f23d61hartiipv6_input(int fd __unused, void *udata)
7337f23d61harti{
7347f23d61harti	struct port_sock *sock = udata;
7357f23d61harti
7367f23d61harti	sock->input.peerlen = sizeof(struct sockaddr_in6);
7377f23d61harti	snmpd_input(&sock->input, &sock->port->tport);
7387f23d61harti}
7397f23d61harti
7407f23d61harti/**
7417f23d61harti * Activate an IPv6 socket.
7427f23d61harti *
7437f23d61harti * \param sock	thhe socket to activate
7447f23d61harti *
7457f23d61harti * \return error code
7467f23d61harti */
7477f23d61hartistatic int
7487f23d61hartiipv6_activate_sock(struct port_sock *sock)
7497f23d61harti{
7507f23d61harti	if ((sock->input.fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1) {
7517f23d61harti		syslog(LOG_ERR, "creating UDP6 socket: %m");
7527f23d61harti		return (SNMP_ERR_RES_UNAVAIL);
7537f23d61harti	}
7547f23d61harti
7557f23d61harti	const struct sockaddr_in6 *sin =
7567f23d61harti	    (const struct sockaddr_in6 *)&sock->bind_addr;
7577f23d61harti
7587f23d61harti	if (memcmp(&sin->sin6_addr, &in6addr_any, sizeof(in6addr_any)) == 0) {
7597f23d61harti		/* need to know from which address to return */
7607f23d61harti		static const int on = 1;
7617f23d61harti
7627f23d61harti		if (setsockopt(sock->input.fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
7637f23d61harti		    &on, sizeof(on)) == -1) {
7647f23d61harti			syslog(LOG_ERR, "setsockopt(IP6_PKTINFO): %m");
7657f23d61harti			(void)close(sock->input.fd);
7667f23d61harti			sock->input.fd = -1;
7677f23d61harti			return (SNMP_ERR_GENERR);
7687f23d61harti		}
7697f23d61harti		sock->set_ret_source = true;
7707f23d61harti	}
7717f23d61harti
7727f23d61harti	if (bind(sock->input.fd, (const struct sockaddr *)sin, sizeof(*sin))) {
7737f23d61harti		if (community != COMM_INITIALIZE && errno == EADDRNOTAVAIL) {
7747f23d61harti			(void)close(sock->input.fd);
7757f23d61harti			sock->input.fd = -1;
7767f23d61harti			return (SNMP_ERR_INCONS_NAME);
7777f23d61harti		}
7787f23d61harti		char buf[INET6_ADDRSTRLEN];
7797f23d61harti		syslog(LOG_ERR, "bind: %s:%u:%u %m", inet_ntop(AF_INET6,
7807f23d61harti		    &sin->sin6_addr, buf, sizeof(buf)), sin->sin6_scope_id,
7817f23d61harti		    ntohs(sin->sin6_port));
7827f23d61harti		(void)close(sock->input.fd);
7837f23d61harti		sock->input.fd = -1;
7847f23d61harti		return (SNMP_ERR_GENERR);
7857f23d61harti	}
7867f23d61harti	if ((sock->input.id = fd_select(sock->input.fd, ipv6_input,
7877f23d61harti	    sock, NULL)) == NULL) {
7887f23d61harti		(void)close(sock->input.fd);
7897f23d61harti		sock->input.fd = -1;
7907f23d61harti		return (SNMP_ERR_GENERR);
7917f23d61harti	}
7927f23d61harti	sock->input.peer = (struct sockaddr *)&sock->ret_dest;
7937f23d61harti
7947f23d61harti	sock->parse_ctrl = ipv6_parse_ctrl;
7957f23d61harti	sock->setsrc = ipv6_setsrc;
7967f23d61harti
7977f23d61harti	return (SNMP_ERR_NOERROR);
7987f23d61harti}
7997f23d61harti
8007f23d61harti/**
8017f23d61harti * Open an IPv6 socket.
8027f23d61harti *
8037f23d61harti * \param port	inet port
8047f23d61harti *
8057f23d61harti * \return SNMP error code
8067f23d61harti */
8077f23d61hartistatic int
8087f23d61hartiipv6_activate(struct inet_port *p)
8097f23d61harti{
8107f23d61harti	struct port_sock *sock = TAILQ_FIRST(&p->socks);
8117f23d61harti	assert(sock);
8127f23d61harti
8137f23d61harti	const int ret = ipv6_activate_sock(sock);
8147f23d61harti
8157f23d61harti	if (ret == SNMP_ERR_NOERROR)
8167f23d61harti		p->row_status = RowStatus_active;
8177f23d61harti	return (ret);
8187f23d61harti}
8197f23d61harti
8207f23d61harti/**
8217f23d61harti * Close an IPv6 socket. Keep the sock object.
8227f23d61harti *
8237f23d61harti * \param p	inet port
8247f23d61harti */
8257f23d61hartistatic void
8267f23d61hartiipv6_deactivate(struct inet_port *p)
8277f23d61harti{
8287f23d61harti	struct port_sock *sock = TAILQ_FIRST(&p->socks);
8297f23d61harti	assert(sock);
8307f23d61harti	assert(!TAILQ_NEXT(sock, link));
8317f23d61harti
8327f23d61harti	snmpd_input_close(&sock->input);
8337f23d61harti
8347f23d61harti	p->row_status = RowStatus_notInService;
8357f23d61harti}
8367f23d61harti
8377f23d61harti/**
8387f23d61harti * IPv6 specific part of message processing. The control data may contain
8397f23d61harti * credentials and packet info that contains the destination address of
8407f23d61harti * the packet.
8417f23d61harti *
8427f23d61harti * \param sock	the sock object
8437f23d61harti * \param msg	the received message
8447f23d61harti */
8457f23d61hartistatic void
8467f23d61hartiipv6_parse_ctrl(struct port_sock *sock, const struct msghdr *msg)
8477f23d61harti{
8487f23d61harti	struct sockcred *cred = NULL;
8497f23d61harti
8507f23d61harti	for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL;
8517f23d61harti	    cmsg = CMSG_NXTHDR(msg, cmsg)) {
8527f23d61harti
8537f23d61harti		if (cmsg->cmsg_level == IPPROTO_IPV6 &&
8547f23d61harti		    cmsg->cmsg_type == IPV6_PKTINFO) {
8557f23d61harti			const struct in6_pktinfo *info =
8567f23d61harti			    (const struct in6_pktinfo *)(const void *)
8577f23d61harti			    CMSG_DATA(cmsg);
8587f23d61harti			sock->ret_source.a6.ipi6_addr = info->ipi6_addr;
8597f23d61harti			sock->ret_source.a6.ipi6_ifindex =
8607f23d61harti			    !IN6_IS_ADDR_LINKLOCAL(&info->ipi6_addr) ? 0:
8617f23d61harti			    info->ipi6_ifindex;
8627f23d61harti
8637f23d61harti		} else if (cmsg->cmsg_level == SOL_SOCKET &&
8647f23d61harti		    cmsg->cmsg_type == SCM_CREDS) {
8657f23d61harti			cred = (struct sockcred *)(void *)CMSG_DATA(cmsg);
8667f23d61harti		}
8677f23d61harti	}
8687f23d61harti
8697f23d61harti	sock->input.priv = 0;
8707f23d61harti	if (sock->input.cred && cred)
8717f23d61harti		/* remote end has sent credentials */
8727f23d61harti		sock->input.priv = (cred->sc_euid == 0);
8737f23d61harti}
8747f23d61harti
8757f23d61harti/**
8767f23d61harti * Set the source address option for IPv4 sockets.
8777f23d61harti *
8787f23d61harti * \param sock	socket object
8797f23d61harti * \param msg	message
8807f23d61harti */
8817f23d61hartistatic void
882a72ba8cglebiusipv6_setsrc(struct port_sock *sock, struct msghdr *msg, char *cbuf)
8837f23d61harti{
884a72ba8cglebius	struct cmsghdr *cmsg;
885a72ba8cglebius
886a72ba8cglebius	msg->msg_control = cbuf;
887a72ba8cglebius	msg->msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
8887f23d61harti
8897f23d61harti	/* select outgoing interface by setting source address */
890a72ba8cglebius	cmsg = CMSG_FIRSTHDR(msg);
8917f23d61harti	cmsg->cmsg_level = IPPROTO_IPV6;
8927f23d61harti	cmsg->cmsg_type = IPV6_PKTINFO;
8937f23d61harti	cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
8947f23d61harti	memcpy(CMSG_DATA(cmsg), &sock->ret_source.a6,
8957f23d61harti	    sizeof(struct in6_pktinfo));
8967f23d61harti}
8977f23d61harti
8987f23d61harti/**
8997f23d61harti * IPv6z creation stuff. Parse the index, fill socket address and creates
9007f23d61harti * a port_sock.
9017f23d61harti *
9027f23d61harti * \param port		the port to create
9037f23d61harti * \param params	parameters from the SNMP SET
9047f23d61harti *
9057f23d61harti * \return SNMP error
9067f23d61harti */
9077f23d61hartistatic int
9087f23d61hartiipv6z_create(struct inet_port *port, struct inet_port_params *params)
9097f23d61harti{
9107f23d61harti	if (params->addr_len != 20)
9117f23d61harti		return (SNMP_ERR_INCONS_VALUE);
9127f23d61harti
9137f23d61harti	u_int scope_id = 0;
9147f23d61harti	for (u_int i = 16; i < 20; i++) {
9157f23d61harti		scope_id <<= 8;
9167f23d61harti		scope_id |= params->addr[i];
9177f23d61harti	}
9187f23d61harti
9197f23d61harti	const int ret = ipv6_create_common(port, params, scope_id);
9207f23d61harti	if (ret != SNMP_ERR_NOERROR)
9217f23d61harti		return (ret);
9227f23d61harti
9237f23d61harti	return (SNMP_ERR_NOERROR);
9247f23d61harti}
9257f23d61harti
9267f23d61harti/**
9277f23d61harti * DNS name creation stuff. Parse the index and save the string.
9287f23d61harti * This does not create a socket struct.
9297f23d61harti *
9307f23d61harti * \param port		the port to create
9317f23d61harti * \param params	parameters from the SNMP SET
9327f23d61harti *
9337f23d61harti * \return SNMP error
9347f23d61harti */
9357f23d61hartistatic int
9367f23d61hartidns_create(struct inet_port *port, struct inet_port_params *params)
9377f23d61harti{
9387f23d61harti	if (params->addr_len > 64)
9397f23d61harti		return (SNMP_ERR_INCONS_VALUE);
9407f23d61harti
9417f23d61harti	if (strnlen(params->addr, params->addr_len) !=
9427f23d61harti	    params->addr_len)
9437f23d61harti		return (SNMP_ERR_INCONS_VALUE);
9447f23d61harti
9457f23d61harti	if ((port->dns_addr = realloc(params->addr,
9467f23d61harti	    params->addr_len + 1)) == NULL)
9477f23d61harti		return (SNMP_ERR_GENERR);
9487f23d61harti
9497f23d61harti	port->dns_addr[params->addr_len] = '\0';
9507f23d61harti	params->addr = NULL;
9517f23d61harti
9527f23d61harti	port->dns_port = htons(params->port);
9537f23d61harti
9547f23d61harti	return (SNMP_ERR_NOERROR);
9557f23d61harti}
9567f23d61harti
9577f23d61harti/**
9587f23d61harti * Open sockets. This loops through all the addresses returned by getaddrinfo
9597f23d61harti * and opens a socket for each of them.
9607f23d61harti *
9617f23d61harti * \param port	inet port
9627f23d61harti *
9637f23d61harti * \return SNMP error code
9647f23d61harti */
9657f23d61hartistatic int
9667f23d61hartidns_activate(struct inet_port *port)
9677f23d61harti{
9687f23d61harti	struct addrinfo hints;
9697f23d61harti	memset(&hints, 0, sizeof(hints));
9707f23d61harti	hints.ai_family = AF_UNSPEC;
9717f23d61harti	hints.ai_socktype = SOCK_DGRAM;		// XXX udp-only
9727f23d61harti	hints.ai_flags = AI_ADDRCONFIG | AI_PASSIVE | AI_NUMERICSERV;
9737f23d61harti
9747f23d61harti	char portbuf[8];
9757f23d61harti	sprintf(portbuf, "%hu", ntohs(port->dns_port));
9767f23d61harti
9777f23d61harti	struct addrinfo *res0;
9787f23d61harti	int error = getaddrinfo(port->dns_addr[0] == '\0'
9797f23d61harti	    ? NULL : port->dns_addr,
9807f23d61harti	    portbuf, &hints, &res0);
9817f23d61harti
9827f23d61harti	if (error) {
9837f23d61harti		syslog(LOG_ERR, "cannot resolve address '%s': %s",
9847f23d61harti		    port->dns_addr, gai_strerror(error));
9857f23d61harti		return (SNMP_ERR_GENERR);
9867f23d61harti	}
9877f23d61harti
9887f23d61harti	for (struct addrinfo *res = res0; res != NULL; res = res->ai_next) {
9897f23d61harti		if (res->ai_family != AF_INET && res->ai_family != AF_INET6)
9907f23d61harti			continue;
9917f23d61harti
9927f23d61harti		struct port_sock *sock = calloc(1, sizeof(struct port_sock));
9937f23d61harti		if (sock == NULL)
9947f23d61harti			return (SNMP_ERR_GENERR);
9957f23d61harti
9967f23d61harti		snmpd_input_init(&sock->input);
9977f23d61harti		sock->port = port;
9987f23d61harti
9997f23d61harti		int ret = SNMP_ERR_NOERROR;
10007f23d61harti
10017f23d61harti		if (res->ai_family == AF_INET) {
10027f23d61harti			*(struct sockaddr_in *)&sock->bind_addr =
10037f23d61harti			    *(struct sockaddr_in *)(void *)res->ai_addr;
10047f23d61harti			ret = ipv4_activate_sock(sock);
10057f23d61harti		} else {
10067f23d61harti			*(struct sockaddr_in6 *)&sock->bind_addr =
10077f23d61harti			    *(struct sockaddr_in6 *)(void *)res->ai_addr;
10087f23d61harti			ret = ipv6_activate_sock(sock);
10097f23d61harti		}
10107f23d61harti
10117f23d61harti		if (ret != SNMP_ERR_NOERROR)
10127f23d61harti			free(sock);
10137f23d61harti		else
10147f23d61harti			TAILQ_INSERT_HEAD(&port->socks, sock, link);
10157f23d61harti	}
10167f23d61harti
10177f23d61harti	if (!TAILQ_EMPTY(&port->socks))
10187f23d61harti		port->row_status = RowStatus_active;
10197f23d61harti
10207f23d61harti	freeaddrinfo(res0);
10217f23d61harti	return (SNMP_ERR_GENERR);
10227f23d61harti}
10237f23d61harti
10247f23d61harti/**
10257f23d61harti * Deactive the socket. Close all open sockets and delete all sock objects.
10267f23d61harti *
10277f23d61harti * \param port	inet port
10287f23d61harti */
10297f23d61hartistatic void
10307f23d61hartidns_deactivate(struct inet_port *port)
10317f23d61harti{
10327f23d61harti	while (!TAILQ_EMPTY(&port->socks)) {
10337f23d61harti		struct port_sock *sock = TAILQ_FIRST(&port->socks);
10347f23d61harti		TAILQ_REMOVE(&port->socks, sock, link);
10357f23d61harti		snmpd_input_close(&sock->input);
10367f23d61harti		free(sock);
10377f23d61harti	}
10387f23d61harti	port->row_status = RowStatus_notInService;
10397f23d61harti}
10407f23d61harti
10417f23d61hartistatic int
10427f23d61hartiinet_create(struct inet_port_params *params, struct inet_port **pp)
10437f23d61harti{
10447f23d61harti	int err = SNMP_ERR_NOERROR;
10457f23d61harti	struct inet_port *port = NULL;
10467f23d61harti
10477f23d61harti	if (params->port > 0xffff) {
10487f23d61harti		err = SNMP_ERR_NO_CREATION;
10497f23d61harti		goto fail;
10507f23d61harti	}
10517f23d61harti
10527f23d61harti	if ((port = malloc(sizeof(*port))) == NULL) {
10537f23d61harti		err =  SNMP_ERR_GENERR;
10547f23d61harti		goto fail;
10557f23d61harti	}
10567f23d61harti	memset(port, 0, sizeof(*port));
10577f23d61harti	TAILQ_INIT(&port->socks);
10587f23d61harti
10597f23d61harti	port->proto = params->proto;
10607f23d61harti	port->tport.index = params->index;
10617f23d61harti
10627f23d61harti	switch (params->type) {
10637f23d61harti
10647f23d61harti	  case InetAddressType_ipv4:
10657f23d61harti		port->create = ipv4_create;
10667f23d61harti		port->activate = ipv4_activate;
10677f23d61harti		port->deactivate = ipv4_deactivate;
10687f23d61harti		break;
10697f23d61harti
10707f23d61harti	  case InetAddressType_ipv6:
10717f23d61harti		port->create = ipv6_create;
10727f23d61harti		port->activate = ipv6_activate;
10737f23d61harti		port->deactivate = ipv6_deactivate;
10747f23d61harti		break;
10757f23d61harti
10767f23d61harti	  case InetAddressType_ipv6z:
10777f23d61harti		port->create = ipv6z_create;
10787f23d61harti		port->activate = ipv6_activate;
10797f23d61harti		port->deactivate = ipv6_deactivate;
10807f23d61harti		break;
10817f23d61harti
10827f23d61harti	  case InetAddressType_dns:
10837f23d61harti		port->create = dns_create;
10847f23d61harti		port->activate = dns_activate;
10857f23d61harti		port->deactivate = dns_deactivate;
10867f23d61harti		break;
10877f23d61harti
10887f23d61harti	  default:
10897f23d61harti		err = SNMP_ERR_NO_CREATION;
10907f23d61harti		goto fail;
10917f23d61harti	}
10927f23d61harti
10937f23d61harti	if ((err = port->create(port, params)) != SNMP_ERR_NOERROR)
10947f23d61harti		goto fail;
10957f23d61harti
10967f23d61harti	*pp = port;
10977f23d61harti	trans_insert_port(my_trans, &port->tport);
10987f23d61harti	return (err);
10997f23d61harti
11007f23d61hartifail:
11017f23d61harti	free(port->dns_addr);
11027f23d61harti	free(port);
11037f23d61harti	return (err);
11047f23d61harti}
11057f23d61harti
11067f23d61hartistatic int
11077f23d61harticreate_and_go(struct snmp_context *ctx, struct inet_port_params *params)
11087f23d61harti{
11097f23d61harti	int err;
11107f23d61harti	struct inet_port *port;
11117f23d61harti
11127f23d61harti	if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR)
11137f23d61harti		return (err);
11147f23d61harti
11157f23d61harti	port->row_status = RowStatus_createAndGo;
11167f23d61harti	ctx->scratch->ptr1 = port;
11177f23d61harti
11187f23d61harti	if (community == COMM_INITIALIZE)
11197f23d61harti		return (err);
11207f23d61harti
11217f23d61harti	return (inet_activate(&port->tport));
11227f23d61harti}
11237f23d61harti
11247f23d61hartistatic int
11257f23d61harticreate_and_wait(struct snmp_context *ctx, struct inet_port_params *params)
11267f23d61harti{
11277f23d61harti	int err;
11287f23d61harti	struct inet_port *port;
11297f23d61harti
11307f23d61harti	if ((err = inet_create(params, &port)) != SNMP_ERR_NOERROR)
11317f23d61harti		return (err);
11327f23d61harti
11337f23d61harti	port->row_status = RowStatus_createAndWait;
11347f23d61harti	ctx->scratch->ptr1 = port;
11357f23d61harti
11367f23d61harti	return (err);
11377f23d61harti}
11387f23d61harti
11397f23d61harti/**
11407f23d61harti * This is called to set a RowStatus value in the port table during
11417f23d61harti * SET processing.
11427f23d61harti *
11437f23d61harti * When this is called during initialization time and the RowStatus is set
11447f23d61harti * to CreateAndGo, the port is actually not activated. This is done when
11457f23d61harti * the main code calls the init() for all ports later.
11467f23d61harti */
11477f23d61hartistatic int
11487f23d61hartiinet_port_set(struct snmp_context *ctx, struct inet_port *port,
11497f23d61harti    struct inet_port_params *params, enum RowStatus nstatus)
11507f23d61harti{
11517f23d61harti	switch (nstatus) {
11527f23d61harti
11537f23d61harti	  case RowStatus_createAndGo:
11547f23d61harti		if (port != NULL)
11557f23d61harti			return (SNMP_ERR_INCONS_VALUE);
11567f23d61harti		ctx->scratch->int1 = SET_CREATED;
11577f23d61harti		return (create_and_go(ctx, params));
11587f23d61harti
11597f23d61harti	  case RowStatus_createAndWait:
11607f23d61harti		if (port != NULL)
11617f23d61harti			return (SNMP_ERR_INCONS_VALUE);
11627f23d61harti		ctx->scratch->int1 = SET_CREATED;
11637f23d61harti		return (create_and_wait(ctx, params));
11647f23d61harti
11657f23d61harti	  case RowStatus_active:
11667f23d61harti		if (port == NULL)
11677f23d61harti			return (SNMP_ERR_INCONS_VALUE);
11687f23d61harti
11697f23d61harti		switch (port->row_status) {
11707f23d61harti
11717f23d61harti		  case RowStatus_notReady:
11727f23d61harti			/* this can not happend */
11737f23d61harti			abort();
11747f23d61harti
11757f23d61harti		  case RowStatus_notInService:
11767f23d61harti			ctx->scratch->int1 = SET_ACTIVATED;
11777f23d61harti			return (inet_activate(&port->tport));
11787f23d61harti
11797f23d61harti		  case RowStatus_active:
11807f23d61harti			return (SNMP_ERR_NOERROR);
11817f23d61harti
11827f23d61harti		  case RowStatus_createAndGo:
11837f23d61harti		  case RowStatus_createAndWait:
11847f23d61harti		  case RowStatus_destroy:
11857f23d61harti			abort();
11867f23d61harti		}
11877f23d61harti		break;
11887f23d61harti
11897f23d61harti	  case RowStatus_notInService:
11907f23d61harti		if (port == NULL)
11917f23d61harti			return (SNMP_ERR_INCONS_VALUE);
11927f23d61harti
11937f23d61harti		switch (port->row_status) {
11947f23d61harti
11957f23d61harti		  case RowStatus_notReady:
11967f23d61harti			/* this can not happend */
11977f23d61harti			abort();
11987f23d61harti
11997f23d61harti		  case RowStatus_notInService:
12007f23d61harti			return (SNMP_ERR_NOERROR);
12017f23d61harti
12027f23d61harti		  case RowStatus_active:
12037f23d61harti			/* this is done during commit */
12047f23d61harti			ctx->scratch->int1 = SET_DEACTIVATE;
12057f23d61harti			return (SNMP_ERR_NOERROR);
12067f23d61harti
12077f23d61harti		  case RowStatus_createAndGo:
12087f23d61harti		  case RowStatus_createAndWait:
12097f23d61harti		  case RowStatus_destroy:
12107f23d61harti			abort();
12117f23d61harti		}
12127f23d61harti		break;
12137f23d61harti
12147f23d61harti	  case RowStatus_destroy:
12157f23d61harti		/* this is done during commit */
12167f23d61harti		ctx->scratch->int1 = SET_DESTROY;
12177f23d61harti		return (SNMP_ERR_NOERROR);
12187f23d61harti
12197f23d61harti	  case RowStatus_notReady:
12207f23d61harti		return (SNMP_ERR_WRONG_VALUE);
12217f23d61harti	}
12227f23d61harti	abort();
12237f23d61harti}
12247f23d61harti
12257f23d61harti/*
12267f23d61harti * Port table
12277f23d61harti */
12287f23d61hartiint
12297f23d61hartiop_snmp_trans_inet(struct snmp_context *ctx, struct snmp_value *value,
12307f23d61harti    u_int sub, u_int iidx __unused, enum snmp_op op)
12317f23d61harti{
12327f23d61harti	asn_subid_t which = value->var.subs[sub - 1];
12337f23d61harti	struct inet_port *port;
12347f23d61harti	struct inet_port_params params;
12357f23d61harti	int ret;
12367f23d61harti
12377f23d61harti	switch (op) {
12387f23d61harti
12397f23d61harti	  case SNMP_OP_GETNEXT:
12407f23d61harti		if ((port = (struct inet_port *)trans_next_port(my_trans,
12417f23d61harti		    &value->var, sub)) == NULL)
12427f23d61harti			return (SNMP_ERR_NOSUCHNAME);
12437f23d61harti		index_append(&value->var, sub, &port->tport.index);
12447f23d61harti		goto fetch;
12457f23d61harti
12467f23d61harti	  case SNMP_OP_GET:
12477f23d61harti		if ((port = (struct inet_port *)trans_find_port(my_trans,
12487f23d61harti		    &value->var, sub)) == NULL)
12497f23d61harti			return (SNMP_ERR_NOSUCHNAME);
12507f23d61harti		goto fetch;
12517f23d61harti
12527f23d61harti	  case SNMP_OP_SET:
12537f23d61harti		port = (struct inet_port *)trans_find_port(my_trans,
12547f23d61harti		    &value->var, sub);
12557f23d61harti
12567f23d61harti		if (which != LEAF_begemotSnmpdTransInetStatus)
12577f23d61harti			abort();
12587f23d61harti		if (!isok_RowStatus(value->v.integer))
12597f23d61harti			return (SNMP_ERR_WRONG_VALUE);
12607f23d61harti
12617f23d61harti		if (index_decode(&value->var, sub, iidx, &params.type,
12627f23d61harti		    &params.addr, &params.addr_len, &params.port,
12637f23d61harti		    &params.proto))
12647f23d61harti			return (SNMP_ERR_NO_CREATION);
12657f23d61harti
12667f23d61harti		asn_slice_oid(&params.index, &value->var, sub, value->var.len);
12677f23d61harti
12687f23d61harti		ret = inet_port_set(ctx, port, &params,
12697f23d61harti		    (enum RowStatus)value->v.integer);
12707f23d61harti
12717f23d61harti		free(params.addr);
12727f23d61harti		return (ret);
12737f23d61harti
12747f23d61harti	  case SNMP_OP_ROLLBACK:
12757f23d61harti		if ((port = (struct inet_port *)trans_find_port(my_trans,
12767f23d61harti		    &value->var, sub)) == NULL)
12777f23d61harti			/* cannot happen */
12787f23d61harti			abort();
12797f23d61harti
12807f23d61harti		switch (ctx->scratch->int1) {
12817f23d61harti
12827f23d61harti		  case SET_CREATED:
12837f23d61harti			/* newly created */
12847f23d61harti			assert(port != NULL);
12857f23d61harti			inet_destroy_port(&port->tport);
12867f23d61harti			return (SNMP_ERR_NOERROR);
12877f23d61harti
12887f23d61harti		  case SET_DESTROY:
12897f23d61harti			/* do it now */
12907f23d61harti			assert(port != NULL);
12917f23d61harti			return (SNMP_ERR_NOERROR);
12927f23d61harti
12937f23d61harti		  case SET_ACTIVATED:
12947f23d61harti			deactivate_port(port);
12957f23d61harti			return (SNMP_ERR_NOERROR);
12967f23d61harti
12977f23d61harti		  case SET_DEACTIVATE:
12987f23d61harti			return (SNMP_ERR_NOERROR);
12997f23d61harti		}
13007f23d61harti		abort();
13017f23d61harti
13027f23d61harti	  case SNMP_OP_COMMIT:
13037f23d61harti		if ((port = (struct inet_port *)trans_find_port(my_trans,
13047f23d61harti		    &value->var, sub)) == NULL)
13057f23d61harti			/* cannot happen */
13067f23d61harti			abort();
13077f23d61harti
13087f23d61harti		switch (ctx->scratch->int1) {
13097f23d61harti
13107f23d61harti		  case SET_CREATED:
13117f23d61harti			/* newly created */
13127f23d61harti			assert(port != NULL);
13137f23d61harti			return (SNMP_ERR_NOERROR);
13147f23d61harti
13157f23d61harti		  case SET_DESTROY:
13167f23d61harti			/* do it now */
13177f23d61harti			assert(port != NULL);
13187f23d61harti			inet_destroy_port(&port->tport);
13197f23d61harti			return (SNMP_ERR_NOERROR);
13207f23d61harti
13217f23d61harti		  case SET_ACTIVATED:
13227f23d61harti			return (SNMP_ERR_NOERROR);
13237f23d61harti
13247f23d61harti		  case SET_DEACTIVATE:
13257f23d61harti			deactivate_port(port);
13267f23d61harti			return (SNMP_ERR_NOERROR);
13277f23d61harti		}
13287f23d61harti		abort();
13297f23d61harti	}
13307f23d61harti	abort();
13317f23d61harti
13327f23d61harti  fetch:
13337f23d61harti	switch (which) {
13347f23d61harti
13357f23d61harti	  case LEAF_begemotSnmpdTransInetStatus:
13367f23d61harti		value->v.integer = port->row_status;
13377f23d61harti		break;
13387f23d61harti
13397f23d61harti	  default:
13407f23d61harti		abort();
13417f23d61harti	}
13427f23d61harti
13437f23d61harti	return (SNMP_ERR_NOERROR);
13447f23d61harti}
1345