From: Linus Nordberg Date: Mon, 18 Feb 2013 16:12:20 +0000 (+0100) Subject: WIP commit for listener support. X-Git-Url: http://www.project-moonshot.org/gitweb/?p=radsecproxy.git;a=commitdiff_plain;h=ec47efe2b20e8ff21489744caaf646c692243860 WIP commit for listener support. --- diff --git a/lib/Makefile.am b/lib/Makefile.am index ebbe6cf..06ea8d5 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -33,6 +33,7 @@ libradsec_la_SOURCES = \ debug.c \ err.c \ event.c \ + listener.c \ message.c \ peer.c \ radsec.c \ diff --git a/lib/compat.h b/lib/compat.h index f132779..63de65f 100644 --- a/lib/compat.h +++ b/lib/compat.h @@ -1,5 +1,11 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ +#ifdef _WIN32 +#define INLINE __inline +#else +#define INLINE inline +#endif + ssize_t compat_send (int sockfd, const void *buf, size_t len, int flags); ssize_t compat_recv (int sockfd, void *buf, size_t len, int flags); diff --git a/lib/conn.c b/lib/conn.c index 815784f..d8c1569 100644 --- a/lib/conn.c +++ b/lib/conn.c @@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -49,12 +49,12 @@ conn_activate_timeout (struct rs_connection *conn) { assert (conn); assert (conn->tev); - assert (conn->evb); - if (conn->timeout.tv_sec || conn->timeout.tv_usec) + assert (conn->base_.ctx->evb); + if (conn->base_.timeout.tv_sec || conn->base_.timeout.tv_usec) { rs_debug (("%s: activating timer: %d.%d\n", __func__, conn->timeout.tv_sec, conn->timeout.tv_usec)); - if (evtimer_add (conn->tev, &conn->timeout)) + if (evtimer_add (conn->tev, &conn->base_.timeout)) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "evtimer_add: %d", errno); } @@ -65,8 +65,8 @@ int conn_type_tls (const struct rs_connection *conn) { assert (conn->active_peer); - return conn->realm->type == RS_CONN_TYPE_TLS - || conn->realm->type == RS_CONN_TYPE_DTLS; + return conn->base_.realm->type == RS_CONN_TYPE_TLS + || conn->base_.realm->type == RS_CONN_TYPE_DTLS; } int @@ -77,22 +77,35 @@ conn_cred_psk (const struct rs_connection *conn) conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK; } - -/* Public functions. */ -int -rs_conn_create (struct rs_context *ctx, - struct rs_connection **conn, - const char *config) +void +conn_init (struct rs_context *ctx, + struct rs_conn_base *connbase, + enum rs_conn_subtype type) { - struct rs_connection *c; + switch (type) + { + case RS_CONN_OBJTYPE_BASE: + connbase->magic = RS_CONN_MAGIC_BASE; + break; + case RS_CONN_OBJTYPE_GENERIC: + connbase->magic = RS_CONN_MAGIC_GENERIC; + break; + case RS_CONN_OBJTYPE_LISTENER: + connbase->magic = RS_CONN_MAGIC_LISTENER; + break; + default: + assert ("invalid connection subtype" == NULL); + } - c = (struct rs_connection *) malloc (sizeof(struct rs_connection)); - if (!c) - return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); + connbase->ctx = ctx; + connbase->fd = -1; +} - memset (c, 0, sizeof(struct rs_connection)); - c->ctx = ctx; - c->fd = -1; +int +conn_configure (struct rs_context *ctx, + struct rs_conn_base *connbase, + const char *config) +{ if (config) { struct rs_realm *r = rs_conf_find_realm (ctx, config); @@ -100,34 +113,59 @@ rs_conn_create (struct rs_context *ctx, { struct rs_peer *p; - c->realm = r; - c->peers = r->peers; /* FIXME: Copy instead? */ - for (p = c->peers; p != NULL; p = p->next) - p->conn = c; - c->timeout.tv_sec = r->timeout; - c->tryagain = r->retries; + connbase->realm = r; + connbase->peers = r->peers; /* FIXME: Copy instead? */ + for (p = connbase->peers; p != NULL; p = p->next) + p->conn = TO_GENERIC_CONN(connbase); + connbase->timeout.tv_sec = r->timeout; + connbase->tryagain = r->retries; } else { - c->realm = rs_malloc (ctx, sizeof (struct rs_realm)); - if (!c->realm) + connbase->realm = rs_malloc (ctx, sizeof (struct rs_realm)); + if (!connbase->realm) return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); - memset (c->realm, 0, sizeof (struct rs_realm)); + memset (connbase->realm, 0, sizeof (struct rs_realm)); } } + return RSE_OK; +} + +/* Public functions. */ +int +rs_conn_create (struct rs_context *ctx, + struct rs_connection **conn, + const char *config) +{ + int err = RSE_OK; + struct rs_connection *c = NULL; + assert (ctx); + + c = rs_calloc (ctx, 1, sizeof (struct rs_connection)); + if (c == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); + conn_init (ctx, &c->base_, RS_CONN_OBJTYPE_GENERIC); + err = conn_configure (ctx, &c->base_, config); + if (err) + goto errout; if (conn) *conn = c; return RSE_OK; + + errout: + if (c) + rs_free (ctx, c); + return err; } void rs_conn_set_type (struct rs_connection *conn, rs_conn_type_t type) { assert (conn); - assert (conn->realm); - conn->realm->type = type; + assert (conn->base_.realm); + conn->base_.realm->type = type; } int @@ -147,8 +185,8 @@ rs_conn_disconnect (struct rs_connection *conn) assert (conn); - err = evutil_closesocket (conn->fd); - conn->fd = -1; + err = evutil_closesocket (conn->base_.fd); + conn->base_.fd = -1; return err; } @@ -174,16 +212,14 @@ rs_conn_destroy (struct rs_connection *conn) if (conn->tev) event_free (conn->tev); - if (conn->bev) - bufferevent_free (conn->bev); - if (conn->rev) - event_free (conn->rev); - if (conn->wev) - event_free (conn->wev); - if (conn->evb) - event_base_free (conn->evb); + if (conn->base_.bev) + bufferevent_free (conn->base_.bev); + if (conn->base_.rev) + event_free (conn->base_.rev); + if (conn->base_.wev) + event_free (conn->base_.wev); - rs_free (conn->ctx, conn); + rs_free (conn->base_.ctx, conn); return err; } @@ -230,15 +266,15 @@ rs_conn_get_current_peer (struct rs_connection *conn, } int -rs_conn_dispatch(struct rs_connection *conn) +rs_conn_dispatch (struct rs_connection *conn) { assert (conn); - return event_base_loop (conn->evb, EVLOOP_ONCE); + return event_base_loop (conn->base_.ctx->evb, EVLOOP_ONCE); } #if 0 struct event_base -*rs_conn_get_evb(const struct rs_connection *conn) +*rs_conn_get_evb (const struct rs_connection *conn) { assert (conn); return conn->evb; @@ -249,7 +285,7 @@ int rs_conn_get_fd (struct rs_connection *conn) { assert (conn); assert (conn->active_peer); - return conn->fd; + return conn->base_.fd; } static void @@ -260,10 +296,10 @@ _rcb (struct rs_message *message, void *user_data) assert (msg->conn); msg->flags |= RS_MESSAGE_RECEIVED; - if (msg->conn->bev) - bufferevent_disable (msg->conn->bev, EV_WRITE|EV_READ); + if (msg->conn->base_.bev) + bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ); else - event_del (msg->conn->rev); + event_del (msg->conn->base_.rev); } int @@ -275,32 +311,33 @@ rs_conn_receive_message (struct rs_connection *conn, struct rs_message *msg = NULL; assert (conn); - assert (conn->realm); + assert (conn->base_.realm); assert (!conn_user_dispatch_p (conn)); /* Blocking mode only. */ if (rs_message_create (conn, &msg)) return -1; - assert (conn->evb); - assert (conn->fd >= 0); + assert (conn->base_.ctx->evb); + assert (conn->base_.fd >= 0); conn->callbacks.received_cb = _rcb; - conn->user_data = msg; + conn->base_.user_data = msg; msg->flags &= ~RS_MESSAGE_RECEIVED; - if (conn->bev) /* TCP. */ + if (conn->base_.bev) /* TCP. */ { - bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0); - bufferevent_setcb (conn->bev, tcp_read_cb, NULL, tcp_event_cb, msg); - bufferevent_enable (conn->bev, EV_READ); + bufferevent_setwatermark (conn->base_.bev, EV_READ, RS_HEADER_LEN, 0); + bufferevent_setcb (conn->base_.bev, tcp_read_cb, NULL, tcp_event_cb, msg); + bufferevent_enable (conn->base_.bev, EV_READ); } else /* UDP. */ { /* Put fresh message in user_data for the callback and enable the read event. */ - event_assign (conn->rev, conn->evb, event_get_fd (conn->rev), - EV_READ, event_get_callback (conn->rev), msg); - err = event_add (conn->rev, NULL); + event_assign (conn->base_.rev, conn->base_.ctx->evb, + event_get_fd (conn->base_.rev), EV_READ, + event_get_callback (conn->base_.rev), msg); + err = event_add (conn->base_.rev, NULL); if (err < 0) return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, "event_add: %s", @@ -311,7 +348,7 @@ rs_conn_receive_message (struct rs_connection *conn, } rs_debug (("%s: entering event loop\n", __func__)); - err = event_base_dispatch (conn->evb); + err = event_base_dispatch (conn->base_.ctx->evb); conn->callbacks.received_cb = NULL; if (err < 0) return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, @@ -340,5 +377,5 @@ rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv) { assert (conn); assert (tv); - conn->timeout = *tv; + conn->base_.timeout = *tv; } diff --git a/lib/conn.h b/lib/conn.h index c538a8c..c1543d9 100644 --- a/lib/conn.h +++ b/lib/conn.h @@ -1,4 +1,4 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ int conn_close (struct rs_connection **connp); @@ -6,3 +6,9 @@ int conn_user_dispatch_p (const struct rs_connection *conn); int conn_activate_timeout (struct rs_connection *conn); int conn_type_tls (const struct rs_connection *conn); int conn_cred_psk (const struct rs_connection *conn); +int conn_configure (struct rs_context *ctx, + struct rs_conn_base *connbase, + const char *config); +void conn_init (struct rs_context *ctx, + struct rs_conn_base *connbase, + enum rs_conn_subtype type); diff --git a/lib/err.c b/lib/err.c index 19e1f33..e0bcea9 100644 --- a/lib/err.c +++ b/lib/err.c @@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -159,9 +159,9 @@ int err_conn_push_err (struct rs_connection *conn, struct rs_error *err) { - if (conn->err) - rs_err_free (conn->err); - conn->err = err; /* FIXME: use a stack */ + if (conn->base_.err) + rs_err_free (conn->base_.err); + conn->base_.err = err; /* FIXME: use a stack */ return err->code; } @@ -225,8 +225,8 @@ rs_err_conn_pop (struct rs_connection *conn) if (!conn) return NULL; /* FIXME: RSE_INVALID_CONN */ - err = conn->err; - conn->err = NULL; + err = conn->base_.err; + conn->base_.err = NULL; return err; } @@ -236,8 +236,8 @@ rs_err_conn_peek_code (struct rs_connection *conn) { if (!conn) return -1; /* FIXME: RSE_INVALID_CONN */ - if (conn->err) - return conn->err->code; + if (conn->base_.err) + return conn->base_.err->code; return RSE_OK; } diff --git a/lib/event.c b/lib/event.c index ff64348..f7b936a 100644 --- a/lib/event.c +++ b/lib/event.c @@ -1,4 +1,4 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -99,7 +99,7 @@ event_retransmit_timeout_cb (int fd, short event, void *data) int event_init_socket (struct rs_connection *conn, struct rs_peer *p) { - if (conn->fd != -1) + if (conn->base_.fd != -1) return RSE_OK; if (p->addr_cache == NULL) @@ -110,16 +110,16 @@ event_init_socket (struct rs_connection *conn, struct rs_peer *p) return err_conn_push_err (conn, err); } - conn->fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype, + conn->base_.fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype, p->addr_cache->ai_protocol); - if (conn->fd < 0) + if (conn->base_.fd < 0) return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, "socket: %d (%s)", errno, strerror (errno)); - if (evutil_make_socket_nonblocking (conn->fd) < 0) + if (evutil_make_socket_nonblocking (conn->base_.fd) < 0) { - evutil_closesocket (conn->fd); - conn->fd = -1; + evutil_closesocket (conn->base_.fd); + conn->base_.fd = -1; return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, "evutil_make_socket_nonblocking: %d (%s)", errno, strerror (errno)); @@ -130,28 +130,30 @@ event_init_socket (struct rs_connection *conn, struct rs_peer *p) int event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) { - if (conn->bev) + if (conn->base_.bev) return RSE_OK; - if (conn->realm->type == RS_CONN_TYPE_TCP) + if (conn->base_.realm->type == RS_CONN_TYPE_TCP) { - conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0); - if (!conn->bev) + conn->base_.bev = bufferevent_socket_new (conn->base_.ctx->evb, + conn->base_.fd, 0); + if (!conn->base_.bev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "bufferevent_socket_new"); } #if defined (RS_ENABLE_TLS) - else if (conn->realm->type == RS_CONN_TYPE_TLS) + else if (conn->base_.realm->type == RS_CONN_TYPE_TLS) { if (rs_tls_init (conn)) return -1; /* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things seem to break when be_openssl_ctrl() (in libevent) calls SSL_set_bio() after BIO_new_socket() with flag=1. */ - conn->bev = - bufferevent_openssl_socket_new (conn->evb, conn->fd, conn->tls_ssl, + conn->base_.bev = + bufferevent_openssl_socket_new (conn->base_.ctx->evb, conn->base_.fd, + conn->tls_ssl, BUFFEREVENT_SSL_CONNECTING, 0); - if (!conn->bev) + if (!conn->base_.bev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "bufferevent_openssl_socket_new"); } @@ -160,7 +162,7 @@ event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) { return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__, "%s: unknown connection type: %d", __func__, - conn->realm->type); + conn->base_.realm->type); } return RSE_OK; @@ -188,10 +190,11 @@ event_do_connect (struct rs_connection *conn) } #endif - if (p->conn->bev) /* TCP */ + if (p->conn->base_.bev) /* TCP */ { conn_activate_timeout (conn); /* Connect timeout. */ - err = bufferevent_socket_connect (p->conn->bev, p->addr_cache->ai_addr, + err = bufferevent_socket_connect (p->conn->base_.bev, + p->addr_cache->ai_addr, p->addr_cache->ai_addrlen); if (err < 0) rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__, @@ -202,7 +205,7 @@ event_do_connect (struct rs_connection *conn) } else /* UDP */ { - err = connect (p->conn->fd, + err = connect (p->conn->base_.fd, p->addr_cache->ai_addr, p->addr_cache->ai_addrlen); if (err < 0) @@ -211,7 +214,7 @@ event_do_connect (struct rs_connection *conn) rs_debug (("%s: %d: connect: %d (%s)\n", __func__, p->conn->fd, sockerr, evutil_socket_error_to_string (sockerr))); rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, - "%d: connect: %d (%s)", p->conn->fd, sockerr, + "%d: connect: %d (%s)", p->conn->base_.fd, sockerr, evutil_socket_error_to_string (sockerr)); } } @@ -220,7 +223,7 @@ event_do_connect (struct rs_connection *conn) int event_loopbreak (struct rs_connection *conn) { - int err = event_base_loopbreak (conn->evb); + int err = event_base_loopbreak (conn->base_.ctx->evb); if (err < 0) rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_base_loopbreak: %s", @@ -236,7 +239,7 @@ event_on_disconnect (struct rs_connection *conn) conn->is_connected = 0; rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer)); if (conn->callbacks.disconnected_cb) - conn->callbacks.disconnected_cb (conn->user_data); + conn->callbacks.disconnected_cb (conn->base_.user_data); } /** Internal connect event returning 0 on success or -1 on error. */ @@ -259,7 +262,7 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg) rs_debug (("%s: %p connected\n", __func__, conn->active_peer)); if (conn->callbacks.connected_cb) - conn->callbacks.connected_cb (conn->user_data); + conn->callbacks.connected_cb (conn->base_.user_data); if (msg) message_do_send (msg); @@ -271,7 +274,8 @@ int event_init_eventbase (struct rs_connection *conn) { assert (conn); - if (conn->evb) + assert (conn->base_.ctx); + if (conn->base_.ctx->evb) return RSE_OK; #if defined (DEBUG) @@ -279,8 +283,8 @@ event_init_eventbase (struct rs_connection *conn) event_enable_debug_mode (); #endif event_set_log_callback (_evlog_cb); - conn->evb = event_base_new (); - if (!conn->evb) + conn->base_.ctx->evb = event_base_new (); + if (!conn->base_.ctx->evb) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_base_new"); diff --git a/lib/examples/server.c b/lib/examples/server.c index 88c2bf4..8c304a0 100644 --- a/lib/examples/server.c +++ b/lib/examples/server.c @@ -1,5 +1,8 @@ /* RADIUS/RadSec server using libradsec. */ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + #include #include #include @@ -15,27 +18,33 @@ #define USER_NAME "molgan@PROJECT-MOONSHOT.ORG" #define USER_PW "password" +void +new_conn_cb (struct rs_connection *conn, void *user_data) +{ + printf ("new connection: fd=%d\n", -1); /* conn->fd */ +} struct rs_error * server (struct rs_context *ctx) { + int r = 0; struct rs_error *err = NULL; struct rs_connection *conn = NULL; -#if 0 struct rs_listener *listener = NULL; + const struct rs_listener_callbacks cbs = {}; if (rs_listener_create (ctx, &listener, CONFIG)) goto out; + rs_listener_set_callbacks (listener, &cbs); - while (1) + do { - if (rs_listener_dispatch (listener)) - goto out; + r = rs_listener_dispatch (listener); + printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r); } + while (r == 0); out: -#endif - err = rs_err_ctx_pop (ctx); if (err == NULL) err = rs_err_conn_pop (conn); diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h index 32753ef..fecf8f2 100644 --- a/lib/include/radsec/radsec-impl.h +++ b/lib/include/radsec/radsec-impl.h @@ -1,17 +1,19 @@ /** @file libradsec-impl.h @brief Libraray internal header file for libradsec. */ -/* Copyright 2010, 2011, 2013 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #ifndef _RADSEC_RADSEC_IMPL_H_ #define _RADSEC_RADSEC_IMPL_H_ 1 +#include #include #include #if defined(RS_ENABLE_TLS) #include #endif +#include "compat.h" /* Constants. */ #define RS_HEADER_LEN 4 @@ -86,33 +88,49 @@ struct rs_config { cfg_t *cfg; }; +/** Libradsec context. */ struct rs_context { struct rs_config *config; struct rs_alloc_scheme alloc_scheme; struct rs_error *err; + struct event_base *evb; /* Event base. */ }; -struct rs_connection { +enum rs_conn_subtype { + RS_CONN_OBJTYPE_BASE = 1, + RS_CONN_OBJTYPE_GENERIC, + RS_CONN_OBJTYPE_LISTENER, +}; +#define RS_CONN_MAGIC_BASE 0xAE004711u +#define RS_CONN_MAGIC_GENERIC 0x843AEF47u +#define RS_CONN_MAGIC_LISTENER 0xDCB04783u + +/** Base class for a connection. */ +struct rs_conn_base { + uint32_t magic; /* Must be one of RS_CONN_MAGIC_*. */ struct rs_context *ctx; struct rs_realm *realm; /* Owned by ctx. */ - struct event_base *evb; /* Event base. */ - struct event *tev; /* Timeout event. */ - struct rs_conn_callbacks callbacks; + struct rs_peer *peers; /*< Configured peers. */ + struct timeval timeout; + int tryagain; /* For server failover. */ void *user_data; - struct rs_peer *peers; - struct rs_peer *active_peer; struct rs_error *err; - struct timeval timeout; - char is_connecting; /* FIXME: replace with a single state member */ - char is_connected; /* FIXME: replace with a single state member */ int fd; /* Socket. */ - int tryagain; /* For server failover. */ - int nextid; /* Next RADIUS packet identifier. */ /* TCP transport specifics. */ struct bufferevent *bev; /* Buffer event. */ /* UDP transport specifics. */ struct event *wev; /* Write event (for UDP). */ struct event *rev; /* Read event (for UDP). */ +}; + +/** A "generic" connection. */ +struct rs_connection { + struct rs_conn_base base_; + struct event *tev; /* Timeout event. */ + struct rs_conn_callbacks callbacks; + struct rs_peer *active_peer; + char is_connecting; /* FIXME: replace with a single state member */ + char is_connected; /* FIXME: replace with a single state member */ struct rs_message *out_queue; /* Queue for outgoing UDP packets. */ #if defined(RS_ENABLE_TLS) /* TLS specifics. */ @@ -121,6 +139,14 @@ struct rs_connection { #endif }; +/** A listening connection. Spawns generic connections when peers + * connect to it. */ +struct rs_listener { + struct rs_conn_base base_; + struct evconnlistener *evlistener; + struct rs_listener_callbacks callbacks; +}; + enum rs_message_flags { RS_MESSAGE_HEADER_READ, RS_MESSAGE_RECEIVED, @@ -141,7 +167,10 @@ struct rs_message { } #endif +/************************/ /* Convenience macros. */ + +/* Memory allocation. */ #define rs_calloc(h, nmemb, size) ((h)->alloc_scheme.calloc != NULL \ ? (h)->alloc_scheme.calloc : calloc)((nmemb), (size)) #define rs_malloc(h, size) ((h)->alloc_scheme.malloc != NULL \ @@ -153,6 +182,29 @@ struct rs_message { #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) +/* Basic CPP-based classes, proudly borrowed from Tor. */ +#if defined(__GNUC__) && __GNUC__ > 3 + #define STRUCT_OFFSET(tp, member) __builtin_offsetof(tp, member) +#else + #define STRUCT_OFFSET(tp, member) \ + ((off_t) (((char*)&((tp*)0)->member)-(char*)0)) +#endif +#define SUBTYPE_P(p, subtype, basemember) \ + ((void*) (((char*)(p)) - STRUCT_OFFSET(subtype, basemember))) +#define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, base_)) +static struct rs_connection *TO_GENERIC_CONN (struct rs_conn_base *); +static struct rs_listener *TO_LISTENER_CONN (struct rs_conn_base *); +static INLINE struct rs_connection *TO_GENERIC_CONN (struct rs_conn_base *b) +{ + assert (b->magic == RS_CONN_MAGIC_GENERIC); + return DOWNCAST (struct rs_connection, b); +} +static INLINE struct rs_listener *TO_LISTENER_CONN (struct rs_conn_base *b) +{ + assert (b->magic == RS_CONN_MAGIC_LISTENER); + return DOWNCAST (struct rs_listener, b); +} + #endif /* _RADSEC_RADSEC_IMPL_H_ */ /* Local Variables: */ diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h index 0a43f6f..021f677 100644 --- a/lib/include/radsec/radsec.h +++ b/lib/include/radsec/radsec.h @@ -1,7 +1,8 @@ /** \file radsec.h \brief Public interface for libradsec. */ -/* See LICENSE for licensing information. */ +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ #ifndef _RADSEC_RADSEC_H_ #define _RADSEC_RADSEC_H_ 1 @@ -136,6 +137,7 @@ extern "C" { /* Data types. */ struct rs_context; /* radsec-impl.h */ struct rs_connection; /* radsec-impl.h */ +struct rs_listener; /* radsec-impl.h */ struct rs_message; /* radsec-impl.h */ struct rs_conn; /* radsec-impl.h */ struct rs_error; /* radsec-impl.h */ @@ -161,7 +163,7 @@ typedef void (*rs_conn_message_received_cb) (struct rs_message *message, void *user_data); typedef void (*rs_conn_message_sent_cb) (void *user_data); struct rs_conn_callbacks { - /** Callback invoked when the connection has been established. */ + /** Callback invoked when an outgoing connection has been established. */ rs_conn_connected_cb connected_cb; /** Callback invoked when the connection has been torn down. */ rs_conn_disconnected_cb disconnected_cb; @@ -171,6 +173,12 @@ struct rs_conn_callbacks { rs_conn_message_sent_cb sent_cb; }; +typedef void (*rs_listener_new_conn_cb) (struct rs_connection *conn, + void *user_data); +struct rs_listener_callbacks { + rs_listener_new_conn_cb new_conn_cb; +}; + typedef struct value_pair rs_avp; typedef const struct value_pair rs_const_avp; @@ -210,6 +218,15 @@ int rs_context_read_config(struct rs_context *ctx, const char *config_file); int rs_context_print_config (struct rs_context *ctx, char **buf_out); +/*************/ +/* Listener. */ +/*************/ +int rs_listener_create (struct rs_context *ctx, + struct rs_listener **listener, + const char *config); +void rs_listener_set_callbacks (struct rs_listener *listener, + const struct rs_listener_callbacks *cb); +int rs_listener_dispatch (const struct rs_listener *listener); /****************/ /* Connection. */ @@ -319,7 +336,7 @@ void rs_peer_set_timeout(struct rs_peer *peer, int timeout); void rs_peer_set_retries(struct rs_peer *peer, int retries); /************/ -/* Message. */ +/* Message. */ /************/ /** Create a message associated with connection \a conn. */ int rs_message_create(struct rs_connection *conn, struct rs_message **pkt_out); diff --git a/lib/include/radsec/request-impl.h b/lib/include/radsec/request-impl.h index 9af1394..dbb4244 100644 --- a/lib/include/radsec/request-impl.h +++ b/lib/include/radsec/request-impl.h @@ -1,4 +1,5 @@ -/* See LICENSE for licensing information. */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ #ifndef _RADSEC_REQUEST_IMPL_H_ #define _RADSEC_REQUEST_IMPL_H_ 1 diff --git a/lib/include/radsec/request.h b/lib/include/radsec/request.h index 05b5daa..b78411a 100644 --- a/lib/include/radsec/request.h +++ b/lib/include/radsec/request.h @@ -1,7 +1,8 @@ /** \file request.h \brief Public interface for libradsec request's. */ -/* See LICENSE for licensing information. */ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ #ifndef _RADSEC_REQUEST_H_ #define _RADSEC_REQUEST_H_ 1 diff --git a/lib/message.c b/lib/message.c index 7ded6e7..e010f94 100644 --- a/lib/message.c +++ b/lib/message.c @@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -102,9 +102,9 @@ message_do_send (struct rs_message *msg) #endif /* Put message in output buffer. */ - if (msg->conn->bev) /* TCP. */ + if (msg->conn->base_.bev) /* TCP. */ { - int err = bufferevent_write (msg->conn->bev, msg->rpkt->data, + int err = bufferevent_write (msg->conn->base_.bev, msg->rpkt->data, msg->rpkt->length); if (err < 0) return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, @@ -133,7 +133,7 @@ rs_message_create (struct rs_connection *conn, struct rs_message **msg_out) *msg_out = NULL; - rpkt = rs_malloc (conn->ctx, sizeof(*rpkt) + RS_MAX_PACKET_LEN); + rpkt = rs_malloc (conn->base_.ctx, sizeof(*rpkt) + RS_MAX_PACKET_LEN); if (rpkt == NULL) return rs_err_conn_push (conn, RSE_NOMEM, __func__); @@ -143,10 +143,10 @@ rs_message_create (struct rs_connection *conn, struct rs_message **msg_out) if (err < 0) return rs_err_conn_push (conn, -err, __func__); - p = (struct rs_message *) rs_calloc (conn->ctx, 1, sizeof (*p)); + p = (struct rs_message *) rs_calloc (conn->base_.ctx, 1, sizeof (*p)); if (p == NULL) { - rs_free (conn->ctx, rpkt); + rs_free (conn->base_.ctx, rpkt); return rs_err_conn_push (conn, RSE_NOMEM, __func__); } p->conn = conn; @@ -195,11 +195,11 @@ rs_message_destroy (struct rs_message *msg) { assert (msg); assert (msg->conn); - assert (msg->conn->ctx); + assert (msg->conn->base_.ctx); rs_avp_free (&msg->rpkt->vps); - rs_free (msg->conn->ctx, msg->rpkt); - rs_free (msg->conn->ctx, msg); + rs_free (msg->conn->base_.ctx, msg->rpkt); + rs_free (msg->conn->base_.ctx, msg); } int diff --git a/lib/peer.c b/lib/peer.c index 0ac4114..3e0069b 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -23,7 +23,7 @@ peer_pick_peer (struct rs_connection *conn) if (conn->active_peer) conn->active_peer = conn->active_peer->next; /* Next. */ if (!conn->active_peer) - conn->active_peer = conn->peers; /* From the top. */ + conn->active_peer = conn->base_.peers; /* From the top. */ return conn->active_peer; } @@ -54,7 +54,7 @@ rs_peer_create (struct rs_connection *conn, struct rs_peer **peer_out) { struct rs_peer *peer; - peer = peer_create (conn->ctx, &conn->peers); + peer = peer_create (conn->base_.ctx, &conn->base_.peers); if (peer) { peer->conn = conn; @@ -74,10 +74,10 @@ rs_peer_set_address (struct rs_peer *peer, const char *hostname, { assert (peer); assert (peer->conn); - assert (peer->conn->ctx); + assert (peer->conn->base_.ctx); - peer->hostname = rs_strdup (peer->conn->ctx, hostname); - peer->service = rs_strdup (peer->conn->ctx, service); + peer->hostname = rs_strdup (peer->conn->base_.ctx, hostname); + peer->service = rs_strdup (peer->conn->base_.ctx, service); if (peer->hostname == NULL || peer->service == NULL) return RSE_NOMEM; diff --git a/lib/radsec.c b/lib/radsec.c index e6b79cf..48fe221 100644 --- a/lib/radsec.c +++ b/lib/radsec.c @@ -127,6 +127,9 @@ rs_context_destroy (struct rs_context *ctx) rs_free (ctx, ctx->config); } + if (ctx->evb) + event_base_free (ctx->evb); + free (ctx); } diff --git a/lib/radsec.sym b/lib/radsec.sym index 6378cfd..7e64560 100644 --- a/lib/radsec.sym +++ b/lib/radsec.sym @@ -67,6 +67,9 @@ rs_err_ctx_push rs_err_ctx_push_fl rs_err_free rs_err_msg +rs_listener_create +rs_listener_dispatch +rs_listener_set_callbacks rs_message_append_avp rs_message_avps rs_message_code diff --git a/lib/request.c b/lib/request.c index 8f3635d..17b36de 100644 --- a/lib/request.c +++ b/lib/request.c @@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -30,7 +30,7 @@ int rs_request_create (struct rs_connection *conn, struct rs_request **req_out) { - struct rs_request *req = rs_malloc (conn->ctx, sizeof(*req)); + struct rs_request *req = rs_malloc (conn->base_.ctx, sizeof(*req)); assert (req_out); if (!req) return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); @@ -73,11 +73,11 @@ rs_request_destroy (struct rs_request *request) { assert (request); assert (request->conn); - assert (request->conn->ctx); + assert (request->conn->base_.ctx); if (request->req_msg) rs_message_destroy (request->req_msg); - rs_free (request->conn->ctx, request); + rs_free (request->conn->base_.ctx, request); } static void diff --git a/lib/send.c b/lib/send.c index 1234fdd..6b76958 100644 --- a/lib/send.c +++ b/lib/send.c @@ -1,4 +1,4 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -32,8 +32,8 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg) if (event_init_socket (conn, conn->active_peer)) return -1; - if (conn->realm->type == RS_CONN_TYPE_TCP - || conn->realm->type == RS_CONN_TYPE_TLS) + if (conn->base_.realm->type == RS_CONN_TYPE_TCP + || conn->base_.realm->type == RS_CONN_TYPE_TLS) { if (tcp_init_connect_timer (conn)) return -1; @@ -68,10 +68,10 @@ _wcb (void *user_data) struct rs_message *msg = (struct rs_message *) user_data; assert (msg); msg->flags |= RS_MESSAGE_SENT; - if (msg->conn->bev) - bufferevent_disable (msg->conn->bev, EV_WRITE|EV_READ); + if (msg->conn->base_.bev) + bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ); else - event_del (msg->conn->wev); + event_del (msg->conn->base_.wev); } int @@ -90,22 +90,24 @@ rs_message_send (struct rs_message *msg, void *user_data) if (_conn_open (conn, msg)) return -1; - assert (conn->evb); + assert (conn->base_.ctx); + assert (conn->base_.ctx->evb); assert (conn->active_peer); - assert (conn->fd >= 0); + assert (conn->base_.fd >= 0); - conn->user_data = user_data; + conn->base_.user_data = user_data; - if (conn->bev) /* TCP */ + if (conn->base_.bev) /* TCP */ { - bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, msg); - bufferevent_enable (conn->bev, EV_WRITE); + bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb, msg); + bufferevent_enable (conn->base_.bev, EV_WRITE); } else /* UDP */ { - event_assign (conn->wev, conn->evb, event_get_fd (conn->wev), - EV_WRITE, event_get_callback (conn->wev), msg); - err = event_add (conn->wev, NULL); + event_assign (conn->base_.wev, conn->base_.ctx->evb, + event_get_fd (conn->base_.wev), + EV_WRITE, event_get_callback (conn->base_.wev), msg); + err = event_add (conn->base_.wev, NULL); if (err < 0) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_add: %s", @@ -116,16 +118,16 @@ rs_message_send (struct rs_message *msg, void *user_data) if (!conn_user_dispatch_p (conn)) { conn->callbacks.sent_cb = _wcb; - conn->user_data = msg; + conn->base_.user_data = msg; rs_debug (("%s: entering event loop\n", __func__)); - err = event_base_dispatch (conn->evb); + err = event_base_dispatch (conn->base_.ctx->evb); if (err < 0) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_base_dispatch: %s", evutil_gai_strerror (err)); rs_debug (("%s: event loop done\n", __func__)); conn->callbacks.sent_cb = NULL; - conn->user_data = NULL; + conn->base_.user_data = NULL; if ((msg->flags & RS_MESSAGE_SENT) == 0) { diff --git a/lib/tcp.c b/lib/tcp.c index 1b07136..b8d7906 100644 --- a/lib/tcp.c +++ b/lib/tcp.c @@ -1,4 +1,4 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -31,7 +31,7 @@ _read_header (struct rs_message *msg) { size_t n = 0; - n = bufferevent_read (msg->conn->bev, msg->hdr, RS_HEADER_LEN); + n = bufferevent_read (msg->conn->base_.bev, msg->hdr, RS_HEADER_LEN); if (n == RS_HEADER_LEN) { msg->flags |= RS_MESSAGE_HEADER_READ; @@ -44,7 +44,7 @@ _read_header (struct rs_message *msg) msg->rpkt->length); } memcpy (msg->rpkt->data, msg->hdr, RS_HEADER_LEN); - bufferevent_setwatermark (msg->conn->bev, EV_READ, + bufferevent_setwatermark (msg->conn->base_.bev, EV_READ, msg->rpkt->length - RS_HEADER_LEN, 0); rs_debug (("%s: message header read, total msg len=%d\n", __func__, msg->rpkt->length)); @@ -79,7 +79,7 @@ _read_message (struct rs_message *msg) rs_debug (("%s: trying to read %d octets of message data\n", __func__, msg->rpkt->length - RS_HEADER_LEN)); - n = bufferevent_read (msg->conn->bev, + n = bufferevent_read (msg->conn->base_.bev, msg->rpkt->data + RS_HEADER_LEN, msg->rpkt->length - RS_HEADER_LEN); @@ -87,7 +87,7 @@ _read_message (struct rs_message *msg) if (n == msg->rpkt->length - RS_HEADER_LEN) { - bufferevent_disable (msg->conn->bev, EV_READ); + bufferevent_disable (msg->conn->base_.bev, EV_READ); rs_debug (("%s: complete message read\n", __func__)); msg->flags &= ~RS_MESSAGE_HEADER_READ; memset (msg->hdr, 0, sizeof(*msg->hdr)); @@ -119,7 +119,7 @@ _read_message (struct rs_message *msg) /* Hand over message to user. This changes ownership of msg. Don't touch it afterwards -- it might have been freed. */ if (msg->conn->callbacks.received_cb) - msg->conn->callbacks.received_cb (msg, msg->conn->user_data); + msg->conn->callbacks.received_cb (msg, msg->conn->base_.user_data); } else if (n < 0) /* Buffer frozen. */ rs_debug (("%s: buffer frozen when reading message\n", __func__)); @@ -146,7 +146,7 @@ tcp_read_cb (struct bufferevent *bev, void *user_data) assert (msg->conn); assert (msg->rpkt); - msg->rpkt->sockfd = msg->conn->fd; + msg->rpkt->sockfd = msg->conn->base_.fd; msg->rpkt->vps = NULL; /* FIXME: can this be done when initializing msg? */ /* Read a message header if not already read, return if that @@ -216,15 +216,15 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data) rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr, evutil_socket_error_to_string (sockerr))); rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, - "%d: %d (%s)", conn->fd, sockerr, + "%d: %d (%s)", conn->base_.fd, sockerr, evutil_socket_error_to_string (sockerr)); } #if defined (RS_ENABLE_TLS) if (conn->tls_ssl) /* FIXME: correct check? */ { - for (tlserr = bufferevent_get_openssl_error (conn->bev); + for (tlserr = bufferevent_get_openssl_error (conn->base_.bev); tlserr; - tlserr = bufferevent_get_openssl_error (conn->bev)) + tlserr = bufferevent_get_openssl_error (conn->base_.bev)) { rs_debug (("%s: openssl error: %s\n", __func__, ERR_error_string (tlserr, NULL))); @@ -251,17 +251,18 @@ tcp_write_cb (struct bufferevent *bev, void *ctx) assert (msg->conn); if (msg->conn->callbacks.sent_cb) - msg->conn->callbacks.sent_cb (msg->conn->user_data); + msg->conn->callbacks.sent_cb (msg->conn->base_.user_data); } int tcp_init_connect_timer (struct rs_connection *conn) { assert (conn); + assert (conn->base_.ctx); if (conn->tev) event_free (conn->tev); - conn->tev = evtimer_new (conn->evb, event_conn_timeout_cb, conn); + conn->tev = evtimer_new (conn->base_.ctx->evb, event_conn_timeout_cb, conn); if (!conn->tev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "evtimer_new"); diff --git a/lib/tls.c b/lib/tls.c index 29e7264..979ee3c 100644 --- a/lib/tls.c +++ b/lib/tls.c @@ -1,4 +1,4 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -20,7 +20,7 @@ static struct tls * _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm) { - struct tls *c = rs_malloc (conn->ctx, sizeof (struct tls)); + struct tls *c = rs_malloc (conn->base_.ctx, sizeof (struct tls)); if (c) { @@ -123,8 +123,8 @@ rs_tls_init (struct rs_connection *conn) SSL *ssl = NULL; unsigned long sslerr = 0; - assert (conn->ctx); - ctx = conn->ctx; + assert (conn->base_.ctx); + ctx = conn->base_.ctx; tlsconf = _get_tlsconf (conn, conn->active_peer->realm); if (!tlsconf) diff --git a/lib/udp.c b/lib/udp.c index f598804..71d7003 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -1,4 +1,4 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -45,7 +45,7 @@ _send (struct rs_connection *conn, int fd) /* If there are more messages in queue, add the write event again. */ if (msg->conn->out_queue) { - r = event_add (msg->conn->wev, NULL); + r = event_add (msg->conn->base_.wev, NULL); if (r < 0) return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, "event_add: %s", evutil_gai_strerror (r)); @@ -121,7 +121,7 @@ _evcb (evutil_socket_t fd, short what, void *user_data) /* Hand over message to user. This changes ownership of msg. Don't touch it afterwards -- it might have been freed. */ if (msg->conn->callbacks.received_cb) - msg->conn->callbacks.received_cb (msg, msg->conn->user_data); + msg->conn->callbacks.received_cb (msg, msg->conn->base_.user_data); } else if (what & EV_WRITE) { @@ -135,7 +135,7 @@ _evcb (evutil_socket_t fd, short what, void *user_data) if (msg->conn->out_queue) if (_send (msg->conn, fd) == RSE_OK) if (msg->conn->callbacks.sent_cb) - msg->conn->callbacks.sent_cb (msg->conn->user_data); + msg->conn->callbacks.sent_cb (msg->conn->base_.user_data); } #if defined (DEBUG) @@ -147,16 +147,18 @@ _evcb (evutil_socket_t fd, short what, void *user_data) int udp_init (struct rs_connection *conn, struct rs_message *msg) { - assert (!conn->bev); + assert (!conn->base_.bev); - conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, NULL); - conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, NULL); - if (!conn->rev || !conn->wev) + conn->base_.rev = event_new (conn->base_.ctx->evb, conn->base_.fd, + EV_READ|EV_PERSIST, _evcb, NULL); + conn->base_.wev = event_new (conn->base_.ctx->evb, conn->base_.fd, + EV_WRITE, _evcb, NULL); + if (!conn->base_.rev || !conn->base_.wev) { - if (conn->rev) + if (conn->base_.rev) { - event_free (conn->rev); - conn->rev = NULL; + event_free (conn->base_.rev); + conn->base_.rev = NULL; } /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at least for now (libevent-2.0.5). */ @@ -169,10 +171,13 @@ int udp_init_retransmit_timer (struct rs_connection *conn) { assert (conn); + assert (conn->base_.ctx); + assert (conn->base_.ctx->evb); if (conn->tev) event_free (conn->tev); - conn->tev = evtimer_new (conn->evb, event_retransmit_timeout_cb, conn); + conn->tev = + evtimer_new (conn->base_.ctx->evb, event_retransmit_timeout_cb, conn); if (!conn->tev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "evtimer_new");