xref: /freebsd/contrib/bsnmp/snmpd/trans_lsock.c (revision 6af83ee0d2941d18880b6aaa2b4facd1d30c6106)
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 
50 static const struct asn_oid
51 	oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
52 
53 static int lsock_start(void);
54 static int lsock_stop(int);
55 static void lsock_close_port(struct tport *);
56 static int lsock_init_port(struct tport *);
57 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
58     const struct sockaddr *, size_t);
59 
60 /* exported */
61 const 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 };
70 static struct transport *my_trans;
71 
72 static int
73 lsock_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 
82 static int
83 lsock_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 
95 static int
96 lsock_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  */
105 static int
106 lsock_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  */
201 static void
202 lsock_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  */
213 static void
214 lsock_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  */
237 static void
238 lsock_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  */
253 static void
254 lsock_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  */
294 static int
295 lsock_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  */
388 static ssize_t
389 lsock_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  */
416 struct 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  */
443 static int
444 lsock_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  */
494 int
495 op_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