xref: /minix/external/bsd/dhcp/dist/omapip/connection.c (revision bb9622b5)
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, &region);
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