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