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