]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - contrib/bsnmp/snmpd/trans_lsock.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.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/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
51 static const struct asn_oid
52         oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
53
54 static int lsock_start(void);
55 static int lsock_stop(int);
56 static void lsock_close_port(struct tport *);
57 static int lsock_init_port(struct tport *);
58 static ssize_t lsock_send(struct tport *, const u_char *, size_t,
59     const struct sockaddr *, size_t);
60
61 /* exported */
62 const 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 };
71 static struct transport *my_trans;
72
73 static int
74 lsock_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
83 static int
84 lsock_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
96 static int
97 lsock_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  */
106 static int
107 lsock_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  */
202 static void
203 lsock_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  */
214 static void
215 lsock_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  */
238 static void
239 lsock_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  */
254 static void
255 lsock_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  */
295 static int
296 lsock_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  */
389 static ssize_t
390 lsock_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  */
417 struct 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  */
444 static int
445 lsock_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  */
495 int
496 op_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 }