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