Deleted Added
full compact
trans_lsock.c (133211) trans_lsock.c (142810)
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 *
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 $
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
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>
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
50static const struct asn_oid
51 oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
52
53static int lsock_start(void);
54static int lsock_stop(int);
55static void lsock_close_port(struct tport *);
56static int lsock_init_port(struct tport *);
57static ssize_t lsock_send(struct tport *, const u_char *, size_t,
58 const struct sockaddr *, size_t);
59
60/* exported */
61const 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};
70static struct transport *my_trans;
71
72static int
73lsock_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
82static int
83lsock_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
95static int
96lsock_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 */
105static int
106lsock_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 */
201static void
202lsock_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 */
213static void
214lsock_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 */
237static void
238lsock_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 */
253static void
254lsock_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 */
294static int
295lsock_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 */
388static ssize_t
389lsock_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 */
416struct 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 */
443static int
444lsock_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 */
494int
495op_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}
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
51static const struct asn_oid
52 oid_begemotSnmpdLocalPortTable = OIDX_begemotSnmpdLocalPortTable;
53
54static int lsock_start(void);
55static int lsock_stop(int);
56static void lsock_close_port(struct tport *);
57static int lsock_init_port(struct tport *);
58static ssize_t lsock_send(struct tport *, const u_char *, size_t,
59 const struct sockaddr *, size_t);
60
61/* exported */
62const 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};
71static struct transport *my_trans;
72
73static int
74lsock_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
83static int
84lsock_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
96static int
97lsock_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 */
106static int
107lsock_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 */
202static void
203lsock_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 */
214static void
215lsock_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 */
238static void
239lsock_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 */
254static void
255lsock_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 */
295static int
296lsock_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 */
389static ssize_t
390lsock_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 */
417struct 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 */
444static int
445lsock_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 */
495int
496op_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}