Home | History | Annotate | Line # | Download | only in omapip
      1 /*	$NetBSD: listener.c,v 1.3 2022/04/03 01:10:59 christos Exp $	*/
      2 
      3 /* listener.c
      4 
      5    Subroutines that support the generic listener object. */
      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 (at) isc.org>
     27  *   https://www.isc.org/
     28  *
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __RCSID("$NetBSD: listener.c,v 1.3 2022/04/03 01:10:59 christos Exp $");
     33 
     34 #include "dhcpd.h"
     35 
     36 #include <omapip/omapip_p.h>
     37 #include <errno.h>
     38 
     39 #if defined (TRACING)
     40 omapi_array_t *trace_listeners;
     41 static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
     42 static void trace_listener_remember (omapi_listener_object_t *,
     43 				     const char *, int);
     44 static void trace_listener_accept_stop (trace_type_t *);
     45 trace_type_t *trace_listener_accept;
     46 #endif
     47 
     48 OMAPI_OBJECT_ALLOC (omapi_listener,
     49 		    omapi_listener_object_t, omapi_type_listener)
     50 
     51 isc_result_t omapi_listen (omapi_object_t *h,
     52 			   unsigned port,
     53 			   int max)
     54 {
     55 	omapi_addr_t addr;
     56 
     57 #ifdef DEBUG_PROTOCOL
     58 	log_debug ("omapi_listen(port=%d, max=%d)", port, max);
     59 #endif
     60 
     61 	addr.addrtype = AF_INET;
     62 	addr.addrlen = sizeof (struct in_addr);
     63 	memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
     64 	addr.port = port;
     65 
     66 	return omapi_listen_addr (h, &addr, max);
     67 }
     68 
     69 isc_result_t omapi_listen_addr (omapi_object_t *h,
     70 				omapi_addr_t *addr,
     71 				int max)
     72 {
     73 	isc_result_t status;
     74 	omapi_listener_object_t *obj;
     75 	int i;
     76 
     77 	/* Currently only support IPv4 addresses. */
     78 	if (addr->addrtype != AF_INET)
     79 		return DHCP_R_INVALIDARG;
     80 
     81 	/* Get the handle. */
     82 	obj = (omapi_listener_object_t *)0;
     83 	status = omapi_listener_allocate (&obj, MDL);
     84 	if (status != ISC_R_SUCCESS)
     85 		/*
     86 		 * we could simply return here but by going to
     87 		 * error_exit we keep the code check tools happy
     88 		 * without removing the NULL check on obj at
     89 		 * the exit, which we could skip curently but
     90 		 * might want in the future.
     91 		 */
     92 		goto error_exit;
     93 	obj->socket = -1;
     94 
     95 	/* Connect this object to the inner object. */
     96 	status = omapi_object_reference (&h -> outer,
     97 					 (omapi_object_t *)obj, MDL);
     98 	if (status != ISC_R_SUCCESS)
     99 		goto error_exit;
    100 	status = omapi_object_reference (&obj -> inner, h, MDL);
    101 	if (status != ISC_R_SUCCESS)
    102 		goto error_exit;
    103 
    104 	/* Set up the address on which we will listen... */
    105 	obj -> address.sin_port = htons (addr -> port);
    106 	memcpy (&obj -> address.sin_addr,
    107 		addr -> address, sizeof obj -> address.sin_addr);
    108 #if defined (HAVE_SA_LEN)
    109 	obj -> address.sin_len =
    110 		sizeof (struct sockaddr_in);
    111 #endif
    112 	obj -> address.sin_family = AF_INET;
    113 	memset (&(obj -> address.sin_zero), 0,
    114 		sizeof obj -> address.sin_zero);
    115 
    116 #if defined (TRACING)
    117 	/* If we're playing back a trace file, we remember the object
    118 	   on the trace listener queue. */
    119 	if (trace_playback ()) {
    120 		trace_listener_remember (obj, MDL);
    121 	}  else {
    122 #endif
    123 		/* Create a socket on which to listen. */
    124 		obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
    125 		if (obj->socket == -1) {
    126 			if (errno == EMFILE
    127 			    || errno == ENFILE || errno == ENOBUFS)
    128 				status = ISC_R_NORESOURCES;
    129 			else
    130 				status = ISC_R_UNEXPECTED;
    131 			goto error_exit;
    132 		}
    133 
    134 #if defined (HAVE_SETFD)
    135 		if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
    136 			status = ISC_R_UNEXPECTED;
    137 			goto error_exit;
    138 		}
    139 #endif
    140 
    141 		/* Set the REUSEADDR option so that we don't fail to start if
    142 		   we're being restarted. */
    143 		i = 1;
    144 		if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
    145 				(char *)&i, sizeof i) < 0) {
    146 			status = ISC_R_UNEXPECTED;
    147 			goto error_exit;
    148 		}
    149 
    150 		/* Try to bind to the wildcard address using the port number
    151 		   we were given. */
    152 		i = bind (obj -> socket,
    153 			  (struct sockaddr *)&obj -> address,
    154 			  sizeof obj -> address);
    155 		if (i < 0) {
    156 			if (errno == EADDRINUSE)
    157 				status = ISC_R_ADDRNOTAVAIL;
    158 			else if (errno == EPERM)
    159 				status = ISC_R_NOPERM;
    160 			else
    161 				status = ISC_R_UNEXPECTED;
    162 			goto error_exit;
    163 		}
    164 
    165 		/* Now tell the kernel to listen for connections. */
    166 		if (listen (obj -> socket, max)) {
    167 			status = ISC_R_UNEXPECTED;
    168 			goto error_exit;
    169 		}
    170 
    171 		if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
    172 			status = ISC_R_UNEXPECTED;
    173 			goto error_exit;
    174 		}
    175 
    176 		status = omapi_register_io_object ((omapi_object_t *)obj,
    177 						   omapi_listener_readfd, 0,
    178 						   omapi_accept, 0, 0);
    179 #if defined (TRACING)
    180 	}
    181 #endif
    182 
    183 	omapi_listener_dereference (&obj, MDL);
    184 	return status;
    185 
    186 error_exit:
    187 	if (obj != NULL) {
    188 		if (h->outer == (omapi_object_t *)obj) {
    189 			omapi_object_dereference((omapi_object_t **)&h->outer,
    190 						 MDL);
    191 		}
    192 		if (obj->inner == h) {
    193 			omapi_object_dereference((omapi_object_t **)&obj->inner,
    194 						 MDL);
    195 		}
    196 		if (obj->socket != -1) {
    197 			close(obj->socket);
    198 		}
    199 		omapi_listener_dereference(&obj, MDL);
    200 	}
    201 	return status;
    202 }
    203 
    204 /* Return the socket on which the dispatcher should wait for readiness
    205    to read, for a listener object. */
    206 int omapi_listener_readfd (omapi_object_t *h)
    207 {
    208 	omapi_listener_object_t *l;
    209 
    210 	if (h -> type != omapi_type_listener)
    211 		return -1;
    212 	l = (omapi_listener_object_t *)h;
    213 
    214 	return l -> socket;
    215 }
    216 
    217 /* Reader callback for a listener object.   Accept an incoming connection. */
    218 isc_result_t omapi_accept (omapi_object_t *h)
    219 {
    220 	isc_result_t status;
    221 	socklen_t len;
    222 	omapi_connection_object_t *obj;
    223 	omapi_listener_object_t *listener;
    224 	struct sockaddr_in addr;
    225 	int socket;
    226 
    227 	if (h -> type != omapi_type_listener)
    228 		return DHCP_R_INVALIDARG;
    229 	listener = (omapi_listener_object_t *)h;
    230 
    231 	/* Accept the connection. */
    232 	len = sizeof addr;
    233 	socket = accept (listener -> socket,
    234 			 ((struct sockaddr *)&(addr)), &len);
    235 	if (socket < 0) {
    236 		if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
    237 			return ISC_R_NORESOURCES;
    238 		return ISC_R_UNEXPECTED;
    239 	}
    240 
    241 	if ((MAX_FD_VALUE != 0) && (socket > MAX_FD_VALUE)) {
    242 		close(socket);
    243 		return (ISC_R_NORESOURCES);
    244 	}
    245 
    246 #if defined (TRACING)
    247 	/* If we're recording a trace, remember the connection. */
    248 	if (trace_record ()) {
    249 		trace_iov_t iov [3];
    250 		iov [0].buf = (char *)&addr.sin_port;
    251 		iov [0].len = sizeof addr.sin_port;
    252 		iov [1].buf = (char *)&addr.sin_addr;
    253 		iov [1].len = sizeof addr.sin_addr;
    254 		iov [2].buf = (char *)&listener -> address.sin_port;
    255 		iov [2].len = sizeof listener -> address.sin_port;
    256 		trace_write_packet_iov (trace_listener_accept,
    257 					3, iov, MDL);
    258 	}
    259 #endif
    260 
    261 	obj = (omapi_connection_object_t *)0;
    262 	status = omapi_listener_connect (&obj, listener, socket, &addr);
    263 	if (status != ISC_R_SUCCESS) {
    264 		close (socket);
    265 		return status;
    266 	}
    267 
    268 	status = omapi_register_io_object ((omapi_object_t *)obj,
    269 					   omapi_connection_readfd,
    270 					   omapi_connection_writefd,
    271 					   omapi_connection_reader,
    272 					   omapi_connection_writer,
    273 					   omapi_connection_reaper);
    274 
    275 	/* Lose our reference to the connection, so it'll be gc'd when it's
    276 	   reaped. */
    277 	omapi_connection_dereference (&obj, MDL);
    278 	if (status != ISC_R_SUCCESS)
    279 		omapi_disconnect ((omapi_object_t *)(obj), 1);
    280 	return status;
    281 }
    282 
    283 isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
    284 				     omapi_listener_object_t *listener,
    285 				     int socket,
    286 				     struct sockaddr_in *remote_addr)
    287 {
    288 	isc_result_t status;
    289 	omapi_object_t *h = (omapi_object_t *)listener;
    290 	omapi_addr_t addr;
    291 
    292 #ifdef DEBUG_PROTOCOL
    293 	log_debug ("omapi_accept()");
    294 #endif
    295 
    296 	/* Get the handle. */
    297 	status = omapi_connection_allocate (obj, MDL);
    298 	if (status != ISC_R_SUCCESS)
    299 		return status;
    300 
    301 	(*obj) -> state = omapi_connection_connected;
    302 	(*obj) -> remote_addr = *remote_addr;
    303 	(*obj) -> socket = socket;
    304 
    305 	/* Verify that this host is allowed to connect. */
    306 	if (listener -> verify_addr) {
    307 		addr.addrtype = AF_INET;
    308 		addr.addrlen = sizeof (remote_addr -> sin_addr);
    309 		memcpy (addr.address, &remote_addr -> sin_addr,
    310 			sizeof (remote_addr -> sin_addr));
    311 		addr.port = ntohs(remote_addr -> sin_port);
    312 
    313 		status = (listener -> verify_addr) (h, &addr);
    314 		if (status != ISC_R_SUCCESS) {
    315 			omapi_disconnect ((omapi_object_t *)(*obj), 1);
    316 			omapi_connection_dereference (obj, MDL);
    317 			return status;
    318 		}
    319 	}
    320 
    321 	omapi_listener_reference (&(*obj) -> listener, listener, MDL);
    322 #if defined (TRACING)
    323 	omapi_connection_register (*obj, MDL);
    324 #endif
    325 	status = omapi_signal (h, "connect", (*obj));
    326 	return status;
    327 }
    328 
    329 #if defined (TRACING)
    330 OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
    331 
    332 void omapi_listener_trace_setup (void) {
    333 	trace_listener_accept =
    334 		trace_type_register ("listener-accept", (void *)0,
    335 				     trace_listener_accept_input,
    336 				     trace_listener_accept_stop, MDL);
    337 }
    338 
    339 static void trace_listener_remember (omapi_listener_object_t *obj,
    340 				     const char *file, int line)
    341 {
    342 	isc_result_t status;
    343 	if (!trace_listeners) {
    344 		status = omapi_listener_array_allocate (&trace_listeners,
    345 							file, line);
    346 		if (status != ISC_R_SUCCESS) {
    347 		      foo:
    348 			log_error ("trace_listener_remember: %s",
    349 				   isc_result_totext (status));
    350 			return;
    351 		}
    352 	}
    353 	status = omapi_listener_array_extend (trace_listeners, obj,
    354 					      &obj -> index, MDL);
    355 	if (status != ISC_R_SUCCESS)
    356 		goto foo;
    357 }
    358 
    359 static void trace_listener_accept_input (trace_type_t *ttype,
    360 					 unsigned length, char *buf)
    361 {
    362 	struct in_addr *addr;
    363 	u_int16_t *remote_port;
    364 	u_int16_t *local_port;
    365 	omapi_connection_object_t *obj;
    366 	isc_result_t status;
    367 	struct sockaddr_in remote_addr;
    368 
    369 	addr = (struct in_addr *)buf;
    370 	remote_port = (u_int16_t *)(addr + 1);
    371 	local_port = remote_port + 1;
    372 
    373 	memset (&remote_addr, 0, sizeof remote_addr);
    374 	remote_addr.sin_addr = *addr;
    375 	remote_addr.sin_port = *remote_port;
    376 
    377 	omapi_array_foreach_begin (trace_listeners,
    378 				   omapi_listener_object_t, lp) {
    379 		if (lp -> address.sin_port == *local_port) {
    380 			obj = (omapi_connection_object_t *)0;
    381 			status = omapi_listener_connect (&obj,
    382 							 lp, 0, &remote_addr);
    383 			if (status != ISC_R_SUCCESS) {
    384 				log_error("%s:%d: OMAPI: Failed to connect "
    385 					  "a listener.", MDL);
    386 			}
    387 			omapi_listener_dereference (&lp, MDL);
    388 			return;
    389 		}
    390 	} omapi_array_foreach_end (trace_listeners,
    391 				   omapi_listener_object_t, lp);
    392 	log_error ("trace_listener_accept: %s from %s/%d to port %d",
    393 		   "unexpected connect",
    394 		   inet_ntoa (*addr), *remote_port, *local_port);
    395 }
    396 
    397 static void trace_listener_accept_stop (trace_type_t *ttype) { }
    398 
    399 
    400 #endif
    401 
    402 isc_result_t omapi_listener_configure_security (omapi_object_t *h,
    403 						isc_result_t (*verify_addr)
    404 						 (omapi_object_t *,
    405 						  omapi_addr_t *))
    406 {
    407 	omapi_listener_object_t *l;
    408 
    409 	if (h -> type != omapi_type_listener)
    410 		return DHCP_R_INVALIDARG;
    411 	l = (omapi_listener_object_t *)h;
    412 
    413 	l -> verify_addr = verify_addr;
    414 
    415 	return ISC_R_SUCCESS;
    416 }
    417 
    418 isc_result_t omapi_listener_set_value (omapi_object_t *h,
    419 				      omapi_object_t *id,
    420 				      omapi_data_string_t *name,
    421 				      omapi_typed_data_t *value)
    422 {
    423 	if (h -> type != omapi_type_listener)
    424 		return DHCP_R_INVALIDARG;
    425 
    426 	if (h -> inner && h -> inner -> type -> set_value)
    427 		return (*(h -> inner -> type -> set_value))
    428 			(h -> inner, id, name, value);
    429 	return ISC_R_NOTFOUND;
    430 }
    431 
    432 isc_result_t omapi_listener_get_value (omapi_object_t *h,
    433 				       omapi_object_t *id,
    434 				       omapi_data_string_t *name,
    435 				       omapi_value_t **value)
    436 {
    437 	if (h -> type != omapi_type_listener)
    438 		return DHCP_R_INVALIDARG;
    439 
    440 	if (h -> inner && h -> inner -> type -> get_value)
    441 		return (*(h -> inner -> type -> get_value))
    442 			(h -> inner, id, name, value);
    443 	return ISC_R_NOTFOUND;
    444 }
    445 
    446 isc_result_t omapi_listener_destroy (omapi_object_t *h,
    447 				     const char *file, int line)
    448 {
    449 	omapi_listener_object_t *l;
    450 
    451 	if (h -> type != omapi_type_listener)
    452 		return DHCP_R_INVALIDARG;
    453 	l = (omapi_listener_object_t *)h;
    454 
    455 #ifdef DEBUG_PROTOCOL
    456 	log_debug ("omapi_listener_destroy()");
    457 #endif
    458 
    459 	if (l -> socket != -1) {
    460 		close (l -> socket);
    461 		l -> socket = -1;
    462 	}
    463 	return ISC_R_SUCCESS;
    464 }
    465 
    466 isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
    467 					    const char *name, va_list ap)
    468 {
    469 	if (h -> type != omapi_type_listener)
    470 		return DHCP_R_INVALIDARG;
    471 
    472 	if (h -> inner && h -> inner -> type -> signal_handler)
    473 		return (*(h -> inner -> type -> signal_handler)) (h -> inner,
    474 								  name, ap);
    475 	return ISC_R_NOTFOUND;
    476 }
    477 
    478 /* Write all the published values associated with the object through the
    479    specified connection. */
    480 
    481 isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
    482 					  omapi_object_t *id,
    483 					  omapi_object_t *l)
    484 {
    485 	if (l -> type != omapi_type_listener)
    486 		return DHCP_R_INVALIDARG;
    487 
    488 	if (l -> inner && l -> inner -> type -> stuff_values)
    489 		return (*(l -> inner -> type -> stuff_values)) (c, id,
    490 								l -> inner);
    491 	return ISC_R_SUCCESS;
    492 }
    493