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