summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/HACKING112
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/README2
-rw-r--r--lib/conf.c35
-rw-r--r--lib/confutil.c11
-rw-r--r--lib/conn.c188
-rw-r--r--lib/conn.h10
-rw-r--r--lib/err.c26
-rw-r--r--lib/event.c178
-rw-r--r--lib/event.h13
-rw-r--r--lib/examples/Makefile.am2
-rw-r--r--lib/examples/client-blocking.c4
-rw-r--r--lib/examples/client-dispatch.c7
-rw-r--r--lib/examples/client.conf26
-rw-r--r--lib/examples/server.c120
-rw-r--r--lib/include/radsec/radsec-impl.h19
-rw-r--r--lib/include/radsec/radsec.h68
-rw-r--r--lib/listener.c236
-rw-r--r--lib/listener.h14
-rw-r--r--lib/message.c42
-rw-r--r--lib/peer.c10
-rw-r--r--lib/peer.h4
-rw-r--r--lib/radsec.sym7
-rw-r--r--lib/request.c6
-rw-r--r--lib/send.c22
-rw-r--r--lib/tcp.c71
-rw-r--r--lib/tests/Makefile.am6
-rw-r--r--lib/tls.c25
-rw-r--r--lib/udp.c49
29 files changed, 959 insertions, 358 deletions
diff --git a/lib/HACKING b/lib/HACKING
index d312ce3..62da414 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.2.dev (2013-01-25).
+Status as of libradsec-0.2.0.dev (2013-05-02).
* 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 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).
-
* Dependencies
Details (within parentheses) apply to Debian Wheezy.
@@ -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 6d62c97..6a96d32 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -53,10 +53,10 @@ else
endif
EXTRA_DIST = HACKING LICENSE
+EXTRA_libradsec_la_DEPENDENCIES = radsec.sym
libradsec_la_CFLAGS = \
- $(AM_CFLAGS) -DHAVE_CONFIG_H \
- -DDEBUG -DDEBUG_LEVENT
+ $(AM_CFLAGS) -DHAVE_CONFIG_H -DDEBUG -DDEBUG_LEVENT
libradsec_la_LDFLAGS = \
-version-info $(VER_CUR):$(VER_REV):$(VER_AGE) \
-export-symbols radsec.sym
diff --git a/lib/README b/lib/README
index 8b8c25d..6401333 100644
--- a/lib/README
+++ b/lib/README
@@ -19,7 +19,7 @@ http://git.nordu.net/?p=radsecproxy.git;a=shortlog;h=refs/heads/libradsec
The source code is licensed under a 3-clause BSD license. See LICENSE.
-Libradsec depends on
+Libradsec depends on
- libconfuse
- libevent2
- openssl (if configured with --enable-tls)
diff --git a/lib/conf.c b/lib/conf.c
index f75e9ed..d5a9d0d 100644
--- a/lib/conf.c
+++ b/lib/conf.c
@@ -1,5 +1,5 @@
/* Copyright 2010, 2011, 2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -23,7 +23,7 @@
retries = INT
}
- # realm configuration inherited by clients and servers
+ # realm configuration inherited (and overwritable) by clients and servers
realm STRING {
cacertfile = STRING
#cacertpath = STRING
@@ -46,6 +46,8 @@
# server configuration
realm STRING {
+ listen_addr = STRING
+ listen_service = STRING
client {
hostname = STRING
service = STRING # name or port number
@@ -82,6 +84,8 @@ confload_peers (struct rs_context *ctx,
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");
@@ -105,6 +109,24 @@ confload_peers (struct rs_context *ctx,
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);
@@ -161,7 +183,7 @@ confload_peers (struct rs_context *ctx,
#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)
@@ -170,6 +192,7 @@ confload_peers (struct rs_context *ctx,
return rs_err_ctx_push (ctx, RSE_CONFIG,
"%s: missing both CA file/path and PSK",
r->name);
+#endif
}
return RSE_OK;
@@ -189,6 +212,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
{
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),*/
@@ -204,6 +229,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
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),
@@ -283,10 +310,12 @@ 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",
diff --git a/lib/confutil.c b/lib/confutil.c
index 3a1d639..7bd4a37 100644
--- a/lib/confutil.c
+++ b/lib/confutil.c
@@ -94,10 +94,17 @@ rs_context_print_config (struct rs_context *ctx, char **buf_out)
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",
+ || 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->retries,
+ r->local_addr->hostname,
+ r->local_addr->service))
return -RSE_INTERNAL;
for (p = r->peers; p != NULL; p = p->next)
{
diff --git a/lib/conn.c b/lib/conn.c
index 95e65a4..4c9158c 100644
--- a/lib/conn.c
+++ b/lib/conn.c
@@ -13,6 +13,7 @@
#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"
@@ -20,19 +21,6 @@
#include "tcp.h"
int
-conn_close (struct rs_connection **connp)
-{
- int r = 0;
- assert (connp);
- assert (*connp);
- if ((*connp)->state == RS_CONN_STATE_CONNECTED)
- r = rs_conn_disconnect (*connp);
- if (r == RSE_OK)
- *connp = NULL;
- return r;
-}
-
-int
conn_user_dispatch_p (const struct rs_connection *conn)
{
assert (conn);
@@ -43,7 +31,6 @@ conn_user_dispatch_p (const struct rs_connection *conn)
conn->callbacks.sent_cb);
}
-
int
conn_activate_timeout (struct rs_connection *conn)
{
@@ -62,23 +49,37 @@ conn_activate_timeout (struct rs_connection *conn)
}
int
-conn_type_tls (const struct rs_connection *conn)
+conn_type_tls_p (const struct rs_connection *conn)
+{
+ return TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_TLS
+ || TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_DTLS;
+}
+
+int
+baseconn_type_datagram_p (const struct rs_conn_base *connbase)
{
- assert (conn->base_.active_peer);
- return conn->base_.realm->type == RS_CONN_TYPE_TLS
- || conn->base_.realm->type == RS_CONN_TYPE_DTLS;
+ 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;
}
int
conn_cred_psk (const struct rs_connection *conn)
{
- assert (conn->base_.active_peer);
- return conn->base_.active_peer->transport_cred &&
- conn->base_.active_peer->transport_cred->type == RS_CRED_TLS_PSK;
+ assert (conn);
+ assert (conn->active_peer);
+ return conn->active_peer->transport_cred &&
+ conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK;
}
void
-conn_init (struct rs_context *ctx,
+conn_init (struct rs_context *ctx, /* FIXME: rename connbase_init? */
struct rs_conn_base *connbase,
enum rs_conn_subtype type)
{
@@ -102,7 +103,7 @@ conn_init (struct rs_context *ctx,
}
int
-conn_configure (struct rs_context *ctx,
+conn_configure (struct rs_context *ctx, /* FIXME: rename conbbase_configure? */
struct rs_conn_base *connbase,
const char *config)
{
@@ -121,15 +122,84 @@ conn_configure (struct rs_context *ctx,
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)
{
- connbase->realm = rs_calloc (ctx, 1, sizeof (struct rs_realm));
- 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,
@@ -179,12 +249,8 @@ rs_conn_add_listener (struct rs_connection *conn,
int
rs_conn_disconnect (struct rs_connection *conn)
{
- int err = 0;
-
- assert (conn);
-
- err = evutil_closesocket (conn->base_.fd);
- conn->base_.fd = -1;
+ int err = baseconn_close (TO_BASE_CONN (conn));
+ conn->state = RS_CONN_STATE_UNDEFINED;
return err;
}
@@ -229,9 +295,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));
}
@@ -243,7 +312,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;
@@ -282,7 +351,6 @@ struct event_base
int rs_conn_get_fd (struct rs_connection *conn)
{
assert (conn);
- assert (conn->base_.active_peer);
return conn->base_.fd;
}
@@ -294,9 +362,9 @@ _rcb (struct rs_message *message, void *user_data)
assert (msg->conn);
msg->flags |= RS_MESSAGE_RECEIVED;
- if (msg->conn->base_.bev)
+ if (msg->conn->base_.bev) /* TCP -- disable bufferevent. */
bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ);
- else
+ else /* UDP -- remove read event. */
event_del (msg->conn->base_.rev);
}
@@ -322,46 +390,29 @@ rs_conn_receive_message (struct rs_connection *conn,
conn->base_.user_data = msg;
msg->flags &= ~RS_MESSAGE_RECEIVED;
- if (conn->base_.bev) /* TCP. */
- {
- bufferevent_setwatermark (conn->base_.bev, EV_READ, RS_HEADER_LEN, 0);
- bufferevent_setcb (conn->base_.bev, tcp_read_cb, NULL, tcp_event_cb, msg);
- bufferevent_enable (conn->base_.bev, EV_READ);
- }
- else /* UDP. */
- {
- /* Put fresh message in user_data for the callback and enable the
- read event. */
- event_assign (conn->base_.rev, conn->base_.ctx->evb,
- event_get_fd (conn->base_.rev), EV_READ,
- event_get_callback (conn->base_.rev), msg);
- err = event_add (conn->base_.rev, NULL);
- if (err < 0)
- return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__,
- "event_add: %s",
- evutil_gai_strerror (err));
-
- /* Activate retransmission timer. */
- conn_activate_timeout (msg->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->base_.ctx->evb);
conn->callbacks.received_cb = NULL;
if (err < 0)
- return rs_err_conn_push_fl (msg->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 ((msg->flags & RS_MESSAGE_RECEIVED) == 0
+ if ((msg->flags & RS_MESSAGE_RECEIVED) == 0 /* No message. */
|| (req_msg
- && message_verify_response (msg->conn, msg, req_msg) != RSE_OK))
+ && message_verify_response (conn, msg, req_msg) != RSE_OK))
{
- if (rs_err_conn_peek_code (msg->conn) == RSE_OK)
- /* No message and no error on the stack _should_ mean that the
- server hung up on us. */
- rs_err_conn_push (msg->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);
}
@@ -377,3 +428,10 @@ rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv)
assert (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 c1543d9..3e34453 100644
--- a/lib/conn.h
+++ b/lib/conn.h
@@ -1,10 +1,8 @@
/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
-int conn_close (struct rs_connection **connp);
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,
@@ -12,3 +10,11 @@ int conn_configure (struct rs_context *ctx,
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/err.c b/lib/err.c
index 413ab3e..a0fe8a0 100644
--- a/lib/err.c
+++ b/lib/err.c
@@ -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;
@@ -158,11 +159,12 @@ rs_err_ctx_push_fl (struct rs_context *ctx, int code, const char *file,
int
err_connbase_push_err (struct rs_conn_base *connbase, struct rs_error *err)
{
-
+ assert (connbase);
if (connbase->err)
rs_err_free (connbase->err);
connbase->err = err; /* FIXME: use a stack */
+ assert (err);
return err->code;
}
@@ -185,6 +187,7 @@ rs_err_connbase_push (struct rs_conn_base *connbase, int code,
{
int r = 0;
+ assert (connbase);
va_list args;
va_start (args, fmt);
r = _connbase_err_vpush_fl (connbase, code, NULL, 0, fmt, args);
@@ -198,6 +201,7 @@ 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);
@@ -249,18 +253,30 @@ 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->base_.err;
- conn->base_.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)
{
diff --git a/lib/event.c b/lib/event.c
index c651e4b..fadcc14 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>
@@ -27,6 +28,7 @@
#include "event.h"
#include "message.h"
#include "conn.h"
+#include "listener.h"
#include "debug.h"
#if defined (DEBUG)
@@ -72,7 +74,7 @@ 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->base_.fd, conn->base_.active_peer));
+ __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,70 +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->base_.fd, conn->base_.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->base_.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_connbase_push_err (TO_BASE_CONN (conn), err);
+ return err_connbase_push_err (connbase, err);
}
- conn->base_.fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype,
- p->addr_cache->ai_protocol);
- if (conn->base_.fd < 0)
- return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
- "socket: %d (%s)",
- errno, strerror (errno));
- if (evutil_make_socket_nonblocking (conn->base_.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->base_.fd);
- conn->base_.fd = -1;
- return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
- "evutil_make_socket_nonblocking: %d (%s)",
- errno, strerror (errno));
+ 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->base_.bev)
+ struct rs_conn_base *connbase = NULL;
+ assert (conn);
+ connbase = TO_BASE_CONN(conn);
+
+ if (connbase->bev)
return RSE_OK;
- if (conn->base_.realm->type == RS_CONN_TYPE_TCP)
+ if (connbase->transport == RS_CONN_TYPE_TCP)
{
- conn->base_.bev = bufferevent_socket_new (conn->base_.ctx->evb,
- conn->base_.fd, 0);
- if (!conn->base_.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->base_.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->base_.bev =
- bufferevent_openssl_socket_new (conn->base_.ctx->evb, conn->base_.fd,
- conn->tls_ssl,
- BUFFEREVENT_SSL_CONNECTING, 0);
- if (!conn->base_.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");
}
@@ -162,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->base_.realm->type);
+ connbase->transport);
}
return RSE_OK;
@@ -176,10 +226,10 @@ event_do_connect (struct rs_connection *conn)
size_t peer_addrlen;
assert (conn);
- assert (conn->base_.active_peer);
- assert (conn->base_.active_peer->addr_cache);
- peer_addr = conn->base_.active_peer->addr_cache->ai_addr;
- peer_addrlen = conn->base_.active_peer->addr_cache->ai_addrlen;
+ assert (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);
@@ -217,11 +267,9 @@ event_do_connect (struct rs_connection *conn)
rs_debug (("%s: %d: connect: %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: connect: %d (%s)", conn->base_.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;
@@ -235,7 +283,7 @@ event_loopbreak (struct rs_connection *conn)
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;
}
@@ -244,21 +292,21 @@ void
event_on_disconnect (struct rs_connection *conn)
{
conn->state = RS_CONN_STATE_UNDEFINED;
- rs_debug (("%s: %p disconnected\n", __func__,
- TO_BASE_CONN(conn)->active_peer));
+ rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer));
if (conn->callbacks.disconnected_cb)
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_message *msg)
+event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg)
{
assert (conn->state == RS_CONN_STATE_CONNECTING);
- assert (conn->base_.active_peer);
+ 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__));
@@ -267,7 +315,7 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg)
#endif /* RS_ENABLE_TLS */
conn->state = RS_CONN_STATE_CONNECTED;
- rs_debug (("%s: %p connected\n", __func__, TO_BASE_CONN(conn)->active_peer));
+ rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
if (conn->callbacks.connected_cb)
conn->callbacks.connected_cb (conn->base_.user_data);
@@ -278,12 +326,24 @@ event_on_connect (struct rs_connection *conn, struct rs_message *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);
- assert (conn->base_.ctx);
- if (conn->base_.ctx->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)
@@ -291,10 +351,10 @@ event_init_eventbase (struct rs_connection *conn)
event_enable_debug_mode ();
#endif
event_set_log_callback (_evlog_cb);
- conn->base_.ctx->evb = event_base_new ();
- if (!conn->base_.ctx->evb)
- return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
- "event_base_new");
+ 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 3fcea61..63fccc5 100644
--- a/lib/event.h
+++ b/lib/event.h
@@ -1,12 +1,13 @@
-/* Copyright 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
+ See LICENSE for licensing information. */
void event_on_disconnect (struct rs_connection *conn);
-int event_on_connect (struct rs_connection *conn, struct rs_message *msg);
+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 8dc4f58..d86f4f8 100644
--- a/lib/examples/Makefile.am
+++ b/lib/examples/Makefile.am
@@ -1,6 +1,6 @@
AUTOMAKE_OPTIONS = foreign
INCLUDES = -I$(top_srcdir)/include
-AM_CFLAGS = -Wall -Werror -g
+AM_CFLAGS = -Wall -Werror -g -DDEBUG -DDEBUG_LEVENT
LDADD = ../libradsec.la #-lefence
CFLAGS = $(AM_CFLAGS) -DUSE_CONFIG_FILE
diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c
index d2ee9f4..82a4453 100644
--- a/lib/examples/client-blocking.c
+++ b/lib/examples/client-blocking.c
@@ -85,9 +85,9 @@ blocking_client (const char *av1, const char *av2, const char *av3,
}
else
{
- if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW, SECRET))
+ if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW))
goto cleanup;
- if (rs_message_send (req, NULL))
+ if (rs_message_send (req))
goto cleanup;
if (rs_conn_receive_message (conn, req, &resp))
goto cleanup;
diff --git a/lib/examples/client-dispatch.c b/lib/examples/client-dispatch.c
index e007654..8a80ec6 100644
--- a/lib/examples/client-dispatch.c
+++ b/lib/examples/client-dispatch.c
@@ -67,14 +67,13 @@ dispatching_client (struct rs_context *ctx)
if (rs_conn_create(ctx, &conn, CONFIG))
goto out;
- rs_conn_set_callbacks (conn, &cb);
- if (rs_packet_create_authn_request (conn, &req_msg,
- USER_NAME, USER_PW, SECRET))
+ 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, &state))
+ if (rs_packet_send (req_msg))
goto out;
while (1)
diff --git a/lib/examples/client.conf b/lib/examples/client.conf
index 32af3c0..288a084 100644
--- a/lib/examples/client.conf
+++ b/lib/examples/client.conf
@@ -9,6 +9,17 @@ 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
@@ -22,7 +33,22 @@ realm blocking-tls {
#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 testcli {
+ 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 = "4711"
secret = "sikrit"
}
}
diff --git a/lib/examples/server.c b/lib/examples/server.c
index 8c304a0..fb51866 100644
--- a/lib/examples/server.c
+++ b/lib/examples/server.c
@@ -1,7 +1,7 @@
/* RADIUS/RadSec server using libradsec. */
/* Copyright 2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#include <stdio.h>
#include <stdlib.h>
@@ -11,49 +11,129 @@
#include <event2/event.h>
#include "debug.h" /* For rs_dump_message(). */
-#define CONFIG_FILE "examples/test.conf"
+#define CONFIG_FILE "examples/server.conf"
#define CONFIG "tls"
#define SECRET "sikrit"
#define USER_NAME "molgan@PROJECT-MOONSHOT.ORG"
#define USER_PW "password"
-void
+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)
{
- printf ("new connection: fd=%d\n", -1); /* conn->fd */
+ 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_connection *conn = NULL;
struct rs_listener *listener = NULL;
- const struct rs_listener_callbacks cbs = {};
+ 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);
+ 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);
- printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r);
- }
+ 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_conn_pop (conn);
+ err = rs_err_listener_pop (listener);
-#if 0
+ if (read_event)
+ event_free (read_event);
+ read_event = NULL;
if (listener)
- rs_listener_destroy (listener);
+ {
+ assert (rs_listener_close (listener) == RSE_OK);
+ //rs_listener_destroy (listener);
+ }
listener = NULL;
-#endif
return err;
}
@@ -80,13 +160,15 @@ main (int argc, char *argv[])
err = server (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));
+ 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 45ce7f6..28e9e4c 100644
--- a/lib/include/radsec/radsec-impl.h
+++ b/lib/include/radsec/radsec-impl.h
@@ -90,6 +90,7 @@ struct rs_realm {
int timeout;
int retries;
struct rs_listener *listeners;
+ struct rs_peer *local_addr;
struct rs_peer *peers;
struct rs_realm *next;
};
@@ -103,9 +104,10 @@ struct rs_config {
/** 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; /* Event base. */
+ struct event_base *evb;
};
/** Base class for a connection. */
@@ -113,11 +115,11 @@ 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. */
+ enum rs_conn_type transport;
/** For a listener, allowed client addr/port pairs.
- For an outgoing connection, set of servers.
+ For an outgoing connection, set of configured servers.
For an incoming connection, the peer (as the only entry). */
- struct rs_peer *peers; /**< Configured peers. */
- struct rs_peer *active_peer; /**< The other end of the connection. */
+ struct rs_peer *peers;
struct timeval timeout;
int tryagain; /* For server failover. */
void *user_data;
@@ -137,16 +139,13 @@ enum rs_conn_state {
RS_CONN_STATE_CONNECTED,
};
-/** A "generic" connection. */
+/** 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;
-#if 0
- char is_connecting; /* FIXME: replace with a single state member */
- char is_connected; /* FIXME: replace with a single state member */
-#endif /* 0 */
+ struct rs_peer *active_peer;
struct rs_message *out_queue; /* Queue for outgoing UDP packets. */
#if defined(RS_ENABLE_TLS)
/* TLS specifics. */
@@ -156,7 +155,7 @@ struct rs_connection {
};
/** A listening connection. Spawns generic connections when peers
- * connect to it. */
+ connect to it. */
struct rs_listener {
struct rs_conn_base base_;
struct evconnlistener *evlistener;
diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h
index cb98db7..2858f9e 100644
--- a/lib/include/radsec/radsec.h
+++ b/lib/include/radsec/radsec.h
@@ -22,6 +22,7 @@
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
+#include "compat.h"
enum rs_error_code {
RSE_OK = 0,
@@ -72,7 +73,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,
@@ -174,10 +175,14 @@ struct rs_conn_callbacks {
rs_conn_message_sent_cb sent_cb;
};
-typedef void (*rs_listener_new_conn_cb) (struct rs_connection *conn,
- void *user_data);
-typedef void (*rs_listener_error_cb) (void *user_data);
+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;
};
@@ -228,8 +233,13 @@ 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);
+ 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. */
@@ -277,7 +287,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);
@@ -356,28 +367,27 @@ 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 message \a msg on the connection associated with \a msg.
- \a user_data is sent to the \a rs_conn_message_received_cb callback
- registered with the connection. If no callback is registered with
- the connection, the event loop is run by \a rs_message_send and it
- blocks until the message has been succesfully sent.
+/** 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.
- \return On success, RSE_OK (0) is returned. On error, !0 is
+ \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_message_send(struct rs_message *msg, void *user_data);
+ the connection. The error can be accessed using \a
+ rs_err_conn_pop. */
+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,
- \a user_pw and \a secret where \secret is the RADIUS shared
- secret. */
+ 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,
- const char *secret);
+ const char *user_pw);
/*** Append \a tail to message \a msg. */
int
@@ -439,15 +449,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,
- ...);
+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/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
index 47590ca..a40e00b 100644
--- a/lib/message.c
+++ b/lib/message.c
@@ -28,15 +28,15 @@ message_verify_response (struct rs_connection *conn,
int err;
assert (conn);
- assert (conn->base_.active_peer);
- assert (conn->base_.active_peer->secret);
+ assert (conn->active_peer);
+ assert (conn->active_peer->secret);
assert (response);
assert (response->rpkt);
assert (request);
assert (request->rpkt);
- response->rpkt->secret = conn->base_.active_peer->secret;
- response->rpkt->sizeof_secret = strlen (conn->base_.active_peer->secret);
+ 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);
@@ -71,11 +71,11 @@ message_do_send (struct rs_message *msg)
assert (msg);
assert (msg->conn);
- assert (msg->conn->base_.active_peer);
- assert (msg->conn->base_.active_peer->secret);
+ assert (msg->conn->active_peer);
+ assert (msg->conn->active_peer->secret);
assert (msg->rpkt);
- msg->rpkt->secret = msg->conn->base_.active_peer->secret;
+ msg->rpkt->secret = msg->conn->active_peer->secret;
msg->rpkt->sizeof_secret = strlen (msg->rpkt->secret);
/* Encode message. */
@@ -89,16 +89,16 @@ message_do_send (struct rs_message *msg)
return rs_err_conn_push_fl (msg->conn, -err, __FILE__, __LINE__,
"nr_packet_sign");
#if defined (DEBUG)
- {
- char host[80], serv[80];
-
- getnameinfo (msg->conn->base_.active_peer->addr_cache->ai_addr,
- msg->conn->base_.active_peer->addr_cache->ai_addrlen,
- host, sizeof(host), serv, sizeof(serv),
- 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
- rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv));
- rs_dump_message (msg);
- }
+ 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. */
@@ -160,8 +160,7 @@ int
rs_message_create_authn_request (struct rs_connection *conn,
struct rs_message **msg_out,
const char *user_name,
- const char *user_pw,
- const char *secret)
+ const char *user_pw)
{
struct rs_message *msg;
int err;
@@ -174,6 +173,7 @@ rs_message_create_authn_request (struct rs_connection *conn,
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;
@@ -181,7 +181,9 @@ rs_message_create_authn_request (struct rs_connection *conn,
if (user_pw)
{
- msg->rpkt->secret = secret;
+ /* 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;
diff --git a/lib/peer.c b/lib/peer.c
index b6ec167..bbc3bf5 100644
--- a/lib/peer.c
+++ b/lib/peer.c
@@ -20,12 +20,12 @@ peer_pick_peer (struct rs_connection *conn)
{
assert (conn);
- if (conn->base_.active_peer)
- conn->base_.active_peer = conn->base_.active_peer->next; /* Next. */
- if (!conn->base_.active_peer)
- conn->base_.active_peer = conn->base_.peers; /* From the top. */
+ if (conn->active_peer)
+ conn->active_peer = conn->active_peer->next; /* Next. */
+ if (conn->active_peer == NULL)
+ conn->active_peer = TO_BASE_CONN (conn)->peers; /* From the top. */
- return conn->base_.active_peer;
+ return conn->active_peer;
}
struct rs_peer *
diff --git a/lib/peer.h b/lib/peer.h
index 95be0b0..2a8b43c 100644
--- a/lib/peer.h
+++ b/lib/peer.h
@@ -1,5 +1,5 @@
-/* Copyright 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
+ See LICENSE for licensing information. */
struct rs_peer *peer_create (struct rs_context *ctx, struct rs_peer **rootp);
struct rs_peer *peer_pick_peer (struct rs_connection *conn);
diff --git a/lib/radsec.sym b/lib/radsec.sym
index 6ad1361..9dd7186 100644
--- a/lib/radsec.sym
+++ b/lib/radsec.sym
@@ -42,7 +42,7 @@ rs_conn_del_callbacks
rs_conn_destroy
rs_conn_disconnect
rs_conn_dispatch
-rs_conn_fd
+rs_conn_get_fd
rs_conn_get_callbacks
rs_conn_get_current_peer
rs_conn_receive_message
@@ -66,9 +66,14 @@ rs_err_ctx_pop
rs_err_ctx_push
rs_err_ctx_push_fl
rs_err_free
+rs_err_listener_pop
rs_err_msg
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
diff --git a/lib/request.c b/lib/request.c
index 17b36de..611cbdf 100644
--- a/lib/request.c
+++ b/lib/request.c
@@ -60,7 +60,7 @@ rs_request_create_authn (struct rs_connection *conn,
if (rs_request_create (conn, &req))
return -1;
- if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw, secret))
+ if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw))
return -1;
if (req_out)
@@ -103,7 +103,7 @@ rs_request_send (struct rs_request *request, struct rs_message **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;
@@ -112,7 +112,7 @@ rs_request_send (struct rs_request *request, struct rs_message **resp_msg)
{
rs_conn_set_timeout (conn, &rt);
- r = rs_message_send (request->req_msg, NULL);
+ r = rs_message_send (request->req_msg);
if (r == RSE_OK)
{
r = rs_conn_receive_message (request->conn,
diff --git a/lib/send.c b/lib/send.c
index fab89a9..3ed6c93 100644
--- a/lib/send.c
+++ b/lib/send.c
@@ -21,15 +21,15 @@
static int
_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->base_.active_peer)
+ if (!conn->active_peer)
peer_pick_peer (conn);
- if (!conn->base_.active_peer)
+ if (!conn->active_peer)
return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
- if (event_init_socket (conn, conn->base_.active_peer))
+ if (event_init_socket (&conn->base_, conn->active_peer))
return -1;
if (conn->base_.realm->type == RS_CONN_TYPE_TCP
@@ -37,7 +37,7 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg)
{
if (tcp_init_connect_timer (conn))
return -1;
- if (event_init_bufferevent (conn, conn->base_.active_peer))
+ if (event_init_bufferevent (conn))
return -1;
}
else
@@ -59,7 +59,7 @@ static int
_conn_is_open_p (struct rs_connection *conn)
{
return conn->state == RS_CONN_STATE_CONNECTED
- && conn->base_.active_peer != NULL;
+ && conn->active_peer != NULL;
}
/* User callback used when we're dispatching for user. */
@@ -76,7 +76,7 @@ _wcb (void *user_data)
}
int
-rs_message_send (struct rs_message *msg, void *user_data)
+rs_message_send (struct rs_message *msg)
{
struct rs_connection *conn = NULL;
int err = 0;
@@ -93,14 +93,13 @@ rs_message_send (struct rs_message *msg, void *user_data)
assert (conn->base_.ctx);
assert (conn->base_.ctx->evb);
- assert (conn->base_.active_peer);
+ assert (conn->active_peer);
assert (conn->base_.fd >= 0);
- conn->base_.user_data = user_data;
-
if (conn->base_.bev) /* TCP */
{
- bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb, msg);
+ bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb,
+ msg);
bufferevent_enable (conn->base_.bev, EV_WRITE);
}
else /* UDP */
@@ -118,6 +117,7 @@ rs_message_send (struct rs_message *msg, 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->base_.user_data = msg;
rs_debug (("%s: entering event loop\n", __func__));
diff --git a/lib/tcp.c b/lib/tcp.c
index f5673f5..7264244 100644
--- a/lib/tcp.c
+++ b/lib/tcp.c
@@ -31,36 +31,27 @@ _read_header (struct rs_message *msg)
{
size_t n = 0;
- n = bufferevent_read (msg->conn->base_.bev, msg->hdr, RS_HEADER_LEN);
+ n = bufferevent_read (TO_BASE_CONN(msg->conn)->bev, msg->hdr, RS_HEADER_LEN);
if (n == RS_HEADER_LEN)
{
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)
- {
- conn_close (&msg->conn);
- return rs_err_conn_push (msg->conn, RSE_INVALID_MSG,
- "invalid message length: %d",
- msg->rpkt->length);
- }
+ 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 (msg->conn->base_.bev, EV_READ,
+ 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. */
- {
- conn_close (&msg->conn);
- return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__,
- "got %d octets reading header", n);
- }
-
- return 0;
+ return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__,
+ "got %d octets reading header", n);
+ return RSE_OK;
}
/** Read a message, check that it's valid RADIUS and hand it off to
@@ -98,12 +89,9 @@ _read_message (struct rs_message *msg)
- attribute lengths >= 2
- attribute sizes adding up correctly */
err = nr_packet_ok (msg->rpkt);
- if (err != RSE_OK)
- {
- conn_close (&msg->conn);
- return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__,
- "invalid message");
- }
+ if (err)
+ return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__,
+ "invalid message");
#if defined (DEBUG)
/* Find out what happens if there's data left in the buffer. */
@@ -133,11 +121,11 @@ _read_message (struct rs_message *msg)
/* The read callback for TCP.
- Read exactly one RADIUS message from BEV and store it in struct
- rs_message 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)
{
@@ -148,7 +136,7 @@ tcp_read_cb (struct bufferevent *bev, void *user_data)
assert (msg->rpkt);
msg->rpkt->sockfd = msg->conn->base_.fd;
- msg->rpkt->vps = NULL; /* FIXME: can this be done when initializing msg? */
+ 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
@@ -158,8 +146,9 @@ tcp_read_cb (struct bufferevent *bev, void *user_data)
avoid the extra copying. */
if ((msg->flags & RS_MESSAGE_HEADER_READ) == 0)
if (_read_header (msg))
- return; /* Error. */
- _read_message (msg);
+ return; /* Invalid header. */
+ if (_read_message (msg))
+ return; /* Invalid message. */
}
void
@@ -179,15 +168,27 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
assert (msg->conn);
conn = msg->conn;
#if defined (DEBUG)
- assert (msg->conn->base_.active_peer);
- p = conn->base_.active_peer;
+ assert (conn->active_peer);
+ p = conn->active_peer;
#endif
if (events & BEV_EVENT_CONNECTED)
{
- if (conn->tev)
- evtimer_del (conn->tev); /* Cancel connect timer. */
- if (event_on_connect (conn, msg))
+ 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);
diff --git a/lib/tests/Makefile.am b/lib/tests/Makefile.am
index 33ddb51..30f5e0f 100644
--- a/lib/tests/Makefile.am
+++ b/lib/tests/Makefile.am
@@ -4,9 +4,13 @@ AM_CFLAGS = -Wall -Werror -g
TESTS = test-udp
-check_PROGRAMS = test-udp udp-server
+check_PROGRAMS = test-udp udp-server tls-server
test_udp_SOURCES = test-udp.c udp.c
test_udp_LDADD = ../libradsec.la -lcgreen -lm
udp_server_SOURCES = udp-server.c udp.c
+
+tls_server_SOURCES = server.c
+tls_server_LDADD = ../libradsec.la
+
diff --git a/lib/tls.c b/lib/tls.c
index b9fb3cf..788bf5c 100644
--- a/lib/tls.c
+++ b/lib/tls.c
@@ -20,20 +20,20 @@
static struct tls *
_get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
{
- struct tls *c = rs_malloc (conn->base_.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->base_.active_peer);
+ 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 = conn->base_.active_peer->cacertfile;
+ c->cacertfile = conn->active_peer->cacertfile;
c->cacertpath = NULL; /* NYI */
- c->certfile = conn->base_.active_peer->certfile;
- c->certkeyfile = conn->base_.active_peer->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 */
@@ -60,7 +60,7 @@ psk_client_cb (SSL *ssl,
conn = SSL_get_ex_data (ssl, 0);
assert (conn != NULL);
- cred = conn->base_.active_peer->transport_cred;
+ cred = conn->active_peer->transport_cred;
assert (cred != NULL);
/* NOTE: Ignoring identity hint from server. */
@@ -125,8 +125,9 @@ rs_tls_init (struct rs_connection *conn)
assert (conn->base_.ctx);
ctx = conn->base_.ctx;
+ assert (conn->active_peer);
- tlsconf = _get_tlsconf (conn, conn->base_.active_peer->realm);
+ tlsconf = _get_tlsconf (conn, conn->active_peer->realm);
if (!tlsconf)
return -1;
ssl_ctx = tlsgetctx (RAD_TLS, tlsconf);
@@ -147,7 +148,7 @@ rs_tls_init (struct rs_connection *conn)
}
#if defined RS_ENABLE_TLS_PSK
- if (conn->base_.active_peer->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);
@@ -203,9 +204,9 @@ tls_verify_cert (struct rs_connection *conn)
struct in6_addr addr;
const char *hostname = NULL;
- assert (conn->base_.active_peer != NULL);
- assert (conn->base_.active_peer->hostname != NULL);
- hostname = conn->base_.active_peer->hostname;
+ assert (conn->active_peer != NULL);
+ assert (conn->active_peer->hostname != NULL);
+ hostname = conn->active_peer->hostname;
/* verifytlscert() performs basic verification as described by
OpenSSL VERIFY(1), i.e. verification of the certificate chain. */
diff --git a/lib/udp.c b/lib/udp.c
index 22a8375..de78fed 100644
--- a/lib/udp.c
+++ b/lib/udp.c
@@ -16,6 +16,7 @@
#include "event.h"
#include "compat.h"
#include "udp.h"
+#include "conn.h"
/* Send one packet, the first in queue. */
static int
@@ -55,16 +56,16 @@ _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 message 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 message to send. */
+ \a user_data holds a message. */
static void
_evcb (evutil_socket_t fd, short what, void *user_data)
{
int err;
+ struct rs_message *msg = (struct rs_message *) user_data;
+ assert (msg);
+ assert (msg->conn);
rs_debug (("%s: fd=%d what =", __func__, fd));
if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT"));
@@ -74,13 +75,11 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
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. */
- struct rs_message *msg = (struct rs_message *) user_data;
ssize_t r = 0;
-
- assert (msg);
- assert (msg->conn);
+ assert (msg->rpkt);
assert (msg->rpkt->data);
r = compat_recv (fd, msg->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC);
@@ -96,9 +95,9 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
}
/* Hard error. */
- rs_err_conn_push_fl (msg->conn, RSE_SOCKERR, __FILE__, __LINE__,
- "%d: recv: %d (%s)", fd, sockerr,
- evutil_socket_error_to_string (sockerr));
+ rs_err_conn_push (msg->conn, RSE_SOCKERR,
+ "%d: recv: %d (%s)", fd, sockerr,
+ evutil_socket_error_to_string (sockerr));
event_del (msg->conn->tev);
return;
}
@@ -125,12 +124,18 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
}
else if (what & EV_WRITE)
{
- struct rs_message *msg = (struct rs_message *) user_data;
- assert (msg);
- assert (msg->conn);
-
- if (msg->conn->state == RS_CONN_STATE_CONNECTING)
- event_on_connect (msg->conn, msg);
+ 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)
@@ -149,10 +154,12 @@ udp_init (struct rs_connection *conn, struct rs_message *msg)
{
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);
+ EV_READ|EV_PERSIST, _evcb, NULL);
conn->base_.wev = event_new (conn->base_.ctx->evb, conn->base_.fd,
- EV_WRITE, _evcb, NULL);
+ EV_WRITE, _evcb, NULL);
if (!conn->base_.rev || !conn->base_.wev)
{
if (conn->base_.rev)