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