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