aboutsummaryrefslogtreecommitdiff
path: root/omapip/listener.c
diff options
context:
space:
mode:
Diffstat (limited to 'omapip/listener.c')
-rw-r--r--omapip/listener.c478
1 files changed, 478 insertions, 0 deletions
diff --git a/omapip/listener.c b/omapip/listener.c
new file mode 100644
index 0000000..0c4dcb1
--- /dev/null
+++ b/omapip/listener.c
@@ -0,0 +1,478 @@
+/* listener.c
+
+ Subroutines that support the generic listener object. */
+
+/*
+ * Copyright (c) 2004,2007,2009 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1999-2003 by Internet Software Consortium
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Internet Systems Consortium, Inc.
+ * 950 Charter Street
+ * Redwood City, CA 94063
+ * <info@isc.org>
+ * https://www.isc.org/
+ *
+ * This software has been written for Internet Systems Consortium
+ * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
+ * To learn more about Internet Systems Consortium, see
+ * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
+ * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
+ * ``http://www.nominum.com''.
+ */
+
+#include "dhcpd.h"
+
+#include <omapip/omapip_p.h>
+#include <errno.h>
+
+#if defined (TRACING)
+omapi_array_t *trace_listeners;
+static void trace_listener_accept_input (trace_type_t *, unsigned, char *);
+static void trace_listener_remember (omapi_listener_object_t *,
+ const char *, int);
+static void trace_listener_accept_stop (trace_type_t *);
+trace_type_t *trace_listener_accept;
+#endif
+
+OMAPI_OBJECT_ALLOC (omapi_listener,
+ omapi_listener_object_t, omapi_type_listener)
+
+isc_result_t omapi_listen (omapi_object_t *h,
+ unsigned port,
+ int max)
+{
+ omapi_addr_t addr;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_listen(port=%d, max=%d)", port, max);
+#endif
+
+ addr.addrtype = AF_INET;
+ addr.addrlen = sizeof (struct in_addr);
+ memset (addr.address, 0, sizeof addr.address); /* INADDR_ANY */
+ addr.port = port;
+
+ return omapi_listen_addr (h, &addr, max);
+}
+
+isc_result_t omapi_listen_addr (omapi_object_t *h,
+ omapi_addr_t *addr,
+ int max)
+{
+ isc_result_t status;
+ omapi_listener_object_t *obj;
+ int i;
+
+ /* Currently only support IPv4 addresses. */
+ if (addr->addrtype != AF_INET)
+ return DHCP_R_INVALIDARG;
+
+ /* Get the handle. */
+ obj = (omapi_listener_object_t *)0;
+ status = omapi_listener_allocate (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+ obj->socket = -1;
+
+ /* Connect this object to the inner object. */
+ status = omapi_object_reference (&h -> outer,
+ (omapi_object_t *)obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto error_exit;
+ status = omapi_object_reference (&obj -> inner, h, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto error_exit;
+
+ /* Set up the address on which we will listen... */
+ obj -> address.sin_port = htons (addr -> port);
+ memcpy (&obj -> address.sin_addr,
+ addr -> address, sizeof obj -> address.sin_addr);
+#if defined (HAVE_SA_LEN)
+ obj -> address.sin_len =
+ sizeof (struct sockaddr_in);
+#endif
+ obj -> address.sin_family = AF_INET;
+ memset (&(obj -> address.sin_zero), 0,
+ sizeof obj -> address.sin_zero);
+
+#if defined (TRACING)
+ /* If we're playing back a trace file, we remember the object
+ on the trace listener queue. */
+ if (trace_playback ()) {
+ trace_listener_remember (obj, MDL);
+ } else {
+#endif
+ /* Create a socket on which to listen. */
+ obj -> socket = socket (PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (obj->socket == -1) {
+ if (errno == EMFILE
+ || errno == ENFILE || errno == ENOBUFS)
+ status = ISC_R_NORESOURCES;
+ else
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+#if defined (HAVE_SETFD)
+ if (fcntl (obj -> socket, F_SETFD, 1) < 0) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+#endif
+
+ /* Set the REUSEADDR option so that we don't fail to start if
+ we're being restarted. */
+ i = 1;
+ if (setsockopt (obj -> socket, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&i, sizeof i) < 0) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ /* Try to bind to the wildcard address using the port number
+ we were given. */
+ i = bind (obj -> socket,
+ (struct sockaddr *)&obj -> address,
+ sizeof obj -> address);
+ if (i < 0) {
+ if (errno == EADDRINUSE)
+ status = ISC_R_ADDRNOTAVAIL;
+ else if (errno == EPERM)
+ status = ISC_R_NOPERM;
+ else
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ /* Now tell the kernel to listen for connections. */
+ if (listen (obj -> socket, max)) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ if (fcntl (obj -> socket, F_SETFL, O_NONBLOCK) < 0) {
+ status = ISC_R_UNEXPECTED;
+ goto error_exit;
+ }
+
+ status = omapi_register_io_object ((omapi_object_t *)obj,
+ omapi_listener_readfd, 0,
+ omapi_accept, 0, 0);
+#if defined (TRACING)
+ }
+#endif
+
+ omapi_listener_dereference (&obj, MDL);
+ return status;
+
+error_exit:
+ if (obj != NULL) {
+ if (h->outer == (omapi_object_t *)obj) {
+ omapi_object_dereference((omapi_object_t **)&h->outer,
+ MDL);
+ }
+ if (obj->inner == h) {
+ omapi_object_dereference((omapi_object_t **)&obj->inner,
+ MDL);
+ }
+ if (obj->socket != -1) {
+ close(obj->socket);
+ }
+ omapi_listener_dereference(&obj, MDL);
+ }
+ return status;
+}
+
+/* Return the socket on which the dispatcher should wait for readiness
+ to read, for a listener object. */
+int omapi_listener_readfd (omapi_object_t *h)
+{
+ omapi_listener_object_t *l;
+
+ if (h -> type != omapi_type_listener)
+ return -1;
+ l = (omapi_listener_object_t *)h;
+
+ return l -> socket;
+}
+
+/* Reader callback for a listener object. Accept an incoming connection. */
+isc_result_t omapi_accept (omapi_object_t *h)
+{
+ isc_result_t status;
+ socklen_t len;
+ omapi_connection_object_t *obj;
+ omapi_listener_object_t *listener;
+ struct sockaddr_in addr;
+ int socket;
+
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+ listener = (omapi_listener_object_t *)h;
+
+ /* Accept the connection. */
+ len = sizeof addr;
+ socket = accept (listener -> socket,
+ ((struct sockaddr *)&(addr)), &len);
+ if (socket < 0) {
+ if (errno == EMFILE || errno == ENFILE || errno == ENOBUFS)
+ return ISC_R_NORESOURCES;
+ return ISC_R_UNEXPECTED;
+ }
+
+#if defined (TRACING)
+ /* If we're recording a trace, remember the connection. */
+ if (trace_record ()) {
+ trace_iov_t iov [3];
+ iov [0].buf = (char *)&addr.sin_port;
+ iov [0].len = sizeof addr.sin_port;
+ iov [1].buf = (char *)&addr.sin_addr;
+ iov [1].len = sizeof addr.sin_addr;
+ iov [2].buf = (char *)&listener -> address.sin_port;
+ iov [2].len = sizeof listener -> address.sin_port;
+ trace_write_packet_iov (trace_listener_accept,
+ 3, iov, MDL);
+ }
+#endif
+
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_listener_connect (&obj, listener, socket, &addr);
+ if (status != ISC_R_SUCCESS) {
+ close (socket);
+ return status;
+ }
+
+ status = omapi_register_io_object ((omapi_object_t *)obj,
+ omapi_connection_readfd,
+ omapi_connection_writefd,
+ omapi_connection_reader,
+ omapi_connection_writer,
+ omapi_connection_reaper);
+
+ /* Lose our reference to the connection, so it'll be gc'd when it's
+ reaped. */
+ omapi_connection_dereference (&obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ omapi_disconnect ((omapi_object_t *)(obj), 1);
+ return status;
+}
+
+isc_result_t omapi_listener_connect (omapi_connection_object_t **obj,
+ omapi_listener_object_t *listener,
+ int socket,
+ struct sockaddr_in *remote_addr)
+{
+ isc_result_t status;
+ omapi_object_t *h = (omapi_object_t *)listener;
+ omapi_addr_t addr;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_accept()");
+#endif
+
+ /* Get the handle. */
+ status = omapi_connection_allocate (obj, MDL);
+ if (status != ISC_R_SUCCESS)
+ return status;
+
+ (*obj) -> state = omapi_connection_connected;
+ (*obj) -> remote_addr = *remote_addr;
+ (*obj) -> socket = socket;
+
+ /* Verify that this host is allowed to connect. */
+ if (listener -> verify_addr) {
+ addr.addrtype = AF_INET;
+ addr.addrlen = sizeof (remote_addr -> sin_addr);
+ memcpy (addr.address, &remote_addr -> sin_addr,
+ sizeof (remote_addr -> sin_addr));
+ addr.port = ntohs(remote_addr -> sin_port);
+
+ status = (listener -> verify_addr) (h, &addr);
+ if (status != ISC_R_SUCCESS) {
+ omapi_disconnect ((omapi_object_t *)(*obj), 1);
+ omapi_connection_dereference (obj, MDL);
+ return status;
+ }
+ }
+
+ omapi_listener_reference (&(*obj) -> listener, listener, MDL);
+#if defined (TRACING)
+ omapi_connection_register (*obj, MDL);
+#endif
+ status = omapi_signal (h, "connect", (*obj));
+ return status;
+}
+
+#if defined (TRACING)
+OMAPI_ARRAY_TYPE(omapi_listener, omapi_listener_object_t)
+
+void omapi_listener_trace_setup (void) {
+ trace_listener_accept =
+ trace_type_register ("listener-accept", (void *)0,
+ trace_listener_accept_input,
+ trace_listener_accept_stop, MDL);
+}
+
+static void trace_listener_remember (omapi_listener_object_t *obj,
+ const char *file, int line)
+{
+ isc_result_t status;
+ if (!trace_listeners) {
+ status = omapi_listener_array_allocate (&trace_listeners,
+ file, line);
+ if (status != ISC_R_SUCCESS) {
+ foo:
+ log_error ("trace_listener_remember: %s",
+ isc_result_totext (status));
+ return;
+ }
+ }
+ status = omapi_listener_array_extend (trace_listeners, obj,
+ &obj -> index, MDL);
+ if (status != ISC_R_SUCCESS)
+ goto foo;
+}
+
+static void trace_listener_accept_input (trace_type_t *ttype,
+ unsigned length, char *buf)
+{
+ struct in_addr *addr;
+ u_int16_t *remote_port;
+ u_int16_t *local_port;
+ omapi_connection_object_t *obj;
+ isc_result_t status;
+ struct sockaddr_in remote_addr;
+
+ addr = (struct in_addr *)buf;
+ remote_port = (u_int16_t *)(addr + 1);
+ local_port = remote_port + 1;
+
+ memset (&remote_addr, 0, sizeof remote_addr);
+ remote_addr.sin_addr = *addr;
+ remote_addr.sin_port = *remote_port;
+
+ omapi_array_foreach_begin (trace_listeners,
+ omapi_listener_object_t, lp) {
+ if (lp -> address.sin_port == *local_port) {
+ obj = (omapi_connection_object_t *)0;
+ status = omapi_listener_connect (&obj,
+ lp, 0, &remote_addr);
+ omapi_listener_dereference (&lp, MDL);
+ return;
+ }
+ } omapi_array_foreach_end (trace_listeners,
+ omapi_listener_object_t, lp);
+ log_error ("trace_listener_accept: %s from %s/%d to port %d",
+ "unexpected connect",
+ inet_ntoa (*addr), *remote_port, *local_port);
+}
+
+static void trace_listener_accept_stop (trace_type_t *ttype) { }
+
+
+#endif
+
+isc_result_t omapi_listener_configure_security (omapi_object_t *h,
+ isc_result_t (*verify_addr)
+ (omapi_object_t *,
+ omapi_addr_t *))
+{
+ omapi_listener_object_t *l;
+
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+ l = (omapi_listener_object_t *)h;
+
+ l -> verify_addr = verify_addr;
+
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_listener_set_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_typed_data_t *value)
+{
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> set_value)
+ return (*(h -> inner -> type -> set_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_listener_get_value (omapi_object_t *h,
+ omapi_object_t *id,
+ omapi_data_string_t *name,
+ omapi_value_t **value)
+{
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> get_value)
+ return (*(h -> inner -> type -> get_value))
+ (h -> inner, id, name, value);
+ return ISC_R_NOTFOUND;
+}
+
+isc_result_t omapi_listener_destroy (omapi_object_t *h,
+ const char *file, int line)
+{
+ omapi_listener_object_t *l;
+
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+ l = (omapi_listener_object_t *)h;
+
+#ifdef DEBUG_PROTOCOL
+ log_debug ("omapi_listener_destroy()");
+#endif
+
+ if (l -> socket != -1) {
+ close (l -> socket);
+ l -> socket = -1;
+ }
+ return ISC_R_SUCCESS;
+}
+
+isc_result_t omapi_listener_signal_handler (omapi_object_t *h,
+ const char *name, va_list ap)
+{
+ if (h -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (h -> inner && h -> inner -> type -> signal_handler)
+ return (*(h -> inner -> type -> signal_handler)) (h -> inner,
+ name, ap);
+ return ISC_R_NOTFOUND;
+}
+
+/* Write all the published values associated with the object through the
+ specified connection. */
+
+isc_result_t omapi_listener_stuff_values (omapi_object_t *c,
+ omapi_object_t *id,
+ omapi_object_t *l)
+{
+ if (l -> type != omapi_type_listener)
+ return DHCP_R_INVALIDARG;
+
+ if (l -> inner && l -> inner -> type -> stuff_values)
+ return (*(l -> inner -> type -> stuff_values)) (c, id,
+ l -> inner);
+ return ISC_R_SUCCESS;
+}
+