3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
6 * Author: Harti Brandt <harti@freebsd.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $Begemot: bsnmp/snmpd/trans_lsock.c,v 1.6 2005/02/25 11:50:25 brandt_h Exp $
31 * Local domain socket transport
33 #include <sys/types.h>
34 #include <sys/queue.h>
36 #include <sys/ucred.h>
49 #include "trans_lsock.h"
53 static const struct asn_oid
54 oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
56 static int lsock_start(void);
57 static int lsock_stop(int);
58 static void lsock_close_port(struct tport *);
59 static int lsock_init_port(struct tport *);
60 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
61 const struct sockaddr *, size_t);
62 static ssize_t lsock_recv(struct port_input *);
65 const struct transport_def lsock_trans = {
67 OIDX_begemotSnmpdTransLsock,
75 static struct transport *my_trans;
78 lsock_remove(struct tport *tp, intptr_t arg __unused)
80 struct lsock_port *port = (struct lsock_port *)tp;
82 (void)remove(port->name);
91 if (my_trans != NULL) {
92 if (!force && trans_first_port(my_trans) != NULL)
93 return (SNMP_ERR_GENERR);
94 trans_iter_port(my_trans, lsock_remove, 0);
95 return (trans_unregister(my_trans));
97 return (SNMP_ERR_NOERROR);
103 return (trans_register(&lsock_trans, &my_trans));
107 * Open a local port. If this is a datagram socket create also the
111 lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
114 struct lsock_port *port;
115 struct lsock_peer *peer = NULL;
116 int is_stream, need_cred;
119 struct sockaddr_un sa;
121 if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
122 return (SNMP_ERR_BADVALUE);
125 case LOCP_DGRAM_UNPRIV:
130 case LOCP_DGRAM_PRIV:
135 case LOCP_STREAM_UNPRIV:
140 case LOCP_STREAM_PRIV:
146 return (SNMP_ERR_BADVALUE);
149 if ((port = malloc(sizeof(*port))) == NULL)
150 return (SNMP_ERR_GENERR);
152 memset(port, 0, sizeof(*port));
154 if ((peer = malloc(sizeof(*peer))) == NULL) {
156 return (SNMP_ERR_GENERR);
158 memset(peer, 0, sizeof(*peer));
160 if ((port->name = malloc(namelen + 1)) == NULL) {
164 return (SNMP_ERR_GENERR);
166 strncpy(port->name, name, namelen);
167 port->name[namelen] = '\0';
171 LIST_INIT(&port->peers);
173 port->tport.index.len = namelen + 1;
174 port->tport.index.subs[0] = namelen;
175 for (u = 0; u < namelen; u++)
176 port->tport.index.subs[u + 1] = name[u];
179 LIST_INSERT_HEAD(&port->peers, peer, link);
184 peer->input.id = NULL;
185 peer->input.stream = is_stream;
186 peer->input.cred = need_cred;
187 peer->input.peer = (struct sockaddr *)&peer->peer;
190 trans_insert_port(my_trans, &port->tport);
192 if (community != COMM_INITIALIZE &&
193 (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
194 lsock_close_port(&port->tport);
200 return (SNMP_ERR_NOERROR);
204 * Close a local domain peer
207 lsock_peer_close(struct lsock_peer *peer)
210 LIST_REMOVE(peer, link);
211 snmpd_input_close(&peer->input);
219 lsock_close_port(struct tport *tp)
221 struct lsock_port *port = (struct lsock_port *)tp;
222 struct lsock_peer *peer;
224 if (port->str_id != NULL)
225 fd_deselect(port->str_id);
226 if (port->str_sock >= 0)
227 (void)close(port->str_sock);
228 (void)remove(port->name);
230 trans_remove_port(tp);
232 while ((peer = LIST_FIRST(&port->peers)) != NULL)
233 lsock_peer_close(peer);
240 * Input on a local socket (either datagram or stream)
243 lsock_input(int fd __unused, void *udata)
245 struct lsock_peer *peer = udata;
246 struct lsock_port *p = peer->port;
248 peer->input.peerlen = sizeof(peer->peer);
249 if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
250 /* framing or other input error */
251 lsock_peer_close(peer);
255 * A UNIX domain listening socket is ready. This means we have a peer
256 * that we need to accept
259 lsock_listen_input(int fd, void *udata)
261 struct lsock_port *p = udata;
262 struct lsock_peer *peer;
264 if ((peer = malloc(sizeof(*peer))) == NULL) {
265 syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
266 (void)close(accept(fd, NULL, NULL));
269 memset(peer, 0, sizeof(*peer));
273 peer->input.stream = 1;
274 peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
275 p->type == LOCP_STREAM_PRIV);
276 peer->input.peerlen = sizeof(peer->peer);
277 peer->input.peer = (struct sockaddr *)&peer->peer;
279 peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
280 if (peer->input.fd == -1) {
281 syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
286 if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
287 peer, NULL)) == NULL) {
288 close(peer->input.fd);
293 LIST_INSERT_HEAD(&p->peers, peer, link);
297 * Create a local socket
300 lsock_init_port(struct tport *tp)
302 struct lsock_port *p = (struct lsock_port *)tp;
303 struct sockaddr_un sa;
305 if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
306 if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
307 syslog(LOG_ERR, "creating local socket: %m");
308 return (SNMP_ERR_RES_UNAVAIL);
311 strcpy(sa.sun_path, p->name);
312 sa.sun_family = AF_LOCAL;
313 sa.sun_len = strlen(p->name) +
314 offsetof(struct sockaddr_un, sun_path);
316 (void)remove(p->name);
318 if (bind(p->str_sock, (struct sockaddr *)&sa, sizeof(sa))) {
319 if (errno == EADDRNOTAVAIL) {
322 return (SNMP_ERR_INCONS_NAME);
324 syslog(LOG_ERR, "bind: %s %m", p->name);
327 return (SNMP_ERR_GENERR);
329 if (chmod(p->name, 0666) == -1)
330 syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
332 if (listen(p->str_sock, 10) == -1) {
333 syslog(LOG_ERR, "listen: %s %m", p->name);
334 (void)remove(p->name);
337 return (SNMP_ERR_GENERR);
340 p->str_id = fd_select(p->str_sock, lsock_listen_input, p, NULL);
341 if (p->str_id == NULL) {
342 (void)remove(p->name);
345 return (SNMP_ERR_GENERR);
348 struct lsock_peer *peer;
351 peer = LIST_FIRST(&p->peers);
353 if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
354 syslog(LOG_ERR, "creating local socket: %m");
355 return (SNMP_ERR_RES_UNAVAIL);
358 if (setsockopt(peer->input.fd, 0, LOCAL_CREDS, &on,
360 syslog(LOG_ERR, "setsockopt(LOCAL_CREDS): %m");
361 close(peer->input.fd);
363 return (SNMP_ERR_GENERR);
366 strcpy(sa.sun_path, p->name);
367 sa.sun_family = AF_LOCAL;
368 sa.sun_len = strlen(p->name) +
369 offsetof(struct sockaddr_un, sun_path);
371 (void)remove(p->name);
373 if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
374 if (errno == EADDRNOTAVAIL) {
375 close(peer->input.fd);
377 return (SNMP_ERR_INCONS_NAME);
379 syslog(LOG_ERR, "bind: %s %m", p->name);
380 close(peer->input.fd);
382 return (SNMP_ERR_GENERR);
384 if (chmod(p->name, 0666) == -1)
385 syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
387 peer->input.id = fd_select(peer->input.fd, lsock_input,
389 if (peer->input.id == NULL) {
390 (void)remove(p->name);
391 close(peer->input.fd);
393 return (SNMP_ERR_GENERR);
396 return (SNMP_ERR_NOERROR);
403 lsock_send(struct tport *tp, const u_char *buf, size_t len,
404 const struct sockaddr *addr, size_t addrlen)
406 struct lsock_port *p = (struct lsock_port *)tp;
407 struct lsock_peer *peer;
409 if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
410 peer = LIST_FIRST(&p->peers);
413 /* search for the peer */
414 LIST_FOREACH(peer, &p->peers, link)
415 if (peer->input.peerlen == addrlen &&
416 memcmp(peer->input.peer, addr, addrlen) == 0)
424 return (sendto(peer->input.fd, buf, len, 0, addr, addrlen));
428 check_priv_stream(struct port_input *pi)
433 /* obtain the accept time credentials */
434 ucredlen = sizeof(ucred);
436 if (getsockopt(pi->fd, 0, LOCAL_PEERCRED, &ucred, &ucredlen) == 0 &&
437 ucredlen >= sizeof(ucred) && ucred.cr_version == XUCRED_VERSION)
438 pi->priv = (ucred.cr_uid == 0);
447 lsock_recv(struct port_input *pi)
453 msg.msg_control = NULL;
454 msg.msg_controllen = 0;
456 if (pi->buf == NULL) {
457 /* no buffer yet - allocate one */
458 if ((pi->buf = buf_alloc(0)) == NULL) {
459 /* ups - could not get buffer. Return an error
460 * the caller must close the transport. */
463 pi->buflen = buf_size(0);
468 /* try to get a message */
469 msg.msg_name = pi->peer;
470 msg.msg_namelen = pi->peerlen;
473 msg.msg_control = NULL;
474 msg.msg_controllen = 0;
477 iov[0].iov_base = pi->buf + pi->length;
478 iov[0].iov_len = pi->buflen - pi->length;
480 len = recvmsg(pi->fd, &msg, 0);
482 if (len == -1 || len == 0)
489 check_priv_stream(pi);
495 * Dependency to create a lsock port
498 struct snmp_dependency dep;
500 /* index (path name) */
505 struct lsock_port *port;
507 /* which of the fields are set */
510 /* type of the port */
517 #define LD_STATUS 0x02
518 #define LD_CREATE 0x04 /* rollback create */
519 #define LD_DELETE 0x08 /* rollback delete */
522 * dependency handler for lsock ports
525 lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
528 struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
529 int err = SNMP_ERR_NOERROR;
533 case SNMP_DEPOP_COMMIT:
534 if (!(ld->set & LD_STATUS))
535 err = SNMP_ERR_BADVALUE;
536 else if (ld->port == NULL) {
538 err = SNMP_ERR_BADVALUE;
542 err = lsock_open_port(ld->path, ld->pathlen,
543 &ld->port, ld->type);
544 if (err == SNMP_ERR_NOERROR)
545 ld->set |= LD_CREATE;
547 } else if (!ld->status) {
548 /* delete - hard to roll back so defer to finalizer */
549 ld->set |= LD_DELETE;
551 /* modify - read-only */
552 err = SNMP_ERR_READONLY;
556 case SNMP_DEPOP_ROLLBACK:
557 if (ld->set & LD_CREATE) {
559 lsock_close_port(&ld->port->tport);
561 return (SNMP_ERR_NOERROR);
563 case SNMP_DEPOP_FINISH:
564 if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
565 lsock_close_port(&ld->port->tport);
567 return (SNMP_ERR_NOERROR);
576 op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
577 u_int sub, u_int iidx, enum snmp_op op)
579 asn_subid_t which = value->var.subs[sub-1];
580 struct lsock_port *p;
583 struct lsock_dep *ld;
588 case SNMP_OP_GETNEXT:
589 if ((p = (struct lsock_port *)trans_next_port(my_trans,
590 &value->var, sub)) == NULL)
591 return (SNMP_ERR_NOSUCHNAME);
592 index_append(&value->var, sub, &p->tport.index);
596 if ((p = (struct lsock_port *)trans_find_port(my_trans,
597 &value->var, sub)) == NULL)
598 return (SNMP_ERR_NOSUCHNAME);
602 p = (struct lsock_port *)trans_find_port(my_trans,
605 if (index_decode(&value->var, sub, iidx, &name, &namelen))
606 return (SNMP_ERR_NO_CREATION);
608 asn_slice_oid(&didx, &value->var, sub, value->var.len);
609 if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
610 &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
611 lsock_func)) == NULL) {
613 return (SNMP_ERR_GENERR);
616 if (ld->path == NULL) {
618 ld->pathlen = namelen;
626 case LEAF_begemotSnmpdLocalPortStatus:
627 if (ld->set & LD_STATUS)
628 return (SNMP_ERR_INCONS_VALUE);
629 if (!TRUTH_OK(value->v.integer))
630 return (SNMP_ERR_WRONG_VALUE);
632 ld->status = TRUTH_GET(value->v.integer);
633 ld->set |= LD_STATUS;
636 case LEAF_begemotSnmpdLocalPortType:
637 if (ld->set & LD_TYPE)
638 return (SNMP_ERR_INCONS_VALUE);
639 if (value->v.integer < 1 || value->v.integer > 4)
640 return (SNMP_ERR_WRONG_VALUE);
642 ld->type = value->v.integer;
646 return (SNMP_ERR_NOERROR);
648 case SNMP_OP_ROLLBACK:
650 return (SNMP_ERR_NOERROR);
657 * Come here to fetch the value
661 case LEAF_begemotSnmpdLocalPortStatus:
662 value->v.integer = 1;
665 case LEAF_begemotSnmpdLocalPortType:
666 value->v.integer = p->type;
673 return (SNMP_ERR_NOERROR);