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