xref: /freebsd/contrib/bsnmp/snmpd/trans_lsock.c (revision 9b588ef15f47900ff77419eaf2817ce62fb58a14)
170af00a1SHartmut Brandt /*
270af00a1SHartmut Brandt  * Copyright (c) 2003
370af00a1SHartmut Brandt  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
470af00a1SHartmut Brandt  *	All rights reserved.
570af00a1SHartmut Brandt  *
670af00a1SHartmut Brandt  * Author: Harti Brandt <harti@freebsd.org>
770af00a1SHartmut Brandt  *
8896052c1SHartmut Brandt  * Redistribution and use in source and binary forms, with or without
9896052c1SHartmut Brandt  * modification, are permitted provided that the following conditions
10896052c1SHartmut Brandt  * are met:
11896052c1SHartmut Brandt  * 1. Redistributions of source code must retain the above copyright
12896052c1SHartmut Brandt  *    notice, this list of conditions and the following disclaimer.
1370af00a1SHartmut Brandt  * 2. Redistributions in binary form must reproduce the above copyright
1470af00a1SHartmut Brandt  *    notice, this list of conditions and the following disclaimer in the
1570af00a1SHartmut Brandt  *    documentation and/or other materials provided with the distribution.
1670af00a1SHartmut Brandt  *
17896052c1SHartmut Brandt  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18896052c1SHartmut Brandt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19896052c1SHartmut Brandt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20896052c1SHartmut Brandt  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21896052c1SHartmut Brandt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22896052c1SHartmut Brandt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23896052c1SHartmut Brandt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24896052c1SHartmut Brandt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25896052c1SHartmut Brandt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26896052c1SHartmut Brandt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27896052c1SHartmut Brandt  * SUCH DAMAGE.
2870af00a1SHartmut Brandt  *
2994caccb3SHartmut Brandt  * $Begemot: bsnmp/snmpd/trans_lsock.c,v 1.6 2005/02/25 11:50:25 brandt_h Exp $
3070af00a1SHartmut Brandt  *
3170af00a1SHartmut Brandt  * Local domain socket transport
3270af00a1SHartmut Brandt  */
3370af00a1SHartmut Brandt #include <sys/types.h>
34135f7de5SShteryana Shopova #include <sys/queue.h>
3570af00a1SHartmut Brandt #include <sys/stat.h>
360ba351efSEnji Cooper #include <sys/ucred.h>
370327a0e8SEnji Cooper #include <sys/un.h>
3870af00a1SHartmut Brandt 
390327a0e8SEnji Cooper #include <errno.h>
400327a0e8SEnji Cooper #include <stddef.h>
4194caccb3SHartmut Brandt #include <stdio.h>
4270af00a1SHartmut Brandt #include <stdlib.h>
4370af00a1SHartmut Brandt #include <string.h>
440327a0e8SEnji Cooper #include <syslog.h>
4570af00a1SHartmut Brandt #include <unistd.h>
4670af00a1SHartmut Brandt 
4770af00a1SHartmut Brandt #include "snmpmod.h"
4870af00a1SHartmut Brandt #include "snmpd.h"
4970af00a1SHartmut Brandt #include "trans_lsock.h"
5070af00a1SHartmut Brandt #include "tree.h"
5170af00a1SHartmut Brandt #include "oid.h"
5270af00a1SHartmut Brandt 
5370af00a1SHartmut Brandt static const struct asn_oid
5470af00a1SHartmut Brandt 	oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
5570af00a1SHartmut Brandt 
5670af00a1SHartmut Brandt static int lsock_start(void);
5770af00a1SHartmut Brandt static int lsock_stop(int);
5870af00a1SHartmut Brandt static void lsock_close_port(struct tport *);
5970af00a1SHartmut Brandt static int lsock_init_port(struct tport *);
6070af00a1SHartmut Brandt static ssize_t lsock_send(struct tport *, const u_char *, size_t,
6170af00a1SHartmut Brandt     const struct sockaddr *, size_t);
628d7f605bSEnji Cooper static ssize_t lsock_recv(struct tport *, struct port_input *);
6370af00a1SHartmut Brandt 
6470af00a1SHartmut Brandt /* exported */
6570af00a1SHartmut Brandt const struct transport_def lsock_trans = {
6670af00a1SHartmut Brandt 	"lsock",
6770af00a1SHartmut Brandt 	OIDX_begemotSnmpdTransLsock,
6870af00a1SHartmut Brandt 	lsock_start,
6970af00a1SHartmut Brandt 	lsock_stop,
7070af00a1SHartmut Brandt 	lsock_close_port,
7170af00a1SHartmut Brandt 	lsock_init_port,
720ba351efSEnji Cooper 	lsock_send,
7304d17814SAndrey V. Elsukov 	lsock_recv,
7404d17814SAndrey V. Elsukov 	NULL
7570af00a1SHartmut Brandt };
7670af00a1SHartmut Brandt static struct transport *my_trans;
7770af00a1SHartmut Brandt 
7870af00a1SHartmut Brandt static int
lsock_remove(struct tport * tp,intptr_t arg __unused)7970af00a1SHartmut Brandt lsock_remove(struct tport *tp, intptr_t arg __unused)
8070af00a1SHartmut Brandt {
8170af00a1SHartmut Brandt 	struct lsock_port *port = (struct lsock_port *)tp;
8270af00a1SHartmut Brandt 
8370af00a1SHartmut Brandt 	(void)remove(port->name);
8470af00a1SHartmut Brandt 
8570af00a1SHartmut Brandt 	return (-1);
8670af00a1SHartmut Brandt }
8770af00a1SHartmut Brandt 
8870af00a1SHartmut Brandt static int
lsock_stop(int force)8970af00a1SHartmut Brandt lsock_stop(int force)
9070af00a1SHartmut Brandt {
9170af00a1SHartmut Brandt 
9270af00a1SHartmut Brandt 	if (my_trans != NULL) {
9370af00a1SHartmut Brandt 		if (!force && trans_first_port(my_trans) != NULL)
9470af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
958eecd77aSHartmut Brandt 		trans_iter_port(my_trans, lsock_remove, 0);
9670af00a1SHartmut Brandt 		return (trans_unregister(my_trans));
9770af00a1SHartmut Brandt 	}
9870af00a1SHartmut Brandt 	return (SNMP_ERR_NOERROR);
9970af00a1SHartmut Brandt }
10070af00a1SHartmut Brandt 
10170af00a1SHartmut Brandt static int
lsock_start(void)10270af00a1SHartmut Brandt lsock_start(void)
10370af00a1SHartmut Brandt {
10470af00a1SHartmut Brandt 	return (trans_register(&lsock_trans, &my_trans));
10570af00a1SHartmut Brandt }
10670af00a1SHartmut Brandt 
10770af00a1SHartmut Brandt /*
10870af00a1SHartmut Brandt  * Open a local port. If this is a datagram socket create also the
10970af00a1SHartmut Brandt  * one and only peer.
11070af00a1SHartmut Brandt  */
11170af00a1SHartmut Brandt static int
lsock_open_port(u_char * name,size_t namelen,struct lsock_port ** pp,int type)11270af00a1SHartmut Brandt lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
11370af00a1SHartmut Brandt     int type)
11470af00a1SHartmut Brandt {
11570af00a1SHartmut Brandt 	struct lsock_port *port;
11670af00a1SHartmut Brandt 	struct lsock_peer *peer = NULL;
11770af00a1SHartmut Brandt 	int is_stream, need_cred;
11870af00a1SHartmut Brandt 	size_t u;
11970af00a1SHartmut Brandt 	int err;
12070af00a1SHartmut Brandt 	struct sockaddr_un sa;
12170af00a1SHartmut Brandt 
1228eecd77aSHartmut Brandt 	if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
12370af00a1SHartmut Brandt 		return (SNMP_ERR_BADVALUE);
1248eecd77aSHartmut Brandt 
12570af00a1SHartmut Brandt 	switch (type) {
12670af00a1SHartmut Brandt 	  case LOCP_DGRAM_UNPRIV:
12770af00a1SHartmut Brandt 		is_stream = 0;
12870af00a1SHartmut Brandt 		need_cred = 0;
12970af00a1SHartmut Brandt 		break;
13070af00a1SHartmut Brandt 
13170af00a1SHartmut Brandt 	  case LOCP_DGRAM_PRIV:
13270af00a1SHartmut Brandt 		is_stream = 0;
13370af00a1SHartmut Brandt 		need_cred = 1;
13470af00a1SHartmut Brandt 		break;
13570af00a1SHartmut Brandt 
13670af00a1SHartmut Brandt 	  case LOCP_STREAM_UNPRIV:
13770af00a1SHartmut Brandt 		is_stream = 1;
13870af00a1SHartmut Brandt 		need_cred = 0;
13970af00a1SHartmut Brandt 		break;
14070af00a1SHartmut Brandt 
14170af00a1SHartmut Brandt 	  case LOCP_STREAM_PRIV:
14270af00a1SHartmut Brandt 		is_stream = 1;
14370af00a1SHartmut Brandt 		need_cred = 1;
14470af00a1SHartmut Brandt 		break;
14570af00a1SHartmut Brandt 
14670af00a1SHartmut Brandt 	  default:
14770af00a1SHartmut Brandt 		return (SNMP_ERR_BADVALUE);
14870af00a1SHartmut Brandt 	}
14970af00a1SHartmut Brandt 
150446bd8a4SEnji Cooper 	if ((port = calloc(1, sizeof(*port))) == NULL)
15170af00a1SHartmut Brandt 		return (SNMP_ERR_GENERR);
1528eecd77aSHartmut Brandt 
15370af00a1SHartmut Brandt 	if (!is_stream) {
154446bd8a4SEnji Cooper 		if ((peer = calloc(1, sizeof(*peer))) == NULL) {
15570af00a1SHartmut Brandt 			free(port);
15670af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
15770af00a1SHartmut Brandt 		}
15870af00a1SHartmut Brandt 	}
15970af00a1SHartmut Brandt 	if ((port->name = malloc(namelen + 1)) == NULL) {
16070af00a1SHartmut Brandt 		free(port);
16170af00a1SHartmut Brandt 		if (!is_stream)
16270af00a1SHartmut Brandt 			free(peer);
16370af00a1SHartmut Brandt 		return (SNMP_ERR_GENERR);
16470af00a1SHartmut Brandt 	}
16570af00a1SHartmut Brandt 	strncpy(port->name, name, namelen);
16670af00a1SHartmut Brandt 	port->name[namelen] = '\0';
16770af00a1SHartmut Brandt 
16870af00a1SHartmut Brandt 	port->type = type;
16970af00a1SHartmut Brandt 	port->str_sock = -1;
17070af00a1SHartmut Brandt 	LIST_INIT(&port->peers);
17170af00a1SHartmut Brandt 
17270af00a1SHartmut Brandt 	port->tport.index.len = namelen + 1;
17370af00a1SHartmut Brandt 	port->tport.index.subs[0] = namelen;
17470af00a1SHartmut Brandt 	for (u = 0; u < namelen; u++)
17570af00a1SHartmut Brandt 		port->tport.index.subs[u + 1] = name[u];
17670af00a1SHartmut Brandt 
17770af00a1SHartmut Brandt 	if (peer != NULL) {
17870af00a1SHartmut Brandt 		LIST_INSERT_HEAD(&port->peers, peer, link);
17970af00a1SHartmut Brandt 
18070af00a1SHartmut Brandt 		peer->port = port;
18170af00a1SHartmut Brandt 
18270af00a1SHartmut Brandt 		peer->input.fd = -1;
18370af00a1SHartmut Brandt 		peer->input.id = NULL;
18470af00a1SHartmut Brandt 		peer->input.stream = is_stream;
18570af00a1SHartmut Brandt 		peer->input.cred = need_cred;
18670af00a1SHartmut Brandt 		peer->input.peer = (struct sockaddr *)&peer->peer;
18770af00a1SHartmut Brandt 	}
18870af00a1SHartmut Brandt 
18970af00a1SHartmut Brandt 	trans_insert_port(my_trans, &port->tport);
19070af00a1SHartmut Brandt 
19170af00a1SHartmut Brandt 	if (community != COMM_INITIALIZE &&
19270af00a1SHartmut Brandt 	    (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
19370af00a1SHartmut Brandt 		lsock_close_port(&port->tport);
19470af00a1SHartmut Brandt 		return (err);
19570af00a1SHartmut Brandt 	}
19670af00a1SHartmut Brandt 
19770af00a1SHartmut Brandt 	*pp = port;
19870af00a1SHartmut Brandt 
19970af00a1SHartmut Brandt 	return (SNMP_ERR_NOERROR);
20070af00a1SHartmut Brandt }
20170af00a1SHartmut Brandt 
20270af00a1SHartmut Brandt /*
20370af00a1SHartmut Brandt  * Close a local domain peer
20470af00a1SHartmut Brandt  */
20570af00a1SHartmut Brandt static void
lsock_peer_close(struct lsock_peer * peer)20670af00a1SHartmut Brandt lsock_peer_close(struct lsock_peer *peer)
20770af00a1SHartmut Brandt {
20870af00a1SHartmut Brandt 
20970af00a1SHartmut Brandt 	LIST_REMOVE(peer, link);
21070af00a1SHartmut Brandt 	snmpd_input_close(&peer->input);
21170af00a1SHartmut Brandt 	free(peer);
21270af00a1SHartmut Brandt }
21370af00a1SHartmut Brandt 
21470af00a1SHartmut Brandt /*
21570af00a1SHartmut Brandt  * Close a local port
21670af00a1SHartmut Brandt  */
21770af00a1SHartmut Brandt static void
lsock_close_port(struct tport * tp)21870af00a1SHartmut Brandt lsock_close_port(struct tport *tp)
21970af00a1SHartmut Brandt {
22070af00a1SHartmut Brandt 	struct lsock_port *port = (struct lsock_port *)tp;
22170af00a1SHartmut Brandt 	struct lsock_peer *peer;
22270af00a1SHartmut Brandt 
22370af00a1SHartmut Brandt 	if (port->str_id != NULL)
22470af00a1SHartmut Brandt 		fd_deselect(port->str_id);
22570af00a1SHartmut Brandt 	if (port->str_sock >= 0)
22670af00a1SHartmut Brandt 		(void)close(port->str_sock);
22770af00a1SHartmut Brandt 	(void)remove(port->name);
22870af00a1SHartmut Brandt 
22970af00a1SHartmut Brandt 	trans_remove_port(tp);
23070af00a1SHartmut Brandt 
23170af00a1SHartmut Brandt 	while ((peer = LIST_FIRST(&port->peers)) != NULL)
23270af00a1SHartmut Brandt 		lsock_peer_close(peer);
23370af00a1SHartmut Brandt 
23470af00a1SHartmut Brandt 	free(port->name);
23570af00a1SHartmut Brandt 	free(port);
23670af00a1SHartmut Brandt }
23770af00a1SHartmut Brandt 
23870af00a1SHartmut Brandt /*
23970af00a1SHartmut Brandt  * Input on a local socket (either datagram or stream)
24070af00a1SHartmut Brandt  */
24170af00a1SHartmut Brandt static void
lsock_input(int fd __unused,void * udata)24270af00a1SHartmut Brandt lsock_input(int fd __unused, void *udata)
24370af00a1SHartmut Brandt {
24470af00a1SHartmut Brandt 	struct lsock_peer *peer = udata;
24570af00a1SHartmut Brandt 	struct lsock_port *p = peer->port;
24670af00a1SHartmut Brandt 
24770af00a1SHartmut Brandt 	peer->input.peerlen = sizeof(peer->peer);
24870af00a1SHartmut Brandt 	if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
24970af00a1SHartmut Brandt 		/* framing or other input error */
25070af00a1SHartmut Brandt 		lsock_peer_close(peer);
25170af00a1SHartmut Brandt }
25270af00a1SHartmut Brandt 
25370af00a1SHartmut Brandt /*
25470af00a1SHartmut Brandt  * A UNIX domain listening socket is ready. This means we have a peer
25570af00a1SHartmut Brandt  * that we need to accept
25670af00a1SHartmut Brandt  */
25770af00a1SHartmut Brandt static void
lsock_listen_input(int fd,void * udata)25870af00a1SHartmut Brandt lsock_listen_input(int fd, void *udata)
25970af00a1SHartmut Brandt {
26070af00a1SHartmut Brandt 	struct lsock_port *p = udata;
26170af00a1SHartmut Brandt 	struct lsock_peer *peer;
26270af00a1SHartmut Brandt 
263446bd8a4SEnji Cooper 	if ((peer = calloc(1, sizeof(*peer))) == NULL) {
26470af00a1SHartmut Brandt 		syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
26570af00a1SHartmut Brandt 		(void)close(accept(fd, NULL, NULL));
26670af00a1SHartmut Brandt 		return;
26770af00a1SHartmut Brandt 	}
26870af00a1SHartmut Brandt 
26970af00a1SHartmut Brandt 	peer->port = p;
27070af00a1SHartmut Brandt 
27170af00a1SHartmut Brandt 	peer->input.stream = 1;
27270af00a1SHartmut Brandt 	peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
27370af00a1SHartmut Brandt 	    p->type == LOCP_STREAM_PRIV);
27470af00a1SHartmut Brandt 	peer->input.peerlen = sizeof(peer->peer);
27570af00a1SHartmut Brandt 	peer->input.peer = (struct sockaddr *)&peer->peer;
27670af00a1SHartmut Brandt 
27770af00a1SHartmut Brandt 	peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
27870af00a1SHartmut Brandt 	if (peer->input.fd == -1) {
27970af00a1SHartmut Brandt 		syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
28070af00a1SHartmut Brandt 		free(peer);
28170af00a1SHartmut Brandt 		return;
28270af00a1SHartmut Brandt 	}
28370af00a1SHartmut Brandt 
28470af00a1SHartmut Brandt 	if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
28570af00a1SHartmut Brandt 	    peer, NULL)) == NULL) {
28670af00a1SHartmut Brandt 		close(peer->input.fd);
28770af00a1SHartmut Brandt 		free(peer);
28870af00a1SHartmut Brandt 		return;
28970af00a1SHartmut Brandt 	}
29070af00a1SHartmut Brandt 
29170af00a1SHartmut Brandt 	LIST_INSERT_HEAD(&p->peers, peer, link);
29270af00a1SHartmut Brandt }
29370af00a1SHartmut Brandt 
29470af00a1SHartmut Brandt /*
29570af00a1SHartmut Brandt  * Create a local socket
29670af00a1SHartmut Brandt  */
29770af00a1SHartmut Brandt static int
lsock_init_port(struct tport * tp)29870af00a1SHartmut Brandt lsock_init_port(struct tport *tp)
29970af00a1SHartmut Brandt {
30070af00a1SHartmut Brandt 	struct lsock_port *p = (struct lsock_port *)tp;
30170af00a1SHartmut Brandt 	struct sockaddr_un sa;
30270af00a1SHartmut Brandt 
30370af00a1SHartmut Brandt 	if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
30470af00a1SHartmut Brandt 		if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
30570af00a1SHartmut Brandt 			syslog(LOG_ERR, "creating local socket: %m");
30670af00a1SHartmut Brandt 			return (SNMP_ERR_RES_UNAVAIL);
30770af00a1SHartmut Brandt 		}
30870af00a1SHartmut Brandt 
30970157df6SEnji Cooper 		strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path));
31070af00a1SHartmut Brandt 		sa.sun_family = AF_LOCAL;
31170157df6SEnji Cooper 		sa.sun_len = SUN_LEN(&sa);
31270af00a1SHartmut Brandt 
31370af00a1SHartmut Brandt 		(void)remove(p->name);
31470af00a1SHartmut Brandt 
31570af00a1SHartmut Brandt 		if (bind(p->str_sock, (struct sockaddr *)&sa, sizeof(sa))) {
31670af00a1SHartmut Brandt 			if (errno == EADDRNOTAVAIL) {
31770af00a1SHartmut Brandt 				close(p->str_sock);
31870af00a1SHartmut Brandt 				p->str_sock = -1;
31970af00a1SHartmut Brandt 				return (SNMP_ERR_INCONS_NAME);
32070af00a1SHartmut Brandt 			}
32170af00a1SHartmut Brandt 			syslog(LOG_ERR, "bind: %s %m", p->name);
32270af00a1SHartmut Brandt 			close(p->str_sock);
32370af00a1SHartmut Brandt 			p->str_sock = -1;
32470af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
32570af00a1SHartmut Brandt 		}
32670af00a1SHartmut Brandt 		if (chmod(p->name, 0666) == -1)
32770af00a1SHartmut Brandt 			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
32870af00a1SHartmut Brandt 
32970af00a1SHartmut Brandt 		if (listen(p->str_sock, 10) == -1) {
33070af00a1SHartmut Brandt 			syslog(LOG_ERR, "listen: %s %m", p->name);
33170af00a1SHartmut Brandt 			(void)remove(p->name);
33270af00a1SHartmut Brandt 			close(p->str_sock);
33370af00a1SHartmut Brandt 			p->str_sock = -1;
33470af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
33570af00a1SHartmut Brandt 		}
33670af00a1SHartmut Brandt 
33770af00a1SHartmut Brandt 		p->str_id = fd_select(p->str_sock, lsock_listen_input, p, NULL);
33870af00a1SHartmut Brandt 		if (p->str_id == NULL) {
33970af00a1SHartmut Brandt 			(void)remove(p->name);
34070af00a1SHartmut Brandt 			close(p->str_sock);
34170af00a1SHartmut Brandt 			p->str_sock = -1;
34270af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
34370af00a1SHartmut Brandt 		}
34470af00a1SHartmut Brandt 	} else {
34570af00a1SHartmut Brandt 		struct lsock_peer *peer;
3460cf0d912SGleb Smirnoff 		const int on = 1;
34770af00a1SHartmut Brandt 
34870af00a1SHartmut Brandt 		peer = LIST_FIRST(&p->peers);
34970af00a1SHartmut Brandt 
35070af00a1SHartmut Brandt 		if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
35170af00a1SHartmut Brandt 			syslog(LOG_ERR, "creating local socket: %m");
35270af00a1SHartmut Brandt 			return (SNMP_ERR_RES_UNAVAIL);
35370af00a1SHartmut Brandt 		}
35470af00a1SHartmut Brandt 
3550cf0d912SGleb Smirnoff 		if (setsockopt(peer->input.fd, 0, LOCAL_CREDS, &on,
3560cf0d912SGleb Smirnoff 		    sizeof(on)) == -1) {
3570cf0d912SGleb Smirnoff 			syslog(LOG_ERR, "setsockopt(LOCAL_CREDS): %m");
3580cf0d912SGleb Smirnoff 			close(peer->input.fd);
3590cf0d912SGleb Smirnoff 			peer->input.fd = -1;
3600cf0d912SGleb Smirnoff 			return (SNMP_ERR_GENERR);
3610cf0d912SGleb Smirnoff 		}
3620cf0d912SGleb Smirnoff 
36370157df6SEnji Cooper 		strlcpy(sa.sun_path, p->name, sizeof(sa.sun_path));
36470af00a1SHartmut Brandt 		sa.sun_family = AF_LOCAL;
36570157df6SEnji Cooper 		sa.sun_len = SUN_LEN(&sa);
36670af00a1SHartmut Brandt 
36770af00a1SHartmut Brandt 		(void)remove(p->name);
36870af00a1SHartmut Brandt 
36970af00a1SHartmut Brandt 		if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
37070af00a1SHartmut Brandt 			if (errno == EADDRNOTAVAIL) {
37170af00a1SHartmut Brandt 				close(peer->input.fd);
37270af00a1SHartmut Brandt 				peer->input.fd = -1;
37370af00a1SHartmut Brandt 				return (SNMP_ERR_INCONS_NAME);
37470af00a1SHartmut Brandt 			}
37570af00a1SHartmut Brandt 			syslog(LOG_ERR, "bind: %s %m", p->name);
37670af00a1SHartmut Brandt 			close(peer->input.fd);
37770af00a1SHartmut Brandt 			peer->input.fd = -1;
37870af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
37970af00a1SHartmut Brandt 		}
38070af00a1SHartmut Brandt 		if (chmod(p->name, 0666) == -1)
38170af00a1SHartmut Brandt 			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
38270af00a1SHartmut Brandt 
38370af00a1SHartmut Brandt 		peer->input.id = fd_select(peer->input.fd, lsock_input,
38470af00a1SHartmut Brandt 		    peer, NULL);
38570af00a1SHartmut Brandt 		if (peer->input.id == NULL) {
38670af00a1SHartmut Brandt 			(void)remove(p->name);
38770af00a1SHartmut Brandt 			close(peer->input.fd);
38870af00a1SHartmut Brandt 			peer->input.fd = -1;
38970af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
39070af00a1SHartmut Brandt 		}
39170af00a1SHartmut Brandt 	}
39270af00a1SHartmut Brandt 	return (SNMP_ERR_NOERROR);
39370af00a1SHartmut Brandt }
39470af00a1SHartmut Brandt 
39570af00a1SHartmut Brandt /*
39670af00a1SHartmut Brandt  * Send something
39770af00a1SHartmut Brandt  */
39870af00a1SHartmut Brandt static ssize_t
lsock_send(struct tport * tp,const u_char * buf,size_t len,const struct sockaddr * addr,size_t addrlen)39970af00a1SHartmut Brandt lsock_send(struct tport *tp, const u_char *buf, size_t len,
40070af00a1SHartmut Brandt     const struct sockaddr *addr, size_t addrlen)
40170af00a1SHartmut Brandt {
40270af00a1SHartmut Brandt 	struct lsock_port *p = (struct lsock_port *)tp;
40370af00a1SHartmut Brandt 	struct lsock_peer *peer;
40470af00a1SHartmut Brandt 
40570af00a1SHartmut Brandt 	if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
40670af00a1SHartmut Brandt 		peer = LIST_FIRST(&p->peers);
40770af00a1SHartmut Brandt 
40870af00a1SHartmut Brandt 	} else {
40970af00a1SHartmut Brandt 		/* search for the peer */
41070af00a1SHartmut Brandt 		LIST_FOREACH(peer, &p->peers, link)
41170af00a1SHartmut Brandt 			if (peer->input.peerlen == addrlen &&
41270af00a1SHartmut Brandt 			    memcmp(peer->input.peer, addr, addrlen) == 0)
41370af00a1SHartmut Brandt 				break;
41470af00a1SHartmut Brandt 		if (peer == NULL) {
41570af00a1SHartmut Brandt 			errno = ENOTCONN;
41670af00a1SHartmut Brandt 			return (-1);
41770af00a1SHartmut Brandt 		}
41870af00a1SHartmut Brandt 	}
41970af00a1SHartmut Brandt 
420*9b588ef1SGleb Smirnoff 	return (sendto(peer->input.fd, buf, len, MSG_NOSIGNAL, addr, addrlen));
42170af00a1SHartmut Brandt }
42270af00a1SHartmut Brandt 
4230ba351efSEnji Cooper static void
check_priv_stream(struct port_input * pi)4240ba351efSEnji Cooper check_priv_stream(struct port_input *pi)
4250ba351efSEnji Cooper {
4260ba351efSEnji Cooper 	struct xucred ucred;
4270ba351efSEnji Cooper 	socklen_t ucredlen;
4280ba351efSEnji Cooper 
4290ba351efSEnji Cooper 	/* obtain the accept time credentials */
4300ba351efSEnji Cooper 	ucredlen = sizeof(ucred);
4310ba351efSEnji Cooper 
4320ba351efSEnji Cooper 	if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
4330ba351efSEnji Cooper 	    ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
4340ba351efSEnji Cooper 		pi->priv = (ucred.cr_uid == 0);
4350ba351efSEnji Cooper 	else
4360ba351efSEnji Cooper 		pi->priv = 0;
4370ba351efSEnji Cooper }
4380ba351efSEnji Cooper 
4390ba351efSEnji Cooper /*
4400ba351efSEnji Cooper  * Receive something
4410ba351efSEnji Cooper  */
4420ba351efSEnji Cooper static ssize_t
lsock_recv(struct tport * tp __unused,struct port_input * pi)4438d7f605bSEnji Cooper lsock_recv(struct tport *tp __unused, struct port_input *pi)
4440ba351efSEnji Cooper {
4450ba351efSEnji Cooper 	struct msghdr msg;
4460ba351efSEnji Cooper 	struct iovec iov[1];
4470ba351efSEnji Cooper 	ssize_t len;
4480ba351efSEnji Cooper 
4490ba351efSEnji Cooper 	msg.msg_control = NULL;
4500ba351efSEnji Cooper 	msg.msg_controllen = 0;
4510ba351efSEnji Cooper 
4520ba351efSEnji Cooper 	if (pi->buf == NULL) {
4530ba351efSEnji Cooper 		/* no buffer yet - allocate one */
4540ba351efSEnji Cooper 		if ((pi->buf = buf_alloc(0)) == NULL) {
4550ba351efSEnji Cooper 			/* ups - could not get buffer. Return an error
4560ba351efSEnji Cooper 			 * the caller must close the transport. */
4570ba351efSEnji Cooper 			return (-1);
4580ba351efSEnji Cooper 		}
4590ba351efSEnji Cooper 		pi->buflen = buf_size(0);
4600ba351efSEnji Cooper 		pi->consumed = 0;
4610ba351efSEnji Cooper 		pi->length = 0;
4620ba351efSEnji Cooper 	}
4630ba351efSEnji Cooper 
4640ba351efSEnji Cooper 	/* try to get a message */
4650ba351efSEnji Cooper 	msg.msg_name = pi->peer;
4660ba351efSEnji Cooper 	msg.msg_namelen = pi->peerlen;
4670ba351efSEnji Cooper 	msg.msg_iov = iov;
4680ba351efSEnji Cooper 	msg.msg_iovlen = 1;
4690ba351efSEnji Cooper 	msg.msg_control = NULL;
4700ba351efSEnji Cooper 	msg.msg_controllen = 0;
4710ba351efSEnji Cooper 	msg.msg_flags = 0;
4720ba351efSEnji Cooper 
4730ba351efSEnji Cooper 	iov[0].iov_base = pi->buf + pi->length;
4740ba351efSEnji Cooper 	iov[0].iov_len = pi->buflen - pi->length;
4750ba351efSEnji Cooper 
4760ba351efSEnji Cooper 	len = recvmsg(pi->fd, &msg, 0);
4770ba351efSEnji Cooper 
4780ba351efSEnji Cooper 	if (len == -1 || len == 0)
4790ba351efSEnji Cooper 		/* receive error */
4800ba351efSEnji Cooper 		return (-1);
4810ba351efSEnji Cooper 
4820ba351efSEnji Cooper 	pi->length += len;
4830ba351efSEnji Cooper 
4840ba351efSEnji Cooper 	if (pi->cred)
4850ba351efSEnji Cooper 		check_priv_stream(pi);
4860ba351efSEnji Cooper 
4870ba351efSEnji Cooper 	return (0);
4880ba351efSEnji Cooper }
4890ba351efSEnji Cooper 
49070af00a1SHartmut Brandt /*
49170af00a1SHartmut Brandt  * Dependency to create a lsock port
49270af00a1SHartmut Brandt  */
49370af00a1SHartmut Brandt struct lsock_dep {
49470af00a1SHartmut Brandt 	struct snmp_dependency dep;
49570af00a1SHartmut Brandt 
49670af00a1SHartmut Brandt 	/* index (path name) */
49770af00a1SHartmut Brandt 	u_char *path;
49870af00a1SHartmut Brandt 	size_t pathlen;
49970af00a1SHartmut Brandt 
50070af00a1SHartmut Brandt 	/* the port */
50170af00a1SHartmut Brandt 	struct lsock_port *port;
50270af00a1SHartmut Brandt 
50370af00a1SHartmut Brandt 	/* which of the fields are set */
50470af00a1SHartmut Brandt 	u_int set;
50570af00a1SHartmut Brandt 
50670af00a1SHartmut Brandt 	/* type of the port */
50770af00a1SHartmut Brandt 	int type;
50870af00a1SHartmut Brandt 
50970af00a1SHartmut Brandt 	/* status */
51070af00a1SHartmut Brandt 	int status;
51170af00a1SHartmut Brandt };
51270af00a1SHartmut Brandt #define	LD_TYPE		0x01
51370af00a1SHartmut Brandt #define	LD_STATUS	0x02
51470af00a1SHartmut Brandt #define	LD_CREATE	0x04	/* rollback create */
5158eecd77aSHartmut Brandt #define	LD_DELETE	0x08	/* rollback delete */
51670af00a1SHartmut Brandt 
51770af00a1SHartmut Brandt /*
51870af00a1SHartmut Brandt  * dependency handler for lsock ports
51970af00a1SHartmut Brandt  */
52070af00a1SHartmut Brandt static int
lsock_func(struct snmp_context * ctx,struct snmp_dependency * dep,enum snmp_depop op)52170af00a1SHartmut Brandt lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
52270af00a1SHartmut Brandt     enum snmp_depop op)
52370af00a1SHartmut Brandt {
52470af00a1SHartmut Brandt 	struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
52570af00a1SHartmut Brandt 	int err = SNMP_ERR_NOERROR;
52670af00a1SHartmut Brandt 
52770af00a1SHartmut Brandt 	switch (op) {
52870af00a1SHartmut Brandt 
52970af00a1SHartmut Brandt 	  case SNMP_DEPOP_COMMIT:
53070af00a1SHartmut Brandt 		if (!(ld->set & LD_STATUS))
53170af00a1SHartmut Brandt 			err = SNMP_ERR_BADVALUE;
53270af00a1SHartmut Brandt 		else if (ld->port == NULL) {
53370af00a1SHartmut Brandt 			if (!ld->status)
53470af00a1SHartmut Brandt 				err = SNMP_ERR_BADVALUE;
53570af00a1SHartmut Brandt 
53670af00a1SHartmut Brandt 			else {
53770af00a1SHartmut Brandt 				/* create */
53870af00a1SHartmut Brandt 				err = lsock_open_port(ld->path, ld->pathlen,
53970af00a1SHartmut Brandt 				    &ld->port, ld->type);
54070af00a1SHartmut Brandt 				if (err == SNMP_ERR_NOERROR)
54170af00a1SHartmut Brandt 					ld->set |= LD_CREATE;
54270af00a1SHartmut Brandt 			}
54370af00a1SHartmut Brandt 		} else if (!ld->status) {
5448eecd77aSHartmut Brandt 			/* delete - hard to roll back so defer to finalizer */
5458eecd77aSHartmut Brandt 			ld->set |= LD_DELETE;
54670af00a1SHartmut Brandt 		} else
54770af00a1SHartmut Brandt 			/* modify - read-only */
54870af00a1SHartmut Brandt 			err = SNMP_ERR_READONLY;
54970af00a1SHartmut Brandt 
55070af00a1SHartmut Brandt 		return (err);
55170af00a1SHartmut Brandt 
55270af00a1SHartmut Brandt 	  case SNMP_DEPOP_ROLLBACK:
55370af00a1SHartmut Brandt 		if (ld->set & LD_CREATE) {
55470af00a1SHartmut Brandt 			/* was create */
55570af00a1SHartmut Brandt 			lsock_close_port(&ld->port->tport);
55670af00a1SHartmut Brandt 		}
55770af00a1SHartmut Brandt 		return (SNMP_ERR_NOERROR);
5588eecd77aSHartmut Brandt 
5598eecd77aSHartmut Brandt 	  case SNMP_DEPOP_FINISH:
5608eecd77aSHartmut Brandt 		if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
5618eecd77aSHartmut Brandt 			lsock_close_port(&ld->port->tport);
5628eecd77aSHartmut Brandt 		free(ld->path);
5638eecd77aSHartmut Brandt 		return (SNMP_ERR_NOERROR);
56470af00a1SHartmut Brandt 	}
56570af00a1SHartmut Brandt 	abort();
56670af00a1SHartmut Brandt }
56770af00a1SHartmut Brandt 
56870af00a1SHartmut Brandt /*
56970af00a1SHartmut Brandt  * Local port table
57070af00a1SHartmut Brandt  */
57170af00a1SHartmut Brandt int
op_lsock_port(struct snmp_context * ctx,struct snmp_value * value,u_int sub,u_int iidx,enum snmp_op op)57270af00a1SHartmut Brandt op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
57370af00a1SHartmut Brandt     u_int sub, u_int iidx, enum snmp_op op)
57470af00a1SHartmut Brandt {
57570af00a1SHartmut Brandt 	asn_subid_t which = value->var.subs[sub-1];
57670af00a1SHartmut Brandt 	struct lsock_port *p;
57770af00a1SHartmut Brandt 	u_char *name;
57870af00a1SHartmut Brandt 	size_t namelen;
57970af00a1SHartmut Brandt 	struct lsock_dep *ld;
58070af00a1SHartmut Brandt 	struct asn_oid didx;
58170af00a1SHartmut Brandt 
58270af00a1SHartmut Brandt 	switch (op) {
58370af00a1SHartmut Brandt 
58470af00a1SHartmut Brandt 	  case SNMP_OP_GETNEXT:
58570af00a1SHartmut Brandt 		if ((p = (struct lsock_port *)trans_next_port(my_trans,
58670af00a1SHartmut Brandt 		    &value->var, sub)) == NULL)
58770af00a1SHartmut Brandt 			return (SNMP_ERR_NOSUCHNAME);
58870af00a1SHartmut Brandt 		index_append(&value->var, sub, &p->tport.index);
58970af00a1SHartmut Brandt 		break;
59070af00a1SHartmut Brandt 
59170af00a1SHartmut Brandt 	  case SNMP_OP_GET:
59270af00a1SHartmut Brandt 		if ((p = (struct lsock_port *)trans_find_port(my_trans,
59370af00a1SHartmut Brandt 		    &value->var, sub)) == NULL)
59470af00a1SHartmut Brandt 			return (SNMP_ERR_NOSUCHNAME);
59570af00a1SHartmut Brandt 		break;
59670af00a1SHartmut Brandt 
59770af00a1SHartmut Brandt 	  case SNMP_OP_SET:
59870af00a1SHartmut Brandt 		p = (struct lsock_port *)trans_find_port(my_trans,
59970af00a1SHartmut Brandt 		    &value->var, sub);
60070af00a1SHartmut Brandt 
60170af00a1SHartmut Brandt 		if (index_decode(&value->var, sub, iidx, &name, &namelen))
60270af00a1SHartmut Brandt 			return (SNMP_ERR_NO_CREATION);
60370af00a1SHartmut Brandt 
60470af00a1SHartmut Brandt 		asn_slice_oid(&didx, &value->var, sub, value->var.len);
60570af00a1SHartmut Brandt 		if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
60670af00a1SHartmut Brandt 		    &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
60770af00a1SHartmut Brandt 		    lsock_func)) == NULL) {
60870af00a1SHartmut Brandt 			free(name);
60970af00a1SHartmut Brandt 			return (SNMP_ERR_GENERR);
61070af00a1SHartmut Brandt 		}
61170af00a1SHartmut Brandt 
61270af00a1SHartmut Brandt 		if (ld->path == NULL) {
61370af00a1SHartmut Brandt 			ld->path = name;
61470af00a1SHartmut Brandt 			ld->pathlen = namelen;
61570af00a1SHartmut Brandt 		} else {
61670af00a1SHartmut Brandt 			free(name);
61770af00a1SHartmut Brandt 		}
61870af00a1SHartmut Brandt 		ld->port = p;
61970af00a1SHartmut Brandt 
62070af00a1SHartmut Brandt 		switch (which) {
62170af00a1SHartmut Brandt 
62270af00a1SHartmut Brandt 		  case LEAF_begemotSnmpdLocalPortStatus:
62370af00a1SHartmut Brandt 			if (ld->set & LD_STATUS)
62470af00a1SHartmut Brandt 				return (SNMP_ERR_INCONS_VALUE);
62570af00a1SHartmut Brandt 			if (!TRUTH_OK(value->v.integer))
62670af00a1SHartmut Brandt 				return (SNMP_ERR_WRONG_VALUE);
62770af00a1SHartmut Brandt 
62870af00a1SHartmut Brandt 			ld->status = TRUTH_GET(value->v.integer);
62970af00a1SHartmut Brandt 			ld->set |= LD_STATUS;
63070af00a1SHartmut Brandt 			break;
63170af00a1SHartmut Brandt 
63270af00a1SHartmut Brandt 		  case LEAF_begemotSnmpdLocalPortType:
63370af00a1SHartmut Brandt 			if (ld->set & LD_TYPE)
63470af00a1SHartmut Brandt 				return (SNMP_ERR_INCONS_VALUE);
63570af00a1SHartmut Brandt 			if (value->v.integer < 1 || value->v.integer > 4)
63670af00a1SHartmut Brandt 				return (SNMP_ERR_WRONG_VALUE);
63770af00a1SHartmut Brandt 
63870af00a1SHartmut Brandt 			ld->type = value->v.integer;
63970af00a1SHartmut Brandt 			ld->set |= LD_TYPE;
64070af00a1SHartmut Brandt 			break;
64170af00a1SHartmut Brandt 		}
64270af00a1SHartmut Brandt 		return (SNMP_ERR_NOERROR);
64370af00a1SHartmut Brandt 
64470af00a1SHartmut Brandt 	  case SNMP_OP_ROLLBACK:
64570af00a1SHartmut Brandt 	  case SNMP_OP_COMMIT:
64670af00a1SHartmut Brandt 		return (SNMP_ERR_NOERROR);
64770af00a1SHartmut Brandt 
64870af00a1SHartmut Brandt 	  default:
64970af00a1SHartmut Brandt 		abort();
65070af00a1SHartmut Brandt 	}
65170af00a1SHartmut Brandt 
65270af00a1SHartmut Brandt 	/*
65370af00a1SHartmut Brandt 	 * Come here to fetch the value
65470af00a1SHartmut Brandt 	 */
65570af00a1SHartmut Brandt 	switch (which) {
65670af00a1SHartmut Brandt 
65770af00a1SHartmut Brandt 	  case LEAF_begemotSnmpdLocalPortStatus:
65870af00a1SHartmut Brandt 		value->v.integer = 1;
65970af00a1SHartmut Brandt 		break;
66070af00a1SHartmut Brandt 
66170af00a1SHartmut Brandt 	  case LEAF_begemotSnmpdLocalPortType:
66270af00a1SHartmut Brandt 		value->v.integer = p->type;
66370af00a1SHartmut Brandt 		break;
66470af00a1SHartmut Brandt 
66570af00a1SHartmut Brandt 	  default:
66670af00a1SHartmut Brandt 		abort();
66770af00a1SHartmut Brandt 	}
66870af00a1SHartmut Brandt 
66970af00a1SHartmut Brandt 	return (SNMP_ERR_NOERROR);
67070af00a1SHartmut Brandt }
671