trans_lsock.c revision 310901
1/*
2 * Copyright (c) 2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
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.
16 *
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
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/snmpd/trans_lsock.c,v 1.6 2005/02/25 11:50:25 brandt_h Exp $
30 *
31 * Local domain socket transport
32 */
33#include <sys/types.h>
34#include <sys/queue.h>
35#include <sys/stat.h>
36#include <sys/un.h>
37
38#include <errno.h>
39#include <stddef.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <syslog.h>
44#include <unistd.h>
45
46#include "snmpmod.h"
47#include "snmpd.h"
48#include "trans_lsock.h"
49#include "tree.h"
50#include "oid.h"
51
52static const struct asn_oid
53	oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
54
55static int lsock_start(void);
56static int lsock_stop(int);
57static void lsock_close_port(struct tport *);
58static int lsock_init_port(struct tport *);
59static ssize_t lsock_send(struct tport *, const u_char *, size_t,
60    const struct sockaddr *, size_t);
61
62/* exported */
63const struct transport_def lsock_trans = {
64	"lsock",
65	OIDX_begemotSnmpdTransLsock,
66	lsock_start,
67	lsock_stop,
68	lsock_close_port,
69	lsock_init_port,
70	lsock_send
71};
72static struct transport *my_trans;
73
74static int
75lsock_remove(struct tport *tp, intptr_t arg __unused)
76{
77	struct lsock_port *port = (struct lsock_port *)tp;
78
79	(void)remove(port->name);
80
81	return (-1);
82}
83
84static int
85lsock_stop(int force)
86{
87
88	if (my_trans != NULL) {
89		if (!force && trans_first_port(my_trans) != NULL)
90			return (SNMP_ERR_GENERR);
91		trans_iter_port(my_trans, lsock_remove, 0);
92		return (trans_unregister(my_trans));
93	}
94	return (SNMP_ERR_NOERROR);
95}
96
97static int
98lsock_start(void)
99{
100	return (trans_register(&lsock_trans, &my_trans));
101}
102
103/*
104 * Open a local port. If this is a datagram socket create also the
105 * one and only peer.
106 */
107static int
108lsock_open_port(u_char *name, size_t namelen, struct lsock_port **pp,
109    int type)
110{
111	struct lsock_port *port;
112	struct lsock_peer *peer = NULL;
113	int is_stream, need_cred;
114	size_t u;
115	int err;
116	struct sockaddr_un sa;
117
118	if (namelen == 0 || namelen + 1 > sizeof(sa.sun_path))
119		return (SNMP_ERR_BADVALUE);
120
121	switch (type) {
122	  case LOCP_DGRAM_UNPRIV:
123		is_stream = 0;
124		need_cred = 0;
125		break;
126
127	  case LOCP_DGRAM_PRIV:
128		is_stream = 0;
129		need_cred = 1;
130		break;
131
132	  case LOCP_STREAM_UNPRIV:
133		is_stream = 1;
134		need_cred = 0;
135		break;
136
137	  case LOCP_STREAM_PRIV:
138		is_stream = 1;
139		need_cred = 1;
140		break;
141
142	  default:
143		return (SNMP_ERR_BADVALUE);
144	}
145
146	if ((port = malloc(sizeof(*port))) == NULL)
147		return (SNMP_ERR_GENERR);
148
149	memset(port, 0, sizeof(*port));
150	if (!is_stream) {
151		if ((peer = malloc(sizeof(*peer))) == NULL) {
152			free(port);
153			return (SNMP_ERR_GENERR);
154		}
155		memset(peer, 0, sizeof(*peer));
156	}
157	if ((port->name = malloc(namelen + 1)) == NULL) {
158		free(port);
159		if (!is_stream)
160			free(peer);
161		return (SNMP_ERR_GENERR);
162	}
163	strncpy(port->name, name, namelen);
164	port->name[namelen] = '\0';
165
166	port->type = type;
167	port->str_sock = -1;
168	LIST_INIT(&port->peers);
169
170	port->tport.index.len = namelen + 1;
171	port->tport.index.subs[0] = namelen;
172	for (u = 0; u < namelen; u++)
173		port->tport.index.subs[u + 1] = name[u];
174
175	if (peer != NULL) {
176		LIST_INSERT_HEAD(&port->peers, peer, link);
177
178		peer->port = port;
179
180		peer->input.fd = -1;
181		peer->input.id = NULL;
182		peer->input.stream = is_stream;
183		peer->input.cred = need_cred;
184		peer->input.peer = (struct sockaddr *)&peer->peer;
185	}
186
187	trans_insert_port(my_trans, &port->tport);
188
189	if (community != COMM_INITIALIZE &&
190	    (err = lsock_init_port(&port->tport)) != SNMP_ERR_NOERROR) {
191		lsock_close_port(&port->tport);
192		return (err);
193	}
194
195	*pp = port;
196
197	return (SNMP_ERR_NOERROR);
198}
199
200/*
201 * Close a local domain peer
202 */
203static void
204lsock_peer_close(struct lsock_peer *peer)
205{
206
207	LIST_REMOVE(peer, link);
208	snmpd_input_close(&peer->input);
209	free(peer);
210}
211
212/*
213 * Close a local port
214 */
215static void
216lsock_close_port(struct tport *tp)
217{
218	struct lsock_port *port = (struct lsock_port *)tp;
219	struct lsock_peer *peer;
220
221	if (port->str_id != NULL)
222		fd_deselect(port->str_id);
223	if (port->str_sock >= 0)
224		(void)close(port->str_sock);
225	(void)remove(port->name);
226
227	trans_remove_port(tp);
228
229	while ((peer = LIST_FIRST(&port->peers)) != NULL)
230		lsock_peer_close(peer);
231
232	free(port->name);
233	free(port);
234}
235
236/*
237 * Input on a local socket (either datagram or stream)
238 */
239static void
240lsock_input(int fd __unused, void *udata)
241{
242	struct lsock_peer *peer = udata;
243	struct lsock_port *p = peer->port;
244
245	peer->input.peerlen = sizeof(peer->peer);
246	if (snmpd_input(&peer->input, &p->tport) == -1 && peer->input.stream)
247		/* framing or other input error */
248		lsock_peer_close(peer);
249}
250
251/*
252 * A UNIX domain listening socket is ready. This means we have a peer
253 * that we need to accept
254 */
255static void
256lsock_listen_input(int fd, void *udata)
257{
258	struct lsock_port *p = udata;
259	struct lsock_peer *peer;
260
261	if ((peer = malloc(sizeof(*peer))) == NULL) {
262		syslog(LOG_WARNING, "%s: peer malloc failed", p->name);
263		(void)close(accept(fd, NULL, NULL));
264		return;
265	}
266	memset(peer, 0, sizeof(*peer));
267
268	peer->port = p;
269
270	peer->input.stream = 1;
271	peer->input.cred = (p->type == LOCP_DGRAM_PRIV ||
272	    p->type == LOCP_STREAM_PRIV);
273	peer->input.peerlen = sizeof(peer->peer);
274	peer->input.peer = (struct sockaddr *)&peer->peer;
275
276	peer->input.fd = accept(fd, peer->input.peer, &peer->input.peerlen);
277	if (peer->input.fd == -1) {
278		syslog(LOG_WARNING, "%s: accept failed: %m", p->name);
279		free(peer);
280		return;
281	}
282
283	if ((peer->input.id = fd_select(peer->input.fd, lsock_input,
284	    peer, NULL)) == NULL) {
285		close(peer->input.fd);
286		free(peer);
287		return;
288	}
289
290	LIST_INSERT_HEAD(&p->peers, peer, link);
291}
292
293/*
294 * Create a local socket
295 */
296static int
297lsock_init_port(struct tport *tp)
298{
299	struct lsock_port *p = (struct lsock_port *)tp;
300	struct sockaddr_un sa;
301
302	if (p->type == LOCP_STREAM_PRIV || p->type == LOCP_STREAM_UNPRIV) {
303		if ((p->str_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
304			syslog(LOG_ERR, "creating local socket: %m");
305			return (SNMP_ERR_RES_UNAVAIL);
306		}
307
308		strcpy(sa.sun_path, p->name);
309		sa.sun_family = AF_LOCAL;
310		sa.sun_len = strlen(p->name) +
311		    offsetof(struct sockaddr_un, sun_path);
312
313		(void)remove(p->name);
314
315		if (bind(p->str_sock, (struct sockaddr *)&sa, sizeof(sa))) {
316			if (errno == EADDRNOTAVAIL) {
317				close(p->str_sock);
318				p->str_sock = -1;
319				return (SNMP_ERR_INCONS_NAME);
320			}
321			syslog(LOG_ERR, "bind: %s %m", p->name);
322			close(p->str_sock);
323			p->str_sock = -1;
324			return (SNMP_ERR_GENERR);
325		}
326		if (chmod(p->name, 0666) == -1)
327			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
328
329		if (listen(p->str_sock, 10) == -1) {
330			syslog(LOG_ERR, "listen: %s %m", p->name);
331			(void)remove(p->name);
332			close(p->str_sock);
333			p->str_sock = -1;
334			return (SNMP_ERR_GENERR);
335		}
336
337		p->str_id = fd_select(p->str_sock, lsock_listen_input, p, NULL);
338		if (p->str_id == NULL) {
339			(void)remove(p->name);
340			close(p->str_sock);
341			p->str_sock = -1;
342			return (SNMP_ERR_GENERR);
343		}
344	} else {
345		struct lsock_peer *peer;
346		const int on = 1;
347
348		peer = LIST_FIRST(&p->peers);
349
350		if ((peer->input.fd = socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0) {
351			syslog(LOG_ERR, "creating local socket: %m");
352			return (SNMP_ERR_RES_UNAVAIL);
353		}
354
355		if (setsockopt(peer->input.fd, 0, LOCAL_CREDS, &on,
356		    sizeof(on)) == -1) {
357			syslog(LOG_ERR, "setsockopt(LOCAL_CREDS): %m");
358			close(peer->input.fd);
359			peer->input.fd = -1;
360			return (SNMP_ERR_GENERR);
361		}
362
363		strcpy(sa.sun_path, p->name);
364		sa.sun_family = AF_LOCAL;
365		sa.sun_len = strlen(p->name) +
366		    offsetof(struct sockaddr_un, sun_path);
367
368		(void)remove(p->name);
369
370		if (bind(peer->input.fd, (struct sockaddr *)&sa, sizeof(sa))) {
371			if (errno == EADDRNOTAVAIL) {
372				close(peer->input.fd);
373				peer->input.fd = -1;
374				return (SNMP_ERR_INCONS_NAME);
375			}
376			syslog(LOG_ERR, "bind: %s %m", p->name);
377			close(peer->input.fd);
378			peer->input.fd = -1;
379			return (SNMP_ERR_GENERR);
380		}
381		if (chmod(p->name, 0666) == -1)
382			syslog(LOG_WARNING, "chmod(%s,0666): %m", p->name);
383
384		peer->input.id = fd_select(peer->input.fd, lsock_input,
385		    peer, NULL);
386		if (peer->input.id == NULL) {
387			(void)remove(p->name);
388			close(peer->input.fd);
389			peer->input.fd = -1;
390			return (SNMP_ERR_GENERR);
391		}
392	}
393	return (SNMP_ERR_NOERROR);
394}
395
396/*
397 * Send something
398 */
399static ssize_t
400lsock_send(struct tport *tp, const u_char *buf, size_t len,
401    const struct sockaddr *addr, size_t addrlen)
402{
403	struct lsock_port *p = (struct lsock_port *)tp;
404	struct lsock_peer *peer;
405
406	if (p->type == LOCP_DGRAM_PRIV || p->type == LOCP_DGRAM_UNPRIV) {
407		peer = LIST_FIRST(&p->peers);
408
409	} else {
410		/* search for the peer */
411		LIST_FOREACH(peer, &p->peers, link)
412			if (peer->input.peerlen == addrlen &&
413			    memcmp(peer->input.peer, addr, addrlen) == 0)
414				break;
415		if (peer == NULL) {
416			errno = ENOTCONN;
417			return (-1);
418		}
419	}
420
421	return (sendto(peer->input.fd, buf, len, 0, addr, addrlen));
422}
423
424/*
425 * Dependency to create a lsock port
426 */
427struct lsock_dep {
428	struct snmp_dependency dep;
429
430	/* index (path name) */
431	u_char *path;
432	size_t pathlen;
433
434	/* the port */
435	struct lsock_port *port;
436
437	/* which of the fields are set */
438	u_int set;
439
440	/* type of the port */
441	int type;
442
443	/* status */
444	int status;
445};
446#define	LD_TYPE		0x01
447#define	LD_STATUS	0x02
448#define	LD_CREATE	0x04	/* rollback create */
449#define	LD_DELETE	0x08	/* rollback delete */
450
451/*
452 * dependency handler for lsock ports
453 */
454static int
455lsock_func(struct snmp_context *ctx, struct snmp_dependency *dep,
456    enum snmp_depop op)
457{
458	struct lsock_dep *ld = (struct lsock_dep *)(void *)dep;
459	int err = SNMP_ERR_NOERROR;
460
461	switch (op) {
462
463	  case SNMP_DEPOP_COMMIT:
464		if (!(ld->set & LD_STATUS))
465			err = SNMP_ERR_BADVALUE;
466		else if (ld->port == NULL) {
467			if (!ld->status)
468				err = SNMP_ERR_BADVALUE;
469
470			else {
471				/* create */
472				err = lsock_open_port(ld->path, ld->pathlen,
473				    &ld->port, ld->type);
474				if (err == SNMP_ERR_NOERROR)
475					ld->set |= LD_CREATE;
476			}
477		} else if (!ld->status) {
478			/* delete - hard to roll back so defer to finalizer */
479			ld->set |= LD_DELETE;
480		} else
481			/* modify - read-only */
482			err = SNMP_ERR_READONLY;
483
484		return (err);
485
486	  case SNMP_DEPOP_ROLLBACK:
487		if (ld->set & LD_CREATE) {
488			/* was create */
489			lsock_close_port(&ld->port->tport);
490		}
491		return (SNMP_ERR_NOERROR);
492
493	  case SNMP_DEPOP_FINISH:
494		if ((ld->set & LD_DELETE) && ctx->code == SNMP_RET_OK)
495			lsock_close_port(&ld->port->tport);
496		free(ld->path);
497		return (SNMP_ERR_NOERROR);
498	}
499	abort();
500}
501
502/*
503 * Local port table
504 */
505int
506op_lsock_port(struct snmp_context *ctx, struct snmp_value *value,
507    u_int sub, u_int iidx, enum snmp_op op)
508{
509	asn_subid_t which = value->var.subs[sub-1];
510	struct lsock_port *p;
511	u_char *name;
512	size_t namelen;
513	struct lsock_dep *ld;
514	struct asn_oid didx;
515
516	switch (op) {
517
518	  case SNMP_OP_GETNEXT:
519		if ((p = (struct lsock_port *)trans_next_port(my_trans,
520		    &value->var, sub)) == NULL)
521			return (SNMP_ERR_NOSUCHNAME);
522		index_append(&value->var, sub, &p->tport.index);
523		break;
524
525	  case SNMP_OP_GET:
526		if ((p = (struct lsock_port *)trans_find_port(my_trans,
527		    &value->var, sub)) == NULL)
528			return (SNMP_ERR_NOSUCHNAME);
529		break;
530
531	  case SNMP_OP_SET:
532		p = (struct lsock_port *)trans_find_port(my_trans,
533		    &value->var, sub);
534
535		if (index_decode(&value->var, sub, iidx, &name, &namelen))
536			return (SNMP_ERR_NO_CREATION);
537
538		asn_slice_oid(&didx, &value->var, sub, value->var.len);
539		if ((ld = (struct lsock_dep *)(void *)snmp_dep_lookup(ctx,
540		    &oid_begemotSnmpdLocalPortTable, &didx, sizeof(*ld),
541		    lsock_func)) == NULL) {
542			free(name);
543			return (SNMP_ERR_GENERR);
544		}
545
546		if (ld->path == NULL) {
547			ld->path = name;
548			ld->pathlen = namelen;
549		} else {
550			free(name);
551		}
552		ld->port = p;
553
554		switch (which) {
555
556		  case LEAF_begemotSnmpdLocalPortStatus:
557			if (ld->set & LD_STATUS)
558				return (SNMP_ERR_INCONS_VALUE);
559			if (!TRUTH_OK(value->v.integer))
560				return (SNMP_ERR_WRONG_VALUE);
561
562			ld->status = TRUTH_GET(value->v.integer);
563			ld->set |= LD_STATUS;
564			break;
565
566		  case LEAF_begemotSnmpdLocalPortType:
567			if (ld->set & LD_TYPE)
568				return (SNMP_ERR_INCONS_VALUE);
569			if (value->v.integer < 1 || value->v.integer > 4)
570				return (SNMP_ERR_WRONG_VALUE);
571
572			ld->type = value->v.integer;
573			ld->set |= LD_TYPE;
574			break;
575		}
576		return (SNMP_ERR_NOERROR);
577
578	  case SNMP_OP_ROLLBACK:
579	  case SNMP_OP_COMMIT:
580		return (SNMP_ERR_NOERROR);
581
582	  default:
583		abort();
584	}
585
586	/*
587	 * Come here to fetch the value
588	 */
589	switch (which) {
590
591	  case LEAF_begemotSnmpdLocalPortStatus:
592		value->v.integer = 1;
593		break;
594
595	  case LEAF_begemotSnmpdLocalPortType:
596		value->v.integer = p->type;
597		break;
598
599	  default:
600		abort();
601	}
602
603	return (SNMP_ERR_NOERROR);
604}
605