Home | History | Annotate | Line # | Download | only in omapip
connection.c revision 1.3.2.1
      1 /*	$NetBSD: connection.c,v 1.3.2.1 2024/02/29 11:39:56 martin 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 (at) isc.org>
     27  *   https://www.isc.org/
     28  *
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __RCSID("$NetBSD: connection.c,v 1.3.2.1 2024/02/29 11:39:56 martin 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)
     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 static isc_result_t ctring_from_attribute(omapi_object_t *obj, char *attr_name,
     53                                           char **cstr);
     54 
     55 OMAPI_OBJECT_ALLOC (omapi_connection,
     56 		    omapi_connection_object_t, omapi_type_connection)
     57 
     58 isc_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 
    109 isc_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)
    257 omapi_array_t *omapi_connections;
    258 
    259 OMAPI_ARRAY_TYPE(omapi_connection, omapi_connection_object_t)
    260 
    261 void 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 
    270 void 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 
    329 static 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 
    429 static void trace_connect_stop (trace_type_t *ttype) { }
    430 
    431 static 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 
    456 static 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 
    463 isc_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 
    568 isc_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.  */
    585 int 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  */
    602 int 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 
    611 isc_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 
    633 static 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. */
    749 isc_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 
    773 static 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 
    813 isc_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, &region);
    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 
    882 isc_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 
    897 isc_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 
    966 isc_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 
   1040 isc_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 
   1061 isc_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 
   1080 isc_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 */
   1104 static 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