From f3252b34114236c65624862d2a572f5e290e8cc8 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Fri, 1 Mar 2013 14:34:21 +0100 Subject: [PATCH] Revamping for listeners. Split rs_connection into rs_baseconn plus rs_connection and rs_listener. Connections now has a state variable. Connect buffer event and fd of _source_ connection, not that of conn->active_peer. The connection object referred to by a peer is not meant for using as a connection, only for reporting errors on. Make sure things are sane even when not using a config file. Bump library interface version to 1.0.0 since it's changed. --- lib/Makefile.am | 39 +++++++++------ lib/conn.c | 38 +++++++-------- lib/err.c | 50 ++++++++++++++++---- lib/err.h | 7 +-- lib/event.c | 76 ++++++++++++++++------------- lib/examples/client-blocking.c | 40 +++++++++++++--- lib/include/radsec/radsec-impl.h | 100 +++++++++++++++++++++++---------------- lib/include/radsec/radsec.h | 24 ++++++++-- lib/message.c | 24 +++++----- lib/peer.c | 98 +++++++++++++++++++++++++++----------- lib/peer.h | 1 + lib/radsec.sym | 5 +- lib/send.c | 19 ++++---- lib/tcp.c | 10 ++-- lib/tls.c | 20 ++++---- lib/udp.c | 2 +- lib/util.c | 9 ++-- 17 files changed, 360 insertions(+), 202 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 06ea8d5..6d62c97 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -3,19 +3,22 @@ ACLOCAL_AMFLAGS = -I m4 # Shared library interface version, i.e. -version-info to Libtool, # expressed as three integers CURRENT:REVISION:AGE. - -# CURRENT is the version number of the current interface. Increment -# CURRENT when the library interface changes. - +# +# CURRENT is the version number of the current interface. +# Increment CURRENT when the library interface changes. +# # REVISION is the version number of the _implementation_ of the -# CURRENT interface. Set REVISION to 0 when CURRENT changes, -# else increment. - +# CURRENT interface. +# Set REVISION to 0 when CURRENT changes, else increment. +# # AGE is the number of interfaces this library implements, i.e. how -# many versions before CURRENT that are supported. Increment AGE -# when the library interface is _extended_. Set AGE to 0 when the -# library interface is _changed_. +# many versions before CURRENT that are supported. +# Increment AGE when the library interface is _extended_. +# Set AGE to 0 when the library interface is _changed_. +VER_CUR = 1 +VER_REV = 0 +VER_AGE = 0 SUBDIRS = radius radsecproxy . include examples @@ -44,13 +47,19 @@ libradsec_la_SOURCES = \ util.c if RS_ENABLE_TLS -libradsec_la_SOURCES += tls.c + libradsec_la_SOURCES += tls.c else -libradsec_la_SOURCES += md5.c + libradsec_la_SOURCES += md5.c endif EXTRA_DIST = HACKING LICENSE -libradsec_la_LIBADD = radsecproxy/libradsec-radsecproxy.la radius/libradsec-radius.la -libradsec_la_LDFLAGS = -version-info 0:0:0 -export-symbols radsec.sym -libradsec_la_CFLAGS = $(AM_CFLAGS) -DHAVE_CONFIG_H -Werror # -DDEBUG -DDEBUG_LEVENT +libradsec_la_CFLAGS = \ + $(AM_CFLAGS) -DHAVE_CONFIG_H \ + -DDEBUG -DDEBUG_LEVENT +libradsec_la_LDFLAGS = \ + -version-info $(VER_CUR):$(VER_REV):$(VER_AGE) \ + -export-symbols radsec.sym +libradsec_la_LIBADD = \ + radsecproxy/libradsec-radsecproxy.la \ + radius/libradsec-radius.la diff --git a/lib/conn.c b/lib/conn.c index d8c1569..95e65a4 100644 --- a/lib/conn.c +++ b/lib/conn.c @@ -1,5 +1,5 @@ /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H #include @@ -25,7 +25,7 @@ conn_close (struct rs_connection **connp) int r = 0; assert (connp); assert (*connp); - if ((*connp)->is_connected) + if ((*connp)->state == RS_CONN_STATE_CONNECTED) r = rs_conn_disconnect (*connp); if (r == RSE_OK) *connp = NULL; @@ -53,7 +53,7 @@ conn_activate_timeout (struct rs_connection *conn) 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)); + conn->base_.timeout.tv_sec, conn->base_.timeout.tv_usec)); if (evtimer_add (conn->tev, &conn->base_.timeout)) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "evtimer_add: %d", errno); @@ -64,7 +64,7 @@ conn_activate_timeout (struct rs_connection *conn) int conn_type_tls (const struct rs_connection *conn) { - assert (conn->active_peer); + assert (conn->base_.active_peer); return conn->base_.realm->type == RS_CONN_TYPE_TLS || conn->base_.realm->type == RS_CONN_TYPE_DTLS; } @@ -72,9 +72,9 @@ conn_type_tls (const struct rs_connection *conn) int conn_cred_psk (const struct rs_connection *conn) { - assert (conn->active_peer); - return conn->active_peer->transport_cred && - conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK; + assert (conn->base_.active_peer); + return conn->base_.active_peer->transport_cred && + conn->base_.active_peer->transport_cred->type == RS_CRED_TLS_PSK; } void @@ -111,23 +111,21 @@ conn_configure (struct rs_context *ctx, struct rs_realm *r = rs_conf_find_realm (ctx, config); if (r) { - struct rs_peer *p; - connbase->realm = r; connbase->peers = r->peers; /* FIXME: Copy instead? */ +#if 0 for (p = connbase->peers; p != NULL; p = p->next) - p->conn = TO_GENERIC_CONN(connbase); + p->connbase = connbase; +#endif connbase->timeout.tv_sec = r->timeout; connbase->tryagain = r->retries; } - else - { - 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 (connbase->realm, 0, sizeof (struct rs_realm)); - } + } + if (connbase->realm == NULL) + { + connbase->realm = rs_calloc (ctx, 1, sizeof (struct rs_realm)); + if (connbase->realm == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); } return RSE_OK; } @@ -200,7 +198,7 @@ rs_conn_destroy (struct rs_connection *conn) /* NOTE: conn->realm is owned by context. */ /* NOTE: conn->peers is owned by context. */ - if (conn->is_connected) + if (conn->state == RS_CONN_STATE_CONNECTED) err = rs_conn_disconnect (conn); #if defined (RS_ENABLE_TLS) @@ -284,7 +282,7 @@ struct event_base int rs_conn_get_fd (struct rs_connection *conn) { assert (conn); - assert (conn->active_peer); + assert (conn->base_.active_peer); return conn->base_.fd; } diff --git a/lib/err.c b/lib/err.c index e0bcea9..413ab3e 100644 --- a/lib/err.c +++ b/lib/err.c @@ -1,5 +1,5 @@ /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H #include @@ -156,26 +156,41 @@ rs_err_ctx_push_fl (struct rs_context *ctx, int code, const char *file, } int -err_conn_push_err (struct rs_connection *conn, struct rs_error *err) +err_connbase_push_err (struct rs_conn_base *connbase, struct rs_error *err) { - if (conn->base_.err) - rs_err_free (conn->base_.err); - conn->base_.err = err; /* FIXME: use a stack */ + if (connbase->err) + rs_err_free (connbase->err); + connbase->err = err; /* FIXME: use a stack */ return err->code; } static int -_conn_err_vpush_fl (struct rs_connection *conn, int code, const char *file, - int line, const char *fmt, va_list args) +_connbase_err_vpush_fl (struct rs_conn_base *connbase, int code, + const char *file, int line, const char *fmt, + va_list args) { struct rs_error *err = _err_vcreate (code, file, line, fmt, args); if (!err) return RSE_NOMEM; - return err_conn_push_err (conn, err); + return err_connbase_push_err (connbase, err); +} + +int +rs_err_connbase_push (struct rs_conn_base *connbase, int code, + const char *fmt, ...) +{ + int r = 0; + + va_list args; + va_start (args, fmt); + r = _connbase_err_vpush_fl (connbase, code, NULL, 0, fmt, args); + va_end (args); + + return r; } int @@ -185,7 +200,22 @@ rs_err_conn_push (struct rs_connection *conn, int code, const char *fmt, ...) va_list args; va_start (args, fmt); - r = _conn_err_vpush_fl (conn, code, NULL, 0, fmt, args); + r = _connbase_err_vpush_fl (TO_BASE_CONN (conn), code, NULL, 0, fmt, args); + va_end (args); + + return r; +} + +int +rs_err_connbase_push_fl (struct rs_conn_base *connbase, int code, + const char *file, + int line, const char *fmt, ...) +{ + int r = 0; + + va_list args; + va_start (args, fmt); + r = _connbase_err_vpush_fl (connbase, code, file, line, fmt, args); va_end (args); return r; @@ -199,7 +229,7 @@ rs_err_conn_push_fl (struct rs_connection *conn, int code, const char *file, va_list args; va_start (args, fmt); - r = _conn_err_vpush_fl (conn, code, file, line, fmt, args); + r = _connbase_err_vpush_fl (TO_BASE_CONN (conn), code, file, line, fmt, args); va_end (args); return r; diff --git a/lib/err.h b/lib/err.h index 6615ac8..de5851f 100644 --- a/lib/err.h +++ b/lib/err.h @@ -1,9 +1,10 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ struct rs_error *err_create (unsigned int code, const char *file, int line, const char *fmt, ...); -int err_conn_push_err (struct rs_connection *conn, struct rs_error *err); +int err_connbase_push_err (struct rs_conn_base *, struct rs_error *); +int rs_err_connbase_push (struct rs_conn_base *, int, const char *, ...); diff --git a/lib/event.c b/lib/event.c index f7b936a..c651e4b 100644 --- a/lib/event.c +++ b/lib/event.c @@ -1,5 +1,5 @@ /* Copyright 2011,2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H #include @@ -72,8 +72,8 @@ event_conn_timeout_cb (int fd, short event, void *data) if (event & EV_TIMEOUT) { rs_debug (("%s: connection timeout on %p (fd %d) connecting to %p\n", - __func__, conn, conn->fd, conn->active_peer)); - conn->is_connecting = 0; + __func__, conn, conn->base_.fd, conn->base_.active_peer)); + conn->state = RS_CONN_STATE_UNDEFINED; rs_err_conn_push_fl (conn, RSE_TIMEOUT_CONN, __FILE__, __LINE__, NULL); event_loopbreak (conn); } @@ -90,7 +90,7 @@ event_retransmit_timeout_cb (int fd, short event, void *data) if (event & EV_TIMEOUT) { rs_debug (("%s: retransmission timeout on %p (fd %d) sending to %p\n", - __func__, conn, conn->fd, conn->active_peer)); + __func__, conn, conn->base_.fd, conn->base_.active_peer)); rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); event_loopbreak (conn); } @@ -107,7 +107,7 @@ event_init_socket (struct rs_connection *conn, struct rs_peer *p) struct rs_error *err = rs_resolve (&p->addr_cache, p->realm->type, p->hostname, p->service); if (err != NULL) - return err_conn_push_err (conn, err); + return err_connbase_push_err (TO_BASE_CONN (conn), err); } conn->base_.fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype, @@ -171,59 +171,67 @@ event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) void event_do_connect (struct rs_connection *conn) { - struct rs_peer *p; int err, sockerr; + struct sockaddr *peer_addr; + size_t peer_addrlen; assert (conn); - assert (conn->active_peer); - p = conn->active_peer; + assert (conn->base_.active_peer); + assert (conn->base_.active_peer->addr_cache); + peer_addr = conn->base_.active_peer->addr_cache->ai_addr; + peer_addrlen = conn->base_.active_peer->addr_cache->ai_addrlen; + + /* We don't connect listeners. */ + assert (conn->base_.magic == RS_CONN_MAGIC_GENERIC); #if defined (DEBUG) { char host[80], serv[80]; - getnameinfo (p->addr_cache->ai_addr, - p->addr_cache->ai_addrlen, - host, sizeof(host), serv, sizeof(serv), + getnameinfo (peer_addr, peer_addrlen, + host, sizeof(host), + serv, sizeof(serv), 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/); rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv)); } #endif - if (p->conn->base_.bev) /* TCP */ + if (conn->base_.bev) /* TCP */ { conn_activate_timeout (conn); /* Connect timeout. */ - err = bufferevent_socket_connect (p->conn->base_.bev, - p->addr_cache->ai_addr, - p->addr_cache->ai_addrlen); + err = bufferevent_socket_connect (conn->base_.bev, + peer_addr, peer_addrlen); if (err < 0) - rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_socket_connect: %s", - evutil_gai_strerror (err)); + rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, + "bufferevent_socket_connect: %s", + evutil_gai_strerror (err)); else - p->conn->is_connecting = 1; + conn->state = RS_CONN_STATE_CONNECTING; } else /* UDP */ { - err = connect (p->conn->base_.fd, - p->addr_cache->ai_addr, - p->addr_cache->ai_addrlen); + err = connect (conn->base_.fd, peer_addr, peer_addrlen); if (err < 0) { - sockerr = evutil_socket_geterror (p->conn->fd); - rs_debug (("%s: %d: connect: %d (%s)\n", __func__, p->conn->fd, + sockerr = evutil_socket_geterror (conn->base_.fd); + rs_debug (("%s: %d: connect: %d (%s)\n", __func__, + conn->base_.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->base_.fd, sockerr, + rs_err_conn_push_fl (conn, RSE_SOCKERR, + __FILE__, __LINE__, + "%d: connect: %d (%s)", conn->base_.fd, + sockerr, evutil_socket_error_to_string (sockerr)); } + else + conn->state = RS_CONN_STATE_CONNECTING; } } int event_loopbreak (struct rs_connection *conn) { - int err = event_base_loopbreak (conn->base_.ctx->evb); + int err = event_base_loopbreak (TO_BASE_CONN(conn)->ctx->evb); if (err < 0) rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "event_base_loopbreak: %s", @@ -235,9 +243,9 @@ event_loopbreak (struct rs_connection *conn) void event_on_disconnect (struct rs_connection *conn) { - conn->is_connecting = 0; - conn->is_connected = 0; - rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer)); + conn->state = RS_CONN_STATE_UNDEFINED; + rs_debug (("%s: %p disconnected\n", __func__, + TO_BASE_CONN(conn)->active_peer)); if (conn->callbacks.disconnected_cb) conn->callbacks.disconnected_cb (conn->base_.user_data); } @@ -246,8 +254,8 @@ event_on_disconnect (struct rs_connection *conn) int event_on_connect (struct rs_connection *conn, struct rs_message *msg) { - assert (!conn->is_connecting); - assert (conn->active_peer); + assert (conn->state == RS_CONN_STATE_CONNECTING); + assert (conn->base_.active_peer); #if defined (RS_ENABLE_TLS) if (conn_type_tls(conn) && !conn_cred_psk(conn)) @@ -258,8 +266,8 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg) } #endif /* RS_ENABLE_TLS */ - conn->is_connected = 1; - rs_debug (("%s: %p connected\n", __func__, conn->active_peer)); + conn->state = RS_CONN_STATE_CONNECTED; + rs_debug (("%s: %p connected\n", __func__, TO_BASE_CONN(conn)->active_peer)); if (conn->callbacks.connected_cb) conn->callbacks.connected_cb (conn->base_.user_data); diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c index 3ea4b51..d2ee9f4 100644 --- a/lib/examples/client-blocking.c +++ b/lib/examples/client-blocking.c @@ -1,7 +1,11 @@ /* RADIUS/RadSec client using libradsec in blocking mode. */ +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + #include #include +#include #include #include #include @@ -13,7 +17,7 @@ #define USER_PW "password" struct rs_error * -blocking_client (const char *config_fn, const char *configuration, +blocking_client (const char *av1, const char *av2, const char *av3, int use_request_object_flag) { struct rs_context *h = NULL; @@ -22,6 +26,15 @@ blocking_client (const char *config_fn, const char *configuration, struct rs_message *req = NULL, *resp = NULL; struct rs_error *err = NULL; int r; +#if defined (USE_CONFIG_FILE) + const char *config_fn= av1; + const char *configuration = av2; +#else + const char *host = av1; + const char *service = av2; + const char *proto = av3; + struct rs_peer *server; +#endif r = rs_context_create (&h); if (r) @@ -31,15 +44,25 @@ blocking_client (const char *config_fn, const char *configuration, } #if !defined (USE_CONFIG_FILE) + /* Do it without a configuration file by setting all stuff "by + hand". Doesn't work for TLS at the moment because we don't have an + API for setting the X509 cert file names and such. */ { - struct rs_peer *server; + int conn_type = RS_CONN_TYPE_UDP; if (rs_conn_create (h, &conn, NULL)) goto cleanup; - rs_conn_set_type (conn, RS_CONN_TYPE_UDP); - if (rs_peer_create (conn, &server)) + if (proto) + { + if (!strncmp (proto, "udp", strlen ("udp"))) + conn_type = RS_CONN_TYPE_UDP; + else if (!strncmp (proto, "tls", strlen ("tls"))) + conn_type = RS_CONN_TYPE_TLS; + } + rs_conn_set_type (conn, conn_type); + if (rs_peer_create_for_conn (conn, &server)) goto cleanup; - if (rs_peer_set_address (server, av1, av2)) + if (rs_peer_set_address (server, host, service)) goto cleanup; rs_peer_set_timeout (server, 1); rs_peer_set_retries (server, 3); @@ -85,6 +108,10 @@ blocking_client (const char *config_fn, const char *configuration, err = rs_err_ctx_pop (h); if (err == RSE_OK) err = rs_err_conn_pop (conn); +#if !defined (USE_CONFIG_FILE) + rs_peer_free_address (server); + rs_peer_free_secret (server); +#endif if (resp) rs_message_destroy (resp); if (request) @@ -118,7 +145,8 @@ main (int argc, char *argv[]) } if (argc < 3) usage (argc, argv); - err = blocking_client (argv[1], argv[2], use_request_object_flag); + err = blocking_client (argv[1], argv[2], argc >= 3 ? argv[3] : NULL, + use_request_object_flag); if (err) { fprintf (stderr, "error: %s: %d\n", rs_err_msg (err), rs_err_code (err, 0)); diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h index fecf8f2..45ce7f6 100644 --- a/lib/include/radsec/radsec-impl.h +++ b/lib/include/radsec/radsec-impl.h @@ -1,5 +1,5 @@ /** @file libradsec-impl.h - @brief Libraray internal header file for libradsec. */ + @brief Libraray internal header file for libradsec. */ /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ @@ -15,13 +15,18 @@ #endif #include "compat.h" -/* Constants. */ +/**************/ +/* Constants. */ #define RS_HEADER_LEN 4 +#define RS_CONN_MAGIC_BASE 0xAE004711u +#define RS_CONN_MAGIC_GENERIC 0x843AEF47u +#define RS_CONN_MAGIC_LISTENER 0xDCB04783u -/* Data types. */ +/***************/ +/* Data types. */ enum rs_cred_type { RS_CRED_NONE = 0, - /* TLS pre-shared keys, RFC 4279. */ + /* TLS pre-shared keys, RFC 4279. */ RS_CRED_TLS_PSK, /* RS_CRED_TLS_DH_PSK, */ /* RS_CRED_TLS_RSA_PSK, */ @@ -34,6 +39,17 @@ enum rs_key_encoding { }; typedef unsigned int rs_key_encoding_t; +enum rs_peer_type { + RS_PEER_TYPE_CLIENT = 1, + RS_PEER_TYPE_SERVER = 2 +}; + +enum rs_conn_subtype { + RS_CONN_OBJTYPE_BASE = 1, + RS_CONN_OBJTYPE_GENERIC, + RS_CONN_OBJTYPE_LISTENER, +}; + #if defined (__cplusplus) extern "C" { #endif @@ -50,19 +66,14 @@ struct rs_error { char buf[1024]; }; -enum rs_peer_type { - RS_PEER_TYPE_CLIENT = 1, - RS_PEER_TYPE_SERVER = 2 -}; - -/** Configuration object for a connection. */ +/** Configuration object for a connection. */ struct rs_peer { enum rs_peer_type type; - struct rs_connection *conn; + struct rs_conn_base *connbase; /**< For error handling. */ struct rs_realm *realm; char *hostname; char *service; - char *secret; /* RADIUS secret. */ + char *secret; /* RADIUS secret. */ struct evutil_addrinfo *addr_cache; char *cacertfile; char *cacertpath; @@ -72,17 +83,18 @@ struct rs_peer { struct rs_peer *next; }; -/** Configuration object for a RADIUS realm. */ +/** Configuration object for a RADIUS realm. */ struct rs_realm { char *name; enum rs_conn_type type; int timeout; int retries; + struct rs_listener *listeners; struct rs_peer *peers; struct rs_realm *next; }; -/** Top configuration object. */ +/** Top configuration object. */ struct rs_config { struct rs_realm *realms; cfg_t *cfg; @@ -93,47 +105,51 @@ struct rs_context { struct rs_config *config; struct rs_alloc_scheme alloc_scheme; struct rs_error *err; - struct event_base *evb; /* Event base. */ -}; - -enum rs_conn_subtype { - RS_CONN_OBJTYPE_BASE = 1, - RS_CONN_OBJTYPE_GENERIC, - RS_CONN_OBJTYPE_LISTENER, + struct event_base *evb; /* Event base. */ }; -#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 rs_peer *peers; /*< Configured peers. */ + struct rs_realm *realm; /* Owned by ctx. */ + /** For a listener, allowed client addr/port pairs. + For an outgoing connection, set of servers. + For an incoming connection, the peer (as the only entry). */ + struct rs_peer *peers; /**< Configured peers. */ + struct rs_peer *active_peer; /**< The other end of the connection. */ struct timeval timeout; - int tryagain; /* For server failover. */ + int tryagain; /* For server failover. */ void *user_data; struct rs_error *err; - int fd; /* Socket. */ - /* 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). */ + int fd; /* Socket. */ + /* 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). */ +}; + + +enum rs_conn_state { + RS_CONN_STATE_UNDEFINED = 0, + RS_CONN_STATE_CONNECTING, + RS_CONN_STATE_CONNECTED, }; /** A "generic" connection. */ struct rs_connection { struct rs_conn_base base_; - struct event *tev; /* Timeout event. */ + struct event *tev; /* Timeout event. */ struct rs_conn_callbacks callbacks; - struct rs_peer *active_peer; + enum rs_conn_state state; +#if 0 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. */ +#endif /* 0 */ + struct rs_message *out_queue; /* Queue for outgoing UDP packets. */ #if defined(RS_ENABLE_TLS) - /* TLS specifics. */ + /* TLS specifics. */ SSL_CTX *tls_ctx; SSL *tls_ssl; #endif @@ -145,6 +161,7 @@ struct rs_listener { struct rs_conn_base base_; struct evconnlistener *evlistener; struct rs_listener_callbacks callbacks; + struct rs_listener *next; }; enum rs_message_flags { @@ -159,16 +176,16 @@ struct rs_message { struct rs_connection *conn; unsigned int flags; uint8_t hdr[RS_HEADER_LEN]; - struct radius_packet *rpkt; /* FreeRADIUS object. */ - struct rs_message *next; /* Used for UDP output queue. */ + struct radius_packet *rpkt; /* FreeRADIUS object. */ + struct rs_message *next; /* Used for UDP output queue. */ }; #if defined (__cplusplus) } #endif -/************************/ -/* Convenience macros. */ +/***********************/ +/* Convenience macros. */ /* Memory allocation. */ #define rs_calloc(h, nmemb, size) ((h)->alloc_scheme.calloc != NULL \ @@ -192,6 +209,7 @@ struct rs_message { #define SUBTYPE_P(p, subtype, basemember) \ ((void*) (((char*)(p)) - STRUCT_OFFSET(subtype, basemember))) #define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, base_)) +#define TO_BASE_CONN(c) (&((c)->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) diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h index 021f677..cb98db7 100644 --- a/lib/include/radsec/radsec.h +++ b/lib/include/radsec/radsec.h @@ -1,5 +1,5 @@ /** \file radsec.h - \brief Public interface for libradsec. */ + \brief Public interface for libradsec. */ /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ @@ -136,6 +136,7 @@ extern "C" { /* Data types. */ struct rs_context; /* radsec-impl.h */ +struct rs_conn_base; /* radsec-impl.h */ struct rs_connection; /* radsec-impl.h */ struct rs_listener; /* radsec-impl.h */ struct rs_message; /* radsec-impl.h */ @@ -175,8 +176,10 @@ struct rs_conn_callbacks { typedef void (*rs_listener_new_conn_cb) (struct rs_connection *conn, void *user_data); +typedef void (*rs_listener_error_cb) (void *user_data); struct rs_listener_callbacks { rs_listener_new_conn_cb new_conn_cb; + rs_listener_error_cb error_cb; }; typedef struct value_pair rs_avp; @@ -327,11 +330,20 @@ int rs_conn_get_fd(struct rs_connection *conn); void rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv); /* Peer -- client and server. */ -int rs_peer_create(struct rs_connection *conn, struct rs_peer **peer_out); +/** Create a peer and add it to list of peers held by \a conn. */ +int rs_peer_create_for_conn (struct rs_connection *conn, + struct rs_peer **peer_out); +/** Create a peer and add it to list of peers held by \a listener. */ +int rs_peer_create_for_listener (struct rs_listener *listener, + struct rs_peer **peer_out); +/** Set RADIUS secret for \a peer. Free resurces with \a rs_peer_free_secret. */ +int rs_peer_set_secret(struct rs_peer *peer, const char *secret); +/** Free resources allocated by \a rs_peer_set_secret. */ +void rs_peer_free_secret (struct rs_peer *peer); int rs_peer_set_address(struct rs_peer *peer, const char *hostname, const char *service); -int rs_peer_set_secret(struct rs_peer *peer, const char *secret); +void rs_peer_free_address (struct rs_peer *peer); void rs_peer_set_timeout(struct rs_peer *peer, int timeout); void rs_peer_set_retries(struct rs_peer *peer, int retries); @@ -427,6 +439,12 @@ int rs_err_conn_push_fl(struct rs_connection *conn, int line, const char *fmt, ...); +int rs_err_connbase_push_fl (struct rs_conn_base *connbase, + int code, + const char *file, + int line, + const char *fmt, + ...); /** Pop the first error from the error FIFO associated with connection \a conn or NULL if there are no errors in the FIFO. */ struct rs_error *rs_err_conn_pop(struct rs_connection *conn); diff --git a/lib/message.c b/lib/message.c index e010f94..47590ca 100644 --- a/lib/message.c +++ b/lib/message.c @@ -1,5 +1,5 @@ /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H #include @@ -28,21 +28,21 @@ message_verify_response (struct rs_connection *conn, int err; assert (conn); - assert (conn->active_peer); - assert (conn->active_peer->secret); + assert (conn->base_.active_peer); + assert (conn->base_.active_peer->secret); assert (response); assert (response->rpkt); assert (request); assert (request->rpkt); - response->rpkt->secret = conn->active_peer->secret; - response->rpkt->sizeof_secret = strlen (conn->active_peer->secret); + response->rpkt->secret = conn->base_.active_peer->secret; + response->rpkt->sizeof_secret = strlen (conn->base_.active_peer->secret); /* Verify header and message authenticator. */ err = nr_packet_verify (response->rpkt, request->rpkt); if (err) { - if (conn->is_connected) + if (conn->state == RS_CONN_STATE_CONNECTED) rs_conn_disconnect(conn); return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__, "nr_packet_verify"); @@ -52,7 +52,7 @@ message_verify_response (struct rs_connection *conn, err = nr_packet_decode (response->rpkt, request->rpkt); if (err) { - if (conn->is_connected) + if (conn->state == RS_CONN_STATE_CONNECTED) rs_conn_disconnect(conn); return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__, "nr_packet_decode"); @@ -71,11 +71,11 @@ message_do_send (struct rs_message *msg) assert (msg); assert (msg->conn); - assert (msg->conn->active_peer); - assert (msg->conn->active_peer->secret); + assert (msg->conn->base_.active_peer); + assert (msg->conn->base_.active_peer->secret); assert (msg->rpkt); - msg->rpkt->secret = msg->conn->active_peer->secret; + msg->rpkt->secret = msg->conn->base_.active_peer->secret; msg->rpkt->sizeof_secret = strlen (msg->rpkt->secret); /* Encode message. */ @@ -92,8 +92,8 @@ message_do_send (struct rs_message *msg) { char host[80], serv[80]; - getnameinfo (msg->conn->active_peer->addr_cache->ai_addr, - msg->conn->active_peer->addr_cache->ai_addrlen, + getnameinfo (msg->conn->base_.active_peer->addr_cache->ai_addr, + msg->conn->base_.active_peer->addr_cache->ai_addrlen, host, sizeof(host), serv, sizeof(serv), 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/); rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv)); diff --git a/lib/peer.c b/lib/peer.c index 3e0069b..b6ec167 100644 --- a/lib/peer.c +++ b/lib/peer.c @@ -1,5 +1,5 @@ /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H #include @@ -20,12 +20,12 @@ peer_pick_peer (struct rs_connection *conn) { assert (conn); - if (conn->active_peer) - conn->active_peer = conn->active_peer->next; /* Next. */ - if (!conn->active_peer) - conn->active_peer = conn->base_.peers; /* From the top. */ + if (conn->base_.active_peer) + conn->base_.active_peer = conn->base_.active_peer->next; /* Next. */ + if (!conn->base_.active_peer) + conn->base_.active_peer = conn->base_.peers; /* From the top. */ - return conn->active_peer; + return conn->base_.active_peer; } struct rs_peer * @@ -33,10 +33,9 @@ peer_create (struct rs_context *ctx, struct rs_peer **rootp) { struct rs_peer *p; - p = (struct rs_peer *) rs_malloc (ctx, sizeof(*p)); + p = (struct rs_peer *) rs_calloc (ctx, 1, sizeof(*p)); if (p) { - memset (p, 0, sizeof(struct rs_peer)); if (*rootp) { p->next = (*rootp)->next; @@ -48,36 +47,49 @@ peer_create (struct rs_context *ctx, struct rs_peer **rootp) return p; } -/* Public functions. */ int -rs_peer_create (struct rs_connection *conn, struct rs_peer **peer_out) +peer_create_for_connbase (struct rs_conn_base *connbase, + struct rs_peer **peer_out) { struct rs_peer *peer; - peer = peer_create (conn->base_.ctx, &conn->base_.peers); - if (peer) - { - peer->conn = conn; - peer->realm->timeout = 2; /* FIXME: Why? */ - peer->realm->retries = 2; /* FIXME: Why? */ - } - else - return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); + peer = peer_create (connbase->ctx, &connbase->peers); + if (peer == NULL) + return rs_err_connbase_push_fl (connbase, RSE_NOMEM, __FILE__, __LINE__, + NULL); + peer->connbase = connbase; + peer->realm = connbase->realm; + if (*peer_out) *peer_out = peer; return RSE_OK; } +/* Public functions. */ +int +rs_peer_create_for_conn (struct rs_connection *conn, struct rs_peer **peer_out) +{ + return peer_create_for_connbase (TO_BASE_CONN (conn), peer_out); +} + int -rs_peer_set_address (struct rs_peer *peer, const char *hostname, +rs_peer_create_for_listener (struct rs_listener *listener, + struct rs_peer **peer_out) +{ + return peer_create_for_connbase (TO_BASE_CONN (listener), peer_out); +} + +int +rs_peer_set_address (struct rs_peer *peer, + const char *hostname, const char *service) { assert (peer); - assert (peer->conn); - assert (peer->conn->base_.ctx); + assert (peer->connbase); + assert (peer->connbase->ctx); - peer->hostname = rs_strdup (peer->conn->base_.ctx, hostname); - peer->service = rs_strdup (peer->conn->base_.ctx, service); + peer->hostname = rs_strdup (peer->connbase->ctx, hostname); + peer->service = rs_strdup (peer->connbase->ctx, service); if (peer->hostname == NULL || peer->service == NULL) return RSE_NOMEM; @@ -85,12 +97,28 @@ rs_peer_set_address (struct rs_peer *peer, const char *hostname, } void +rs_peer_free_address (struct rs_peer *peer) +{ + assert (peer); + assert (peer->connbase); + assert (peer->connbase->ctx); + + if (peer->hostname) + rs_free (peer->connbase->ctx, peer->hostname); + peer->hostname = NULL; + if (peer->service) + rs_free (peer->connbase->ctx, peer->service); + peer->service = NULL; +} + +void rs_peer_set_timeout (struct rs_peer *peer, int timeout) { assert (peer); assert (peer->realm); peer->realm->timeout = timeout; } + void rs_peer_set_retries (struct rs_peer *peer, int retries) { @@ -102,12 +130,26 @@ rs_peer_set_retries (struct rs_peer *peer, int retries) int rs_peer_set_secret (struct rs_peer *peer, const char *secret) { - if (peer->secret) - free (peer->secret); - peer->secret = (char *) malloc (strlen(secret) + 1); + assert (peer); + assert (peer->connbase); + assert (peer->connbase->ctx); + + rs_peer_free_secret (peer); + peer->secret = rs_calloc (peer->connbase->ctx, 1, strlen(secret) + 1); if (!peer->secret) - return rs_err_conn_push (peer->conn, RSE_NOMEM, NULL); + return rs_err_connbase_push (peer->connbase, RSE_NOMEM, NULL); strcpy (peer->secret, secret); return RSE_OK; } +void +rs_peer_free_secret (struct rs_peer *peer) +{ + assert (peer); + assert (peer->connbase); + assert (peer->connbase->ctx); + + if (peer->secret) + rs_free (peer->connbase->ctx, peer->secret); + peer->secret = NULL; +} diff --git a/lib/peer.h b/lib/peer.h index 4e976c5..95be0b0 100644 --- a/lib/peer.h +++ b/lib/peer.h @@ -3,3 +3,4 @@ struct rs_peer *peer_create (struct rs_context *ctx, struct rs_peer **rootp); struct rs_peer *peer_pick_peer (struct rs_connection *conn); +int peer_create_for_connbase (struct rs_conn_base *, struct rs_peer **); diff --git a/lib/radsec.sym b/lib/radsec.sym index 7e64560..6ad1361 100644 --- a/lib/radsec.sym +++ b/lib/radsec.sym @@ -77,7 +77,10 @@ rs_message_create rs_message_create_authn_request rs_message_destroy rs_message_send -rs_peer_create +rs_peer_create_for_conn +rs_peer_create_for_listener +rs_peer_free_address +rs_peer_free_secret rs_peer_set_address rs_peer_set_retries rs_peer_set_secret diff --git a/lib/send.c b/lib/send.c index 6b76958..fab89a9 100644 --- a/lib/send.c +++ b/lib/send.c @@ -24,12 +24,12 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg) if (event_init_eventbase (conn)) return -1; - if (!conn->active_peer) + if (!conn->base_.active_peer) peer_pick_peer (conn); - if (!conn->active_peer) + if (!conn->base_.active_peer) return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL); - if (event_init_socket (conn, conn->active_peer)) + if (event_init_socket (conn, conn->base_.active_peer)) return -1; if (conn->base_.realm->type == RS_CONN_TYPE_TCP @@ -37,7 +37,7 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg) { if (tcp_init_connect_timer (conn)) return -1; - if (event_init_bufferevent (conn, conn->active_peer)) + if (event_init_bufferevent (conn, conn->base_.active_peer)) return -1; } else @@ -48,9 +48,9 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg) return -1; } - if (!conn->is_connected) - if (!conn->is_connecting) - event_do_connect (conn); + if (conn->state != RS_CONN_STATE_CONNECTED + && conn->state != RS_CONN_STATE_CONNECTING) + event_do_connect (conn); return RSE_OK; } @@ -58,7 +58,8 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg) static int _conn_is_open_p (struct rs_connection *conn) { - return conn->active_peer && conn->is_connected; + return conn->state == RS_CONN_STATE_CONNECTED + && conn->base_.active_peer != NULL; } /* User callback used when we're dispatching for user. */ @@ -92,7 +93,7 @@ rs_message_send (struct rs_message *msg, void *user_data) assert (conn->base_.ctx); assert (conn->base_.ctx->evb); - assert (conn->active_peer); + assert (conn->base_.active_peer); assert (conn->base_.fd >= 0); conn->base_.user_data = user_data; diff --git a/lib/tcp.c b/lib/tcp.c index b8d7906..f5673f5 100644 --- a/lib/tcp.c +++ b/lib/tcp.c @@ -109,7 +109,8 @@ _read_message (struct rs_message *msg) /* Find out what happens if there's data left in the buffer. */ { size_t rest = 0; - rest = evbuffer_get_length (bufferevent_get_input (msg->conn->bev)); + rest = + evbuffer_get_length (bufferevent_get_input (msg->conn->base_.bev)); if (rest) rs_debug (("%s: returning with %d octets left in buffer\n", __func__, rest)); @@ -178,11 +179,10 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data) assert (msg->conn); conn = msg->conn; #if defined (DEBUG) - assert (msg->conn->active_peer); - p = conn->active_peer; + assert (msg->conn->base_.active_peer); + p = conn->base_.active_peer; #endif - conn->is_connecting = 0; if (events & BEV_EVENT_CONNECTED) { if (conn->tev) @@ -213,7 +213,7 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data) } else { - rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr, + rs_debug (("%s: %d: %d (%s)\n", __func__, conn->base_.fd, sockerr, evutil_socket_error_to_string (sockerr))); rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, "%d: %d (%s)", conn->base_.fd, sockerr, diff --git a/lib/tls.c b/lib/tls.c index 979ee3c..b9fb3cf 100644 --- a/lib/tls.c +++ b/lib/tls.c @@ -26,14 +26,14 @@ _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm) { memset (c, 0, sizeof (struct tls)); /* _conn_open() should've picked a peer by now. */ - assert (conn->active_peer); + assert (conn->base_.active_peer); /* TODO: Make sure old radsecproxy code doesn't free these all of a sudden, or strdup them. */ c->name = realm->name; - c->cacertfile = conn->active_peer->cacertfile; + c->cacertfile = conn->base_.active_peer->cacertfile; c->cacertpath = NULL; /* NYI */ - c->certfile = conn->active_peer->certfile; - c->certkeyfile = conn->active_peer->certkeyfile; + c->certfile = conn->base_.active_peer->certfile; + c->certkeyfile = conn->base_.active_peer->certkeyfile; c->certkeypwd = NULL; /* NYI */ c->cacheexpiry = 0; /* NYI */ c->crlcheck = 0; /* NYI */ @@ -60,7 +60,7 @@ psk_client_cb (SSL *ssl, conn = SSL_get_ex_data (ssl, 0); assert (conn != NULL); - cred = conn->active_peer->transport_cred; + cred = conn->base_.active_peer->transport_cred; assert (cred != NULL); /* NOTE: Ignoring identity hint from server. */ @@ -126,7 +126,7 @@ rs_tls_init (struct rs_connection *conn) assert (conn->base_.ctx); ctx = conn->base_.ctx; - tlsconf = _get_tlsconf (conn, conn->active_peer->realm); + tlsconf = _get_tlsconf (conn, conn->base_.active_peer->realm); if (!tlsconf) return -1; ssl_ctx = tlsgetctx (RAD_TLS, tlsconf); @@ -147,7 +147,7 @@ rs_tls_init (struct rs_connection *conn) } #if defined RS_ENABLE_TLS_PSK - if (conn->active_peer->transport_cred != NULL) + if (conn->base_.active_peer->transport_cred != NULL) { SSL_set_psk_client_callback (ssl, psk_client_cb); SSL_set_ex_data (ssl, 0, conn); @@ -203,9 +203,9 @@ tls_verify_cert (struct rs_connection *conn) struct in6_addr addr; const char *hostname = NULL; - assert (conn->active_peer->conn == conn); - assert (conn->active_peer->hostname != NULL); - hostname = conn->active_peer->hostname; + assert (conn->base_.active_peer != NULL); + assert (conn->base_.active_peer->hostname != NULL); + hostname = conn->base_.active_peer->hostname; /* verifytlscert() performs basic verification as described by OpenSSL VERIFY(1), i.e. verification of the certificate chain. */ diff --git a/lib/udp.c b/lib/udp.c index 71d7003..22a8375 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -129,7 +129,7 @@ _evcb (evutil_socket_t fd, short what, void *user_data) assert (msg); assert (msg->conn); - if (!msg->conn->is_connected) + if (msg->conn->state == RS_CONN_STATE_CONNECTING) event_on_connect (msg->conn, msg); if (msg->conn->out_queue) diff --git a/lib/util.c b/lib/util.c index eceaec9..671fcde 100644 --- a/lib/util.c +++ b/lib/util.c @@ -11,9 +11,10 @@ rs_strdup (struct rs_context *ctx, const char *s) { char *buf = rs_calloc (ctx, 1, strlen (s) + 1); - if (buf != NULL) - return strcpy (buf, s); + if (buf) + strcpy (buf, s); + else + rs_err_ctx_push (ctx, RSE_NOMEM, NULL); - rs_err_ctx_push (ctx, RSE_NOMEM, NULL); - return NULL; + return buf; } -- 2.1.4