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