1/* $NetBSD: connection.c,v 1.4 2022/04/03 01:10:59 christos Exp $ */ 2 3/* connection.c 4 5 Subroutines for dealing with connections. */ 6 7/* 8 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC") 9 * Copyright (c) 1999-2003 by Internet Software Consortium 10 * 11 * This Source Code Form is subject to the terms of the Mozilla Public 12 * License, v. 2.0. If a copy of the MPL was not distributed with this 13 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22 * 23 * Internet Systems Consortium, Inc. 24 * PO Box 360 25 * Newmarket, NH 03857 USA 26 * <info@isc.org> 27 * https://www.isc.org/ 28 * 29 */ 30 31#include <sys/cdefs.h> 32__RCSID("$NetBSD: connection.c,v 1.4 2022/04/03 01:10:59 christos Exp $"); 33 34#include "dhcpd.h" 35#include <isc/util.h> 36#include <omapip/omapip_p.h> 37#include <arpa/inet.h> 38#include <arpa/nameser.h> 39#include <errno.h> 40 41#if defined (TRACING) 42static void trace_connect_input (trace_type_t *, unsigned, char *); 43static void trace_connect_stop (trace_type_t *); 44static void trace_disconnect_input (trace_type_t *, unsigned, char *); 45static void trace_disconnect_stop (trace_type_t *); 46trace_type_t *trace_connect; 47trace_type_t *trace_disconnect; 48extern omapi_array_t *trace_listeners; 49#endif 50static isc_result_t omapi_connection_connect_internal (omapi_object_t *); 51 52static isc_result_t ctring_from_attribute(omapi_object_t *obj, char *attr_name, 53 char **cstr); 54 55OMAPI_OBJECT_ALLOC (omapi_connection, 56 omapi_connection_object_t, omapi_type_connection) 57 58isc_result_t omapi_connect (omapi_object_t *c, 59 const char *server_name, 60 unsigned port) 61{ 62 struct hostent *he; 63 unsigned i, hix; 64 omapi_addr_list_t *addrs = (omapi_addr_list_t *)0; 65 struct in_addr foo; 66 isc_result_t status; 67 68#ifdef DEBUG_PROTOCOL 69 log_debug ("omapi_connect(%s, port=%d)", server_name, port); 70#endif 71 72 if (!inet_aton (server_name, &foo)) { 73 /* If we didn't get a numeric address, try for a domain 74 name. It's okay for this call to block. */ 75 he = gethostbyname (server_name); 76 if (!he) 77 return DHCP_R_HOSTUNKNOWN; 78 for (i = 0; he -> h_addr_list [i]; i++) 79 ; 80 if (i == 0) 81 return DHCP_R_HOSTUNKNOWN; 82 hix = i; 83 84 status = omapi_addr_list_new (&addrs, hix, MDL); 85 if (status != ISC_R_SUCCESS) 86 return status; 87 for (i = 0; i < hix; i++) { 88 addrs -> addresses [i].addrtype = he -> h_addrtype; 89 addrs -> addresses [i].addrlen = he -> h_length; 90 memcpy (addrs -> addresses [i].address, 91 he -> h_addr_list [i], 92 (unsigned)he -> h_length); 93 addrs -> addresses [i].port = port; 94 } 95 } else { 96 status = omapi_addr_list_new (&addrs, 1, MDL); 97 if (status != ISC_R_SUCCESS) 98 return status; 99 addrs -> addresses [0].addrtype = AF_INET; 100 addrs -> addresses [0].addrlen = sizeof foo; 101 memcpy (addrs -> addresses [0].address, &foo, sizeof foo); 102 addrs -> addresses [0].port = port; 103 } 104 status = omapi_connect_list (c, addrs, (omapi_addr_t *)0); 105 omapi_addr_list_dereference (&addrs, MDL); 106 return status; 107} 108 109isc_result_t omapi_connect_list (omapi_object_t *c, 110 omapi_addr_list_t *remote_addrs, 111 omapi_addr_t *local_addr) 112{ 113 isc_result_t status; 114 omapi_connection_object_t *obj; 115 int flag; 116 struct sockaddr_in local_sin; 117 118 obj = (omapi_connection_object_t *)0; 119 status = omapi_connection_allocate (&obj, MDL); 120 if (status != ISC_R_SUCCESS) 121 return status; 122 123 status = omapi_object_reference (&c -> outer, (omapi_object_t *)obj, 124 MDL); 125 if (status != ISC_R_SUCCESS) { 126 omapi_connection_dereference (&obj, MDL); 127 return status; 128 } 129 status = omapi_object_reference (&obj -> inner, c, MDL); 130 if (status != ISC_R_SUCCESS) { 131 omapi_connection_dereference (&obj, MDL); 132 return status; 133 } 134 135 /* Store the address list on the object. */ 136 omapi_addr_list_reference (&obj -> connect_list, remote_addrs, MDL); 137 obj -> cptr = 0; 138 obj -> state = omapi_connection_unconnected; 139 140#if defined (TRACING) 141 /* If we're playing back, don't actually try to connect - just leave 142 the object available for a subsequent connect or disconnect. */ 143 if (!trace_playback ()) { 144#endif 145 /* Create a socket on which to communicate. */ 146 obj -> socket = 147 socket (PF_INET, SOCK_STREAM, IPPROTO_TCP); 148 if (obj -> socket < 0) { 149 omapi_connection_dereference (&obj, MDL); 150 if (errno == EMFILE || errno == ENFILE 151 || errno == ENOBUFS) 152 return ISC_R_NORESOURCES; 153 return ISC_R_UNEXPECTED; 154 } 155 156 /* Set up the local address, if any. */ 157 if (local_addr) { 158 /* Only do TCPv4 so far. */ 159 if (local_addr -> addrtype != AF_INET) { 160 close(obj->socket); 161 omapi_connection_dereference (&obj, MDL); 162 return DHCP_R_INVALIDARG; 163 } 164 local_sin.sin_port = htons (local_addr -> port); 165 memcpy (&local_sin.sin_addr, 166 local_addr -> address, 167 local_addr -> addrlen); 168#if defined (HAVE_SA_LEN) 169 local_sin.sin_len = sizeof local_addr; 170#endif 171 local_sin.sin_family = AF_INET; 172 memset (&local_sin.sin_zero, 0, 173 sizeof local_sin.sin_zero); 174 175 if (bind (obj -> socket, (struct sockaddr *)&local_sin, 176 sizeof local_sin) < 0) { 177 omapi_connection_object_t **objp = &obj; 178 omapi_object_t **o = (omapi_object_t **)objp; 179 close(obj->socket); 180 omapi_object_dereference(o, MDL); 181 if (errno == EADDRINUSE) 182 return ISC_R_ADDRINUSE; 183 if (errno == EADDRNOTAVAIL) 184 return ISC_R_ADDRNOTAVAIL; 185 if (errno == EACCES) 186 return ISC_R_NOPERM; 187 return ISC_R_UNEXPECTED; 188 } 189 obj -> local_addr = local_sin; 190 } 191 192#if defined(F_SETFD) 193 if (fcntl (obj -> socket, F_SETFD, 1) < 0) { 194 close (obj -> socket); 195 omapi_connection_dereference (&obj, MDL); 196 return ISC_R_UNEXPECTED; 197 } 198#endif 199 200 /* Set the SO_REUSEADDR flag (this should not fail). */ 201 flag = 1; 202 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR, 203 (char *)&flag, sizeof flag) < 0) { 204 omapi_connection_dereference (&obj, MDL); 205 return ISC_R_UNEXPECTED; 206 } 207 208 /* Set the file to nonblocking mode. */ 209 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) { 210 omapi_connection_dereference (&obj, MDL); 211 return ISC_R_UNEXPECTED; 212 } 213 214#ifdef SO_NOSIGPIPE 215 /* 216 * If available stop the OS from killing our 217 * program on a SIGPIPE failure 218 */ 219 flag = 1; 220 if (setsockopt(obj->socket, SOL_SOCKET, SO_NOSIGPIPE, 221 (char *)&flag, sizeof(flag)) < 0) { 222 omapi_connection_dereference (&obj, MDL); 223 return ISC_R_UNEXPECTED; 224 } 225#endif 226 227 status = (omapi_register_io_object 228 ((omapi_object_t *)obj, 229 0, omapi_connection_writefd, 230 0, omapi_connection_connect, 231 omapi_connection_reaper)); 232 if (status != ISC_R_SUCCESS) 233 goto out; 234 status = omapi_connection_connect_internal ((omapi_object_t *) 235 obj); 236 /* 237 * inprogress is the same as success but used 238 * to indicate to the dispatch code that we should 239 * mark the socket as requiring more attention. 240 * Routines calling this function should handle 241 * success properly. 242 */ 243 if (status == ISC_R_INPROGRESS) { 244 status = ISC_R_SUCCESS; 245 } 246#if defined (TRACING) 247 } 248 omapi_connection_register (obj, MDL); 249#endif 250 251 out: 252 omapi_connection_dereference (&obj, MDL); 253 return status; 254} 255 256#if defined (TRACING) 257omapi_array_t *omapi_connections; 258 259OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t) 260 261void omapi_connection_trace_setup (void) { 262 trace_connect = trace_type_register ("connect", (void *)0, 263 trace_connect_input, 264 trace_connect_stop, MDL); 265 trace_disconnect = trace_type_register ("disconnect", (void *)0, 266 trace_disconnect_input, 267 trace_disconnect_stop, MDL); 268} 269 270void omapi_connection_register (omapi_connection_object_t *obj, 271 const char *file, int line) 272{ 273 isc_result_t status; 274 trace_iov_t iov [6]; 275 int iov_count = 0; 276 int32_t connect_index, listener_index; 277 static int32_t index; 278 279 if (!omapi_connections) { 280 status = omapi_connection_array_allocate (&omapi_connections, 281 file, line); 282 if (status != ISC_R_SUCCESS) 283 return; 284 } 285 286 status = omapi_connection_array_extend (omapi_connections, obj, 287 (int *)0, file, line); 288 if (status != ISC_R_SUCCESS) { 289 obj -> index = -1; 290 return; 291 } 292 293#if defined (TRACING) 294 if (trace_record ()) { 295 /* Connection registration packet: 296 297 int32_t index 298 int32_t listener_index [-1 means no listener] 299 u_int16_t remote_port 300 u_int16_t local_port 301 u_int32_t remote_addr 302 u_int32_t local_addr */ 303 304 connect_index = htonl (index); 305 index++; 306 if (obj -> listener) 307 listener_index = htonl (obj -> listener -> index); 308 else 309 listener_index = htonl (-1); 310 iov [iov_count].buf = (char *)&connect_index; 311 iov [iov_count++].len = sizeof connect_index; 312 iov [iov_count].buf = (char *)&listener_index; 313 iov [iov_count++].len = sizeof listener_index; 314 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_port; 315 iov [iov_count++].len = sizeof obj -> remote_addr.sin_port; 316 iov [iov_count].buf = (char *)&obj -> local_addr.sin_port; 317 iov [iov_count++].len = sizeof obj -> local_addr.sin_port; 318 iov [iov_count].buf = (char *)&obj -> remote_addr.sin_addr; 319 iov [iov_count++].len = sizeof obj -> remote_addr.sin_addr; 320 iov [iov_count].buf = (char *)&obj -> local_addr.sin_addr; 321 iov [iov_count++].len = sizeof obj -> local_addr.sin_addr; 322 323 status = trace_write_packet_iov (trace_connect, 324 iov_count, iov, file, line); 325 } 326#endif 327} 328 329static void trace_connect_input (trace_type_t *ttype, 330 unsigned length, char *buf) 331{ 332 struct sockaddr_in remote, local; 333 int32_t connect_index, listener_index; 334 char *s = buf; 335 omapi_connection_object_t *obj; 336 isc_result_t status; 337 int i; 338 339 if (length != ((sizeof connect_index) + 340 (sizeof remote.sin_port) + 341 (sizeof remote.sin_addr)) * 2) { 342 log_error ("Trace connect: invalid length %d", length); 343 return; 344 } 345 346 memset (&remote, 0, sizeof remote); 347 memset (&local, 0, sizeof local); 348 memcpy (&connect_index, s, sizeof connect_index); 349 s += sizeof connect_index; 350 memcpy (&listener_index, s, sizeof listener_index); 351 s += sizeof listener_index; 352 memcpy (&remote.sin_port, s, sizeof remote.sin_port); 353 s += sizeof remote.sin_port; 354 memcpy (&local.sin_port, s, sizeof local.sin_port); 355 s += sizeof local.sin_port; 356 memcpy (&remote.sin_addr, s, sizeof remote.sin_addr); 357 s += sizeof remote.sin_addr; 358 memcpy (&local.sin_addr, s, sizeof local.sin_addr); 359 s += sizeof local.sin_addr; 360 POST(s); 361 362 connect_index = ntohl (connect_index); 363 listener_index = ntohl (listener_index); 364 365 /* If this was a connect to a listener, then we just slap together 366 a new connection. */ 367 if (listener_index != -1) { 368 omapi_listener_object_t *listener; 369 listener = (omapi_listener_object_t *)0; 370 omapi_array_foreach_begin (trace_listeners, 371 omapi_listener_object_t, lp) { 372 if (lp -> address.sin_port == local.sin_port) { 373 omapi_listener_reference (&listener, lp, MDL); 374 omapi_listener_dereference (&lp, MDL); 375 break; 376 } 377 } omapi_array_foreach_end (trace_listeners, 378 omapi_listener_object_t, lp); 379 if (!listener) { 380 log_error ("%s%ld, addr %s, port %d", 381 "Spurious traced listener connect - index ", 382 (long int)listener_index, 383 inet_ntoa (local.sin_addr), 384 ntohs (local.sin_port)); 385 return; 386 } 387 obj = (omapi_connection_object_t *)0; 388 status = omapi_listener_connect (&obj, listener, -1, &remote); 389 if (status != ISC_R_SUCCESS) { 390 log_error ("traced listener connect: %s", 391 isc_result_totext (status)); 392 } 393 if (obj) 394 omapi_connection_dereference (&obj, MDL); 395 omapi_listener_dereference (&listener, MDL); 396 return; 397 } 398 399 /* Find the matching connect object, if there is one. */ 400 omapi_array_foreach_begin (omapi_connections, 401 omapi_connection_object_t, lp) { 402 for (i = 0; (lp->connect_list && 403 i < lp->connect_list->count); i++) { 404 if (!memcmp (&remote.sin_addr, 405 &lp->connect_list->addresses[i].address, 406 sizeof remote.sin_addr) && 407 (ntohs (remote.sin_port) == 408 lp->connect_list->addresses[i].port)) { 409 lp->state = omapi_connection_connected; 410 lp->remote_addr = remote; 411 lp->remote_addr.sin_family = AF_INET; 412 omapi_addr_list_dereference(&lp->connect_list, MDL); 413 lp->index = connect_index; 414 status = omapi_signal_in((omapi_object_t *)lp, 415 "connect"); 416 omapi_connection_dereference (&lp, MDL); 417 return; 418 } 419 } 420 } omapi_array_foreach_end (omapi_connections, 421 omapi_connection_object_t, lp); 422 423 log_error ("Spurious traced connect - index %ld, addr %s, port %d", 424 (long int)connect_index, inet_ntoa (remote.sin_addr), 425 ntohs (remote.sin_port)); 426 return; 427} 428 429static void trace_connect_stop (trace_type_t *ttype) { } 430 431static void trace_disconnect_input (trace_type_t *ttype, 432 unsigned length, char *buf) 433{ 434 int32_t *index; 435 if (length != sizeof *index) { 436 log_error ("trace disconnect: wrong length %d", length); 437 return; 438 } 439 440 index = (int32_t *)buf; 441 442 omapi_array_foreach_begin (omapi_connections, 443 omapi_connection_object_t, lp) { 444 if (lp -> index == ntohl (*index)) { 445 omapi_disconnect ((omapi_object_t *)lp, 1); 446 omapi_connection_dereference (&lp, MDL); 447 return; 448 } 449 } omapi_array_foreach_end (omapi_connections, 450 omapi_connection_object_t, lp); 451 452 log_error ("trace disconnect: no connection matching index %ld", 453 (long int)ntohl (*index)); 454} 455 456static void trace_disconnect_stop (trace_type_t *ttype) { } 457#endif 458 459/* Disconnect a connection object from the remote end. If force is nonzero, 460 close the connection immediately. Otherwise, shut down the receiving end 461 but allow any unsent data to be sent before actually closing the socket. */ 462 463isc_result_t omapi_disconnect (omapi_object_t *h, 464 int force) 465{ 466 omapi_connection_object_t *c; 467 468#ifdef DEBUG_PROTOCOL 469 log_debug ("omapi_disconnect(force=%d)", force); 470#endif 471 472 c = (omapi_connection_object_t *)h; 473 if (c -> type != omapi_type_connection) 474 return DHCP_R_INVALIDARG; 475 476#if defined (TRACING) 477 if (trace_record ()) { 478 isc_result_t status; 479 int32_t index; 480 481 index = htonl (c -> index); 482 status = trace_write_packet (trace_disconnect, 483 sizeof index, (char *)&index, 484 MDL); 485 if (status != ISC_R_SUCCESS) { 486 trace_stop (); 487 log_error ("trace_write_packet: %s", 488 isc_result_totext (status)); 489 } 490 } 491 if (!trace_playback ()) { 492#endif 493 if (!force) { 494 /* If we're already disconnecting, we don't have to do 495 anything. */ 496 if (c -> state == omapi_connection_disconnecting) 497 return ISC_R_SUCCESS; 498 499 /* Try to shut down the socket - this sends a FIN to 500 the remote end, so that it won't send us any more 501 data. If the shutdown succeeds, and we still 502 have bytes left to write, defer closing the socket 503 until that's done. */ 504 if (!shutdown (c -> socket, SHUT_RD)) { 505 if (c -> out_bytes > 0) { 506 c -> state = 507 omapi_connection_disconnecting; 508 return ISC_R_SUCCESS; 509 } 510 } 511 } 512 close (c -> socket); 513#if defined (TRACING) 514 } 515#endif 516 c -> state = omapi_connection_closed; 517 518#if 0 519 /* 520 * Disconnecting from the I/O object seems incorrect as it doesn't 521 * cause the I/O object to be cleaned and released. Previous to 522 * using the isc socket library this wouldn't have caused a problem 523 * with the socket library we would have a reference to a closed 524 * socket. Instead we now do an unregister to properly free the 525 * I/O object. 526 */ 527 528 /* Disconnect from I/O object, if any. */ 529 if (h -> outer) { 530 if (h -> outer -> inner) 531 omapi_object_dereference (&h -> outer -> inner, MDL); 532 omapi_object_dereference (&h -> outer, MDL); 533 } 534#else 535 if (h->outer) { 536 omapi_unregister_io_object(h); 537 } 538#endif 539 540 /* If whatever created us registered a signal handler, send it 541 a disconnect signal. */ 542 omapi_signal (h, "disconnect", h); 543 544 /* Disconnect from protocol object, if any. */ 545 if (h->inner != NULL) { 546 if (h->inner->outer != NULL) { 547 omapi_object_dereference(&h->inner->outer, MDL); 548 } 549 omapi_object_dereference(&h->inner, MDL); 550 } 551 552 /* XXX: the code to free buffers should be in the dereference 553 function, but there is no special-purpose function to 554 dereference connections, so these just get leaked */ 555 /* Free any buffers */ 556 if (c->inbufs != NULL) { 557 omapi_buffer_dereference(&c->inbufs, MDL); 558 } 559 c->in_bytes = 0; 560 if (c->outbufs != NULL) { 561 omapi_buffer_dereference(&c->outbufs, MDL); 562 } 563 c->out_bytes = 0; 564 565 return ISC_R_SUCCESS; 566} 567 568isc_result_t omapi_connection_require (omapi_object_t *h, unsigned bytes) 569{ 570 omapi_connection_object_t *c; 571 572 if (h -> type != omapi_type_connection) 573 return DHCP_R_INVALIDARG; 574 c = (omapi_connection_object_t *)h; 575 576 c -> bytes_needed = bytes; 577 if (c -> bytes_needed <= c -> in_bytes) { 578 return ISC_R_SUCCESS; 579 } 580 return DHCP_R_NOTYET; 581} 582 583/* Return the socket on which the dispatcher should wait for readiness 584 to read, for a connection object. */ 585int omapi_connection_readfd (omapi_object_t *h) 586{ 587 omapi_connection_object_t *c; 588 if (h -> type != omapi_type_connection) 589 return -1; 590 c = (omapi_connection_object_t *)h; 591 if (c -> state != omapi_connection_connected) 592 return -1; 593 return c -> socket; 594} 595 596/* 597 * Return the socket on which the dispatcher should wait for readiness 598 * to write, for a connection object. When bytes are buffered we should 599 * also poke the dispatcher to tell it to start or re-start watching the 600 * socket. 601 */ 602int omapi_connection_writefd (omapi_object_t *h) 603{ 604 omapi_connection_object_t *c; 605 if (h -> type != omapi_type_connection) 606 return -1; 607 c = (omapi_connection_object_t *)h; 608 return c->socket; 609} 610 611isc_result_t omapi_connection_connect (omapi_object_t *h) 612{ 613 isc_result_t status; 614 615 /* 616 * We use the INPROGRESS status to indicate that 617 * we want more from the socket. In this case we 618 * have now connected and are trying to write to 619 * the socket for the first time. For the signaling 620 * code this is the same as a SUCCESS so we don't 621 * pass it on as a signal. 622 */ 623 status = omapi_connection_connect_internal (h); 624 if (status == ISC_R_INPROGRESS) 625 return ISC_R_INPROGRESS; 626 627 if (status != ISC_R_SUCCESS) 628 omapi_signal (h, "status", status); 629 630 return ISC_R_SUCCESS; 631} 632 633static isc_result_t omapi_connection_connect_internal (omapi_object_t *h) 634{ 635 int error = 0; 636 omapi_connection_object_t *c; 637 socklen_t sl; 638 isc_result_t status; 639 640 if (h -> type != omapi_type_connection) 641 return DHCP_R_INVALIDARG; 642 c = (omapi_connection_object_t *)h; 643 644 if (c -> state == omapi_connection_connecting) { 645 sl = sizeof error; 646 if (getsockopt (c -> socket, SOL_SOCKET, SO_ERROR, 647 (char *)&error, &sl) < 0) { 648 omapi_disconnect (h, 1); 649 return ISC_R_SUCCESS; 650 } 651 if (!error) 652 c -> state = omapi_connection_connected; 653 } 654 if (c -> state == omapi_connection_connecting || 655 c -> state == omapi_connection_unconnected) { 656 if (c -> cptr >= c -> connect_list -> count) { 657 switch (error) { 658 case ECONNREFUSED: 659 status = ISC_R_CONNREFUSED; 660 break; 661 case ENETUNREACH: 662 status = ISC_R_NETUNREACH; 663 break; 664 default: 665 status = uerr2isc (error); 666 break; 667 } 668 omapi_disconnect (h, 1); 669 return status; 670 } 671 672 if (c -> connect_list -> addresses [c -> cptr].addrtype != 673 AF_INET) { 674 omapi_disconnect (h, 1); 675 return DHCP_R_INVALIDARG; 676 } 677 678 memcpy (&c -> remote_addr.sin_addr, 679 &c -> connect_list -> addresses [c -> cptr].address, 680 sizeof c -> remote_addr.sin_addr); 681 c -> remote_addr.sin_family = AF_INET; 682 c -> remote_addr.sin_port = 683 htons (c -> connect_list -> addresses [c -> cptr].port); 684#if defined (HAVE_SA_LEN) 685 c -> remote_addr.sin_len = sizeof c -> remote_addr; 686#endif 687 memset (&c -> remote_addr.sin_zero, 0, 688 sizeof c -> remote_addr.sin_zero); 689 ++c -> cptr; 690 691 error = connect (c -> socket, 692 (struct sockaddr *)&c -> remote_addr, 693 sizeof c -> remote_addr); 694 if (error < 0) { 695 error = errno; 696 if (error != EINPROGRESS) { 697 omapi_disconnect (h, 1); 698 switch (error) { 699 case ECONNREFUSED: 700 status = ISC_R_CONNREFUSED; 701 break; 702 case ENETUNREACH: 703 status = ISC_R_NETUNREACH; 704 break; 705 default: 706 status = uerr2isc (error); 707 break; 708 } 709 return status; 710 } 711 c -> state = omapi_connection_connecting; 712 return DHCP_R_INCOMPLETE; 713 } 714 c -> state = omapi_connection_connected; 715 } 716 717 /* I don't know why this would fail, so I'm tempted not to test 718 the return value. */ 719 sl = sizeof (c -> local_addr); 720 if (getsockname (c -> socket, 721 (struct sockaddr *)&c -> local_addr, &sl) < 0) { 722 } 723 724 /* Reregister with the I/O object. If we don't already have an 725 I/O object this turns into a register call, otherwise we simply 726 modify the pointers in the I/O object. */ 727 728 status = omapi_reregister_io_object (h, 729 omapi_connection_readfd, 730 omapi_connection_writefd, 731 omapi_connection_reader, 732 omapi_connection_writer, 733 omapi_connection_reaper); 734 735 if (status != ISC_R_SUCCESS) { 736 omapi_disconnect (h, 1); 737 return status; 738 } 739 740 omapi_signal_in (h, "connect"); 741 omapi_addr_list_dereference (&c -> connect_list, MDL); 742 return ISC_R_INPROGRESS; 743} 744 745/* Reaper function for connection - if the connection is completely closed, 746 reap it. If it's in the disconnecting state, there were bytes left 747 to write when the user closed it, so if there are now no bytes left to 748 write, we can close it. */ 749isc_result_t omapi_connection_reaper (omapi_object_t *h) 750{ 751 omapi_connection_object_t *c; 752 753 if (h -> type != omapi_type_connection) 754 return DHCP_R_INVALIDARG; 755 756 c = (omapi_connection_object_t *)h; 757 if (c -> state == omapi_connection_disconnecting && 758 c -> out_bytes == 0) { 759#ifdef DEBUG_PROTOCOL 760 log_debug ("omapi_connection_reaper(): disconnect"); 761#endif 762 omapi_disconnect (h, 1); 763 } 764 if (c -> state == omapi_connection_closed) { 765#ifdef DEBUG_PROTOCOL 766 log_debug ("omapi_connection_reaper(): closed"); 767#endif 768 return ISC_R_NOTCONNECTED; 769 } 770 return ISC_R_SUCCESS; 771} 772 773static isc_result_t make_dst_key (dst_key_t **dst_key, omapi_object_t *a) { 774 omapi_value_t *key = 0; 775 char *name_str = 0; 776 char *algorithm_str = 0; 777 isc_result_t status = ISC_R_SUCCESS; 778 779 /* Get the key name as a C string. */ 780 status = ctring_from_attribute(a, "name", &name_str); 781 if (status == ISC_R_SUCCESS) { 782 /* Get the algorithm name as a C string. */ 783 status = ctring_from_attribute(a, "algorithm", &algorithm_str); 784 if (status == ISC_R_SUCCESS) { 785 /* Get the key secret value */ 786 status = omapi_get_value_str(a, 0, "key", &key); 787 if (status == ISC_R_SUCCESS) { 788 /* Now let's try and create the key */ 789 status = isclib_make_dst_key( 790 name_str, 791 algorithm_str, 792 key->value->u.buffer.value, 793 key->value->u.buffer.len, 794 dst_key); 795 796 if (*dst_key == NULL) { 797 status = ISC_R_NOMEMORY; 798 } 799 } 800 } 801 } 802 803 if (name_str) 804 dfree (name_str, MDL); 805 if (algorithm_str) 806 dfree (algorithm_str, MDL); 807 if (key) 808 omapi_value_dereference (&key, MDL); 809 810 return status; 811} 812 813isc_result_t omapi_connection_sign_data (int mode, 814 dst_key_t *key, 815 void **context, 816 const unsigned char *data, 817 const unsigned len, 818 omapi_typed_data_t **result) 819{ 820 omapi_typed_data_t *td = (omapi_typed_data_t *)0; 821 isc_result_t status; 822 dst_context_t **dctx = (dst_context_t **)context; 823 824 /* Create the context for the dst module */ 825 if (mode & SIG_MODE_INIT) { 826 status = dst_context_create(key, dhcp_gbl_ctx.mctx, 827 ISC_LOGCATEGORY_GENERAL, false, 0, dctx); 828 if (status != ISC_R_SUCCESS) { 829 return status; 830 } 831 } 832 833 /* If we have any data add it to the context */ 834 if (len != 0) { 835 isc_region_t region; 836 region.base = (unsigned char *)data; 837 region.length = len; 838 dst_context_adddata(*dctx, ®ion); 839 } 840 841 /* Finish the signature and clean up the context */ 842 if (mode & SIG_MODE_FINAL) { 843 unsigned int sigsize; 844 isc_buffer_t sigbuf; 845 846 status = dst_key_sigsize(key, &sigsize); 847 if (status != ISC_R_SUCCESS) { 848 goto cleanup; 849 } 850 851 status = omapi_typed_data_new (MDL, &td, 852 omapi_datatype_data, 853 sigsize); 854 if (status != ISC_R_SUCCESS) { 855 goto cleanup; 856 } 857 858 isc_buffer_init(&sigbuf, td->u.buffer.value, td->u.buffer.len); 859 status = dst_context_sign(*dctx, &sigbuf); 860 if (status != ISC_R_SUCCESS) { 861 goto cleanup; 862 } 863 864 if (result) { 865 omapi_typed_data_reference (result, td, MDL); 866 } 867 868 cleanup: 869 /* We are done with the context and the td. On success 870 * the td is now referenced from result, on failure we 871 * don't need it any more */ 872 if (td) { 873 omapi_typed_data_dereference (&td, MDL); 874 } 875 dst_context_destroy(dctx); 876 return status; 877 } 878 879 return ISC_R_SUCCESS; 880} 881 882isc_result_t omapi_connection_output_auth_length (omapi_object_t *h, 883 unsigned *l) 884{ 885 omapi_connection_object_t *c; 886 887 if (h->type != omapi_type_connection) 888 return DHCP_R_INVALIDARG; 889 c = (omapi_connection_object_t *)h; 890 891 if (c->out_key == NULL) 892 return ISC_R_NOTFOUND; 893 894 return(dst_key_sigsize(c->out_key, l)); 895} 896 897isc_result_t omapi_connection_set_value (omapi_object_t *h, 898 omapi_object_t *id, 899 omapi_data_string_t *name, 900 omapi_typed_data_t *value) 901{ 902 omapi_connection_object_t *c; 903 isc_result_t status; 904 905 if (h -> type != omapi_type_connection) 906 return DHCP_R_INVALIDARG; 907 c = (omapi_connection_object_t *)h; 908 909 if (omapi_ds_strcmp (name, "input-authenticator") == 0) { 910 if (value && value -> type != omapi_datatype_object) 911 return DHCP_R_INVALIDARG; 912 913 if (c -> in_context) { 914 omapi_connection_sign_data (SIG_MODE_FINAL, 915 c -> in_key, 916 &c -> in_context, 917 0, 0, 918 (omapi_typed_data_t **) 0); 919 } 920 921 if (c->in_key != NULL) { 922 dst_key_free(&c->in_key); 923 } 924 925 if (value) { 926 status = make_dst_key (&c -> in_key, 927 value -> u.object); 928 if (status != ISC_R_SUCCESS) 929 return status; 930 } 931 932 return ISC_R_SUCCESS; 933 } 934 else if (omapi_ds_strcmp (name, "output-authenticator") == 0) { 935 if (value && value -> type != omapi_datatype_object) 936 return DHCP_R_INVALIDARG; 937 938 if (c -> out_context) { 939 omapi_connection_sign_data (SIG_MODE_FINAL, 940 c -> out_key, 941 &c -> out_context, 942 0, 0, 943 (omapi_typed_data_t **) 0); 944 } 945 946 if (c->out_key != NULL) { 947 dst_key_free(&c->out_key); 948 } 949 950 if (value) { 951 status = make_dst_key (&c -> out_key, 952 value -> u.object); 953 if (status != ISC_R_SUCCESS) 954 return status; 955 } 956 957 return ISC_R_SUCCESS; 958 } 959 960 if (h -> inner && h -> inner -> type -> set_value) 961 return (*(h -> inner -> type -> set_value)) 962 (h -> inner, id, name, value); 963 return ISC_R_NOTFOUND; 964} 965 966isc_result_t omapi_connection_get_value (omapi_object_t *h, 967 omapi_object_t *id, 968 omapi_data_string_t *name, 969 omapi_value_t **value) 970{ 971 omapi_connection_object_t *c; 972 omapi_typed_data_t *td = (omapi_typed_data_t *)0; 973 isc_result_t status; 974 unsigned int sigsize; 975 976 if (h -> type != omapi_type_connection) 977 return DHCP_R_INVALIDARG; 978 c = (omapi_connection_object_t *)h; 979 980 if (omapi_ds_strcmp (name, "input-signature") == 0) { 981 if (!c -> in_key || !c -> in_context) 982 return ISC_R_NOTFOUND; 983 984 status = omapi_connection_sign_data (SIG_MODE_FINAL, 985 c -> in_key, 986 &c -> in_context, 987 0, 0, &td); 988 if (status != ISC_R_SUCCESS) 989 return status; 990 991 status = omapi_make_value (value, name, td, MDL); 992 omapi_typed_data_dereference (&td, MDL); 993 return status; 994 995 } else if (omapi_ds_strcmp (name, "input-signature-size") == 0) { 996 if (c->in_key == NULL) 997 return ISC_R_NOTFOUND; 998 999 status = dst_key_sigsize(c->in_key, &sigsize); 1000 if (status != ISC_R_SUCCESS) { 1001 return(status); 1002 } 1003 1004 return omapi_make_int_value(value, name, sigsize, MDL); 1005 1006 } else if (omapi_ds_strcmp (name, "output-signature") == 0) { 1007 if (!c -> out_key || !c -> out_context) 1008 return ISC_R_NOTFOUND; 1009 1010 status = omapi_connection_sign_data (SIG_MODE_FINAL, 1011 c -> out_key, 1012 &c -> out_context, 1013 0, 0, &td); 1014 if (status != ISC_R_SUCCESS) 1015 return status; 1016 1017 status = omapi_make_value (value, name, td, MDL); 1018 omapi_typed_data_dereference (&td, MDL); 1019 return status; 1020 1021 } else if (omapi_ds_strcmp (name, "output-signature-size") == 0) { 1022 if (c->out_key == NULL) 1023 return ISC_R_NOTFOUND; 1024 1025 1026 status = dst_key_sigsize(c->out_key, &sigsize); 1027 if (status != ISC_R_SUCCESS) { 1028 return(status); 1029 } 1030 1031 return omapi_make_int_value(value, name, sigsize, MDL); 1032 } 1033 1034 if (h -> inner && h -> inner -> type -> get_value) 1035 return (*(h -> inner -> type -> get_value)) 1036 (h -> inner, id, name, value); 1037 return ISC_R_NOTFOUND; 1038} 1039 1040isc_result_t omapi_connection_destroy (omapi_object_t *h, 1041 const char *file, int line) 1042{ 1043 omapi_connection_object_t *c; 1044 1045#ifdef DEBUG_PROTOCOL 1046 log_debug ("omapi_connection_destroy()"); 1047#endif 1048 1049 if (h -> type != omapi_type_connection) 1050 return ISC_R_UNEXPECTED; 1051 c = (omapi_connection_object_t *)(h); 1052 if (c -> state == omapi_connection_connected) 1053 omapi_disconnect (h, 1); 1054 if (c -> listener) 1055 omapi_listener_dereference (&c -> listener, file, line); 1056 if (c -> connect_list) 1057 omapi_addr_list_dereference (&c -> connect_list, file, line); 1058 return ISC_R_SUCCESS; 1059} 1060 1061isc_result_t omapi_connection_signal_handler (omapi_object_t *h, 1062 const char *name, va_list ap) 1063{ 1064 if (h -> type != omapi_type_connection) 1065 return DHCP_R_INVALIDARG; 1066 1067#ifdef DEBUG_PROTOCOL 1068 log_debug ("omapi_connection_signal_handler(%s)", name); 1069#endif 1070 1071 if (h -> inner && h -> inner -> type -> signal_handler) 1072 return (*(h -> inner -> type -> signal_handler)) (h -> inner, 1073 name, ap); 1074 return ISC_R_NOTFOUND; 1075} 1076 1077/* Write all the published values associated with the object through the 1078 specified connection. */ 1079 1080isc_result_t omapi_connection_stuff_values (omapi_object_t *c, 1081 omapi_object_t *id, 1082 omapi_object_t *m) 1083{ 1084 if (m -> type != omapi_type_connection) 1085 return DHCP_R_INVALIDARG; 1086 1087 if (m -> inner && m -> inner -> type -> stuff_values) 1088 return (*(m -> inner -> type -> stuff_values)) (c, id, 1089 m -> inner); 1090 return ISC_R_SUCCESS; 1091} 1092 1093/* @brief Fetches the value of an attribute in an object as an allocated 1094 * C string 1095 * 1096 * @param obj ompapi object containing the desire attribute 1097 * @param attr_name name of the desired attribute 1098 * @param[out] cstr pointer in which to place the allocated C string's address 1099 * 1100 * Caller is responsible for freeing (via dfree) the allocated string. 1101 * 1102 * @return ISC_R_SUCCESS if successful, otherwise indicates the type of failure 1103*/ 1104static isc_result_t ctring_from_attribute(omapi_object_t *obj, char *attr_name, 1105 char **cstr) { 1106 isc_result_t status = ISC_R_SUCCESS; 1107 omapi_value_t *attr = 0; 1108 1109 /* Find the attribute in the object. */ 1110 status = omapi_get_value_str(obj, (omapi_object_t *)0, attr_name, 1111 &attr); 1112 if (status != ISC_R_SUCCESS) { 1113 return (status); 1114 } 1115 1116 /* Got it, let's make sure it's either data or string type. */ 1117 if (attr->value->type != omapi_datatype_data && 1118 attr->value->type != omapi_datatype_string) { 1119 return (DHCP_R_INVALIDARG); 1120 } 1121 1122 /* Make a C string from the attribute value. */ 1123 *cstr = dmalloc (attr->value->u.buffer.len + 1, MDL); 1124 if (!(*cstr)) { 1125 status = ISC_R_NOMEMORY; 1126 } else { 1127 memcpy (*cstr, attr->value->u.buffer.value, 1128 attr->value->u.buffer.len); 1129 (*cstr)[attr->value->u.buffer.len] = 0; 1130 } 1131 1132 /* Get rid of the attribute reference */ 1133 if (attr) { 1134 omapi_value_dereference (&attr, MDL); 1135 } 1136 1137 return (status); 1138} 1139