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 } 605