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