HACKING file for libradsec (in Emacs -*- org -*- mode).
- Status as of libradsec-0.2.0.dev (2013-05-02).
-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
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.
- 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
- [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
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?
+
# Shared library interface version, i.e. -version-info to Libtool,
# expressed as three integers CURRENT:REVISION:AGE.
#
- # CURRENT is the version number of the current interface.
- # Increment CURRENT when the library interface changes.
+ # CURRENT is the version number of the current interface. Increment
+ # CURRENT when the library interface has changed or has been extended.
#
# REVISION is the version number of the _implementation_ of the
- # CURRENT interface.
- # Set REVISION to 0 when CURRENT changes, else increment.
+ # CURRENT interface. Set REVISION to 0 when CURRENT changes, else
+ # increment.
#
# AGE is the number of interfaces this library implements, i.e. how
- # many versions before CURRENT that are supported.
- # Increment AGE when the library interface is _extended_.
- # Set AGE to 0 when the library interface is _changed_.
+ # many versions before CURRENT that are supported. Increment AGE when
+ # the library interface is _extended_. Set AGE to 0 when the library
+ # interface is _changed_.
+VER_CUR = 1
+VER_REV = 0
+VER_AGE = 0
- SUBDIRS = radius radsecproxy . include examples
+ SUBDIRS = radius radsecproxy include . examples
+ DIST_SUBDIRS = $(SUBDIRS) tests
INCLUDES = -I$(srcdir)/include
AM_CFLAGS = -Wall -Werror -g
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 \
util.c
if RS_ENABLE_TLS
-libradsec_la_SOURCES += tls.c
+ libradsec_la_SOURCES += tls.c
else
-libradsec_la_SOURCES += md5.c
+ libradsec_la_SOURCES += md5.c
endif
- EXTRA_DIST = HACKING LICENSE
+ libradsec_la_SOURCES += \
+ compat.h \
+ conn.h \
+ debug.h \
+ err.h \
+ event.h \
+ md5.h \
- packet.h \
+ peer.h \
+ radsec.h \
+ tcp.h \
+ tls.h \
+ 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
++ $(AM_CFLAGS) -DHAVE_CONFIG_H #-DDEBUG -DDEBUG_LEVENT
+libradsec_la_LDFLAGS = \
+ -version-info $(VER_CUR):$(VER_REV):$(VER_AGE) \
- -export-symbols radsec.sym
++ -export-symbols $(srcdir)/radsec.sym
+libradsec_la_LIBADD = \
+ radsecproxy/libradsec-radsecproxy.la \
+ radius/libradsec-radius.la
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
http://git.nordu.net/?p=radsecproxy.git;a=shortlog;h=refs/heads/libradsec
- The source code is licensed under a 3-clause BSD license. See LICENSE.
+ 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
+ sh autogen.sh && ./configure && make
- If any of the libraries are not found, try setting environment
- variable LDFLAGS at configure time like so:
+
+ There are a couple of options that can be used when configuring. See
+
+ ./configure --help
+
+ for the full list. Worth mentioning here is --enable-tls and
+ --enable-tls-psk.
+
+ If the preprocessor has a hard time finding some of the header files
+ are, try setting environment variable CPPFLAGS at configure
+ time. Example:
+
+ CPPFLAGS="-I/usr/local/include" ./configure --enable-tls
+
+ If the link editor has trouble finding any of the libraries needed,
+ try setting environment variable LDFLAGS at configure time. Example:
LDFLAGS="-L/usr/local/lib" ./configure --enable-tls
- /* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ /* 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);
- /* Copyright 2010, 2011, 2013 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
#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),
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[] =
{
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);
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(). */
# -*- Autoconf -*- script for libradsec.
- AC_PREREQ([2.65])
+ 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])
# Checks for library functions.
AC_CHECK_FUNCS([memset socket strdup strerror strrchr])
- AC_CONFIG_FILES([Makefile
+ AC_CONFIG_FILES([Makefile libradsec.spec
radsecproxy/Makefile
- radius/Makefile
- include/Makefile
+ radius/Makefile
+ include/Makefile
examples/Makefile
tests/Makefile])
AC_OUTPUT
- /* Copyright 2010,2011,2013 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
#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
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->base_.ctx->evb);
- if (conn->base_.timeout.tv_sec || conn->base_.timeout.tv_usec)
- 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->base_.timeout.tv_sec, conn->base_.timeout.tv_usec));
- if (evtimer_add (conn->tev, &conn->base_.timeout))
- 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);
- assert (conn->active_peer);
- return conn->active_peer->transport_cred &&
- conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK;
++ 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
int
rs_conn_disconnect (struct rs_connection *conn)
{
- int err = baseconn_close (TO_BASE_CONN (conn));
- conn->state = RS_CONN_STATE_UNDEFINED;
+ int err = 0;
+
+ 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;
}
/* 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)
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;
}
}
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));
}
}
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;
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;
}
{
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;
}
/* Copyright 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
#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,
/* Copyright 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#define hd(p, l) { int i; \
for (i = 1; i <= l; i++) { \
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)
- /* Copyright 2011,2013 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
#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>
#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)
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);
}
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");
}
{
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;
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;
}
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__));
}
#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)
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;
}
- /* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
-/* 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);
AUTOMAKE_OPTIONS = foreign
- INCLUDES = -I$(top_srcdir)/include
- AM_CFLAGS = -Wall -Werror -g -DDEBUG -DDEBUG_LEVENT
+ 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
/* 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)
}
#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);
if (use_request_object_flag)
{
- if (rs_request_create_authn (conn, &request, USER_NAME, USER_PW, SECRET))
+ if (rs_request_create_authn (conn, &request, USER_NAME, USER_PW))
goto cleanup;
if (rs_request_send (request, &resp))
goto cleanup;
}
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__);
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)
}
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));
/** @file libradsec-impl.h
- @brief Libraray internal header file for libradsec. */
+ @brief Libraray internal header file for libradsec. */
- /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
+ /* Copyright 2010-2013 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
#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, */
};
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
char *identity;
char *secret;
enum rs_key_encoding secret_encoding;
- unsigned int secret_len;
};
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: */
/** \file radsec.h
- \brief Public interface for libradsec. */
+ \brief Public interface for libradsec. */
- /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
+ /* Copyright 2010-2013 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
#ifndef _RADSEC_RADSEC_H_
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
+#include "compat.h"
enum rs_error_code {
RSE_OK = 0,
RSE_INVALID_CTX = 3,
RSE_INVALID_CONN = 4,
RSE_CONN_TYPE_MISMATCH = 5,
- RSE_FR = 6,
RSE_BADADDR = 7,
RSE_NOPEER = 8,
RSE_EVENT = 9, /* libevent error. */
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. */
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,
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 */
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;
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
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);
/** 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);
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. */
+ 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. */
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);
- /* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ /* Copyright 2010-2011 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
#ifndef _RADSEC_REQUEST_IMPL_H_
{
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;
};
/** \file request.h
\brief Public interface for libradsec request's. */
- /* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ /* Copyright 2010-2013 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
#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)
/** 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 user_name and \a user_pw attributes. \a user_name and \a user_pw
- are optional and can be NULL. If \a user_name and \a user_pw are provided,
- \a secret must also be provided. \a secret is used for "hiding" the
- password. */
+ newly created RADIUS authentication message, possibly with \a
+ user_name and \a user_pw attributes. \a user_name and _user_pw
+ are optional and can be NULL. */
int rs_request_create_authn(struct rs_connection *conn,
struct rs_request **req_out,
const char *user_name,
- const char *user_pw,
- const char *secret);
+ const char *user_pw);
/** Send request \a req and wait for a matching response. The
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)
}
- /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
-/* 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
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;
}
{
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;
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;
}
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)
{
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;
+}
- /* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ /* Copyright 2010-2013 NORDUnet A/S. All rights reserved.
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
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);
}
rs_free (ctx, ctx->config);
}
+ if (ctx->evb)
+ event_base_free (ctx->evb);
+
free (ctx);
}
- /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
-/* 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
#include <config.h>
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);
}
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;
rs_request_create_authn (struct rs_connection *conn,
struct rs_request **req_out,
const char *user_name,
- const char *user_pw,
- const char *secret)
+ const char *user_pw)
{
struct rs_request *req = NULL;
assert (req_out);
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)
{
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
}
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;
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;
{
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. */
return r;
}
-struct rs_packet *
+struct rs_message *
rs_request_get_reqmsg (const struct rs_request *request)
{
assert (request);
/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
#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"
#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)
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;
}
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",
/* 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);
- /* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ /* Copyright 2011-2013 NORDUnet A/S. All rights reserved.
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
#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"
#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)
- return rs_err_conn_push (msg->conn, RSE_INVALID_MSG,
- "invalid message length: %d",
- msg->rpkt->length);
++ {
++ 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. */
- return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__,
- "got %d octets reading header", n);
+ {
+ 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)
- return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__,
- "invalid message");
+ {
+ 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
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. */
- if (_read_message (msg))
- return; /* Invalid message. */
++ _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)
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);
}
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)));
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");
AUTOMAKE_OPTIONS = foreign
- INCLUDES = -I$(top_srcdir)/include
+ INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)
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
- test_udp_LDADD = ../libradsec.la -lcgreen -lm
+ 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_server_SOURCES = udp-server.c udp.c udp.h
+
+tls_server_SOURCES = server.c
+tls_server_LDADD = ../libradsec.la
-
- /* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ /* 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 <openssl/ssl.h>
#include <openssl/err.h>
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 */
{
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. */
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:
{
BN_clear_free (bn);
return 0;
}
- cred->secret_len = BN_bn2bin (bn, psk);
+ secret_len = BN_bn2bin (bn, psk);
BN_clear_free (bn);
}
break;
assert (!"unknown psk encoding");
}
- return cred->secret_len;
+ return secret_len;
}
#endif /* RS_ENABLE_TLS_PSK */
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)
}
#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);
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;
-/* Copyright 2011 NORDUnet A/S. All rights reserved.
+/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
#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__));
}
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;
- assert (msg);
- assert (msg->conn);
rs_debug (("%s: fd=%d what =", __func__, fd));
- if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT"));
+ if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT -- shouldn't happen!"));
if (what & EV_READ) rs_debug ((" READ"));
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
that fd is readable! */
rs_debug (("%s: EAGAIN reading UDP packet -- wot?"));
- return;
+ goto err_out;
}
/* 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);
- return;
+ 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",
- msg->rpkt->length);
- return;
++ "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 (msg->conn, err, __FILE__, __LINE__,
- "invalid message");
- return;
- 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;
- #if defined (DEBUG)
- if (what & EV_TIMEOUT)
- rs_debug (("%s: timeout on UDP event, shouldn't happen\n", __func__));
- #endif
+ 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). */
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");
/* Copyright 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ 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);