xref: /netbsd-src/external/mpl/dhcp/dist/omapip/listener.c (revision f407d9293b6650aa8c33d6a995f797bb6aaefd90)
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@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 
OMAPI_OBJECT_ALLOC(omapi_listener,omapi_listener_object_t,omapi_type_listener)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 
omapi_listen_addr(omapi_object_t * h,omapi_addr_t * addr,int max)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. */
omapi_listener_readfd(omapi_object_t * h)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. */
omapi_accept(omapi_object_t * h)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 
omapi_listener_connect(omapi_connection_object_t ** obj,omapi_listener_object_t * listener,int socket,struct sockaddr_in * remote_addr)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)
OMAPI_ARRAY_TYPE(omapi_listener,omapi_listener_object_t)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 
trace_listener_remember(omapi_listener_object_t * obj,const char * file,int line)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 
trace_listener_accept_input(trace_type_t * ttype,unsigned length,char * buf)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 
trace_listener_accept_stop(trace_type_t * ttype)397 static void trace_listener_accept_stop (trace_type_t *ttype) { }
398 
399 
400 #endif
401 
omapi_listener_configure_security(omapi_object_t * h,isc_result_t (* verify_addr)(omapi_object_t *,omapi_addr_t *))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 
omapi_listener_set_value(omapi_object_t * h,omapi_object_t * id,omapi_data_string_t * name,omapi_typed_data_t * value)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 
omapi_listener_get_value(omapi_object_t * h,omapi_object_t * id,omapi_data_string_t * name,omapi_value_t ** value)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 
omapi_listener_destroy(omapi_object_t * h,const char * file,int line)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 
omapi_listener_signal_handler(omapi_object_t * h,const char * name,va_list ap)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 
omapi_listener_stuff_values(omapi_object_t * c,omapi_object_t * id,omapi_object_t * l)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