diff options
Diffstat (limited to 'lib/listener.c')
-rw-r--r-- | lib/listener.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/lib/listener.c b/lib/listener.c new file mode 100644 index 0000000..877c162 --- /dev/null +++ b/lib/listener.c @@ -0,0 +1,236 @@ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <assert.h> +#include <event2/listener.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#include "listener.h" +#include "conn.h" +#include "peer.h" +#include "event.h" +#include "debug.h" + +struct rs_listener * +listener_create (struct rs_context *ctx, struct rs_listener **rootp) +{ + struct rs_listener *listener; + + listener = rs_calloc (ctx, 1, sizeof (*listener)); + if (listener) + { + if (*rootp == NULL) + *rootp = listener; + else + { + listener->next = (*rootp)->next; + (*rootp)->next = listener; + } + } + return listener; +} + +void +listener_accept_cb_ (struct evconnlistener *evconnlistener, + evutil_socket_t newfd, + struct sockaddr *srcaddr_sa, + int srcaddr_len, + void *user_data) +{ + int err = RSE_OK; + struct rs_listener *l = NULL; + struct rs_connection *newconn = NULL; + struct rs_context *ctx = NULL; + struct rs_peer *clients = NULL; + + l = (struct rs_listener *) user_data; + assert (l); + assert (l->base_.magic == RS_CONN_MAGIC_LISTENER); + assert (l->evlistener == evconnlistener); + ctx = l->base_.ctx; + assert (ctx); + +#if defined (DEBUG) + { + char host[80], port[80]; + getnameinfo (srcaddr_sa, srcaddr_len, host, sizeof(host), + port, sizeof(port), 0); + rs_debug (("%s: incoming connection from %s:%s\n", __func__, host, port)); + } +#endif + +/* +Application needs to specify acceptable clients -- we need to verify +src addr in the UDP case and x509 client cert in the TLS case. A list +of peers with proper pointers to realms should be a good way of doing +this. + +Ask the application for a list of acceptable clients. Default to +accepting any potential configured client block in the realm of the +listener. Note that this for this to be an opption, the application +must have read a config file. If there is no configuration, reject the +client. +*/ + if (l->callbacks.client_filter_cb) + clients = l->callbacks.client_filter_cb (l, TO_BASE_CONN(l)->user_data); + if (clients == NULL) + clients = connbase_get_peers (TO_BASE_CONN(l)); + if (clients == NULL) + { + rs_debug (("%s: didn't get a client list for listener %p\n", + __func__, l)); + return; + } + rs_debug (("%s: using client list %p\n", __func__, clients)); + + err = rs_conn_create (ctx, &newconn, NULL); + if (err) + { + rs_debug (("%s: failed creating a new struct rs_connection: %d\n", + __func__, err)); + return; /* FIXME: Verify that this is handled. */ + } + + assert(clients); + /* TODO: Picking the very first peer is not really what we want to + do. For UDP, we can look at src ip and try to find a matching + peer. For TLS, it's worse because we don't have the certificate + until we've accepted the TCP connection. */ + TO_BASE_CONN(newconn)->realm = clients->realm; + newconn->active_peer = clients; + + TO_BASE_CONN(newconn)->fd = newfd; + TO_BASE_CONN(newconn)->transport = TO_BASE_CONN(l)->realm->type; + err = event_init_bufferevent (newconn); + if (err) + { + rs_debug (("%s: failed init bev: %d\n", __func__, err)); + goto errout; + } + + /* Create a message and set up a read event. This installs the + callback performing the TLS verification. */ + { /* FIXME */ + struct rs_message *msg = NULL; + err = rs_message_create (newconn, &msg); + if (err) + abort (); /* FIXME */ + conn_add_read_event (newconn, msg); + } + + if (l->callbacks.new_conn_cb) + l->callbacks.new_conn_cb (newconn, TO_BASE_CONN(l)->user_data); + return; /* Success. */ + + errout: + rs_conn_destroy (newconn); + if (l->callbacks.error_cb) + l->callbacks.error_cb (newconn, TO_BASE_CONN(l)->user_data); +} + +void +listener_err_cb_ (struct evconnlistener *listener, void *user_data) +{ + rs_debug (("%s: FIXME: handle error\n", __func__)); +} + +/* Public functions. */ +int +rs_listener_create (struct rs_context *ctx, + struct rs_listener **listener_out, + const char *config) +{ + int err = RSE_OK; + struct rs_listener *listener = NULL; + + assert (ctx); + + listener = rs_calloc (ctx, 1, sizeof (*listener)); + if (listener == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); + conn_init (ctx, TO_BASE_CONN (listener), RS_CONN_OBJTYPE_LISTENER); + err = conn_configure (ctx, TO_BASE_CONN (listener), config); + if (err) + goto errout; + + if (listener_out) + *listener_out = listener; + return RSE_OK; + + errout: + if (listener) + rs_free (ctx, listener); + return err; +} + +void +rs_listener_set_callbacks (struct rs_listener *listener, + const struct rs_listener_callbacks *cb, + void *user_data) +{ + assert (listener); + TO_BASE_CONN(listener)->user_data = user_data; + memcpy (&listener->callbacks, cb, sizeof (listener->callbacks)); +} + +int +rs_listener_listen (struct rs_listener *listener) +{ + int err = RSE_OK; + struct rs_conn_base *connbase = NULL; + assert (listener); + connbase = TO_BASE_CONN (listener); + + err = event_init_eventbase (connbase); + if (err) + return err; + err = event_init_socket (connbase, connbase->realm->local_addr); + if (err) + return err; +#if 0 + { + struct linger l; + l.l_onoff = 1; + l.l_linger = 0; + rs_debug (("%s: setting SO_LINGER 0s on fd %d\n", __func__, connbase->fd)); + assert (0 == setsockopt (connbase->fd, SOL_SOCKET, SO_LINGER, + (void*)&l, sizeof(l))); + } +#endif + return err; +} + +int +rs_listener_dispatch (const struct rs_listener *listener) +{ + assert (listener); + assert (TO_BASE_CONN(listener)->ctx); + return event_base_dispatch (TO_BASE_CONN(listener)->ctx->evb); +} + +int +rs_listener_close (struct rs_listener *l) +{ + int err = baseconn_close (TO_BASE_CONN (l)); + return err; +} + +struct event_base * +rs_listener_get_eventbase (const struct rs_listener *l) +{ + assert (TO_BASE_CONN (l)); + assert (TO_BASE_CONN (l)->ctx); + return TO_BASE_CONN (l)->ctx->evb; +} + +int +rs_listener_get_fd (const struct rs_listener *l) +{ + assert (TO_BASE_CONN (l)); + return TO_BASE_CONN (l)->fd; +} |