omapip/listener.c

Go to the documentation of this file.
00001 /* listener.c
00002 
00003    Subroutines that support the generic listener object. */
00004 
00005 /*
00006  * Copyright (c) 2012,2014 by Internet Systems Consortium, Inc. ("ISC")
00007  * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
00008  * Copyright (c) 1999-2003 by Internet Software Consortium
00009  *
00010  * Permission to use, copy, modify, and distribute this software for any
00011  * purpose with or without fee is hereby granted, provided that the above
00012  * copyright notice and this permission notice appear in all copies.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
00015  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
00016  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
00017  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00018  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
00019  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
00020  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00021  *
00022  *   Internet Systems Consortium, Inc.
00023  *   950 Charter Street
00024  *   Redwood City, CA 94063
00025  *   <info@isc.org>
00026  *   https://www.isc.org/
00027  *
00028  */
00029 
00030 #include "dhcpd.h"
00031 
00032 #include <omapip/omapip_p.h>
00033 #include <errno.h>
00034 
00035 #if defined (TRACING)
00036 omapi_array_t *trace_listeners;
00037 static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
00038 static void trace_listener_remember (omapi_listener_object_t *,
00039                                      const char *, int);
00040 static void trace_listener_accept_stop (trace_type_t *);
00041 trace_type_t *trace_listener_accept;
00042 #endif
00043 
00044 OMAPI_OBJECT_ALLOC (omapi_listener,
00045                     omapi_listener_object_t, omapi_type_listener)
00046 
00047 isc_result_t omapi_listen (omapi_object_t *h,
00048                            unsigned port,
00049                            int max)
00050 {
00051         omapi_addr_t addr;
00052 
00053 #ifdef DEBUG_PROTOCOL
00054         log_debug ("omapi_listen(port=%d, max=%d)", port, max);
00055 #endif
00056 
00057         addr.addrtype = AF_INET;
00058         addr.addrlen = sizeof (struct in_addr);
00059         memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
00060         addr.port = port;
00061 
00062         return omapi_listen_addr (h, &addr, max);
00063 }
00064 
00065 isc_result_t omapi_listen_addr (omapi_object_t *h,
00066                                 omapi_addr_t *addr,
00067                                 int max)
00068 {
00069         isc_result_t status;
00070         omapi_listener_object_t *obj;
00071         int i;
00072 
00073         /* Currently only support IPv4 addresses. */
00074         if (addr->addrtype != AF_INET)
00075                 return DHCP_R_INVALIDARG;
00076 
00077         /* Get the handle. */
00078         obj = (omapi_listener_object_t *)0;
00079         status = omapi_listener_allocate (&obj, MDL);
00080         if (status != ISC_R_SUCCESS)
00081                 /*
00082                  * we could simply return here but by going to
00083                  * error_exit we keep the code check tools happy
00084                  * without removing the NULL check on obj at
00085                  * the exit, which we could skip curently but
00086                  * might want in the future.
00087                  */
00088                 goto error_exit;
00089         obj->socket = -1;
00090 
00091         /* Connect this object to the inner object. */
00092         status = omapi_object_reference (&h -> outer,
00093                                          (omapi_object_t *)obj, MDL);
00094         if (status != ISC_R_SUCCESS)
00095                 goto error_exit;
00096         status = omapi_object_reference (&obj -> inner, h, MDL);
00097         if (status != ISC_R_SUCCESS)
00098                 goto error_exit;
00099 
00100         /* Set up the address on which we will listen... */
00101         obj -> address.sin_port = htons (addr -> port);
00102         memcpy (&obj -> address.sin_addr,
00103                 addr -> address, sizeof obj -> address.sin_addr);
00104 #if defined (HAVE_SA_LEN)
00105         obj -> address.sin_len =
00106                 sizeof (struct sockaddr_in);
00107 #endif
00108         obj -> address.sin_family = AF_INET;
00109         memset (&(obj -> address.sin_zero), 0,
00110                 sizeof obj -> address.sin_zero);
00111 
00112 #if defined (TRACING)
00113         /* If we're playing back a trace file, we remember the object
00114            on the trace listener queue. */
00115         if (trace_playback ()) {
00116                 trace_listener_remember (obj, MDL);
00117         }  else {
00118 #endif
00119                 /* Create a socket on which to listen. */
00120                 obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
00121                 if (obj->socket == -1) {
00122                         if (errno == EMFILE
00123                             || errno == ENFILE || errno == ENOBUFS)
00124                                 status = ISC_R_NORESOURCES;
00125                         else
00126                                 status = ISC_R_UNEXPECTED;
00127                         goto error_exit;
00128                 }
00129 
00130 #if defined (HAVE_SETFD)
00131                 if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
00132                         status = ISC_R_UNEXPECTED;
00133                         goto error_exit;
00134                 }
00135 #endif
00136 
00137                 /* Set the REUSEADDR option so that we don't fail to start if
00138                    we're being restarted. */
00139                 i = 1;
00140                 if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
00141                                 (char *)&i, sizeof i) < 0) {
00142                         status = ISC_R_UNEXPECTED;
00143                         goto error_exit;
00144                 }
00145 
00146                 /* Try to bind to the wildcard address using the port number
00147                    we were given. */
00148                 i = bind (obj -> socket,
00149                           (struct sockaddr *)&obj -> address,
00150                           sizeof obj -> address);
00151                 if (i < 0) {
00152                         if (errno == EADDRINUSE)
00153                                 status = ISC_R_ADDRNOTAVAIL;
00154                         else if (errno == EPERM)
00155                                 status = ISC_R_NOPERM;
00156                         else
00157                                 status = ISC_R_UNEXPECTED;
00158                         goto error_exit;
00159                 }
00160 
00161                 /* Now tell the kernel to listen for connections. */
00162                 if (listen (obj -> socket, max)) {
00163                         status = ISC_R_UNEXPECTED;
00164                         goto error_exit;
00165                 }
00166 
00167                 if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
00168                         status = ISC_R_UNEXPECTED;
00169                         goto error_exit;
00170                 }
00171 
00172                 status = omapi_register_io_object ((omapi_object_t *)obj,
00173                                                    omapi_listener_readfd, 0,
00174                                                    omapi_accept, 0, 0);
00175 #if defined (TRACING)
00176         }
00177 #endif
00178 
00179         omapi_listener_dereference (&obj, MDL);
00180         return status;
00181 
00182 error_exit:
00183         if (obj != NULL) {
00184                 if (h->outer == (omapi_object_t *)obj) {
00185                         omapi_object_dereference((omapi_object_t **)&h->outer, 
00186                                                  MDL);
00187                 }
00188                 if (obj->inner == h) {
00189                         omapi_object_dereference((omapi_object_t **)&obj->inner,
00190                                                  MDL);
00191                 }
00192                 if (obj->socket != -1) {
00193                         close(obj->socket);
00194                 }
00195                 omapi_listener_dereference(&obj, MDL);
00196         }
00197         return status;
00198 }
00199 
00200 /* Return the socket on which the dispatcher should wait for readiness
00201    to read, for a listener object. */
00202 int omapi_listener_readfd (omapi_object_t *h)
00203 {
00204         omapi_listener_object_t *l;
00205 
00206         if (h -> type != omapi_type_listener)
00207                 return -1;
00208         l = (omapi_listener_object_t *)h;
00209         
00210         return l -> socket;
00211 }
00212 
00213 /* Reader callback for a listener object.   Accept an incoming connection. */
00214 isc_result_t omapi_accept (omapi_object_t *h)
00215 {
00216         isc_result_t status;
00217         socklen_t len;
00218         omapi_connection_object_t *obj;
00219         omapi_listener_object_t *listener;
00220         struct sockaddr_in addr;
00221         int socket;
00222 
00223         if (h -> type != omapi_type_listener)
00224                 return DHCP_R_INVALIDARG;
00225         listener = (omapi_listener_object_t *)h;
00226 
00227         /* Accept the connection. */
00228         len = sizeof addr;
00229         socket = accept (listener -> socket,
00230                          ((struct sockaddr *)&(addr)), &len);
00231         if (socket < 0) {
00232                 if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
00233                         return ISC_R_NORESOURCES;
00234                 return ISC_R_UNEXPECTED;
00235         }
00236         
00237 #if defined (TRACING)
00238         /* If we're recording a trace, remember the connection. */
00239         if (trace_record ()) {
00240                 trace_iov_t iov [3];
00241                 iov [0].buf = (char *)&addr.sin_port;
00242                 iov [0].len = sizeof addr.sin_port;
00243                 iov [1].buf = (char *)&addr.sin_addr;
00244                 iov [1].len = sizeof addr.sin_addr;
00245                 iov [2].buf = (char *)&listener -> address.sin_port;
00246                 iov [2].len = sizeof listener -> address.sin_port;
00247                 trace_write_packet_iov (trace_listener_accept,
00248                                         3, iov, MDL);
00249         }
00250 #endif
00251 
00252         obj = (omapi_connection_object_t *)0;
00253         status = omapi_listener_connect (&obj, listener, socket, &addr);
00254         if (status != ISC_R_SUCCESS) {
00255                 close (socket);
00256                 return status;
00257         }
00258 
00259         status = omapi_register_io_object ((omapi_object_t *)obj,
00260                                            omapi_connection_readfd,
00261                                            omapi_connection_writefd,
00262                                            omapi_connection_reader,
00263                                            omapi_connection_writer,
00264                                            omapi_connection_reaper);
00265 
00266         /* Lose our reference to the connection, so it'll be gc'd when it's
00267            reaped. */
00268         omapi_connection_dereference (&obj, MDL);
00269         if (status != ISC_R_SUCCESS)
00270                 omapi_disconnect ((omapi_object_t *)(obj), 1);
00271         return status;
00272 }
00273 
00274 isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
00275                                      omapi_listener_object_t *listener,
00276                                      int socket,
00277                                      struct sockaddr_in *remote_addr)
00278 {
00279         isc_result_t status;
00280         omapi_object_t *h = (omapi_object_t *)listener;
00281         omapi_addr_t addr;
00282 
00283 #ifdef DEBUG_PROTOCOL
00284         log_debug ("omapi_accept()");
00285 #endif
00286         
00287         /* Get the handle. */
00288         status = omapi_connection_allocate (obj, MDL);
00289         if (status != ISC_R_SUCCESS)
00290                 return status;
00291 
00292         (*obj) -> state = omapi_connection_connected;
00293         (*obj) -> remote_addr = *remote_addr;
00294         (*obj) -> socket = socket;
00295 
00296         /* Verify that this host is allowed to connect. */
00297         if (listener -> verify_addr) {
00298                 addr.addrtype = AF_INET;
00299                 addr.addrlen = sizeof (remote_addr -> sin_addr);
00300                 memcpy (addr.address, &remote_addr -> sin_addr,
00301                         sizeof (remote_addr -> sin_addr));
00302                 addr.port = ntohs(remote_addr -> sin_port);
00303 
00304                 status = (listener -> verify_addr) (h, &addr);
00305                 if (status != ISC_R_SUCCESS) {
00306                         omapi_disconnect ((omapi_object_t *)(*obj), 1);
00307                         omapi_connection_dereference (obj, MDL);
00308                         return status;
00309                 }
00310         }
00311 
00312         omapi_listener_reference (&(*obj) -> listener, listener, MDL);
00313 #if defined (TRACING)
00314         omapi_connection_register (*obj, MDL);
00315 #endif
00316         status = omapi_signal (h, "connect", (*obj));
00317         return status;
00318 }
00319 
00320 #if defined (TRACING)
00321 OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
00322 
00323 void omapi_listener_trace_setup (void) {
00324         trace_listener_accept =
00325                 trace_type_register ("listener-accept", (void *)0,
00326                                      trace_listener_accept_input,
00327                                      trace_listener_accept_stop, MDL);
00328 }
00329 
00330 static void trace_listener_remember (omapi_listener_object_t *obj,
00331                                      const char *file, int line)
00332 {
00333         isc_result_t status;
00334         if (!trace_listeners) {
00335                 status = omapi_listener_array_allocate (&trace_listeners,
00336                                                         file, line);
00337                 if (status != ISC_R_SUCCESS) {
00338                       foo:
00339                         log_error ("trace_listener_remember: %s",
00340                                    isc_result_totext (status));
00341                         return;
00342                 }
00343         }
00344         status = omapi_listener_array_extend (trace_listeners, obj,
00345                                               &obj -> index, MDL);
00346         if (status != ISC_R_SUCCESS)
00347                 goto foo;
00348 }
00349 
00350 static void trace_listener_accept_input (trace_type_t *ttype,
00351                                          unsigned length, char *buf)
00352 {
00353         struct in_addr *addr;
00354         u_int16_t *remote_port;
00355         u_int16_t *local_port;
00356         omapi_connection_object_t *obj;
00357         isc_result_t status;
00358         struct sockaddr_in remote_addr;
00359 
00360         addr = (struct in_addr *)buf;
00361         remote_port = (u_int16_t *)(addr + 1);
00362         local_port = remote_port + 1;
00363 
00364         memset (&remote_addr, 0, sizeof remote_addr);
00365         remote_addr.sin_addr = *addr;
00366         remote_addr.sin_port = *remote_port;
00367 
00368         omapi_array_foreach_begin (trace_listeners,
00369                                    omapi_listener_object_t, lp) {
00370                 if (lp -> address.sin_port == *local_port) {
00371                         obj = (omapi_connection_object_t *)0;
00372                         status = omapi_listener_connect (&obj,
00373                                                          lp, 0, &remote_addr);
00374                         if (status != ISC_R_SUCCESS) {
00375                                 log_error("%s:%d: OMAPI: Failed to connect "
00376                                           "a listener.", MDL);
00377                         }
00378                         omapi_listener_dereference (&lp, MDL);
00379                         return;
00380                 }
00381         } omapi_array_foreach_end (trace_listeners,
00382                                    omapi_listener_object_t, lp);
00383         log_error ("trace_listener_accept: %s from %s/%d to port %d",
00384                    "unexpected connect",
00385                    inet_ntoa (*addr), *remote_port, *local_port);
00386 }
00387 
00388 static void trace_listener_accept_stop (trace_type_t *ttype) { }
00389 
00390 
00391 #endif
00392 
00393 isc_result_t omapi_listener_configure_security (omapi_object_t *h,
00394                                                 isc_result_t (*verify_addr)
00395                                                  (omapi_object_t *,
00396                                                   omapi_addr_t *))
00397 {
00398         omapi_listener_object_t *l;
00399 
00400         if (h -> type != omapi_type_listener)
00401                 return DHCP_R_INVALIDARG;
00402         l = (omapi_listener_object_t *)h;
00403 
00404         l -> verify_addr = verify_addr;
00405 
00406         return ISC_R_SUCCESS;
00407 }
00408 
00409 isc_result_t omapi_listener_set_value (omapi_object_t *h,
00410                                       omapi_object_t *id,
00411                                       omapi_data_string_t *name,
00412                                       omapi_typed_data_t *value)
00413 {
00414         if (h -> type != omapi_type_listener)
00415                 return DHCP_R_INVALIDARG;
00416         
00417         if (h -> inner && h -> inner -> type -> set_value)
00418                 return (*(h -> inner -> type -> set_value))
00419                         (h -> inner, id, name, value);
00420         return ISC_R_NOTFOUND;
00421 }
00422 
00423 isc_result_t omapi_listener_get_value (omapi_object_t *h,
00424                                        omapi_object_t *id,
00425                                        omapi_data_string_t *name,
00426                                        omapi_value_t **value)
00427 {
00428         if (h -> type != omapi_type_listener)
00429                 return DHCP_R_INVALIDARG;
00430         
00431         if (h -> inner && h -> inner -> type -> get_value)
00432                 return (*(h -> inner -> type -> get_value))
00433                         (h -> inner, id, name, value);
00434         return ISC_R_NOTFOUND;
00435 }
00436 
00437 isc_result_t omapi_listener_destroy (omapi_object_t *h,
00438                                      const char *file, int line)
00439 {
00440         omapi_listener_object_t *l;
00441 
00442         if (h -> type != omapi_type_listener)
00443                 return DHCP_R_INVALIDARG;
00444         l = (omapi_listener_object_t *)h;
00445 
00446 #ifdef DEBUG_PROTOCOL
00447         log_debug ("omapi_listener_destroy()");
00448 #endif
00449         
00450         if (l -> socket != -1) {
00451                 close (l -> socket);
00452                 l -> socket = -1;
00453         }
00454         return ISC_R_SUCCESS;
00455 }
00456 
00457 isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
00458                                             const char *name, va_list ap)
00459 {
00460         if (h -> type != omapi_type_listener)
00461                 return DHCP_R_INVALIDARG;
00462         
00463         if (h -> inner && h -> inner -> type -> signal_handler)
00464                 return (*(h -> inner -> type -> signal_handler)) (h -> inner,
00465                                                                   name, ap);
00466         return ISC_R_NOTFOUND;
00467 }
00468 
00469 /* Write all the published values associated with the object through the
00470    specified connection. */
00471 
00472 isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
00473                                           omapi_object_t *id,
00474                                           omapi_object_t *l)
00475 {
00476         if (l -> type != omapi_type_listener)
00477                 return DHCP_R_INVALIDARG;
00478 
00479         if (l -> inner && l -> inner -> type -> stuff_values)
00480                 return (*(l -> inner -> type -> stuff_values)) (c, id,
00481                                                                 l -> inner);
00482         return ISC_R_SUCCESS;
00483 }
00484 

Generated on 5 Apr 2014 for ISC DHCP by  doxygen 1.6.1