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