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