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>
47 #include "trans_lsock.h"
51 static const struct asn_oid
52 oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
54 static int lsock_start(void);
55 static int lsock_stop(int);
56 static void lsock_close_port(struct tport *);
57 static int lsock_init_port(struct tport *);
58 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
59 const struct sockaddr *, size_t);
62 const struct transport_def lsock_trans = {
64 OIDX_begemotSnmpdTransLsock,
71 static struct transport *my_trans;
74 lsock_remove(struct tport *tp, intptr_t arg __unused)
76 struct lsock_port *port = (struct lsock_port *)tp;
78 (void)remove(port->name);
87 if (my_trans != NULL) {
88 if (!force && trans_first_port(my_trans) != NULL)
89 return (SNMP_ERR_GENERR);
90 trans_iter_port(my_trans, lsock_remove, 0);
91 return (trans_unregister(my_trans));
93 return (SNMP_ERR_NOERROR);
99 return (trans_register(&lsock_trans, &my_trans));
103 * Open a local port. If this is a datagram socket create also the
107 lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
110 struct lsock_port *port;
111 struct lsock_peer *peer = NULL;
112 int is_stream, need_cred;
115 struct sockaddr_un sa;
117 if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
118 return (SNMP_ERR_BADVALUE);
121 case LOCP_DGRAM_UNPRIV:
126 case LOCP_DGRAM_PRIV:
131 case LOCP_STREAM_UNPRIV:
136 case LOCP_STREAM_PRIV:
142 return (SNMP_ERR_BADVALUE);
145 if ((port = malloc(sizeof(*port))) == NULL)
146 return (SNMP_ERR_GENERR);
148 memset(port, 0, sizeof(*port));
150 if ((peer = malloc(sizeof(*peer))) == NULL) {
152 return (SNMP_ERR_GENERR);
154 memset(peer, 0, sizeof(*peer));
156 if ((port->name = malloc(namelen + 1)) == NULL) {
160 return (SNMP_ERR_GENERR);
162 strncpy(port->name, name, namelen);
163 port->name[namelen] = '\0';
167 LIST_INIT(&port->peers);
169 port->tport.index.len = namelen + 1;
170 port->tport.index.subs[0] = namelen;
171 for (u = 0; u < namelen; u++)
172 port->tport.index.subs[u + 1] = name[u];
175 LIST_INSERT_HEAD(&port->peers, peer, link);
180 peer->input.id = NULL;
181 peer->input.stream = is_stream;
182 peer->input.cred = need_cred;
183 peer->input.peer = (struct sockaddr *)&peer->peer;
186 trans_insert_port(my_trans, &port->tport);
188 if (community != COMM_INITIALIZE &&
189 (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
190 lsock_close_port(&port->tport);
196 return (SNMP_ERR_NOERROR);
200 * Close a local domain peer
203 lsock_peer_close(struct lsock_peer *peer)
206 LIST_REMOVE(peer, link);
207 snmpd_input_close(&peer->input);
215 lsock_close_port(struct tport *tp)
217 struct lsock_port *port = (struct lsock_port *)tp;
218 struct lsock_peer *peer;
220 if (port->str_id != NULL)
221 fd_deselect(port->str_id);
222 if (port->str_sock >= 0)
223 (void)close(port->str_sock);
224 (void)remove(port->name);
226 trans_remove_port(tp);
228 while ((peer = LIST_FIRST(&port->peers)) != NULL)
229 lsock_peer_close(peer);
236 * Input on a local socket (either datagram or stream)
239 lsock_input(int fd __unused, void *udata)
241 struct lsock_peer *peer = udata;
242 struct lsock_port *p = peer->port;
244 peer->input.peerlen = sizeof(peer->peer);
245 if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
246 /* framing or other input error */
247 lsock_peer_close(peer);
251 * A UNIX domain listening socket is ready. This means we have a peer
252 * that we need to accept
255 lsock_listen_input(int fd, void *udata)
257 struct lsock_port *p = udata;
258 struct lsock_peer *peer;
260 if ((peer = malloc(sizeof(*peer))) == NULL) {
261 syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
262 (void)close(accept(fd, NULL, NULL));
265 memset(peer, 0, sizeof(*peer));
269 peer->input.stream = 1;
270 peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
271 p->type == LOCP_STREAM_PRIV);
272 peer->input.peerlen = sizeof(peer->peer);
273 peer->input.peer = (struct sockaddr *)&peer->peer;
275 peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
276 if (peer->input.fd == -1) {
277 syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
282 if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
283 peer, NULL)) == NULL) {
284 close(peer->input.fd);
289 LIST_INSERT_HEAD(&p->peers, peer, link);
293 * Create a local socket
296 lsock_init_port(struct tport *tp)
298 struct lsock_port *p = (struct lsock_port *)tp;
299 struct sockaddr_un sa;
301 if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
302 if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
303 syslog(LOG_ERR, "creating local socket: %m");
304 return (SNMP_ERR_RES_UNAVAIL);
307 strcpy(sa.sun_path, p->name);
308 sa.sun_family = AF_LOCAL;
309 sa.sun_len = strlen(p->name) +
310 offsetof(struct sockaddr_un, sun_path);
312 (void)remove(p->name);
314 if (bind(p->str_sock, (struct sockaddr *)&sa, sizeof(sa))) {
315 if (errno == EADDRNOTAVAIL) {
318 return (SNMP_ERR_INCONS_NAME);
320 syslog(LOG_ERR, "bind: %s %m", p->name);
323 return (SNMP_ERR_GENERR);
325 if (chmod(p->name, 0666) == -1)
326 syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
328 if (listen(p->str_sock, 10) == -1) {
329 syslog(LOG_ERR, "listen: %s %m", p->name);
330 (void)remove(p->name);
333 return (SNMP_ERR_GENERR);
336 p->str_id = fd_select(p->str_sock, lsock_listen_input, p, NULL);
337 if (p->str_id == NULL) {
338 (void)remove(p->name);
341 return (SNMP_ERR_GENERR);
344 struct lsock_peer *peer;
346 peer = LIST_FIRST(&p->peers);
348 if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
349 syslog(LOG_ERR, "creating local socket: %m");
350 return (SNMP_ERR_RES_UNAVAIL);
353 strcpy(sa.sun_path, p->name);
354 sa.sun_family = AF_LOCAL;
355 sa.sun_len = strlen(p->name) +
356 offsetof(struct sockaddr_un, sun_path);
358 (void)remove(p->name);
360 if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
361 if (errno == EADDRNOTAVAIL) {
362 close(peer->input.fd);
364 return (SNMP_ERR_INCONS_NAME);
366 syslog(LOG_ERR, "bind: %s %m", p->name);
367 close(peer->input.fd);
369 return (SNMP_ERR_GENERR);
371 if (chmod(p->name, 0666) == -1)
372 syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
374 peer->input.id = fd_select(peer->input.fd, lsock_input,
376 if (peer->input.id == NULL) {
377 (void)remove(p->name);
378 close(peer->input.fd);
380 return (SNMP_ERR_GENERR);
383 return (SNMP_ERR_NOERROR);
390 lsock_send(struct tport *tp, const u_char *buf, size_t len,
391 const struct sockaddr *addr, size_t addrlen)
393 struct lsock_port *p = (struct lsock_port *)tp;
394 struct lsock_peer *peer;
396 if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
397 peer = LIST_FIRST(&p->peers);
400 /* search for the peer */
401 LIST_FOREACH(peer, &p->peers, link)
402 if (peer->input.peerlen == addrlen &&
403 memcmp(peer->input.peer, addr, addrlen) == 0)
411 return (sendto(peer->input.fd, buf, len, 0, addr, addrlen));
415 * Dependency to create a lsock port
418 struct snmp_dependency dep;
420 /* index (path name) */
425 struct lsock_port *port;
427 /* which of the fields are set */
430 /* type of the port */
437 #define LD_STATUS 0x02
438 #define LD_CREATE 0x04 /* rollback create */
439 #define LD_DELETE 0x08 /* rollback delete */
442 * dependency handler for lsock ports
445 lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
448 struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
449 int err = SNMP_ERR_NOERROR;
453 case SNMP_DEPOP_COMMIT:
454 if (!(ld->set & LD_STATUS))
455 err = SNMP_ERR_BADVALUE;
456 else if (ld->port == NULL) {
458 err = SNMP_ERR_BADVALUE;
462 err = lsock_open_port(ld->path, ld->pathlen,
463 &ld->port, ld->type);
464 if (err == SNMP_ERR_NOERROR)
465 ld->set |= LD_CREATE;
467 } else if (!ld->status) {
468 /* delete - hard to roll back so defer to finalizer */
469 ld->set |= LD_DELETE;
471 /* modify - read-only */
472 err = SNMP_ERR_READONLY;
476 case SNMP_DEPOP_ROLLBACK:
477 if (ld->set & LD_CREATE) {
479 lsock_close_port(&ld->port->tport);
481 return (SNMP_ERR_NOERROR);
483 case SNMP_DEPOP_FINISH:
484 if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
485 lsock_close_port(&ld->port->tport);
487 return (SNMP_ERR_NOERROR);
496 op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
497 u_int sub, u_int iidx, enum snmp_op op)
499 asn_subid_t which = value->var.subs[sub-1];
500 struct lsock_port *p;
503 struct lsock_dep *ld;
508 case SNMP_OP_GETNEXT:
509 if ((p = (struct lsock_port *)trans_next_port(my_trans,
510 &value->var, sub)) == NULL)
511 return (SNMP_ERR_NOSUCHNAME);
512 index_append(&value->var, sub, &p->tport.index);
516 if ((p = (struct lsock_port *)trans_find_port(my_trans,
517 &value->var, sub)) == NULL)
518 return (SNMP_ERR_NOSUCHNAME);
522 p = (struct lsock_port *)trans_find_port(my_trans,
525 if (index_decode(&value->var, sub, iidx, &name, &namelen))
526 return (SNMP_ERR_NO_CREATION);
528 asn_slice_oid(&didx, &value->var, sub, value->var.len);
529 if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
530 &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
531 lsock_func)) == NULL) {
533 return (SNMP_ERR_GENERR);
536 if (ld->path == NULL) {
538 ld->pathlen = namelen;
546 case LEAF_begemotSnmpdLocalPortStatus:
547 if (ld->set & LD_STATUS)
548 return (SNMP_ERR_INCONS_VALUE);
549 if (!TRUTH_OK(value->v.integer))
550 return (SNMP_ERR_WRONG_VALUE);
552 ld->status = TRUTH_GET(value->v.integer);
553 ld->set |= LD_STATUS;
556 case LEAF_begemotSnmpdLocalPortType:
557 if (ld->set & LD_TYPE)
558 return (SNMP_ERR_INCONS_VALUE);
559 if (value->v.integer < 1 || value->v.integer > 4)
560 return (SNMP_ERR_WRONG_VALUE);
562 ld->type = value->v.integer;
566 return (SNMP_ERR_NOERROR);
568 case SNMP_OP_ROLLBACK:
570 return (SNMP_ERR_NOERROR);
577 * Come here to fetch the value
581 case LEAF_begemotSnmpdLocalPortStatus:
582 value->v.integer = 1;
585 case LEAF_begemotSnmpdLocalPortType:
586 value->v.integer = p->type;
593 return (SNMP_ERR_NOERROR);