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