diff options
Diffstat (limited to 'lib')
43 files changed, 2570 insertions, 1044 deletions
diff --git a/lib/CHANGES b/lib/CHANGES new file mode 100644 index 0000000..135fd4b --- /dev/null +++ b/lib/CHANGES @@ -0,0 +1,8 @@ +Changes in version 0.0.3 - 2013-??-?? + libradsec 0.0.3 <summary goes here> + + o Compatible internal code changes: + - Most places that used to say 'packet' now say 'message', + including public API:s. Preprocessor directives (#define) are in + place to make the public interface compatible with previous + releases. diff --git a/lib/HACKING b/lib/HACKING index a92f0f9..c896324 100644 --- a/lib/HACKING +++ b/lib/HACKING @@ -1,6 +1,6 @@ HACKING file for libradsec (in Emacs -*- org -*- mode). -Status as of libradsec-0.0.4.dev (2013-05-06). +Status as of libradsec-0.2.0.dev (2013-05-06). * Build instructions sh autogen.sh @@ -9,43 +9,6 @@ make examples/client -r examples/client.conf blocking-tls; echo $? -* Design of the API -- There are three usage modes: - - - Application uses blocking send and receive calls (blocking - mode). This is typically fine for a simple client. - - - Application registers callbacks with libradsec and runs the - libevent dispatch loop (a.k.a. user dispatch mode). This would - probably how to implement a server or a proxy. - - - Application runs its own event loop, using fd's for select and - performs I/O using libradsec send/receive functions - (a.k.a. on-your-own mode). Might be useful for an application - which already has an event loop that wants to add RadSec - functionality. - -- Apart from configuration and error handling, an application - shouldn't need to handle TCP and UDP connections - differently. Similarly, the use of TLS/DTLS or not shouldn't - influence the libradsec calls made by the application. - -- Configuration is done either by using the API or by pointing at a - configuration file which is parsed by libradsec. - -- Fully reentrant. - -- Application chooses allocation regime. - -Note that as of 0.0.2.dev libradsec suffers from way too much focus on -the behaviour of a blocking client and is totally useless as a server. -Not only does it lack most of the functions needed for writing a -server but it also contains at least one architectural mishap which -kills the server idea -- a connection timeout (TCP) or a retransmit -timeout (UDP) will result in the event loop being broken. The same -thing will happen if there's an error on a TCP connection, f.ex. a -failing certificate validation (TLS). - * Dependencies Details (within parentheses) apply to Debian Wheezy. @@ -56,7 +19,7 @@ Details (within parentheses) apply to Debian Wheezy. - OpenSSL (1.0.1c-4) -- optional, for TLS and DTLS support sudo apt-get install libssl-dev libssl1.0.0 -* Functionality and quality in 0.0.x +* Functionality and quality in 0.2.x ** Not well tested - reading config file - [TCP] short read @@ -66,6 +29,8 @@ Details (within parentheses) apply to Debian Wheezy. - [TLS] verification of CN ** Known issues +- error handling when server can't bind to given listen_addr+port +- configuration of listen_addr/service is per realm - error stack is only one entry deep - custom allocation scheme is not used in all places @@ -89,3 +54,74 @@ a crash, catching the crash in gdb and providing a backtrace is highly valuable for debugging. Contact: mailto:linus+libradsec@nordu.net + + +* Design of the API +- There are three usage modes: + + - Application uses blocking send and receive calls (blocking + mode). This is typically fine for a simple client. + + - Application registers callbacks with libradsec and runs the + libevent dispatch loop (a.k.a. user dispatch mode). This would + probably be how one would implement a server or a proxy. + + - Application runs its own event loop, using fd's for select and + performs I/O using libradsec send/receive functions + (a.k.a. on-your-own mode). Might be useful for an application + which already has an event loop that wants to add RadSec + functionality. + +- Apart from configuration and error handling, an application + shouldn't need to handle TCP and UDP connections + differently. Similarly, the use of TLS/DTLS or not shouldn't + influence the libradsec calls made by the application. + +- Configuration is done either by using the API or by pointing at a + configuration file which is parsed by libradsec. + +- Fully reentrant. + +- Application chooses allocation regime. + +Note that as of 0.0.2.dev libradsec suffers from way too much focus on +the behaviour of a blocking client and is totally useless as a server. +Not only does it lack most of the functions needed for writing a +server but it also contains at least one architectural mishap which +kills the server idea -- a connection timeout (TCP) or a retransmit +timeout (UDP) will result in the event loop being broken. The same +thing will happen if there's an error on a TCP connection, f.ex. a +failing certificate validation (TLS). + +* Notes on internals +** How to connect an outgoing connection? +Connecting is not done explicitly by the application but implicitly by +rs_message_send(). The application can treat connections in the same +way regardless of whether they're connection-oriented (i.e. TCP) or +not (UDP). + +rs_message_send(msg) + if msg->conn is open: mesasge_do_send(msg) + else: + -> _conn_open(conn, msg) + pick configured peer + event_init_socket(peer) + if TCP or TLS: + init tcp timers + event_init_bufferevent(conn, peer) + else: + init udp timers + if not connected and not connecting: + event_do_connect(conn) + + if TCP: + bufferevent_setcb() + bufferevent_enable() + else: + event_assign(write_ev) ; libevent func? + event_add(write_ev) + + if not in user-dispatch-mode: + event_base_dispatch() +** How to bind a listener and start listening for incoming connections? + diff --git a/lib/Makefile.am b/lib/Makefile.am index 237294a..e202218 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -16,6 +16,9 @@ ACLOCAL_AMFLAGS = -I m4 # 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 DIST_SUBDIRS = $(SUBDIRS) tests @@ -29,11 +32,13 @@ libradsec_la_SOURCES = \ avp.c \ compat.c \ conf.c \ + confutil.c \ conn.c \ debug.c \ err.c \ event.c \ - packet.c \ + listener.c \ + message.c \ peer.c \ radsec.c \ request.c \ @@ -43,9 +48,9 @@ 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 libradsec_la_SOURCES += \ @@ -55,7 +60,6 @@ libradsec_la_SOURCES += \ err.h \ event.h \ md5.h \ - packet.h \ peer.h \ radsec.h \ tcp.h \ @@ -63,9 +67,14 @@ libradsec_la_SOURCES += \ udp.h \ util.h -EXTRA_DIST = HACKING LICENSE libradsec.spec radsec.sym -AM_DISTCHECK_CONFIGURE_FLAGS = --enable-tls --enable-tls-psk +EXTRA_DIST = HACKING LICENSE radsec.sym +EXTRA_libradsec_la_DEPENDENCIES = radsec.sym -libradsec_la_LIBADD = radsecproxy/libradsec-radsecproxy.la radius/libradsec-radius.la -libradsec_la_LDFLAGS = -version-info 0:0:0 -export-symbols $(srcdir)/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 $(srcdir)/radsec.sym +libradsec_la_LIBADD = \ + radsecproxy/libradsec-radsecproxy.la \ + radius/libradsec-radius.la @@ -1,6 +1,15 @@ Libradsec is a RADIUS library for clients doing RADIUS over UDP or -TLS. The goal is to add support for writing servers (and thus proxies) -and to add transports TCP and DTLS. +TLS. The goal is to eventually add transports TCP and DTLS. + + + * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * + +This branch (libradsec-server-support) is extremely unstable and will +see changes its to public API:s for sure. It _will_ be rebased without +any warning what so ever. Yuo probably don't want to follow this +branch. + + * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * NOTE * The canonical pickup point is @@ -11,12 +20,15 @@ The source code is licensed under a 3-clause BSD license. See the LICENSE file. -Libradsec depends on +Libradsec depends on - libconfuse - libevent2 - openssl (if configured with --enable-tls) +For changes between releases, see the CHANGES file. + + To compile the library and the examples, do something like sh autogen.sh && ./configure && make diff --git a/lib/compat.h b/lib/compat.h index d3083e9..bb43e9b 100644 --- a/lib/compat.h +++ b/lib/compat.h @@ -1,5 +1,11 @@ /* Copyright 2011 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); @@ -15,54 +15,222 @@ #include "util.h" #include "debug.h" -#if 0 - # common config options - - # common realm config options +#if 0 /* Configuration file syntax. */ + # realm specific configuration realm STRING { type = "UDP"|"TCP"|"TLS"|"DTLS" timeout = INT retries = INT + } + + # realm configuration inherited (and overwritable) by clients and servers + realm STRING { cacertfile = STRING #cacertpath = STRING certfile = STRING certkeyfile = STRING - pskstr = STRING # Transport pre-shared key, UTF-8 form. + pskstr = STRING # Transport pre-shared key, UTF-8 form. pskhexstr = STRING # Transport pre-shared key, ASCII hex form. pskid = STRING pskex = "PSK"|"DHE_PSK"|"RSA_PSK" } - # client specific realm config options + # client configuration realm STRING { server { hostname = STRING - service = STRING + service = STRING # name or port number secret = STRING # RADIUS secret } } + + # server configuration + realm STRING { + listen_addr = STRING + listen_service = STRING + client { + hostname = STRING + service = STRING # name or port number + secret = STRING # RADIUS secret + } + } +#endif + +struct confcommon { + struct rs_credentials *transport_cred; + char *cacertfile; + char *cacertpath; + char *certfile; + char *certkeyfile; + char *pskstr; + char *pskhexstr; +}; + +#define CONFGET_STR(dst,cfg,key,def) do { \ + (dst) = cfg_getstr ((cfg), (key)); \ + if ((dst) == NULL) (dst) = (def); \ + } while (0) +#define CONFGET_INT(dst,cfg,key,def) do { \ + (dst) = cfg_getint ((cfg), (key)); \ + if ((dst) == -1) (dst) = (def); \ + } while (0) + +static int +confload_peers (struct rs_context *ctx, + /*const*/ cfg_t *cfg_realm, + enum rs_peer_type type, + struct rs_realm *r) +{ + const char *peer_type_str[] = {"<no type>", "client", "server"}; + cfg_t *cfg_peer = NULL; + int j; + char *def_listen_addr = cfg_getstr (cfg_realm, "listen_addr"); + char *def_listen_service = cfg_getstr (cfg_realm, "listen_service"); + char *def_cacertfile = cfg_getstr (cfg_realm, "cacertfile"); + /*char *def_cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/ + char *def_certfile = cfg_getstr (cfg_realm, "certfile"); + char *def_certkeyfile = cfg_getstr (cfg_realm, "certkeyfile"); + char *def_pskstr = cfg_getstr (cfg_realm, "pskstr"); + char *def_pskhexstr = cfg_getstr (cfg_realm, "pskhexstr"); + + for (j = 0; j < cfg_size (cfg_realm, peer_type_str[type]); j++) + { + char *pskstr = NULL; + char *pskhexstr = NULL; + struct rs_peer *p = peer_create (ctx, &r->peers); + if (p == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, + NULL); + p->type = type; + p->realm = r; + + cfg_peer = cfg_getnsec (cfg_realm, peer_type_str[type], j); + p->hostname = cfg_getstr (cfg_peer, "hostname"); + p->service = cfg_getstr (cfg_peer, "service"); + p->secret = cfg_getstr (cfg_peer, "secret"); + + if (type == RS_PEER_TYPE_CLIENT) + { + struct rs_peer *lp = peer_create (ctx, &r->local_addr); /* FIXME: this should be saved per peer, not realm */ + if (lp == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, + NULL); + lp->realm = r; + fprintf (stderr, " *** %s %s ***\n", def_listen_addr, def_listen_service); +#if 0 + CONFGET_STR (lp->hostname, cfg_peer, "listen_addr", def_listen_addr); + CONFGET_STR (lp->service, cfg_peer, "listen_service", + def_listen_service); +#else + lp->hostname = "127.0.0.1"; + lp->service = "4711"; #endif + } + + CONFGET_STR (p->cacertfile, cfg_peer, "cacertfile", def_cacertfile); + CONFGET_STR (p->certfile, cfg_peer, "certfile", def_certfile); + CONFGET_STR (p->certkeyfile, cfg_peer, "certkeyfile", def_certkeyfile); + CONFGET_STR (pskstr, cfg_peer, "pskstr", def_pskstr); + CONFGET_STR (pskhexstr, cfg_peer, "pskhexstr", def_pskhexstr); + + if (pskstr || pskhexstr) + { +#if defined RS_ENABLE_TLS_PSK + char *def_pskex = cfg_getstr (cfg_realm, "pskex"); + char *tmp_pskex = NULL; + rs_cred_type_t type = RS_CRED_NONE; + struct rs_credentials *cred = NULL; + + CONFGET_STR (tmp_pskex, cfg_peer, "pskex", def_pskex); + if (!strcmp (tmp_pskex, "PSK")) + type = RS_CRED_TLS_PSK; + else + { + /* TODO: push a warning on the error stack:*/ + /*rs_err_ctx_push (ctx, RSE_WARN, "%s: unsupported PSK key exchange" + " algorithm -- PSK not used", kex);*/ + } + + if (type != RS_CRED_NONE) + { + char *def_pskid = cfg_getstr (cfg_realm, "pskid"); + cred = rs_calloc (ctx, 1, sizeof (*cred)); + if (cred == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, + NULL); + cred->type = type; + CONFGET_STR (cred->identity, cfg_peer, "pskid", def_pskid); + if (pskhexstr) + { + cred->secret_encoding = RS_KEY_ENCODING_ASCII_HEX; + cred->secret = pskhexstr; + if (pskstr) + ; /* TODO: warn that we're ignoring pskstr */ + } + else + { + cred->secret_encoding = RS_KEY_ENCODING_UTF8; + cred->secret = pskstr; + } + + p->transport_cred = cred; + } +#else /* !RS_ENABLE_TLS_PSK */ + /* TODO: push a warning on the error stack: */ + /* rs_err_ctx_push (ctx, RSE_WARN, "libradsec wasn't configured with " + "support for TLS preshared keys, ignoring pskstr " + "and pskhexstr");*/ +#endif /* RS_ENABLE_TLS_PSK */ + } + +#if defined (RS_ENABLE_TLS) + /* For a TLS or DTLS client or server, validate that we have either of CA + cert file/path or PSK. */ + if ((r->type == RS_CONN_TYPE_TLS || r->type == RS_CONN_TYPE_DTLS) + && (p->cacertfile == NULL && p->cacertpath == NULL) + && p->transport_cred == NULL) + return rs_err_ctx_push (ctx, RSE_CONFIG, + "%s: missing both CA file/path and PSK", + r->name); +#endif + } + + return RSE_OK; +} /* FIXME: Leaking memory in error cases. */ int rs_context_read_config(struct rs_context *ctx, const char *config_file) { - cfg_t *cfg, *cfg_realm, *cfg_server; + cfg_t *cfg, *cfg_realm; int err = 0; - int i, j; + int i; const char *s; struct rs_config *config = NULL; - cfg_opt_t server_opts[] = + cfg_opt_t peer_opts[] = { CFG_STR ("hostname", NULL, CFGF_NONE), CFG_STR ("service", "2083", CFGF_NONE), + CFG_STR ("listen_addr", "127.0.0.1", CFGF_NONE), /* Clients only. */ + CFG_STR ("listen_service", "0", CFGF_NONE), /* Clients only. */ CFG_STR ("secret", "radsec", CFGF_NONE), + CFG_STR ("cacertfile", NULL, CFGF_NONE), + /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/ + CFG_STR ("certfile", NULL, CFGF_NONE), + CFG_STR ("certkeyfile", NULL, CFGF_NONE), + CFG_STR ("pskstr", NULL, CFGF_NONE), + CFG_STR ("pskhexstr", NULL, CFGF_NONE), + CFG_STR ("pskid", NULL, CFGF_NONE), + CFG_STR ("pskex", "PSK", CFGF_NONE), + CFG_END () }; cfg_opt_t realm_opts[] = { CFG_STR ("type", "UDP", CFGF_NONE), + CFG_STR ("listen_addr", "127.0.0.1", CFGF_NONE), + CFG_STR ("listen_service", "0", CFGF_NONE), CFG_INT ("timeout", 2, CFGF_NONE), /* FIXME: Remove? */ CFG_INT ("retries", 2, CFGF_NONE), /* FIXME: Remove? */ CFG_STR ("cacertfile", NULL, CFGF_NONE), @@ -73,7 +241,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) CFG_STR ("pskhexstr", NULL, CFGF_NONE), CFG_STR ("pskid", NULL, CFGF_NONE), CFG_STR ("pskex", "PSK", CFGF_NONE), - CFG_SEC ("server", server_opts, CFGF_MULTI), + CFG_SEC ("server", peer_opts, CFGF_MULTI), + CFG_SEC ("client", peer_opts, CFGF_MULTI), CFG_END () }; cfg_opt_t opts[] = @@ -111,8 +280,9 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) { struct rs_realm *r = NULL; const char *typestr; - char *pskstr = NULL, *pskhexstr = NULL; + struct confcommon cc; + memset (&cc, 0, sizeof(cc)); r = rs_calloc (ctx, 1, sizeof(*r)); if (r == NULL) return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); @@ -140,95 +310,27 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file) r->type = RS_CONN_TYPE_UDP; else if (strcmp (typestr, "TCP") == 0) r->type = RS_CONN_TYPE_TCP; +#if defined (RS_ENABLE_TLS) else if (strcmp (typestr, "TLS") == 0) r->type = RS_CONN_TYPE_TLS; else if (strcmp (typestr, "DTLS") == 0) r->type = RS_CONN_TYPE_DTLS; +#endif else return rs_err_ctx_push (ctx, RSE_CONFIG, "%s: invalid connection type: %s", r->name, typestr); + r->timeout = cfg_getint (cfg_realm, "timeout"); r->retries = cfg_getint (cfg_realm, "retries"); - r->cacertfile = cfg_getstr (cfg_realm, "cacertfile"); - /*r->cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/ - r->certfile = cfg_getstr (cfg_realm, "certfile"); - r->certkeyfile = cfg_getstr (cfg_realm, "certkeyfile"); - - pskstr = cfg_getstr (cfg_realm, "pskstr"); - pskhexstr = cfg_getstr (cfg_realm, "pskhexstr"); - if (pskstr || pskhexstr) - { -#if defined RS_ENABLE_TLS_PSK - char *kex = cfg_getstr (cfg_realm, "pskex"); - rs_cred_type_t type = RS_CRED_NONE; - struct rs_credentials *cred = NULL; - assert (kex != NULL); - - if (!strcmp (kex, "PSK")) - type = RS_CRED_TLS_PSK; - else - { - /* TODO: push a warning on the error stack:*/ - /*rs_err_ctx_push (ctx, RSE_WARN, "%s: unsupported PSK key exchange" - " algorithm -- PSK not used", kex);*/ - } - - if (type != RS_CRED_NONE) - { - cred = rs_calloc (ctx, 1, sizeof (*cred)); - if (cred == NULL) - return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, - NULL); - cred->type = type; - cred->identity = cfg_getstr (cfg_realm, "pskid"); - if (pskhexstr) - { - cred->secret_encoding = RS_KEY_ENCODING_ASCII_HEX; - cred->secret = pskhexstr; - if (pskstr) - ; /* TODO: warn that we're ignoring pskstr */ - } - else - { - cred->secret_encoding = RS_KEY_ENCODING_UTF8; - cred->secret = pskstr; - } - - r->transport_cred = cred; - } -#else /* !RS_ENABLE_TLS_PSK */ - /* TODO: push a warning on the error stack: */ - /* rs_err_ctx_push (ctx, RSE_WARN, "libradsec wasn't configured with " - "support for TLS preshared keys, ignoring pskstr " - "and pskhexstr");*/ -#endif /* RS_ENABLE_TLS_PSK */ - } - - /* For TLS and DTLS realms, validate that we either have (i) CA - cert file or path or (ii) PSK. */ - if ((r->type == RS_CONN_TYPE_TLS || r->type == RS_CONN_TYPE_DTLS) - && (r->cacertfile == NULL && r->cacertpath == NULL) - && r->transport_cred == NULL) - return rs_err_ctx_push (ctx, RSE_CONFIG, - "%s: missing both CA file/path and PSK", - r->name); - - /* Add peers, one per server stanza. */ - for (j = 0; j < cfg_size (cfg_realm, "server"); j++) - { - struct rs_peer *p = peer_create (ctx, &r->peers); - if (p == NULL) - return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, - NULL); - p->realm = r; - - cfg_server = cfg_getnsec (cfg_realm, "server", j); - p->hostname = cfg_getstr (cfg_server, "hostname"); - p->service = cfg_getstr (cfg_server, "service"); - p->secret = cfg_getstr (cfg_server, "secret"); - } + /* Add client and server peers. */ + err = confload_peers (ctx, cfg_realm, RS_PEER_TYPE_CLIENT, r); + if (err) + return err; + err = confload_peers (ctx, cfg_realm, RS_PEER_TYPE_SERVER, r); + if (err) + return err; } /* Save config object in context, for freeing in rs_context_destroy(). */ diff --git a/lib/configure.ac b/lib/configure.ac index ab775e4..b8b3231 100644 --- a/lib/configure.ac +++ b/lib/configure.ac @@ -1,7 +1,7 @@ # -*- Autoconf -*- script for libradsec. AC_PREREQ([2.63]) -AC_INIT([libradsec], [0.0.4.dev], [linus+libradsec@nordu.net]) +AC_INIT([libradsec], [0.2.0.dev], [linus+libradsec@nordu.net]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([radsec.c]) AC_CONFIG_AUX_DIR([build-aux]) diff --git a/lib/confutil.c b/lib/confutil.c new file mode 100644 index 0000000..7bd4a37 --- /dev/null +++ b/lib/confutil.c @@ -0,0 +1,153 @@ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> + +#if 0 +/* This code triggers the memory-stomping-detector of electric fence */ + +#define MEMCHUNK 30 + +static int +print_to_buf (const struct rs_context *ctx, + char **buf_ptr, ssize_t *i_ptr, ssize_t *len_ptr, + const char *fmt, ...) +{ + char *buf = *buf_ptr; + ssize_t i = *i_ptr; + ssize_t len = *len_ptr; + + va_list args; + for (;;) + { + int n; + va_start (args, fmt); + fprintf (stdout, "sprintf (%p + %ld, %ld, \"%s\") -->", + buf, i, len, buf); + fflush (stdout); + n = vsnprintf (buf + i, len - i, fmt, args); + fprintf (stdout, "%d\n", n); + va_end (args); + if (n < 0) + return -RSE_INTERNAL; + if (n >= len - i) + { + int newlen = len + MEMCHUNK; + buf = rs_realloc (ctx, buf, newlen); + if (buf == NULL) + return -RSE_NOMEM; + len = newlen; + continue; + } + len -= n; + i += n; + + *buf_ptr = buf; + *i_ptr = i; + *len_ptr = len; + return RSE_OK; + } +} +#endif /* 0 */ + +static int +pp (char **out, size_t *len, const char *fmt, ...) +{ + int n; + va_list args; + va_start (args, fmt); + n = vsnprintf (*out, *len, fmt, args); + va_end (args); + if (n == -1 || n >= *len) + return -RSE_INTERNAL; + *out += n; + *len -= n; + return RSE_OK; +} + +int +rs_context_print_config (struct rs_context *ctx, char **buf_out) +{ + char *buf = rs_malloc (ctx, 8192); + char *out = NULL; + size_t len = 8192; + struct rs_config *cfg = ctx->config; + struct rs_realm *r = NULL; + struct rs_peer *p = NULL; + char *peer_type[] = {"<no type>", "client", "server"}; + char *realm_type[] = {"<no type>", "UDP", "TCP", "TLS", "DTLS"}; + char *cred_type[] = {"<no type>", "PSK", "DHE_PSK", "RSA_PSK"}; + + out = buf; + assert (out); + assert (cfg); + + for (r = cfg->realms; r != NULL; r = r->next) + { + if (pp (&out, &len, "realm %s {\n", r->name) + || pp (&out, &len, + "\ttype = \"%s\"\n" + "\ttimeout = %d\n" + "\tretries = %d\n" + "\tlisten_addr = \"%s\"\n" + "\tlisten_service = \"%s\"\n", + realm_type[r->type], + r->timeout, + r->retries, + r->local_addr->hostname, + r->local_addr->service)) + return -RSE_INTERNAL; + for (p = r->peers; p != NULL; p = p->next) + { + if (pp (&out, &len, + "\t%s {\n" + "\t\thostname = \"%s\"\n" + "\t\tservice = \"%s\"\n" + "\t\tsecret = \"%s\"\n", + peer_type[p->type], + p->hostname, + p->service, + p->secret)) + return -RSE_INTERNAL; + if (p->cacertfile) + if (pp (&out, &len, "\t\tcacertfile = \"%s\"\n", p->cacertfile)) + return -RSE_INTERNAL; + if (p->certfile) + if (pp (&out, &len, "\t\tcertfile = \"%s\"\n", p->certfile)) + return -RSE_INTERNAL; + if (p->certkeyfile) + if (pp (&out, &len, "\t\tcertkeyfile = \"%s\"\n", p->certkeyfile)) + return -RSE_INTERNAL; + if (p->transport_cred) + { + if (pp (&out, &len, "\t\tpskex = \"%s\"\n", + cred_type[p->transport_cred->type]) + || pp (&out, &len, "\t\tpskid = \"%s\"\n", + p->transport_cred->identity) + || pp (&out, &len, + "\t\t%s = \"%s\"\n", (p->transport_cred->secret_encoding + == RS_KEY_ENCODING_ASCII_HEX + ? "pskhexstr" : "pskstr"), + p->transport_cred->secret)) + return -RSE_INTERNAL; + } + if (pp (&out, &len, "\t}\n")) + return -RSE_INTERNAL; + } + if (pp (&out, &len, "}\n")) + return -RSE_INTERNAL; + } + + if (buf_out) + *buf_out = buf; + return RSE_OK; +} @@ -13,10 +13,11 @@ #include <event2/bufferevent.h> #include <radsec/radsec.h> #include <radsec/radsec-impl.h> +#include "err.h" #include "debug.h" #include "conn.h" #include "event.h" -#include "packet.h" +#include "message.h" #include "tcp.h" int @@ -30,89 +31,211 @@ conn_user_dispatch_p (const struct rs_connection *conn) conn->callbacks.sent_cb); } - int conn_activate_timeout (struct rs_connection *conn) { + const struct rs_conn_base *connbase; assert (conn); + connbase = TO_BASE_CONN (conn); + assert (connbase->ctx); + assert (connbase->ctx->evb); assert (conn->tev); - assert (conn->evb); - if (conn->timeout.tv_sec || conn->timeout.tv_usec) + if (connbase->timeout.tv_sec || connbase->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)) - return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "evtimer_add: %d", errno); + connbase->timeout.tv_sec, connbase->timeout.tv_usec)); + if (evtimer_add (conn->tev, &connbase->timeout)) + return rs_err_conn_push (conn, RSE_EVENT, "evtimer_add: %d", errno); } return RSE_OK; } int -conn_type_tls (const struct rs_connection *conn) +conn_type_tls_p (const struct rs_connection *conn) { - return conn->realm->type == RS_CONN_TYPE_TLS - || conn->realm->type == RS_CONN_TYPE_DTLS; + return TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_TLS + || TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_DTLS; } int -conn_cred_psk (const struct rs_connection *conn) +baseconn_type_datagram_p (const struct rs_conn_base *connbase) { - return conn->realm->transport_cred && - conn->realm->transport_cred->type == RS_CRED_TLS_PSK; + return connbase->transport == RS_CONN_TYPE_UDP + || connbase->transport == RS_CONN_TYPE_DTLS; } +int +baseconn_type_stream_p (const struct rs_conn_base *connbase) +{ + return connbase->transport == RS_CONN_TYPE_TCP + || connbase->transport == RS_CONN_TYPE_TLS; +} -/* Public functions. */ int -rs_conn_create (struct rs_context *ctx, - struct rs_connection **conn, - const char *config) +conn_cred_psk (const struct rs_connection *conn) { - struct rs_connection *c; + assert (conn); + return conn->active_peer != NULL + && conn->active_peer->transport_cred + && conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK; +} - c = (struct rs_connection *) malloc (sizeof(struct rs_connection)); - if (!c) - return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); +void +conn_init (struct rs_context *ctx, /* FIXME: rename connbase_init? */ + struct rs_conn_base *connbase, + enum rs_conn_subtype type) +{ + 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); + } - memset (c, 0, sizeof(struct rs_connection)); - c->ctx = ctx; - c->fd = -1; + connbase->ctx = ctx; + connbase->fd = -1; +} + +int +conn_configure (struct rs_context *ctx, /* FIXME: rename conbbase_configure? */ + struct rs_conn_base *connbase, + const char *config) +{ if (config) { struct rs_realm *r = rs_conf_find_realm (ctx, config); if (r) { - struct rs_peer *p; - - c->realm = r; - c->peers = r->peers; /* FIXME: Copy instead? */ - for (p = c->peers; p; p = p->next) - p->conn = c; - c->timeout.tv_sec = r->timeout; - c->tryagain = r->retries; - } - else - { - c->realm = rs_malloc (ctx, sizeof (struct rs_realm)); - if (!c->realm) - return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, - NULL); - memset (c->realm, 0, sizeof (struct rs_realm)); + connbase->realm = r; + connbase->peers = r->peers; /* FIXME: Copy instead? */ +#if 0 + for (p = connbase->peers; p != NULL; p = p->next) + p->connbase = connbase; +#endif + connbase->timeout.tv_sec = r->timeout; + connbase->tryagain = r->retries; } } +#if 0 /* incoming connections don't have a realm (a config object), update: they do, but "somebody else" is setting this up <-- FIXME */ + if (connbase->realm == NULL) + { + struct rs_realm *r = rs_calloc (ctx, 1, sizeof (struct rs_realm)); + if (r == NULL) + return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL); + r->next = ctx->realms; + ctx->realms = connbase->realm = r; + } +#else + if (connbase->realm) + connbase->transport = connbase->realm->type; +#endif + + return RSE_OK; +} + +int +conn_add_read_event (struct rs_connection *conn, void *user_data) +{ + struct rs_conn_base *connbase = TO_BASE_CONN(conn); + int err; + + assert(connbase); + + if (connbase->bev) /* TCP (including TLS). */ + { + bufferevent_setwatermark (connbase->bev, EV_READ, RS_HEADER_LEN, 0); + bufferevent_setcb (connbase->bev, tcp_read_cb, NULL, tcp_event_cb, + user_data); + bufferevent_enable (connbase->bev, EV_READ); + } + else /* UDP. */ + { + /* Put fresh message in user_data for the callback and enable the + read event. */ + event_assign (connbase->rev, connbase->ctx->evb, + event_get_fd (connbase->rev), EV_READ, + event_get_callback (connbase->rev), + user_data); + err = event_add (connbase->rev, NULL); + if (err < 0) + return rs_err_connbase_push_fl (connbase, RSE_EVENT, __FILE__, __LINE__, + "event_add: %s", + evutil_gai_strerror (err)); + + /* Activate retransmission timer. */ + conn_activate_timeout (conn); + } + + return RSE_OK; +} + +/** Return !=0 if \a conn is an originating connection, i.e. if its + peer is a server. */ +int +conn_originating_p (const struct rs_connection *conn) +{ + return conn->active_peer->type == RS_PEER_TYPE_SERVER; +} + +int +baseconn_close (struct rs_conn_base *connbase) +{ + int err = 0; + assert (connbase); + + rs_debug (("%s: closing fd %d\n", __func__, connbase->fd)); + + err = evutil_closesocket (connbase->fd); + if (err) + err = rs_err_connbase_push (connbase, RSE_EVENT, + "evutil_closesocket: %d (%s)", + errno, strerror (errno)); + connbase->fd = -1; + return err; +} + +/* 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 @@ -132,27 +255,27 @@ rs_conn_disconnect (struct rs_connection *conn) assert (conn); - if (conn->is_connected) + if (conn->state == RS_CONN_STATE_CONNECTED) event_on_disconnect (conn); - if (conn->bev) + if (TO_BASE_CONN (conn)->bev) { - bufferevent_free (conn->bev); - conn->bev = NULL; + bufferevent_free (TO_BASE_CONN (conn)->bev); + TO_BASE_CONN (conn)->bev = NULL; } - if (conn->rev) + if (TO_BASE_CONN (conn)->rev) { - event_free (conn->rev); - conn->rev = NULL; + event_free (TO_BASE_CONN (conn)->rev); + TO_BASE_CONN (conn)->rev = NULL; } - if (conn->wev) + if (TO_BASE_CONN (conn)->wev) { - event_free (conn->wev); - conn->wev = NULL; + event_free (TO_BASE_CONN (conn)->wev); + TO_BASE_CONN (conn)->wev = NULL; } - err = evutil_closesocket (conn->fd); - conn->fd = -1; + err = evutil_closesocket (TO_BASE_CONN (conn)->fd); + TO_BASE_CONN (conn)->fd = -1; return err; } @@ -166,7 +289,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) @@ -178,16 +301,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; } @@ -199,9 +320,12 @@ rs_conn_set_eventbase (struct rs_connection *conn, struct event_base *eb) } void -rs_conn_set_callbacks (struct rs_connection *conn, struct rs_conn_callbacks *cb) +rs_conn_set_callbacks (struct rs_connection *conn, + struct rs_conn_callbacks *cb, + void *user_data) { assert (conn); + TO_BASE_CONN(conn)->user_data = user_data; memcpy (&conn->callbacks, cb, sizeof (conn->callbacks)); } @@ -213,7 +337,7 @@ rs_conn_del_callbacks (struct rs_connection *conn) } struct rs_conn_callbacks * -rs_conn_get_callbacks(struct rs_connection *conn) +rs_conn_get_callbacks (struct rs_connection *conn) { assert (conn); return &conn->callbacks; @@ -233,93 +357,92 @@ rs_conn_get_current_peer (struct rs_connection *conn, return rs_err_conn_push_fl (conn, RSE_NOSYS, __FILE__, __LINE__, NULL); } -int rs_conn_fd (struct rs_connection *conn) +int +rs_conn_dispatch (struct rs_connection *conn) +{ + assert (conn); + return event_base_loop (conn->base_.ctx->evb, EVLOOP_ONCE); +} + +#if 0 +struct event_base +*rs_conn_get_evb (const struct rs_connection *conn) { assert (conn); - assert (conn->active_peer); - return conn->fd; + return conn->evb; +} +#endif + +int rs_conn_get_fd (struct rs_connection *conn) +{ + assert (conn); + return conn->base_.fd; } static void -_rcb (struct rs_packet *packet, void *user_data) +_rcb (struct rs_message *message, void *user_data) { - struct rs_packet *pkt = (struct rs_packet *) user_data; - assert (pkt); - assert (pkt->conn); - - pkt->flags |= RS_PACKET_RECEIVED; - if (pkt->conn->bev) - bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); - else - event_del (pkt->conn->rev); + struct rs_message *msg = (struct rs_message *) user_data; + assert (msg); + assert (msg->conn); + + msg->flags |= RS_MESSAGE_RECEIVED; + if (msg->conn->base_.bev) /* TCP -- disable bufferevent. */ + bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ); + else /* UDP -- remove read event. */ + event_del (msg->conn->base_.rev); } int -rs_conn_receive_packet (struct rs_connection *conn, - struct rs_packet *req_msg, - struct rs_packet **pkt_out) +rs_conn_receive_message (struct rs_connection *conn, + struct rs_message *req_msg, + struct rs_message **msg_out) { int err = 0; - struct rs_packet *pkt = NULL; + 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_packet_create (conn, &pkt)) + 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 = pkt; - pkt->flags &= ~RS_PACKET_RECEIVED; + conn->base_.user_data = msg; + msg->flags &= ~RS_MESSAGE_RECEIVED; - if (conn->bev) /* TCP. */ - { - bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0); - bufferevent_setcb (conn->bev, tcp_read_cb, NULL, tcp_event_cb, pkt); - bufferevent_enable (conn->bev, EV_READ); - } - else /* UDP. */ - { - /* Put fresh packet 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), pkt); - err = event_add (conn->rev, NULL); - if (err < 0) - return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, - "event_add: %s", - evutil_gai_strerror (err)); - - /* Activate retransmission timer. */ - conn_activate_timeout (pkt->conn); - } + err = conn_add_read_event (conn, msg); + if (err) + return err; 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 (pkt->conn, RSE_EVENT, __FILE__, __LINE__, + 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__)); - if ((pkt->flags & RS_PACKET_RECEIVED) == 0 + if ((msg->flags & RS_MESSAGE_RECEIVED) == 0 /* No message. */ || (req_msg - && packet_verify_response (pkt->conn, pkt, req_msg) != RSE_OK)) + && message_verify_response (conn, msg, req_msg) != RSE_OK)) { - if (rs_err_conn_peek_code (pkt->conn) == RSE_OK) - /* No packet and no error on the stack _should_ mean that the - server hung up on us. */ - rs_err_conn_push (pkt->conn, RSE_DISCO, "no response"); + if (rs_err_conn_peek_code (conn) == RSE_OK) + { + /* No message and no error on the stack _should_ mean that the + server hung up on us. */ + rs_err_conn_push (conn, RSE_DISCO, "no response"); + } return rs_err_conn_peek_code (conn); } - if (pkt_out) - *pkt_out = pkt; + if (msg_out) + *msg_out = msg; return RSE_OK; } @@ -328,5 +451,12 @@ rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv) { assert (conn); assert (tv); - conn->timeout = *tv; + conn->base_.timeout = *tv; +} + +struct rs_peer * +connbase_get_peers (const struct rs_conn_base *connbase) +{ + assert (connbase); + return connbase->peers; } @@ -1,7 +1,20 @@ /* Copyright 2011,2013 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ + See LICENSE for licensing information. */ 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); +int conn_type_tls_p (const struct rs_connection *conn); +int baseconn_type_datagram_p (const struct rs_conn_base *connbase); +int baseconn_type_stream_p (const struct rs_conn_base *connbase); +struct rs_peer *connbase_get_peers (const struct rs_conn_base *connbase); +int conn_add_read_event (struct rs_connection *conn, void *user_data); +int conn_originating_p (const struct rs_connection *conn); +int baseconn_close (struct rs_conn_base *connbase); +struct bufferevent *baseconn_get_bev (struct rs_conn_base *connbase); diff --git a/lib/debug.c b/lib/debug.c index 903c793..66264e5 100644 --- a/lib/debug.c +++ b/lib/debug.c @@ -14,13 +14,13 @@ #include "debug.h" void -rs_dump_packet (const struct rs_packet *pkt) +rs_dump_message (const struct rs_message *msg) { const RADIUS_PACKET *p = NULL; - if (!pkt || !pkt->rpkt) + if (!msg || !msg->rpkt) return; - p = pkt->rpkt; + p = msg->rpkt; fprintf (stderr, "\tCode: %u, Identifier: %u, Lenght: %zu\n", p->code, diff --git a/lib/debug.h b/lib/debug.h index ed62da1..f979528 100644 --- a/lib/debug.h +++ b/lib/debug.h @@ -12,8 +12,8 @@ extern "C" { #endif -struct rs_packet; -void rs_dump_packet (const struct rs_packet *pkt); +struct rs_message; +void rs_dump_message (const struct rs_message *pkt); int _rs_debug (const char *fmt, ...); #if defined (DEBUG) @@ -1,4 +1,4 @@ -/* Copyright 2010-2013 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 @@ -28,7 +28,7 @@ static const char *_errtxt[] = { "authentication failed", /* 12 RSE_BADAUTH */ "internal error", /* 13 RSE_INTERNAL */ "SSL error", /* 14 RSE_SSLERR */ - "invalid packet", /* 15 RSE_INVALID_PKT */ + "invalid message", /* 15 RSE_INVALID_MSG */ "connect timeout", /* 16 RSE_TIMEOUT_CONN */ "invalid argument", /* 17 RSE_INVAL */ "I/O timeout", /* 18 RSE_TIMEOUT_IO */ @@ -121,6 +121,7 @@ _ctx_err_vpush_fl (struct rs_context *ctx, int code, const char *file, return RSE_NOMEM; /* TODO: Implement a stack. */ + assert (ctx); if (ctx->err) rs_err_free (ctx->err); ctx->err = err; @@ -156,28 +157,43 @@ 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) { - assert (conn); - assert (err); - - if (conn->err) - rs_err_free (conn->err); - conn->err = err; /* FIXME: use a stack */ + assert (connbase); + if (connbase->err) + rs_err_free (connbase->err); + connbase->err = err; /* FIXME: use a stack */ + assert (err); 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; + + assert (connbase); + 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,9 +201,25 @@ rs_err_conn_push (struct rs_connection *conn, int code, const char *fmt, ...) { int r = 0; + assert (conn); + va_list args; + va_start (args, fmt); + 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 = _conn_err_vpush_fl (conn, code, NULL, 0, fmt, args); + r = _connbase_err_vpush_fl (connbase, code, file, line, fmt, args); va_end (args); return r; @@ -201,7 +233,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; @@ -221,25 +253,37 @@ rs_err_ctx_pop (struct rs_context *ctx) } struct rs_error * -rs_err_conn_pop (struct rs_connection *conn) +rs_err_connbase_pop (struct rs_conn_base *connbase) { struct rs_error *err; - if (!conn) + if (!connbase) return NULL; /* FIXME: RSE_INVALID_CONN */ - err = conn->err; - conn->err = NULL; + err = connbase->err; + connbase->err = NULL; return err; } +struct rs_error * +rs_err_conn_pop (struct rs_connection *conn) +{ + return rs_err_connbase_pop (TO_BASE_CONN (conn)); +} + +struct rs_error * +rs_err_listener_pop (struct rs_listener *l) +{ + return rs_err_connbase_pop (TO_BASE_CONN (l)); +} + int 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; } @@ -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. */ struct rs_error *err_create (unsigned int code, @@ -6,4 +6,5 @@ struct rs_error *err_create (unsigned int code, 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 802c0b9..dcf7e1c 100644 --- a/lib/event.c +++ b/lib/event.c @@ -10,6 +10,7 @@ #include <errno.h> #include <event2/event.h> +#include <event2/listener.h> #include <event2/bufferevent.h> #if defined (RS_ENABLE_TLS) #include <event2/bufferevent_ssl.h> @@ -25,8 +26,9 @@ #include "err.h" #include "radsec.h" #include "event.h" -#include "packet.h" +#include "message.h" #include "conn.h" +#include "listener.h" #include "debug.h" #if defined (DEBUG) @@ -72,8 +74,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->active_peer)); + conn->state = RS_CONN_STATE_UNDEFINED; rs_err_conn_push_fl (conn, RSE_TIMEOUT_CONN, __FILE__, __LINE__, NULL); event_loopbreak (conn); } @@ -90,68 +92,118 @@ 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->active_peer)); rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL); event_loopbreak (conn); } } +/* FIXME: event_ is actually not such a great prefix given that we + link with libevent which exports 113 symbols prefixed 'event_'. */ int -event_init_socket (struct rs_connection *conn, struct rs_peer *p) +event_init_socket (struct rs_conn_base *connbase, struct rs_peer *p) { - if (conn->fd != -1) + if (connbase->fd != -1) return RSE_OK; + assert (p); + assert (p->realm); + + /* Resolve potential DNS name for peer. */ if (p->addr_cache == NULL) { 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 (connbase, err); } - conn->fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype, - p->addr_cache->ai_protocol); - if (conn->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) + /* Create the socket and make it non-blocking. */ + connbase->fd = socket (p->addr_cache->ai_family, + p->addr_cache->ai_socktype, + p->addr_cache->ai_protocol); + if (connbase->fd < 0) + return rs_err_connbase_push_fl (connbase, RSE_SOCKERR, __FILE__, __LINE__, + "socket: %d (%s)", + errno, strerror (errno)); + if (evutil_make_socket_nonblocking (connbase->fd) < 0) { - evutil_closesocket (conn->fd); - conn->fd = -1; - return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__, - "evutil_make_socket_nonblocking: %d (%s)", - errno, strerror (errno)); + evutil_closesocket (connbase->fd); + connbase->fd = -1; + return rs_err_connbase_push_fl (connbase, RSE_SOCKERR, __FILE__, __LINE__, + "evutil_make_socket_nonblocking: %d (%s)", + errno, strerror (errno)); + } + + /* If we're inititalising the socket for a listener, bind to the + peer address. */ + if (connbase->magic == RS_CONN_MAGIC_LISTENER) + { + assert (p->realm->type == connbase->transport); + if (p->realm->type == RS_CONN_TYPE_TLS + || p->realm->type == RS_CONN_TYPE_TCP) + { + struct rs_listener *listener = TO_LISTENER_CONN (connbase); + listener->evlistener = + evconnlistener_new_bind (listener->base_.ctx->evb, + listener_accept_cb_, + listener, LEV_OPT_REUSEABLE, + LISTENER_BACKLOG, + p->addr_cache->ai_addr, + p->addr_cache->ai_addrlen); + if (listener->evlistener == NULL) + return rs_err_connbase_push (connbase, RSE_EVENT, + "evconnlistener_new_bind: %d (%s)", + errno, strerror (errno)); + + evconnlistener_set_error_cb (listener->evlistener, listener_err_cb_); + } + else + { + return rs_err_connbase_push_fl (connbase, RSE_NOSYS, + __FILE__, __LINE__, NULL); + } } + return RSE_OK; } int -event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer) +event_init_bufferevent (struct rs_connection *conn) { - if (conn->bev) + struct rs_conn_base *connbase = NULL; + assert (conn); + connbase = TO_BASE_CONN(conn); + + if (connbase->bev) return RSE_OK; - if (conn->realm->type == RS_CONN_TYPE_TCP) + if (connbase->transport == RS_CONN_TYPE_TCP) { - conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0); - if (!conn->bev) + connbase->bev = bufferevent_socket_new (connbase->ctx->evb, + connbase->fd, 0); + if (!connbase->bev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_socket_new"); + "bufferevent_socket_new"); } #if defined (RS_ENABLE_TLS) - else if (conn->realm->type == RS_CONN_TYPE_TLS) + else if (connbase->transport == RS_CONN_TYPE_TLS) { + enum bufferevent_ssl_state bev_ssl_state; + 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, - BUFFEREVENT_SSL_CONNECTING, 0); - if (!conn->bev) + bev_ssl_state = conn_originating_p (conn) + ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING; + + /* It would be convenient to pass BEV_OPT_CLOSE_ON_FREE in last + argument (options) but things seem to break when + be_openssl_ctrl() (in libevent) calls SSL_set_bio() after + BIO_new_socket() with flag=1. */ + connbase->bev = + bufferevent_openssl_socket_new (connbase->ctx->evb, connbase->fd, + conn->tls_ssl, bev_ssl_state, 0); + if (!connbase->bev) return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, "bufferevent_openssl_socket_new"); } @@ -160,7 +212,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); + connbase->transport); } return RSE_OK; @@ -169,62 +221,69 @@ 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->active_peer->addr_cache); + peer_addr = conn->active_peer->addr_cache->ai_addr; + peer_addrlen = conn->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->bev) /* TCP */ + if (conn->base_.bev) /* TCP */ { conn_activate_timeout (conn); /* Connect timeout. */ - err = bufferevent_socket_connect (p->conn->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->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->fd, sockerr, - evutil_socket_error_to_string (sockerr)); + rs_err_conn_push (conn, RSE_SOCKERR, + "%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->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", - evutil_gai_strerror (err)); + evutil_gai_strerror (err)); /* FIXME: really gai_strerror? */ return err; } @@ -232,21 +291,22 @@ event_loopbreak (struct rs_connection *conn) void event_on_disconnect (struct rs_connection *conn) { - conn->is_connecting = 0; - conn->is_connected = 0; + conn->state = RS_CONN_STATE_UNDEFINED; 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. */ +/** Internal connect event for originating connections. Returns 0 on + success and -1 on TLS certificate verification failure. */ int -event_on_connect (struct rs_connection *conn, struct rs_packet *pkt) +event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg) { - assert (!conn->is_connecting); + assert (conn->state == RS_CONN_STATE_CONNECTING); + assert (conn->active_peer); #if defined (RS_ENABLE_TLS) - if (conn_type_tls(conn) && !conn_cred_psk(conn)) + if (conn_type_tls_p (conn) && !conn_cred_psk (conn)) if (tls_verify_cert (conn) != RSE_OK) { rs_debug (("%s: server cert verification failed\n", __func__)); @@ -254,23 +314,36 @@ event_on_connect (struct rs_connection *conn, struct rs_packet *pkt) } #endif /* RS_ENABLE_TLS */ - conn->is_connected = 1; + conn->state = RS_CONN_STATE_CONNECTED; 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 (pkt) - packet_do_send (pkt); + if (msg) + message_do_send (msg); return 0; } +/** FIXME: DOC */ int -event_init_eventbase (struct rs_connection *conn) +event_on_connect_term (struct rs_connection *conn, struct rs_message *msg) { - assert (conn); - if (conn->evb) + /* TODO: verify client */ + conn->state = RS_CONN_STATE_CONNECTED; + rs_debug (("%s: WARNING: not checking client cert!!!\n", __func__)); + if (conn->callbacks.connected_cb) + conn->callbacks.connected_cb (conn->base_.user_data); + return 0; +} + +int +event_init_eventbase (struct rs_conn_base *connbase) +{ + assert (connbase); + assert (connbase->ctx); + if (connbase->ctx->evb) return RSE_OK; #if defined (DEBUG) @@ -278,10 +351,10 @@ 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) - return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, - "event_base_new"); + connbase->ctx->evb = event_base_new (); + if (!connbase->ctx->evb) + return rs_err_connbase_push_fl (connbase, RSE_EVENT, __FILE__, __LINE__, + "event_base_new"); return RSE_OK; } diff --git a/lib/event.h b/lib/event.h index bd9ec77..83f24f2 100644 --- a/lib/event.h +++ b/lib/event.h @@ -1,12 +1,13 @@ -/* Copyright 2011-2012 NORDUnet A/S. All rights reserved. +/* Copyright 2011-2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ void event_on_disconnect (struct rs_connection *conn); -int event_on_connect (struct rs_connection *conn, struct rs_packet *pkt); +int event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg); +int event_on_connect_term (struct rs_connection *conn, struct rs_message *msg); int event_loopbreak (struct rs_connection *conn); -int event_init_eventbase (struct rs_connection *conn); -int event_init_socket (struct rs_connection *conn, struct rs_peer *p); -int event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer); +int event_init_eventbase (struct rs_conn_base *connbase); +int event_init_socket (struct rs_conn_base *connbase, struct rs_peer *p); +int event_init_bufferevent (struct rs_connection *conn); void event_do_connect (struct rs_connection *conn); void event_conn_timeout_cb (int fd, short event, void *data); void event_retransmit_timeout_cb (int fd, short event, void *data); diff --git a/lib/examples/Makefile.am b/lib/examples/Makefile.am index f300627..63b6abe 100644 --- a/lib/examples/Makefile.am +++ b/lib/examples/Makefile.am @@ -1,8 +1,10 @@ AUTOMAKE_OPTIONS = foreign INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir) -AM_CFLAGS = -Wall -Werror -g +AM_CFLAGS = -Wall -Werror -g #-DDEBUG -DDEBUG_LEVENT -noinst_PROGRAMS = client +LDADD = ../libradsec.la #-lefence +CFLAGS = $(AM_CFLAGS) -DUSE_CONFIG_FILE + +noinst_PROGRAMS = client client2 server client_SOURCES = client-blocking.c -client_LDADD = ../libradsec.la #-lefence -client_CFLAGS = $(AM_CFLAGS) -DUSE_CONFIG_FILE +client2_SOURCES = client-dispatch.c diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c index cce00bf..bebde65 100644 --- a/lib/examples/client-blocking.c +++ b/lib/examples/client-blocking.c @@ -1,27 +1,40 @@ /* RADIUS/RadSec client using libradsec in blocking mode. */ +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <assert.h> #include <radsec/radsec.h> #include <radsec/request.h> #include "err.h" -#include "debug.h" /* For rs_dump_packet(). */ +#include "debug.h" /* For rs_dump_message(). */ #define SECRET "sikrit" #define USER_NAME "molgan@PROJECT-MOONSHOT.ORG" #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; struct rs_connection *conn = NULL; struct rs_request *request = NULL; - struct rs_packet *req = NULL, *resp = NULL; + 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); @@ -62,21 +85,21 @@ blocking_client (const char *config_fn, const char *configuration, } else { - if (rs_packet_create_authn_request (conn, &req, USER_NAME, USER_PW)) + if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW)) goto cleanup; - if (rs_packet_send (req, NULL)) + if (rs_message_send (req)) goto cleanup; - if (rs_conn_receive_packet (conn, req, &resp)) + if (rs_conn_receive_message (conn, req, &resp)) goto cleanup; } if (resp) { - rs_dump_packet (resp); - if (rs_packet_code (resp) == PW_ACCESS_ACCEPT) + rs_dump_message (resp); + if (rs_message_code (resp) == PW_ACCESS_ACCEPT) printf ("Good auth.\n"); else - printf ("Bad auth: %d\n", rs_packet_code (resp)); + printf ("Bad auth: %d\n", rs_message_code (resp)); } else fprintf (stderr, "%s: no response\n", __func__); @@ -85,8 +108,12 @@ 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_packet_destroy (resp); + rs_message_destroy (resp); if (request) rs_request_destroy (request); if (conn) @@ -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/examples/client-dispatch.c b/lib/examples/client-dispatch.c new file mode 100644 index 0000000..8a80ec6 --- /dev/null +++ b/lib/examples/client-dispatch.c @@ -0,0 +1,133 @@ +/* RADIUS/RadSec client using libradsec in user dispatch mode. */ + +#include <stdio.h> +#include <string.h> +#include <radsec/radsec.h> +#include <event2/event.h> +#include "debug.h" /* For rs_dump_packet(). */ + +#define CONFIG "dispatching-tls" +#define CONFIG_FILE "examples/client.conf" + +#define SECRET "sikrit" +#define USER_NAME "molgan@PROJECT-MOONSHOT.ORG" +#define USER_PW "password" + +struct state { + struct rs_packet *msg; + unsigned packet_sent_flag : 1; + unsigned packet_received_flag : 1; +}; + +static void +connected_cb (void *user_data) +{ + printf ("%s\n", __FUNCTION__); +} + +static void +disconnected_cb (void *user_data) +{ + printf ("%s\n", __FUNCTION__); +} + +static void +msg_received_cb (struct rs_packet *packet, void *user_data) +{ + struct state *state = (struct state *) user_data; + + printf ("%s\n", __FUNCTION__); + + state->msg = packet; + state->packet_received_flag = 1; +} + +static void +msg_sent_cb (void *user_data) +{ + struct state *state = (struct state *) user_data; + + printf ("%s\n", __FUNCTION__); + + rs_packet_destroy (state->msg); + state->packet_sent_flag = 1; +} + +struct rs_error * +dispatching_client (struct rs_context *ctx) +{ + struct rs_connection *conn = NULL; + struct rs_conn_callbacks cb = { connected_cb, disconnected_cb, + msg_received_cb, msg_sent_cb }; + struct rs_packet *req_msg = NULL; + struct rs_error *err = NULL; + struct state state; + + memset (&state, 0, sizeof (state)); + + if (rs_conn_create(ctx, &conn, CONFIG)) + goto out; + rs_conn_set_callbacks (conn, &cb, &state); + if (rs_packet_create_authn_request (conn, &req_msg, USER_NAME, USER_PW)) + goto out; + /* Doesn't really send the message but rather queues it for sending. + msg_received_cb() will be invoked with user_data = &state when + the message has been sent. */ + if (rs_packet_send (req_msg)) + goto out; + + while (1) + { + if (rs_conn_dispatch (conn)) + goto out; + if (state.packet_received_flag) + { + rs_dump_packet (state.msg); /* debug printout */ + if (rs_packet_code (state.msg) == PW_ACCESS_ACCEPT) + printf ("Good auth.\n"); + else + printf ("Bad auth: %d\n", rs_packet_code (state.msg)); + rs_packet_destroy (state.msg); + break; + } + } + + if (rs_conn_destroy(conn)) + goto out; + conn = NULL; + + out: + err = rs_err_ctx_pop (ctx); + if (err == RSE_OK) + err = rs_err_conn_pop (conn); + + if (conn) + rs_conn_destroy(conn); + + return err; +} + +int +main (int argc, char *argv[]) +{ + struct rs_error *err = NULL; + struct rs_context *ctx = NULL; + + if (rs_context_create(&ctx)) + goto out; + if (rs_context_read_config(ctx, CONFIG_FILE)) + goto out; + + err = dispatching_client (ctx); + + out: + if (ctx) + rs_context_destroy(ctx); + + if (err) + { + fprintf (stderr, "error: %s: %d\n", rs_err_msg (err), rs_err_code (err, 0)); + return rs_err_code (err, 1); + } + return 0; +} diff --git a/lib/examples/client-oyo.c b/lib/examples/client-oyo.c new file mode 100644 index 0000000..2cee605 --- /dev/null +++ b/lib/examples/client-oyo.c @@ -0,0 +1,66 @@ +/* RADIUS/RadSec client using libradsec in on-your-own mode. */ + +#include <sys/select.h> +#include <errno.h> +#include <stdio.h> + +int +loop () +{ + int n; + fd_set rfds, wfds, xfds; + //struct timeval timeout = {1,0}; /* 1 second. */ + + fd = FIXME; + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + FD_ZERO(&wfds); + FD_SET(fd, &wfds); + FD_ZERO(&xfds); + FD_SET(fd, &xfds); + + while (1) + { + n = select (fd + 1, &rfds, &wfds, &xfds, NULL); + if (n == 0) + { + /* Timeout. */ + fprintf (stderr, "timeout on fd %d after %d seconds\n", fd, + timeout.tv_sec); + return -1; + } + else if (n == -1) + { + /* Error. */ + perror ("select"); + return -errno; + } + else + { + /* Ready to read/write/<had error>. */ + if (FD_ISSET(fd, &rfds)) + { + printf ("reading msg\n"); + radsec_recv_blocking(fd, &msg_in); + if (!verify_packet(&msg_in)) + } + if (FD_ISSET(fd, &wfds)) + { + radsec_send(fd, &msg_out); + printf ("msg sent\n"); + } + if (FD_ISSET(fd, &xfds)) + { + fprintf (stderr, "error on fd %d\n", fd); + return -1; + } + } + } +} + +int +main (int argc, char *argv[]) +{ + return loop (); +} diff --git a/lib/examples/client.conf b/lib/examples/client.conf index a19b699..288a084 100644 --- a/lib/examples/client.conf +++ b/lib/examples/client.conf @@ -9,31 +9,58 @@ realm blocking-udp { } } +realm testcli-udp { + type = "UDP" + timeout = 2 + retries = 2 + server { + hostname = "srv1" + service = "4711" + secret = "sikrit" + } +} + realm blocking-tls { type = "TLS" timeout = 1 retries = 3 - cacertfile = "tests/demoCA/newcerts/01.pem" - certfile = "tests/demoCA/newcerts/03.pem" - certkeyfile = "tests/demoCA/private/cli1.key" + cacertfile = "/home/linus/p/radsecproxy/demoCA/newcerts/01.pem" + certfile = "/home/linus/p/radsecproxy/demoCA/newcerts/03.pem" + certkeyfile = "/home/linus/p/radsecproxy/demoCA/private/cli1.key" + #pskstr = "sikrit psk" + #pskhexstr = "deadbeef4711" + #pskid = "Client_identity" + #pskex = "PSK" server { hostname = "srv1" + # test setup: radsecproxy fronting freeradius on 2083 service = "2083" + # test setup: examples/server on 4711 + #service = "4711" secret = "sikrit" } } -realm blocking-tls-psk { +realm testcli { type = "TLS" - timeout = 1 - retries = 3 - #pskstr = "sikrit psk" - pskhexstr = "deadbeef4711" - pskid = "Client_identity" - pskex = "PSK" + cacertfile = "/home/linus/p/radsecproxy/demoCA/newcerts/01.pem" + certfile = "/home/linus/p/radsecproxy/demoCA/newcerts/03.pem" + certkeyfile = "/home/linus/p/radsecproxy/demoCA/private/cli1.key" + server { + hostname = "srv1" + service = "4711" + secret = "sikrit" + } +} + +realm dispatching-tls { + type = "TLS" + cacertfile = "/home/linus/p/radsecproxy/demoCA/newcerts/01.pem" + certfile = "/home/linus/p/radsecproxy/demoCA/newcerts/03.pem" + certkeyfile = "/home/linus/p/radsecproxy/demoCA/private/cli1.key" server { hostname = "srv1" - service = "4433" + service = "2083" secret = "sikrit" } } diff --git a/lib/examples/server.c b/lib/examples/server.c new file mode 100644 index 0000000..fb51866 --- /dev/null +++ b/lib/examples/server.c @@ -0,0 +1,174 @@ +/* RADIUS/RadSec server using libradsec. */ + +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <radsec/radsec.h> +#include <event2/event.h> +#include "debug.h" /* For rs_dump_message(). */ + +#define CONFIG_FILE "examples/server.conf" +#define CONFIG "tls" + +#define SECRET "sikrit" +#define USER_NAME "molgan@PROJECT-MOONSHOT.ORG" +#define USER_PW "password" + +static struct rs_peer * +client_filter_cb (const struct rs_listener *listener, + void *user_data) +{ + printf ("DEBUG: listener %p (user_data=%p) asking for a client filter list\n", + listener, user_data); + return NULL; +} + +static void +disco_cb (void *user_data) +{ + struct rs_connection *conn = user_data; + assert (conn); + printf ("DEBUG: conn %p disconnected\n", conn); +} + +static void +read_cb (struct rs_message *message, void *user_data) +{ + struct rs_connection *conn = user_data; + assert (conn); + printf ("DEBUG: msg received on connection %p\n", conn); + rs_dump_message (message); + //if (message_verify_response (conn, fixme)) error; +} + +static void +new_conn_cb (struct rs_connection *conn, void *user_data) +{ + const struct rs_listener *l = user_data; + struct rs_conn_callbacks cb = {NULL, /* connected */ + disco_cb, + read_cb, + NULL}; /* msg sent */ + + printf ("DEBUG: new connection on listener %p: %p, fd=%d\n", + l, conn, rs_conn_get_fd (conn)); + rs_conn_set_callbacks (conn, &cb, conn); +} + +void +err_cb (struct rs_connection *conn, void *user_data) +{ + struct rs_listener *listener = user_data; + struct rs_error *err = NULL; + assert (conn); + err = rs_err_conn_pop (conn); + + printf ("DEBUG: error on conn %p, listener %p: %d (%s)\n", conn, listener, + rs_err_code (err, 0), rs_err_msg (err)); +} + +#if 0 +void +stdin_cb (evutil_socket_t s, short flags, void *user_data) +{ + struct rs_listener *l = user_data; + + printf ("DEBUG: got data on stdin, quitting\n"); + assert (event_base_loopbreak (rs_listener_get_eventbase (l)) == 0); +} +#endif + +struct rs_error * +server (struct rs_context *ctx) +{ + int r = 0; + struct rs_error *err = NULL; + struct rs_listener *listener = NULL; + const struct rs_listener_callbacks cbs = + {client_filter_cb, new_conn_cb, err_cb}; + struct event *read_event = NULL; + + if (rs_listener_create (ctx, &listener, CONFIG)) + goto out; + rs_listener_set_callbacks (listener, &cbs, listener); + if (rs_listener_listen (listener)) + goto out; + +#if 0 + /* Listen on stdin too, for quitting the server nicely without + having to trap SIGKILL. */ + read_event = event_new (rs_listener_get_eventbase (listener), + fileno (stdin), + EV_READ, + stdin_cb, + listener); + assert (read_event != NULL); + assert (event_add (read_event, NULL) == 0); +#endif + + do + r = rs_listener_dispatch (listener); + while (r == 0); + + printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r); + if (r < 0) + printf ("DEBUG: libevent signals error: %s\n", evutil_gai_strerror (r)); + if (r == 1) + printf ("DEBUG: no events registered, exiting\n"); + + out: + err = rs_err_ctx_pop (ctx); + if (err == NULL) + err = rs_err_listener_pop (listener); + + if (read_event) + event_free (read_event); + read_event = NULL; + if (listener) + { + assert (rs_listener_close (listener) == RSE_OK); + //rs_listener_destroy (listener); + } + listener = NULL; + + return err; +} + +int +main (int argc, char *argv[]) +{ + struct rs_error *err = NULL; + struct rs_context *ctx = NULL; + + if (rs_context_create (&ctx)) + goto out; + if (rs_context_read_config (ctx, CONFIG_FILE)) + goto out; + + { /* DEBUG printouts */ + char *buf = NULL; + int err = rs_context_print_config (ctx, &buf); + assert (err == RSE_OK); + fputs (buf, stdout); + free (buf); + } + + err = server (ctx); + + out: + if (err) + { + fprintf (stderr, "%s: error: %s: %d\n", + argv[0], rs_err_msg (err), rs_err_code (err, 0)); + return rs_err_code (err, 1); + } + + if (ctx) + rs_context_destroy (ctx); + + return 0; +} diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h index e472703..7da00dd 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-2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ @@ -7,19 +7,26 @@ #ifndef _RADSEC_RADSEC_IMPL_H_ #define _RADSEC_RADSEC_IMPL_H_ 1 +#include <assert.h> #include <event2/util.h> #include <confuse.h> #if defined(RS_ENABLE_TLS) #include <openssl/ssl.h> #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, */ @@ -32,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 @@ -41,7 +59,6 @@ struct rs_credentials { char *identity; char *secret; enum rs_key_encoding secret_encoding; - unsigned int secret_len; }; struct rs_error { @@ -49,105 +66,161 @@ struct rs_error { char buf[1024]; }; -/** Configuration object for a connection. */ +/** Configuration object for a connection. */ struct rs_peer { - struct rs_connection *conn; + enum rs_peer_type type; + 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; + char *certfile; + char *certkeyfile; + struct rs_credentials *transport_cred; 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; - char *cacertfile; - char *cacertpath; - char *certfile; - char *certkeyfile; - struct rs_credentials *transport_cred; + struct rs_listener *listeners; + struct rs_peer *local_addr; struct rs_peer *peers; struct rs_realm *next; }; -/** Top configuration object. */ +/** Top configuration object. */ struct rs_config { struct rs_realm *realms; cfg_t *cfg; }; +/** Libradsec context. */ struct rs_context { struct rs_config *config; + struct rs_realm *realms; struct rs_alloc_scheme alloc_scheme; struct rs_error *err; + struct event_base *evb; }; -struct rs_connection { +/** 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; - void *user_data; + struct rs_realm *realm; /* Owned by ctx. */ + enum rs_conn_type transport; + /** For a listener, allowed client addr/port pairs. + For an outgoing connection, set of configured servers. + For an incoming connection, the peer (as the only entry). */ 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). */ - struct rs_packet *out_queue; /* Queue for outgoing UDP packets. */ + 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). */ +}; + +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 rs_conn_callbacks callbacks; + enum rs_conn_state state; + struct rs_peer *active_peer; + 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 }; -enum rs_packet_flags { - RS_PACKET_HEADER_READ, - RS_PACKET_RECEIVED, - RS_PACKET_SENT, +/** 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; + struct rs_listener *next; +}; + +enum rs_message_flags { + RS_MESSAGE_HEADER_READ, + RS_MESSAGE_RECEIVED, + RS_MESSAGE_SENT, }; struct radius_packet; -struct rs_packet { +struct rs_message { struct rs_connection *conn; unsigned int flags; uint8_t hdr[RS_HEADER_LEN]; - struct radius_packet *rpkt; /* FreeRADIUS object. */ - struct rs_packet *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. */ -#define rs_calloc(h, nmemb, size) \ - (h->alloc_scheme.calloc ? h->alloc_scheme.calloc : calloc)(nmemb, size) -#define rs_malloc(h, size) \ - (h->alloc_scheme.malloc ? h->alloc_scheme.malloc : malloc)(size) -#define rs_free(h, ptr) \ - (h->alloc_scheme.free ? h->alloc_scheme.free : free)(ptr) -#define rs_realloc(h, realloc, ptr, size) \ - (h->alloc_scheme.realloc ? h->alloc_scheme.realloc : realloc)(ptr, size) +/***********************/ +/* 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 \ + ? (h)->alloc_scheme.malloc : malloc)((size)) +#define rs_free(h, ptr) ((h)->alloc_scheme.free != NULL \ + ? (h)->alloc_scheme.free : free)((ptr)) +#define rs_realloc(h, ptr, size) ((h)->alloc_scheme.realloc != NULL \ + ? (h)->alloc_scheme.realloc : realloc)((ptr), (size)) #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_)) +#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) +{ + 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 d6150bf..6b319d3 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-2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ @@ -22,6 +22,7 @@ #ifdef HAVE_STDINT_H #include <stdint.h> #endif +#include "compat.h" enum rs_error_code { RSE_OK = 0, @@ -38,7 +39,7 @@ enum rs_error_code { RSE_BADAUTH = 12, RSE_INTERNAL = 13, RSE_SSLERR = 14, /* OpenSSL error. */ - RSE_INVALID_PKT = 15, + RSE_INVALID_MSG = 15, RSE_TIMEOUT_CONN = 16, /* Connection timeout. */ RSE_INVAL = 17, /* Invalid argument. */ RSE_TIMEOUT_IO = 18, /* I/O timeout. */ @@ -71,7 +72,7 @@ enum rs_error_code { RSE_MAX = RSE_CERT }; -enum rs_conn_type { +enum rs_conn_type { /* FIXME: Rename rs_transport_type? */ RS_CONN_TYPE_NONE = 0, RS_CONN_TYPE_UDP, RS_CONN_TYPE_TCP, @@ -118,10 +119,27 @@ typedef enum rs_attr_type_t { extern "C" { #endif +/* Backwards compatible with 0.0.2. */ +#define RSE_INVALID_PKT RSE_INVALID_MSG +#define rs_packet rs_message +#define rs_conn_packet_received_cb rs_conn_message_received_cb +#define rs_conn_packet_sent_cb rs_conn_message_sent_cb +#define rs_conn_receive_packet rs_conn_receive_message +#define rs_dump_packet rs_dump_message +#define rs_packet_append_avp rs_message_append_avp +#define rs_packet_avps rs_message_avps +#define rs_packet_code rs_message_code +#define rs_packet_create rs_message_create +#define rs_packet_create_authn_request rs_message_create_authn_request +#define rs_packet_destroy rs_message_destroy +#define rs_packet_send rs_message_send + /* Data types. */ struct rs_context; /* radsec-impl.h */ +struct rs_conn_base; /* radsec-impl.h */ struct rs_connection; /* radsec-impl.h */ -struct rs_packet; /* 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 */ struct rs_peer; /* radsec-impl.h */ @@ -142,18 +160,30 @@ struct rs_alloc_scheme { typedef void (*rs_conn_connected_cb) (void *user_data /* FIXME: peer? */ ); typedef void (*rs_conn_disconnected_cb) (void *user_data /* FIXME: reason? */ ); -typedef void (*rs_conn_packet_received_cb) (struct rs_packet *packet, - void *user_data); -typedef void (*rs_conn_packet_sent_cb) (void *user_data); +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; - /** Callback invoked when a packet was received. */ - rs_conn_packet_received_cb received_cb; - /** Callback invoked when a packet was successfully sent. */ - rs_conn_packet_sent_cb sent_cb; + /** Callback invoked when a message was received. */ + rs_conn_message_received_cb received_cb; + /** Callback invoked when a message was successfully sent. */ + rs_conn_message_sent_cb sent_cb; +}; + +typedef struct rs_peer *(*rs_listener_client_filter_cb) + (const struct rs_listener *listener, void *user_data); +typedef void (*rs_listener_new_conn_cb) + (struct rs_connection *conn, void *user_data); +typedef void (*rs_listener_error_cb) + (struct rs_connection *conn, void *user_data); +struct rs_listener_callbacks { + rs_listener_client_filter_cb client_filter_cb; + rs_listener_new_conn_cb new_conn_cb; + rs_listener_error_cb error_cb; }; typedef struct value_pair rs_avp; @@ -193,18 +223,36 @@ int rs_context_set_alloc_scheme(struct rs_context *ctx, accessed using \a rs_err_ctx_pop. */ 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, + void *user_data); +int rs_listener_listen (struct rs_listener *listener); +int rs_listener_dispatch (const struct rs_listener *listener); +int rs_listener_close (struct rs_listener *l); +struct event_base *rs_listener_get_eventbase (const struct rs_listener *l); +int rs_listener_get_fd (const struct rs_listener *l); + /****************/ /* Connection. */ /****************/ /** Create a connection. \a conn is the address of a pointer to an \a rs_connection, the output. Free the connection using \a rs_conn_destroy. Note that a connection must not be freed before - all packets associated with the connection have been freed. A - packet is associated with a connection when it's created (\a - rs_packet_create) or received (\a rs_conn_receive_packet). + all messages associated with the connection have been freed. A + message is associated with a connection when it's created + (\a rs_message_create) or received (\a rs_conn_receive_message). + + If \a config is not NULL it should be the name of a realm found in + a config file that has already been read using \a rs_context_read_config. - If \a config is not NULL it should be the name of a configuration - found in the config file read in using \a rs_context_read_config. \return On success, RSE_OK (0) is returned. On error, !0 is returned and a struct \a rs_error is pushed on the error stack for the context. The error can be accessed using \a @@ -223,10 +271,10 @@ int rs_conn_add_listener(struct rs_connection *conn, int rs_conn_disconnect (struct rs_connection *conn); /** Disconnect and free memory allocated for connection \a conn. Note - that a connection must not be freed before all packets associated - with the connection have been freed. A packet is associated with - a connection when it's created (\a rs_packet_create) or received - (\a rs_conn_receive_packet). \return RSE_OK (0) on success, !0 * + that a connection must not be freed before all messages associated + with the connection have been freed. A message is associated with + a connection when it's created (\a rs_message_create) or received + (\a rs_conn_receive_message). \return RSE_OK (0) on success, !0 * on error. On error, errno is set appropriately. */ int rs_conn_destroy(struct rs_connection *conn); @@ -238,7 +286,8 @@ int rs_conn_set_eventbase(struct rs_connection *conn, struct event_base *eb); /** Register callbacks \a cb for connection \a conn. */ void rs_conn_set_callbacks(struct rs_connection *conn, - struct rs_conn_callbacks *cb); + struct rs_conn_callbacks *cb, + void *user_data); /** Remove callbacks for connection \a conn. */ void rs_conn_del_callbacks(struct rs_connection *conn); @@ -261,88 +310,115 @@ int rs_conn_get_current_peer(struct rs_connection *conn, If \a req_msg is not NULL, a successfully received RADIUS message is verified against it. If \a pkt_out is not NULL it will upon - return contain a pointer to an \a rs_packet containing the new + return contain a pointer to an \a rs_message containing the new message. \return On error or if the connect (TCP only) or read times out, \a pkt_out will not be changed and one or more errors are pushed on \a conn (available through \a rs_err_conn_pop). */ -int rs_conn_receive_packet(struct rs_connection *conn, - struct rs_packet *request, - struct rs_packet **pkt_out); +int rs_conn_receive_message(struct rs_connection *conn, + struct rs_message *request, + struct rs_message **pkt_out); + +/** Run the dispatcher for the event base associated with \a conn. A + * wrapper around event_base_dispatch() for not having to hand out the + * event base. */ +int rs_conn_dispatch(struct rs_connection *conn); + +#if 0 +/** Get the event base associated with connection \a conn. + * \return struct event_base*. */ +struct event_base *rs_conn_get_evb(const struct rs_connection *conn); +#endif +#define rs_conn_fd rs_conn_get_fd /* Old name. */ /** Get the file descriptor associated with connection \a conn. * \return File descriptor. */ -int rs_conn_fd(struct rs_connection *conn); +int rs_conn_get_fd(struct rs_connection *conn); /** Set the timeout value for connection \a 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); /************/ -/* Packet. */ +/* Message. */ /************/ -/** Create a packet associated with connection \a conn. */ -int rs_packet_create(struct rs_connection *conn, struct rs_packet **pkt_out); - -/** Free all memory allocated for packet \a pkt. */ -void rs_packet_destroy(struct rs_packet *pkt); - -/** Send packet \a pkt on the connection associated with \a pkt. - \a user_data is passed to the \a rs_conn_packet_received_cb callback - registered with the connection. If no callback is registered with - the connection, the event loop is run by \a rs_packet_send and it - blocks until the full packet has been sent. Note that sending can - fail in several ways, f.ex. if the transmission protocol in use - is connection oriented (\a RS_CONN_TYPE_TCP and \a RS_CONN_TYPE_TLS) - and the connection can not be established. Also note that no - retransmission is done, something that is required for connectionless - transport protocols (\a RS_CONN_TYPE_UDP and \a RS_CONN_TYPE_DTLS). - The "request" API with \a rs_request_send can help with this. +/** Create a message associated with connection \a conn. */ +int rs_message_create(struct rs_connection *conn, struct rs_message **pkt_out); + +/** Free all memory allocated for message \a msg. */ +void rs_message_destroy(struct rs_message *msg); + +/** Send \a msg on the connection associated with \a msg. + If no callback is registered with the connection + (\a rs_conn_set_callbacks), the event loop is run by + \a rs_message_send and it blocks until the message has been + succesfully sent. + + Note that sending can fail in several ways, f.ex. if the + transmission protocol in use is connection oriented + (\a RS_CONN_TYPE_TCP and \a RS_CONN_TYPE_TLS) and the connection + can not be established. + + Also note that no retransmission is being done. This is required + for connectionless transport protocols (\a RS_CONN_TYPE_UDP and + \a RS_CONN_TYPE_DTLS). The "request" API with \a rs_request_send can + help with this. \return On success, RSE_OK (0) is returned. On error, !0 is returned and a struct \a rs_error is pushed on the error stack for the connection. The error can be accessed using \a rs_err_conn_pop. */ -int rs_packet_send(struct rs_packet *pkt, void *user_data); - -/** Create a RADIUS authentication request packet associated with - connection \a conn. Optionally, User-Name and User-Password - attributes are added to the packet using the data in \a user_name - and \a user_pw. */ -int rs_packet_create_authn_request(struct rs_connection *conn, - struct rs_packet **pkt, - const char *user_name, - const char *user_pw); - -/*** Append \a tail to packet \a pkt. */ +int rs_message_send(struct rs_message *msg); + +/** Create a RADIUS authentication request message associated with + connection \a conn. Optionally, User-Name and User-Password + attributes are added to the message using the data in \a user_name + and \a user_pw. + FIXME: describe what RADIUS shared secret is being used */ +int rs_message_create_authn_request(struct rs_connection *conn, + struct rs_message **msg, + const char *user_name, + const char *user_pw); + +/*** Append \a tail to message \a msg. */ int -rs_packet_append_avp(struct rs_packet *pkt, - unsigned int attribute, unsigned int vendor, - const void *data, size_t data_len); +rs_message_append_avp(struct rs_message *msg, + unsigned int attribute, unsigned int vendor, + const void *data, size_t data_len); -/*** Get pointer to \a pkt attribute value pairs. */ +/*** Get pointer to \a msg attribute value pairs. */ void -rs_packet_avps(struct rs_packet *pkt, rs_avp ***vps); +rs_message_avps(struct rs_message *msg, rs_avp ***vps); -/*** Get RADIUS packet type of \a pkt. */ +/*** Get RADIUS message type of \a msg. */ unsigned int -rs_packet_code(struct rs_packet *pkt); +rs_message_code(struct rs_message *msg); -/*** Get RADIUS AVP from \a pkt. */ +/*** Get RADIUS AVP from \a msg. */ rs_const_avp * -rs_packet_find_avp(struct rs_packet *pkt, unsigned int attr, unsigned int vendor); +rs_message_find_avp(struct rs_message *msg, unsigned int attr, + unsigned int vendor); -/*** Set packet identifier in \a pkt; returns old identifier */ +/*** Set packet identifier in \a msg; returns old identifier */ int -rs_packet_set_id (struct rs_packet *pkt, int id); +rs_message_set_id (struct rs_message *msg, int id); /************/ /* Config. */ @@ -381,9 +457,17 @@ 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. */ + \a conn or NULL if there are no errors in the FIFO. */ struct rs_error *rs_err_conn_pop(struct rs_connection *conn); +struct rs_error *rs_err_connbase_pop(struct rs_conn_base *connbase); +struct rs_error *rs_err_listener_pop(struct rs_listener *l); int rs_err_conn_peek_code (struct rs_connection *conn); void rs_err_free(struct rs_error *err); diff --git a/lib/include/radsec/request-impl.h b/lib/include/radsec/request-impl.h index 97335e5..685a666 100644 --- a/lib/include/radsec/request-impl.h +++ b/lib/include/radsec/request-impl.h @@ -12,7 +12,7 @@ struct rs_request { struct rs_connection *conn; struct event *timer; - struct rs_packet *req_msg; + struct rs_message *req_msg; struct rs_conn_callbacks saved_cb; void *saved_user_data; }; diff --git a/lib/include/radsec/request.h b/lib/include/radsec/request.h index d4c72b3..c686de3 100644 --- a/lib/include/radsec/request.h +++ b/lib/include/radsec/request.h @@ -7,6 +7,10 @@ #ifndef _RADSEC_REQUEST_H_ #define _RADSEC_REQUEST_H_ 1 +/* Backwards compatible with 0.0.2. */ +#define rs_request_add_reqpkt rs_request_add_reqmsg +#define rs_request_get_reqpkt rs_request_get_reqmsg + struct rs_request; #if defined (__cplusplus) @@ -16,9 +20,8 @@ extern "C" { /** Create a request associated with connection \a conn. */ int rs_request_create(struct rs_connection *conn, struct rs_request **req_out); -/** Add RADIUS request message \a req_msg to request \a req. - FIXME: Rename to rs_request_add_reqmsg? */ -void rs_request_add_reqpkt(struct rs_request *req, struct rs_packet *req_msg); +/** Add RADIUS request message \a req_msg to request \a req. */ +void rs_request_add_reqmsg(struct rs_request *req, struct rs_message *req_msg); /** Create a request associated with connection \a conn containing a newly created RADIUS authentication message, possibly with \a @@ -33,15 +36,15 @@ int rs_request_create_authn(struct rs_connection *conn, response is put in \a resp_msg (if not NULL). NOTE: At present, no more than one outstanding request to a given realm is supported. This will change in a future version. */ -int rs_request_send(struct rs_request *req, struct rs_packet **resp_msg); +int rs_request_send(struct rs_request *req, struct rs_message **resp_msg); /** Free all memory allocated by request \a req including any request - packet associated with the request. Note that a request must be + message associated with the request. Note that a request must be freed before its associated connection can be freed. */ void rs_request_destroy(struct rs_request *req); /** Return request message in request \a req. */ -struct rs_packet *rs_request_get_reqmsg(const struct rs_request *req); +struct rs_message *rs_request_get_reqmsg(const struct rs_request *req); #if defined (__cplusplus) } 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; +} diff --git a/lib/listener.h b/lib/listener.h new file mode 100644 index 0000000..8f7c543 --- /dev/null +++ b/lib/listener.h @@ -0,0 +1,14 @@ +/* Copyright 2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +/** Maximum number of pending connection requests. */ +#define LISTENER_BACKLOG -1 + +void listener_accept_cb_(struct evconnlistener *evconnlistener, + evutil_socket_t fd, + struct sockaddr *sa, + int socklen, + void *data); +void listener_err_cb_(struct evconnlistener *listener, void *user_data); +struct rs_listener *listener_create(struct rs_context *ctx, + struct rs_listener **rootp); diff --git a/lib/message.c b/lib/message.c new file mode 100644 index 0000000..a40e00b --- /dev/null +++ b/lib/message.c @@ -0,0 +1,257 @@ +/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <radius/client.h> +#include <event2/bufferevent.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#include "conn.h" +#include "debug.h" +#include "message.h" + +#if defined (DEBUG) +#include <netdb.h> +#include <sys/socket.h> +#include <event2/buffer.h> +#endif + +int +message_verify_response (struct rs_connection *conn, + struct rs_message *response, + struct rs_message *request) +{ + int err; + + assert (conn); + assert (conn->active_peer); + assert (conn->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); + + /* Verify header and message authenticator. */ + err = nr_packet_verify (response->rpkt, request->rpkt); + if (err) + { + if (conn->state == RS_CONN_STATE_CONNECTED) + rs_conn_disconnect(conn); + return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__, + "nr_packet_verify"); + } + + /* Decode and decrypt. */ + err = nr_packet_decode (response->rpkt, request->rpkt); + if (err) + { + if (conn->state == RS_CONN_STATE_CONNECTED) + rs_conn_disconnect(conn); + return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__, + "nr_packet_decode"); + } + + return RSE_OK; +} + + +/* Badly named function for preparing a RADIUS message and queue it. + FIXME: Rename. */ +int +message_do_send (struct rs_message *msg) +{ + int err; + + assert (msg); + assert (msg->conn); + assert (msg->conn->active_peer); + assert (msg->conn->active_peer->secret); + assert (msg->rpkt); + + msg->rpkt->secret = msg->conn->active_peer->secret; + msg->rpkt->sizeof_secret = strlen (msg->rpkt->secret); + + /* Encode message. */ + err = nr_packet_encode (msg->rpkt, NULL); + if (err < 0) + return rs_err_conn_push_fl (msg->conn, -err, __FILE__, __LINE__, + "nr_packet_encode"); + /* Sign message. */ + err = nr_packet_sign (msg->rpkt, NULL); + if (err < 0) + return rs_err_conn_push_fl (msg->conn, -err, __FILE__, __LINE__, + "nr_packet_sign"); +#if defined (DEBUG) + if (msg->conn->active_peer->addr_cache) + { + char host[80], serv[80]; + getnameinfo (msg->conn->active_peer->addr_cache->ai_addr, + msg->conn->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)); + rs_dump_message (msg); + } +#endif + + /* Put message in output buffer. */ + if (msg->conn->base_.bev) /* TCP. */ + { + 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__, + "bufferevent_write: %s", + evutil_gai_strerror (err)); + } + else /* UDP. */ + { + struct rs_message **pp = &msg->conn->out_queue; + + while (*pp && (*pp)->next) + *pp = (*pp)->next; + *pp = msg; + } + + return RSE_OK; +} + +/* Public functions. */ +int +rs_message_create (struct rs_connection *conn, struct rs_message **msg_out) +{ + struct rs_message *p; + RADIUS_PACKET *rpkt; + int err; + + *msg_out = NULL; + + rpkt = rs_malloc (conn->base_.ctx, sizeof(*rpkt) + RS_MAX_PACKET_LEN); + if (rpkt == NULL) + return rs_err_conn_push (conn, RSE_NOMEM, __func__); + + err = nr_packet_init (rpkt, NULL, NULL, + PW_ACCESS_REQUEST, + rpkt + 1, RS_MAX_PACKET_LEN); + if (err < 0) + return rs_err_conn_push (conn, -err, __func__); + + p = (struct rs_message *) rs_calloc (conn->base_.ctx, 1, sizeof (*p)); + if (p == NULL) + { + rs_free (conn->base_.ctx, rpkt); + return rs_err_conn_push (conn, RSE_NOMEM, __func__); + } + p->conn = conn; + p->rpkt = rpkt; + + *msg_out = p; + return RSE_OK; +} + +int +rs_message_create_authn_request (struct rs_connection *conn, + struct rs_message **msg_out, + const char *user_name, + const char *user_pw) +{ + struct rs_message *msg; + int err; + + if (rs_message_create (conn, msg_out)) + return -1; + + msg = *msg_out; + msg->rpkt->code = PW_ACCESS_REQUEST; + + if (user_name) + { + /* FIXME: use new rs_message_add_avp() */ + err = rs_message_append_avp (msg, PW_USER_NAME, 0, user_name, 0); + if (err) + return err; + } + + if (user_pw) + { + /* FIXME: msg->rpkt->secret = secret; + FIXME: use new rs_message_add_avp() */ + msg->rpkt->secret = "sikrit"; /* FIXME */ + err = rs_message_append_avp (msg, PW_USER_PASSWORD, 0, user_pw, 0); + if (err) + return err; + } + + return RSE_OK; +} + +void +rs_message_destroy (struct rs_message *msg) +{ + assert (msg); + assert (msg->conn); + assert (msg->conn->base_.ctx); + + rs_avp_free (&msg->rpkt->vps); + rs_free (msg->conn->base_.ctx, msg->rpkt); + rs_free (msg->conn->base_.ctx, msg); +} + +int +rs_message_append_avp (struct rs_message *msg, + unsigned int attr, unsigned int vendor, + const void *data, size_t data_len) +{ + const DICT_ATTR *da; + int err; + + assert (msg); + + da = nr_dict_attr_byvalue (attr, vendor); + if (da == NULL) + return RSE_ATTR_TYPE_UNKNOWN; + + err = nr_packet_attr_append (msg->rpkt, NULL, da, data, data_len); + if (err < 0) + return rs_err_conn_push (msg->conn, -err, __func__); + + return RSE_OK; +} + +void +rs_message_avps (struct rs_message *msg, rs_avp ***vps) +{ + assert (msg); + *vps = &msg->rpkt->vps; +} + +unsigned int +rs_message_code (struct rs_message *msg) +{ + assert (msg); + return msg->rpkt->code; +} + +rs_const_avp * +rs_message_find_avp (struct rs_message *msg, unsigned int attr, unsigned int vendor) +{ + assert (msg); + return rs_avp_find_const (msg->rpkt->vps, attr, vendor); +} + +int +rs_message_set_id (struct rs_message *msg, int id) +{ + int old = msg->rpkt->id; + + msg->rpkt->id = id; + + return old; +} diff --git a/lib/message.h b/lib/message.h new file mode 100644 index 0000000..82f2653 --- /dev/null +++ b/lib/message.h @@ -0,0 +1,7 @@ +/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. + See LICENSE for licensing information. */ + +int message_do_send (struct rs_message *msg); +int message_verify_response (struct rs_connection *conn, + struct rs_message *response, + struct rs_message *request); diff --git a/lib/packet.c b/lib/packet.c deleted file mode 100644 index a0b3eb2..0000000 --- a/lib/packet.c +++ /dev/null @@ -1,253 +0,0 @@ -/* Copyright 2010-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 <radius/client.h> -#include <event2/bufferevent.h> -#include <radsec/radsec.h> -#include <radsec/radsec-impl.h> -#include "conn.h" -#include "debug.h" -#include "packet.h" - -#if defined (DEBUG) -#include <netdb.h> -#include <sys/socket.h> -#include <event2/buffer.h> -#endif - -int -packet_verify_response (struct rs_connection *conn, - struct rs_packet *response, - struct rs_packet *request) -{ - int err; - - assert (conn); - assert (conn->active_peer); - assert (conn->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); - - /* Verify header and message authenticator. */ - err = nr_packet_verify (response->rpkt, request->rpkt); - if (err) - { - if (conn->is_connected) - rs_conn_disconnect(conn); - return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__, - "nr_packet_verify"); - } - - /* Decode and decrypt. */ - err = nr_packet_decode (response->rpkt, request->rpkt); - if (err) - { - if (conn->is_connected) - rs_conn_disconnect(conn); - return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__, - "nr_packet_decode"); - } - - return RSE_OK; -} - - -/* Badly named function for preparing a RADIUS message and queue it. - FIXME: Rename. */ -int -packet_do_send (struct rs_packet *pkt) -{ - int err; - - assert (pkt); - assert (pkt->conn); - assert (pkt->conn->active_peer); - assert (pkt->conn->active_peer->secret); - assert (pkt->rpkt); - - pkt->rpkt->secret = pkt->conn->active_peer->secret; - pkt->rpkt->sizeof_secret = strlen (pkt->rpkt->secret); - - /* Encode message. */ - err = nr_packet_encode (pkt->rpkt, NULL); - if (err < 0) - return rs_err_conn_push_fl (pkt->conn, -err, __FILE__, __LINE__, - "nr_packet_encode"); - /* Sign message. */ - err = nr_packet_sign (pkt->rpkt, NULL); - if (err < 0) - return rs_err_conn_push_fl (pkt->conn, -err, __FILE__, __LINE__, - "nr_packet_sign"); -#if defined (DEBUG) - { - char host[80], serv[80]; - - getnameinfo (pkt->conn->active_peer->addr_cache->ai_addr, - pkt->conn->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)); - rs_dump_packet (pkt); - } -#endif - - /* Put message in output buffer. */ - if (pkt->conn->bev) /* TCP. */ - { - int err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data, - pkt->rpkt->length); - if (err < 0) - return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, - "bufferevent_write: %s", - evutil_gai_strerror (err)); - } - else /* UDP. */ - { - struct rs_packet **pp = &pkt->conn->out_queue; - - while (*pp && (*pp)->next) - *pp = (*pp)->next; - *pp = pkt; - } - - return RSE_OK; -} - -/* Public functions. */ -int -rs_packet_create (struct rs_connection *conn, struct rs_packet **pkt_out) -{ - struct rs_packet *p; - RADIUS_PACKET *rpkt; - int err; - - *pkt_out = NULL; - - rpkt = rs_malloc (conn->ctx, sizeof(*rpkt) + RS_MAX_PACKET_LEN); - if (rpkt == NULL) - return rs_err_conn_push (conn, RSE_NOMEM, __func__); - - err = nr_packet_init (rpkt, NULL, NULL, - PW_ACCESS_REQUEST, - rpkt + 1, RS_MAX_PACKET_LEN); - if (err < 0) - return rs_err_conn_push (conn, -err, __func__); - - p = (struct rs_packet *) rs_calloc (conn->ctx, 1, sizeof (*p)); - if (p == NULL) - { - rs_free (conn->ctx, rpkt); - return rs_err_conn_push (conn, RSE_NOMEM, __func__); - } - p->conn = conn; - p->rpkt = rpkt; - - *pkt_out = p; - return RSE_OK; -} - -int -rs_packet_create_authn_request (struct rs_connection *conn, - struct rs_packet **pkt_out, - const char *user_name, const char *user_pw) -{ - struct rs_packet *pkt; - int err; - - if (rs_packet_create (conn, pkt_out)) - return -1; - - pkt = *pkt_out; - pkt->rpkt->code = PW_ACCESS_REQUEST; - - if (user_name) - { - err = rs_packet_append_avp (pkt, PW_USER_NAME, 0, user_name, 0); - if (err) - return err; - } - - if (user_pw) - { - err = rs_packet_append_avp (pkt, PW_USER_PASSWORD, 0, user_pw, 0); - if (err) - return err; - } - - return RSE_OK; -} - -void -rs_packet_destroy (struct rs_packet *pkt) -{ - assert (pkt); - assert (pkt->conn); - assert (pkt->conn->ctx); - - rs_avp_free (&pkt->rpkt->vps); - rs_free (pkt->conn->ctx, pkt->rpkt); - rs_free (pkt->conn->ctx, pkt); -} - -int -rs_packet_append_avp (struct rs_packet *pkt, - unsigned int attr, unsigned int vendor, - const void *data, size_t data_len) -{ - const DICT_ATTR *da; - int err; - - assert (pkt); - - da = nr_dict_attr_byvalue (attr, vendor); - if (da == NULL) - return rs_err_conn_push (pkt->conn, RSE_ATTR_TYPE_UNKNOWN, __func__); - - err = nr_packet_attr_append (pkt->rpkt, NULL, da, data, data_len); - if (err < 0) - return rs_err_conn_push (pkt->conn, -err, __func__); - - return RSE_OK; -} - -void -rs_packet_avps (struct rs_packet *pkt, rs_avp ***vps) -{ - assert (pkt); - *vps = &pkt->rpkt->vps; -} - -unsigned int -rs_packet_code (struct rs_packet *pkt) -{ - assert (pkt); - return pkt->rpkt->code; -} - -rs_const_avp * -rs_packet_find_avp (struct rs_packet *pkt, unsigned int attr, unsigned int vendor) -{ - assert (pkt); - return rs_avp_find_const (pkt->rpkt->vps, attr, vendor); -} - -int -rs_packet_set_id (struct rs_packet *pkt, int id) -{ - int old = pkt->rpkt->id; - - pkt->rpkt->id = id; - - return old; -} diff --git a/lib/packet.h b/lib/packet.h deleted file mode 100644 index 7cdbb35..0000000 --- a/lib/packet.h +++ /dev/null @@ -1,7 +0,0 @@ -/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved. - See LICENSE for licensing information. */ - -int packet_do_send (struct rs_packet *pkt); -int packet_verify_response (struct rs_connection *conn, - struct rs_packet *response, - struct rs_packet *request); @@ -1,4 +1,4 @@ -/* Copyright 2010-2012 NORDUnet A/S. All rights reserved. +/* Copyright 2010-2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ #if defined HAVE_CONFIG_H @@ -22,8 +22,8 @@ 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. */ + if (conn->active_peer == NULL) + conn->active_peer = TO_BASE_CONN (conn)->peers; /* From the top. */ return conn->active_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->ctx, &conn->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->ctx); + assert (peer->connbase); + assert (peer->connbase->ctx); - peer->hostname = rs_strdup (peer->conn->ctx, hostname); - peer->service = rs_strdup (peer->conn->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; +} @@ -1,5 +1,6 @@ -/* Copyright 2011 NORDUnet A/S. All rights reserved. +/* Copyright 2011,2013 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ 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.c b/lib/radsec.c index efd2dc3..fed02c3 100644 --- a/lib/radsec.c +++ b/lib/radsec.c @@ -107,11 +107,11 @@ rs_context_destroy (struct rs_context *ctx) evutil_freeaddrinfo (p->addr_cache); p->addr_cache = NULL; } + rs_free (ctx, p->transport_cred); p = p->next; rs_free (ctx, tmp); } free (r->name); - rs_free (ctx, r->transport_cred); r = r->next; rs_free (ctx, tmp); } @@ -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 f234082..9dd7186 100644 --- a/lib/radsec.sym +++ b/lib/radsec.sym @@ -41,10 +41,11 @@ rs_conn_create rs_conn_del_callbacks rs_conn_destroy rs_conn_disconnect -rs_conn_fd +rs_conn_dispatch +rs_conn_get_fd rs_conn_get_callbacks rs_conn_get_current_peer -rs_conn_receive_packet +rs_conn_receive_message rs_conn_select_peer rs_conn_set_callbacks rs_conn_set_eventbase @@ -52,9 +53,10 @@ rs_conn_set_timeout rs_conn_set_type rs_context_create rs_context_destroy +rs_context_print_config rs_context_read_config rs_context_set_alloc_scheme -rs_dump_packet +rs_dump_message rs_err_code rs_err_conn_peek_code rs_err_conn_pop @@ -64,15 +66,26 @@ rs_err_ctx_pop rs_err_ctx_push rs_err_ctx_push_fl rs_err_free +rs_err_listener_pop rs_err_msg -rs_packet_append_avp -rs_packet_avps -rs_packet_code -rs_packet_create -rs_packet_create_authn_request -rs_packet_destroy -rs_packet_send -rs_peer_create +rs_listener_create +rs_listener_close +rs_listener_dispatch +rs_listener_get_eventbase +rs_listener_get_fd +rs_listener_listen +rs_listener_set_callbacks +rs_message_append_avp +rs_message_avps +rs_message_code +rs_message_create +rs_message_create_authn_request +rs_message_destroy +rs_message_send +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/request.c b/lib/request.c index 3a8b6dd..b75dd92 100644 --- a/lib/request.c +++ b/lib/request.c @@ -1,4 +1,4 @@ -/* Copyright 2010-2011 NORDUnet A/S. All rights reserved. +/* Copyright 2010-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); @@ -41,7 +41,7 @@ rs_request_create (struct rs_connection *conn, struct rs_request **req_out) } void -rs_request_add_reqpkt (struct rs_request *req, struct rs_packet *req_msg) +rs_request_add_reqmsg (struct rs_request *req, struct rs_message *req_msg) { assert (req); req->req_msg = req_msg; @@ -59,7 +59,7 @@ rs_request_create_authn (struct rs_connection *conn, if (rs_request_create (conn, &req)) return -1; - if (rs_packet_create_authn_request (conn, &req->req_msg, user_name, user_pw)) + if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw)) return -1; if (req_out) @@ -72,11 +72,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_packet_destroy (request->req_msg); - rs_free (request->conn->ctx, request); + rs_message_destroy (request->req_msg); + rs_free (request->conn->base_.ctx, request); } static void @@ -88,7 +88,7 @@ _rand_rt (struct timeval *res, uint32_t rtprev, uint32_t factor) } int -rs_request_send (struct rs_request *request, struct rs_packet **resp_msg) +rs_request_send (struct rs_request *request, struct rs_message **resp_msg) { int r = 0; struct rs_connection *conn = NULL; @@ -102,7 +102,7 @@ rs_request_send (struct rs_request *request, struct rs_packet **resp_msg) if (!request || !request->conn || !request->req_msg || !resp_msg) return rs_err_conn_push_fl (conn, RSE_INVAL, __FILE__, __LINE__, NULL); conn = request->conn; - assert (!conn_user_dispatch_p (conn)); /* This function is high level. */ + assert (!conn_user_dispatch_p (conn)); /* This function is high level. */ gettimeofday (&end, NULL); end.tv_sec += MRD; @@ -111,12 +111,12 @@ rs_request_send (struct rs_request *request, struct rs_packet **resp_msg) { rs_conn_set_timeout (conn, &rt); - r = rs_packet_send (request->req_msg, NULL); + r = rs_message_send (request->req_msg); if (r == RSE_OK) { - r = rs_conn_receive_packet (request->conn, - request->req_msg, - resp_msg); + r = rs_conn_receive_message (request->conn, + request->req_msg, + resp_msg); if (r == RSE_OK) break; /* Success. */ @@ -148,7 +148,7 @@ rs_request_send (struct rs_request *request, struct rs_packet **resp_msg) return r; } -struct rs_packet * +struct rs_message * rs_request_get_reqmsg (const struct rs_request *request) { assert (request); @@ -11,7 +11,7 @@ #include <radsec/radsec.h> #include <radsec/radsec-impl.h> #include "debug.h" -#include "packet.h" +#include "message.h" #include "event.h" #include "peer.h" #include "conn.h" @@ -19,9 +19,9 @@ #include "udp.h" static int -_conn_open (struct rs_connection *conn, struct rs_packet *pkt) +_conn_open (struct rs_connection *conn, struct rs_message *msg) { - if (event_init_eventbase (conn)) + if (event_init_eventbase (TO_BASE_CONN (conn))) return -1; if (!conn->active_peer) @@ -29,28 +29,28 @@ _conn_open (struct rs_connection *conn, struct rs_packet *pkt) if (!conn->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->base_, 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; - if (event_init_bufferevent (conn, conn->active_peer)) + if (event_init_bufferevent (conn)) return -1; } else { - if (udp_init (conn, pkt)) + if (udp_init (conn, msg)) return -1; if (udp_init_retransmit_timer (conn)) 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,54 +58,56 @@ _conn_open (struct rs_connection *conn, struct rs_packet *pkt) 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->active_peer != NULL; } /* User callback used when we're dispatching for user. */ static void _wcb (void *user_data) { - struct rs_packet *pkt = (struct rs_packet *) user_data; - assert (pkt); - pkt->flags |= RS_PACKET_SENT; - if (pkt->conn->bev) - bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); + struct rs_message *msg = (struct rs_message *) user_data; + assert (msg); + msg->flags |= RS_MESSAGE_SENT; + if (msg->conn->base_.bev) + bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ); else - event_del (pkt->conn->wev); + event_del (msg->conn->base_.wev); } int -rs_packet_send (struct rs_packet *pkt, void *user_data) +rs_message_send (struct rs_message *msg) { struct rs_connection *conn = NULL; int err = 0; - assert (pkt); - assert (pkt->conn); - conn = pkt->conn; + assert (msg); + assert (msg->conn); + conn = msg->conn; if (_conn_is_open_p (conn)) - packet_do_send (pkt); + message_do_send (msg); else - if (_conn_open (conn, pkt)) + 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; - - if (conn->bev) /* TCP */ + if (conn->base_.bev) /* TCP */ { - bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, pkt); - 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), pkt); - 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", @@ -115,19 +117,20 @@ rs_packet_send (struct rs_packet *pkt, void *user_data) /* Do dispatch, unless the user wants to do it herself. */ if (!conn_user_dispatch_p (conn)) { + /* Blocking mode. */ conn->callbacks.sent_cb = _wcb; - conn->user_data = pkt; + 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 ((pkt->flags & RS_PACKET_SENT) == 0) + if ((msg->flags & RS_MESSAGE_SENT) == 0) { assert (rs_err_conn_peek_code (conn)); return rs_err_conn_peek_code (conn); @@ -16,7 +16,7 @@ #include <radsec/radsec.h> #include <radsec/radsec-impl.h> #include "tcp.h" -#include "packet.h" +#include "message.h" #include "conn.h" #include "debug.h" #include "event.h" @@ -25,133 +25,131 @@ #include <event2/buffer.h> #endif -/** Read one RADIUS packet header. Return !0 on error. */ +/** Read one RADIUS message header. Return !0 on error. */ static int -_read_header (struct rs_packet *pkt) +_read_header (struct rs_message *msg) { size_t n = 0; - n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN); + n = bufferevent_read (TO_BASE_CONN(msg->conn)->bev, msg->hdr, RS_HEADER_LEN); if (n == RS_HEADER_LEN) { - pkt->flags |= RS_PACKET_HEADER_READ; - pkt->rpkt->length = (pkt->hdr[2] << 8) + pkt->hdr[3]; - if (pkt->rpkt->length < 20 || pkt->rpkt->length > RS_MAX_PACKET_LEN) - { - rs_debug (("%s: invalid packet length: %d\n", - __func__, pkt->rpkt->length)); - rs_conn_disconnect (pkt->conn); - return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT, - "invalid packet length: %d", - pkt->rpkt->length); - } - memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN); - bufferevent_setwatermark (pkt->conn->bev, EV_READ, - pkt->rpkt->length - RS_HEADER_LEN, 0); - rs_debug (("%s: packet header read, total pkt len=%d\n", - __func__, pkt->rpkt->length)); + msg->flags |= RS_MESSAGE_HEADER_READ; + msg->rpkt->length = (msg->hdr[2] << 8) + msg->hdr[3]; + if (msg->rpkt->length < 20 || msg->rpkt->length > RS_MAX_PACKET_LEN) + { + rs_debug (("%s: invalid packet length: %d\n", __func__, + msg->rpkt->length)); + rs_conn_disconnect (msg->conn); + return rs_err_conn_push (msg->conn, RSE_INVALID_MSG, + "invalid message length: %d", + msg->rpkt->length); + } + memcpy (msg->rpkt->data, msg->hdr, RS_HEADER_LEN); + bufferevent_setwatermark (TO_BASE_CONN(msg->conn)->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)); } else if (n < 0) - { - rs_debug (("%s: buffer frozen while reading header\n", __func__)); - } + rs_debug (("%s: buffer frozen while reading header\n", __func__)); else /* Error: libevent gave us less than the low watermark. */ { rs_debug (("%s: got: %d octets reading header\n", __func__, n)); - rs_conn_disconnect (pkg->conn); - return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__, - "got %d octets reading header", n); + rs_conn_disconnect (msg->conn); + return rs_err_conn_push (msg->conn, RSE_INTERNAL, + "got %d octets reading header", n); } - return 0; + return RSE_OK; } /** Read a message, check that it's valid RADIUS and hand it off to registered user callback. - The packet is read from the bufferevent associated with \a pkt and - the data is stored in \a pkt->rpkt. + The message is read from the bufferevent associated with \a msg and + the data is stored in \a msg->rpkt. Return 0 on success and !0 on failure. */ static int -_read_packet (struct rs_packet *pkt) +_read_message (struct rs_message *msg) { size_t n = 0; int err; - rs_debug (("%s: trying to read %d octets of packet data\n", __func__, - pkt->rpkt->length - RS_HEADER_LEN)); + rs_debug (("%s: trying to read %d octets of message data\n", __func__, + msg->rpkt->length - RS_HEADER_LEN)); - n = bufferevent_read (pkt->conn->bev, - pkt->rpkt->data + RS_HEADER_LEN, - pkt->rpkt->length - RS_HEADER_LEN); + n = bufferevent_read (msg->conn->base_.bev, + msg->rpkt->data + RS_HEADER_LEN, + msg->rpkt->length - RS_HEADER_LEN); - rs_debug (("%s: read %ld octets of packet data\n", __func__, n)); + rs_debug (("%s: read %ld octets of message data\n", __func__, n)); - if (n == pkt->rpkt->length - RS_HEADER_LEN) + if (n == msg->rpkt->length - RS_HEADER_LEN) { - bufferevent_disable (pkt->conn->bev, EV_READ); - rs_debug (("%s: complete packet read\n", __func__)); - pkt->flags &= ~RS_PACKET_HEADER_READ; - memset (pkt->hdr, 0, sizeof(*pkt->hdr)); + 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)); - /* Checks done by rad_packet_ok: + /* Checks done by nr_packet_ok: - lenghts (FIXME: checks really ok for tcp?) - invalid code field - attribute lengths >= 2 - attribute sizes adding up correctly */ - err = nr_packet_ok (pkt->rpkt); - if (err != RSE_OK) + err = nr_packet_ok (msg->rpkt); + if (err) { rs_debug (("%s: %d: invalid packet\n", __func__, -err)); - rs_conn_disconnect (pkt->conn); - return rs_err_conn_push_fl (pkt->conn, -err, __FILE__, __LINE__, - "invalid packet"); - } + rs_conn_disconnect (msg->conn); + return rs_err_conn_push (msg->conn, -err, "invalid message"); + } #if defined (DEBUG) /* Find out what happens if there's data left in the buffer. */ { size_t rest = 0; - rest = evbuffer_get_length (bufferevent_get_input (pkt->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)); } #endif - /* Hand over message to user. This changes ownership of pkt. + /* Hand over message to user. This changes ownership of msg. Don't touch it afterwards -- it might have been freed. */ - if (pkt->conn->callbacks.received_cb) - pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data); + if (msg->conn->callbacks.received_cb) + msg->conn->callbacks.received_cb (msg, msg->conn->base_.user_data); } else if (n < 0) /* Buffer frozen. */ - rs_debug (("%s: buffer frozen when reading packet\n", __func__)); - else /* Short packet. */ + rs_debug (("%s: buffer frozen when reading message\n", __func__)); + else /* Short message. */ rs_debug (("%s: waiting for another %d octets\n", __func__, - pkt->rpkt->length - RS_HEADER_LEN - n)); + msg->rpkt->length - RS_HEADER_LEN - n)); return 0; } /* The read callback for TCP. - Read exactly one RADIUS message from BEV and store it in struct - rs_packet passed in USER_DATA. + Read exactly one RADIUS message from \a bev and store it in the + struct rs_message passed in \a user_data. Inform upper layer about successful reception of received RADIUS - message by invoking conn->callbacks.recevied_cb(), if !NULL. */ + message by invoking conn->callbacks.recevied_cb(), if not NULL. */ void tcp_read_cb (struct bufferevent *bev, void *user_data) { - struct rs_packet *pkt = (struct rs_packet *) user_data; + struct rs_message *msg = (struct rs_message *) user_data; - assert (pkt); - assert (pkt->conn); - assert (pkt->rpkt); + assert (msg); + assert (msg->conn); + assert (msg->rpkt); - pkt->rpkt->sockfd = pkt->conn->fd; - pkt->rpkt->vps = NULL; /* FIXME: can this be done when initializing pkt? */ + 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 fails. Read a message and have it dispatched to the user @@ -159,16 +157,16 @@ tcp_read_cb (struct bufferevent *bev, void *user_data) Room for improvement: Peek inside buffer (evbuffer_copyout()) to avoid the extra copying. */ - if ((pkt->flags & RS_PACKET_HEADER_READ) == 0) - if (_read_header (pkt)) - return; /* Error. */ - _read_packet (pkt); + if ((msg->flags & RS_MESSAGE_HEADER_READ) == 0) + if (_read_header (msg)) + return; /* Invalid header. */ + _read_message (msg); } void tcp_event_cb (struct bufferevent *bev, short events, void *user_data) { - struct rs_packet *pkt = (struct rs_packet *) user_data; + struct rs_message *msg = (struct rs_message *) user_data; struct rs_connection *conn = NULL; int sockerr = 0; #if defined (RS_ENABLE_TLS) @@ -178,20 +176,31 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data) struct rs_peer *p = NULL; #endif - assert (pkt); - assert (pkt->conn); - conn = pkt->conn; + assert (msg); + assert (msg->conn); + conn = msg->conn; #if defined (DEBUG) - assert (pkt->conn->active_peer); + assert (conn->active_peer); p = conn->active_peer; #endif - conn->is_connecting = 0; if (events & BEV_EVENT_CONNECTED) { - if (conn->tev) - evtimer_del (conn->tev); /* Cancel connect timer. */ - if (event_on_connect (conn, pkt)) + int err = -1; + + if (conn_originating_p (conn)) /* We're a client. */ + { + assert (conn->tev); + if (conn->tev) + evtimer_del (conn->tev); /* Cancel connect timer. */ + err = event_on_connect_orig (conn, msg); + } + else /* We're a server. */ + { + assert (conn->tev == NULL); + err = event_on_connect_term (conn, msg); + } + if (err) { event_on_disconnect (conn); event_loopbreak (conn); @@ -217,18 +226,18 @@ 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->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))); @@ -249,23 +258,24 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data) void tcp_write_cb (struct bufferevent *bev, void *ctx) { - struct rs_packet *pkt = (struct rs_packet *) ctx; + struct rs_message *msg = (struct rs_message *) ctx; - assert (pkt); - assert (pkt->conn); + assert (msg); + assert (msg->conn); - if (pkt->conn->callbacks.sent_cb) - pkt->conn->callbacks.sent_cb (pkt->conn->user_data); + if (msg->conn->callbacks.sent_cb) + 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/tests/Makefile.am b/lib/tests/Makefile.am index dc15264..fcd016b 100644 --- a/lib/tests/Makefile.am +++ b/lib/tests/Makefile.am @@ -4,9 +4,12 @@ AM_CFLAGS = -Wall -Werror -g TESTS = test-udp -check_PROGRAMS = test-udp udp-server +check_PROGRAMS = test-udp udp-server tls-server test_udp_SOURCES = test-udp.c udp.c udp.h test_udp_LDADD = ../libradsec.la -lcunit -lm udp_server_SOURCES = udp-server.c udp.c udp.h + +tls_server_SOURCES = server.c +tls_server_LDADD = ../libradsec.la @@ -21,18 +21,20 @@ 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_calloc (conn->base_.ctx, 1, sizeof (struct tls)); if (c) { - memset (c, 0, sizeof (struct tls)); + assert (realm); + /* _conn_open() should've picked a peer by now. */ + assert (conn->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 = realm->cacertfile; + c->cacertfile = conn->active_peer->cacertfile; c->cacertpath = NULL; /* NYI */ - c->certfile = realm->certfile; - c->certkeyfile = realm->certkeyfile; + c->certfile = conn->active_peer->certfile; + c->certkeyfile = conn->active_peer->certkeyfile; c->certkeypwd = NULL; /* NYI */ c->cacheexpiry = 0; /* NYI */ c->crlcheck = 0; /* NYI */ @@ -55,10 +57,11 @@ psk_client_cb (SSL *ssl, { struct rs_connection *conn = NULL; struct rs_credentials *cred = NULL; + unsigned int secret_len; conn = SSL_get_ex_data (ssl, 0); assert (conn != NULL); - cred = conn->active_peer->realm->transport_cred; + cred = conn->active_peer->transport_cred; assert (cred != NULL); /* NOTE: Ignoring identity hint from server. */ @@ -73,14 +76,14 @@ psk_client_cb (SSL *ssl, switch (cred->secret_encoding) { case RS_KEY_ENCODING_UTF8: - cred->secret_len = strlen (cred->secret); - if (cred->secret_len > max_psk_len) + secret_len = strlen (cred->secret); + if (secret_len > max_psk_len) { rs_err_conn_push (conn, RSE_CRED, "PSK secret longer than max %d", max_psk_len); return 0; } - memcpy (psk, cred->secret, cred->secret_len); + memcpy (psk, cred->secret, secret_len); break; case RS_KEY_ENCODING_ASCII_HEX: { @@ -100,7 +103,7 @@ psk_client_cb (SSL *ssl, BN_clear_free (bn); return 0; } - cred->secret_len = BN_bn2bin (bn, psk); + secret_len = BN_bn2bin (bn, psk); BN_clear_free (bn); } break; @@ -108,7 +111,7 @@ psk_client_cb (SSL *ssl, assert (!"unknown psk encoding"); } - return cred->secret_len; + return secret_len; } #endif /* RS_ENABLE_TLS_PSK */ @@ -121,8 +124,9 @@ 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; + assert (conn->active_peer); tlsconf = _get_tlsconf (conn, conn->active_peer->realm); if (!tlsconf) @@ -145,7 +149,7 @@ rs_tls_init (struct rs_connection *conn) } #if defined RS_ENABLE_TLS_PSK - if (conn->active_peer->realm->transport_cred != NULL) + if (conn->active_peer->transport_cred != NULL) { SSL_set_psk_client_callback (ssl, psk_client_cb); SSL_set_ex_data (ssl, 0, conn); @@ -201,7 +205,7 @@ 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 != NULL); assert (conn->active_peer->hostname != NULL); hostname = conn->active_peer->hostname; @@ -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 @@ -16,38 +16,39 @@ #include "event.h" #include "compat.h" #include "udp.h" +#include "conn.h" /* Send one packet, the first in queue. */ static int _send (struct rs_connection *conn, int fd) { ssize_t r = 0; - struct rs_packet *pkt = conn->out_queue; + struct rs_message *msg = conn->out_queue; - assert (pkt->rpkt); - assert (pkt->rpkt->data); + assert (msg->rpkt); + assert (msg->rpkt->data); /* Send. */ - r = compat_send (fd, pkt->rpkt->data, pkt->rpkt->length, 0); + r = compat_send (fd, msg->rpkt->data, msg->rpkt->length, 0); if (r == -1) { - int sockerr = evutil_socket_geterror (pkt->conn->fd); + int sockerr = evutil_socket_geterror (msg->conn->fd); if (sockerr != EAGAIN) - return rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, + return rs_err_conn_push_fl (msg->conn, RSE_SOCKERR, __FILE__, __LINE__, "%d: send: %d (%s)", fd, sockerr, evutil_socket_error_to_string (sockerr)); } - assert (r == pkt->rpkt->length); - /* Unlink the packet. */ - conn->out_queue = pkt->next; + assert (r == msg->rpkt->length); + /* Unlink the message. */ + conn->out_queue = msg->next; - /* If there are more packets in queue, add the write event again. */ - if (pkt->conn->out_queue) + /* If there are more messages in queue, add the write event again. */ + if (msg->conn->out_queue) { - r = event_add (pkt->conn->wev, NULL); + r = event_add (msg->conn->base_.wev, NULL); if (r < 0) - return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, + return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__, "event_add: %s", evutil_gai_strerror (r)); rs_debug (("%s: re-adding the write event\n", __func__)); } @@ -55,17 +56,14 @@ _send (struct rs_connection *conn, int fd) return RSE_OK; } -/* Callback for conn->wev and conn->rev. FIXME: Rename. +/** Callback for conn->wev and conn->rev. FIXME: Rename. - USER_DATA contains connection for EV_READ and a packet for - EV_WRITE. This is because we don't have a connect/establish entry - point at the user level -- send implies connect so when we're - connected we need the packet to send. */ + \a user_data holds a message. */ static void _evcb (evutil_socket_t fd, short what, void *user_data) { int err; - struct rs_packet *pkt = (struct rs_packet *) user_data; + struct rs_message *msg = (struct rs_message *) user_data; rs_debug (("%s: fd=%d what =", __func__, fd)); if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT -- shouldn't happen!")); @@ -73,20 +71,21 @@ _evcb (evutil_socket_t fd, short what, void *user_data) if (what & EV_WRITE) rs_debug ((" WRITE")); rs_debug (("\n")); - assert (pkt); - assert (pkt->conn); + assert (msg); + assert (msg->conn); if (what & EV_READ) { - /* Read a single UDP packet and stick it in USER_DATA. */ + /* Read a single UDP packet and stick it in the struct + rs_message passed in user_data. */ /* TODO: Verify that unsolicited packets are dropped. */ ssize_t r = 0; + assert (msg->rpkt); + assert (msg->rpkt->data); - assert (pkt->rpkt->data); - - r = compat_recv (fd, pkt->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC); + r = compat_recv (fd, msg->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC); if (r == -1) { - int sockerr = evutil_socket_geterror (pkt->conn->fd); + int sockerr = evutil_socket_geterror (msg->conn->fd); if (sockerr == EAGAIN) { /* FIXME: Really shouldn't happen since we've been told @@ -96,61 +95,74 @@ _evcb (evutil_socket_t fd, short what, void *user_data) } /* Hard error. */ - rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, - "%d: recv: %d (%s)", fd, sockerr, - evutil_socket_error_to_string (sockerr)); - event_del (pkt->conn->tev); + rs_err_conn_push (msg->conn, RSE_SOCKERR, + "%d: recv: %d (%s)", fd, sockerr, + evutil_socket_error_to_string (sockerr)); + event_del (msg->conn->tev); goto err_out; } - event_del (pkt->conn->tev); + event_del (msg->conn->tev); if (r < 20 || r > RS_MAX_PACKET_LEN) /* Short or long packet. */ { - rs_err_conn_push (pkt->conn, RSE_INVALID_PKT, - "invalid packet length: %d", r); + rs_err_conn_push (msg->conn, RSE_INVALID_MSG, + "invalid message length: %d", r); goto err_out; } - pkt->rpkt->length = (pkt->rpkt->data[2] << 8) + pkt->rpkt->data[3]; - err = nr_packet_ok (pkt->rpkt); + msg->rpkt->length = (msg->rpkt->data[2] << 8) + msg->rpkt->data[3]; + err = nr_packet_ok (msg->rpkt); if (err) { - rs_err_conn_push_fl (pkt->conn, -err, __FILE__, __LINE__, - "invalid packet"); + rs_err_conn_push (msg->conn, -err, "invalid message"); goto err_out; } - /* Hand over message to user. This changes ownership of pkt. + /* Hand over message to user. This changes ownership of msg. Don't touch it afterwards -- it might have been freed. */ - if (pkt->conn->callbacks.received_cb) - pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data); + if (msg->conn->callbacks.received_cb) + msg->conn->callbacks.received_cb (msg, msg->conn->base_.user_data); } else if (what & EV_WRITE) { - if (!pkt->conn->is_connected) - event_on_connect (pkt->conn, pkt); - - if (pkt->conn->out_queue) - if (_send (pkt->conn, fd) == RSE_OK) - if (pkt->conn->callbacks.sent_cb) - pkt->conn->callbacks.sent_cb (pkt->conn->user_data); + if (conn_originating_p (msg->conn)) + { + /* We're a client. */ + if (msg->conn->state == RS_CONN_STATE_CONNECTING) + event_on_connect_orig (msg->conn, msg); + } + else + { + /* We're a server. */ + rs_debug (("%s: write event on terminating conn %p\n", + __func__, msg->conn)); + } + + 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->base_.user_data); } return; err_out: - rs_conn_disconnect (pkt->conn); + rs_conn_disconnect (msg->conn); } int -udp_init (struct rs_connection *conn, struct rs_packet *pkt) +udp_init (struct rs_connection *conn, struct rs_message *msg) { - assert (!conn->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) + assert (!conn->base_.bev); + + /* FIXME: Explain why we set EV_PERSIST on the read event but not on + the write event. */ + 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). */ @@ -163,10 +175,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"); @@ -1,5 +1,5 @@ /* Copyright 2011 NORDUnet A/S. All rights reserved. See LICENSE for licensing information. */ -int udp_init (struct rs_connection *conn, struct rs_packet *pkt); +int udp_init (struct rs_connection *conn, struct rs_message *msg); int udp_init_retransmit_timer (struct rs_connection *conn); |