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