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