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