summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2011-03-12 12:41:19 +0100
committerLinus Nordberg <linus@nordu.net>2011-03-12 12:41:19 +0100
commit2af84872cb78becf43f7bf4a654418fb7fc532d5 (patch)
tree7c3f33df5316fd67431fc4e70356d7a62da4543f /lib
parent319c2bf1b2886b2e5cd1c5d60a8950493d2d4d75 (diff)
parentefce8db03af505f76c0c579f2439757bd6998dc9 (diff)
Merge branch 'udp' into libradsec.
Diffstat (limited to 'lib')
-rw-r--r--lib/COPYING2
-rw-r--r--lib/Makefile.am9
-rw-r--r--lib/attr.c8
-rw-r--r--lib/compat.c22
-rw-r--r--lib/compat.h5
-rw-r--r--lib/conf.c20
-rw-r--r--lib/conn.c211
-rw-r--r--lib/conn.h6
-rw-r--r--lib/debug.c3
-rw-r--r--lib/debug.h3
-rw-r--r--lib/err.c97
-rw-r--r--lib/err.h9
-rw-r--r--lib/event.c255
-rw-r--r--lib/event.h12
-rw-r--r--lib/examples/Makefile.am2
-rw-r--r--lib/examples/client-blocking.c72
-rw-r--r--lib/examples/client-udp.conf6
-rw-r--r--lib/include/radsec/radsec-impl.h44
-rw-r--r--lib/include/radsec/radsec.h14
-rw-r--r--lib/include/radsec/request-impl.h1
-rw-r--r--lib/packet.c717
-rw-r--r--lib/packet.h7
-rw-r--r--lib/peer.c50
-rw-r--r--lib/peer.h5
-rw-r--r--lib/radsec.c90
-rw-r--r--lib/request.c130
-rw-r--r--lib/send.c138
-rw-r--r--lib/tcp.c254
-rw-r--r--lib/tcp.h7
-rw-r--r--lib/tls.c3
-rw-r--r--lib/tls.h3
-rw-r--r--lib/udp.c179
-rw-r--r--lib/udp.h5
33 files changed, 1464 insertions, 925 deletions
diff --git a/lib/COPYING b/lib/COPYING
index 0a1858c..7256aa4 100644
--- a/lib/COPYING
+++ b/lib/COPYING
@@ -1,5 +1,3 @@
-Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
-
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 311d3cc..d4d9b78 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -10,14 +10,19 @@ lib_LTLIBRARIES = libradsec.la
libradsec_la_SOURCES = \
attr.c \
+ compat.c \
conf.c \
conn.c \
debug.c \
err.c \
+ event.c \
packet.c \
peer.c \
radsec.c \
- request.c
+ request.c \
+ send.c \
+ tcp.c \
+ udp.c
libradsec_la_SOURCES += \
rsp_debug.c \
@@ -32,4 +37,4 @@ libradsec_la_SOURCES += \
endif
libradsec_la_LDFLAGS = -version-info 0:0:0
-libradsec_la_CFLAGS = $(AM_CFLAGS) #-DDEBUG -DDEBUG_LEVENT -Werror
+libradsec_la_CFLAGS = $(AM_CFLAGS) #-DDEBUG -DDEBUG_LEVENT #-Werror
diff --git a/lib/attr.c b/lib/attr.c
index 29384d5..2face5f 100644
--- a/lib/attr.c
+++ b/lib/attr.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -9,7 +10,10 @@
#include <radsec/radsec-impl.h>
int
-rs_attr_create(struct rs_connection *conn, struct rs_attr **attr, const char *type, const char *val)
+rs_attr_create(struct rs_connection *conn,
+ struct rs_attr **attr,
+ const char *type,
+ const char *val)
{
VALUE_PAIR *vp;
struct rs_attr *a;
diff --git a/lib/compat.c b/lib/compat.c
new file mode 100644
index 0000000..ccc6388
--- /dev/null
+++ b/lib/compat.c
@@ -0,0 +1,22 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include "compat.h"
+
+ssize_t
+compat_send (int sockfd, const void *buf, size_t len, int flags)
+{
+ return send (sockfd, buf, len, flags);
+}
+
+ssize_t
+compat_recv (int sockfd, void *buf, size_t len, int flags)
+{
+ return recv (sockfd, buf, len, flags);
+}
diff --git a/lib/compat.h b/lib/compat.h
new file mode 100644
index 0000000..125f651
--- /dev/null
+++ b/lib/compat.h
@@ -0,0 +1,5 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+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 c8b2a46..0f00fd8 100644
--- a/lib/conf.c
+++ b/lib/conf.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -8,6 +9,7 @@
#include <string.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
+#include "peer.h"
#include "debug.h"
#if 0
@@ -40,15 +42,15 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
cfg_opt_t server_opts[] =
{
CFG_STR ("hostname", NULL, CFGF_NONE),
- CFG_STR ("service", "radius", CFGF_NONE),
- CFG_STR ("secret", NULL, CFGF_NONE),
+ CFG_STR ("service", "2083", CFGF_NONE),
+ CFG_STR ("secret", "radsec", CFGF_NONE),
CFG_END ()
};
cfg_opt_t config_opts[] =
{
CFG_STR ("type", "UDP", CFGF_NONE),
- CFG_INT ("timeout", 2, CFGF_NONE),
- CFG_INT ("retries", 2, CFGF_NONE),
+ CFG_INT ("timeout", 2, CFGF_NONE), /* FIXME: Remove? */
+ CFG_INT ("retries", 2, CFGF_NONE), /* FIXME: Remove? */
CFG_STR ("cacertfile", NULL, CFGF_NONE),
/*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
CFG_STR ("certfile", NULL, CFGF_NONE),
@@ -87,6 +89,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
return rs_err_ctx_push_fl (ctx, RSE_CONFIG, __FILE__, __LINE__,
"missing config name");
r->name = strdup (s);
+ if (!r->name)
+ return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
typestr = cfg_getstr (cfg_config, "type");
if (!strcmp (typestr, "UDP"))
@@ -111,15 +115,15 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
/* Add peers, one per server stanza. */
for (j = 0; j < cfg_size (cfg_config, "server"); j++)
{
- struct rs_peer *p = _rs_peer_create (ctx, &r->peers);
+ struct rs_peer *p = peer_create (ctx, &r->peers);
if (!p)
return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
NULL);
p->realm = r;
cfg_server = cfg_getnsec (cfg_config, "server", j);
- _rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"),
- cfg_getstr (cfg_server, "service"));
+ rs_resolv (&p->addr, r->type, cfg_getstr (cfg_server, "hostname"),
+ cfg_getstr (cfg_server, "service"));
p->secret = cfg_getstr (cfg_server, "secret");
}
}
diff --git a/lib/conn.c b/lib/conn.c
index 904596b..f737820 100644
--- a/lib/conn.c
+++ b/lib/conn.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -6,10 +7,38 @@
#include <string.h>
#include <assert.h>
-#include <debug.h>
#include <event2/event.h>
+#include <event2/bufferevent.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
+#include "debug.h"
+#include "conn.h"
+#include "event.h"
+#include "packet.h"
+#include "tcp.h"
+
+int
+conn_close (struct rs_connection **connp)
+{
+ int r;
+ assert (connp);
+ assert (*connp);
+ r = rs_conn_destroy (*connp);
+ if (!r)
+ *connp = NULL;
+ return r;
+}
+
+int
+conn_user_dispatch_p (const struct rs_connection *conn)
+{
+ assert (conn);
+
+ return (conn->callbacks.connected_cb ||
+ conn->callbacks.disconnected_cb ||
+ conn->callbacks.received_cb ||
+ conn->callbacks.sent_cb);
+}
int
rs_conn_create (struct rs_context *ctx, struct rs_connection **conn,
@@ -35,6 +64,7 @@ rs_conn_create (struct rs_context *ctx, struct rs_connection **conn,
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
@@ -60,45 +90,6 @@ rs_conn_set_type (struct rs_connection *conn, rs_conn_type_t type)
conn->realm->type = type;
}
-
-struct rs_error * /* FIXME: Return int as all the others? */
-_rs_resolv (struct evutil_addrinfo **addr, rs_conn_type_t type,
- const char *hostname, const char *service)
-{
- int err;
- struct evutil_addrinfo hints, *res = NULL;
-
- memset (&hints, 0, sizeof(struct evutil_addrinfo));
- hints.ai_family = AF_INET; /* IPv4 only. TODO: Set AF_UNSPEC. */
- hints.ai_flags = AI_ADDRCONFIG;
- switch (type)
- {
- case RS_CONN_TYPE_NONE:
- return _rs_err_create (RSE_INVALID_CONN, __FILE__, __LINE__, NULL, NULL);
- case RS_CONN_TYPE_TCP:
- /* Fall through. */
- case RS_CONN_TYPE_TLS:
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_protocol = IPPROTO_TCP;
- break;
- case RS_CONN_TYPE_UDP:
- /* Fall through. */
- case RS_CONN_TYPE_DTLS:
- hints.ai_socktype = SOCK_DGRAM;
- hints.ai_protocol = IPPROTO_UDP;
- break;
- default:
- return _rs_err_create (RSE_INVALID_CONN, __FILE__, __LINE__, NULL, NULL);
- }
- err = evutil_getaddrinfo (hostname, service, &hints, &res);
- if (err)
- return _rs_err_create (RSE_BADADDR, __FILE__, __LINE__,
- "%s:%s: bad host name or service name (%s)",
- hostname, service, evutil_gai_strerror(err));
- *addr = res; /* Simply use first result. */
- return NULL;
-}
-
int
rs_conn_add_listener (struct rs_connection *conn, rs_conn_type_t type,
const char *hostname, int port)
@@ -126,25 +117,24 @@ rs_conn_destroy (struct rs_connection *conn)
assert (conn);
- if (conn->is_connected)
- {
- err = rs_conn_disconnect (conn);
- if (err)
- return err;
- }
-
/* NOTE: conn->realm is owned by context. */
/* NOTE: conn->peers is owned by context. */
+ if (conn->is_connected)
+ err = rs_conn_disconnect (conn);
if (conn->tev)
event_free (conn->tev);
+ if (conn->bev)
+ bufferevent_free (conn->bev);
if (conn->evb)
event_base_free (conn->evb);
/* TODO: free tls_ctx */
/* TODO: free tls_ssl */
- return 0;
+ rs_free (conn->ctx, conn);
+
+ return err;
}
int
@@ -157,7 +147,6 @@ void
rs_conn_set_callbacks (struct rs_connection *conn, struct rs_conn_callbacks *cb)
{
assert (conn);
- conn->user_dispatch_flag = 1;
memcpy (&conn->callbacks, cb, sizeof (conn->callbacks));
}
@@ -165,7 +154,6 @@ void
rs_conn_del_callbacks (struct rs_connection *conn)
{
assert (conn);
- conn->user_dispatch_flag = 0;
memset (&conn->callbacks, 0, sizeof (conn->callbacks));
}
@@ -195,3 +183,124 @@ int rs_conn_fd (struct rs_connection *conn)
assert (conn->active_peer);
return conn->fd;
}
+
+static void
+_rcb (struct rs_packet *packet, void *user_data)
+{
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+ assert (pkt);
+ assert (pkt->conn);
+
+ pkt->flags |= rs_packet_received_flag;
+ if (pkt->conn->bev)
+ bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+ else
+ event_del (pkt->conn->rev);
+}
+
+/* Special function used in libradsec blocking dispatching mode,
+ i.e. with socket set to block on read/write and with no libradsec
+ callbacks registered.
+
+ For any other use of libradsec, a the received_cb callback should
+ be registered in the callbacks member of struct rs_connection.
+
+ On successful reception of a RADIUS message it will be verified
+ against REQ_MSG, if !NULL.
+
+ If PKT_OUT is !NULL it will upon return point at a pointer to a
+ struct rs_packet containing the message.
+
+ If anything goes wrong or if the read times out (TODO: explain),
+ PKT_OUT will not be changed and one or more errors are pushed on
+ the connection (available through rs_err_conn_pop()). */
+int
+rs_conn_receive_packet (struct rs_connection *conn,
+ struct rs_packet *req_msg,
+ struct rs_packet **pkt_out)
+{
+ int err = 0;
+ struct rs_packet *pkt = NULL;
+
+ assert (conn);
+ assert (conn->realm);
+ assert (!conn_user_dispatch_p (conn)); /* Dispatching mode only. */
+
+ if (rs_packet_create (conn, &pkt))
+ return -1;
+
+ assert (conn->evb);
+ assert (conn->fd >= 0);
+
+ conn->callbacks.received_cb = _rcb;
+ conn->user_data = pkt;
+ pkt->flags &= ~rs_packet_received_flag;
+
+ 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));
+
+ /* Activae retransmission timer. */
+ conn_activate_timeout (pkt->conn);
+ }
+
+ rs_debug (("%s: entering event loop\n", __func__));
+ err = event_base_dispatch (conn->evb);
+ conn->callbacks.received_cb = NULL;
+ if (err < 0)
+ return rs_err_conn_push_fl (pkt->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_flag) == 0
+ || (req_msg
+ && packet_verify_response (pkt->conn, pkt, req_msg) != RSE_OK))
+ {
+ assert (rs_err_conn_peek_code (pkt->conn));
+ return rs_err_conn_peek_code (conn);
+ }
+
+ if (pkt_out)
+ *pkt_out = pkt;
+ return RSE_OK;
+}
+
+void
+rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv)
+{
+ assert (conn);
+ assert (tv);
+ conn->timeout = *tv;
+}
+
+int
+conn_activate_timeout (struct rs_connection *conn)
+{
+ assert (conn);
+ assert (conn->tev);
+ assert (conn->evb);
+ if (conn->timeout.tv_sec || conn->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);
+ }
+ return RSE_OK;
+}
diff --git a/lib/conn.h b/lib/conn.h
new file mode 100644
index 0000000..18d2da3
--- /dev/null
+++ b/lib/conn.h
@@ -0,0 +1,6 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+int conn_user_dispatch_p (const struct rs_connection *conn);
+int conn_close (struct rs_connection **connp);
+int conn_activate_timeout (struct rs_connection *conn);
diff --git a/lib/debug.c b/lib/debug.c
index 4544f3c..59f25c1 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
diff --git a/lib/debug.h b/lib/debug.h
index 4a899b2..a8d8632 100644
--- a/lib/debug.h
+++ b/lib/debug.h
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#define hd(p, l) { int i; \
for (i = 1; i <= l; i++) { \
diff --git a/lib/err.c b/lib/err.c
index 1e1dfaf..4a90019 100644
--- a/lib/err.c
+++ b/lib/err.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -30,6 +31,8 @@ static const char *_errtxt[] = {
"connect timeout", /* 16 RSE_TIMEOUT_CONN */
"invalid argument", /* 17 RSE_INVAL */
"I/O timeout", /* 18 RSE_TIMEOUT_IO */
+ "timeout", /* 19 RSE_TIMEOUT */
+ "peer disconnected", /* 20 RSE_DISCO */
};
#define ERRTXT_SIZE (sizeof(_errtxt) / sizeof(*_errtxt))
@@ -37,7 +40,7 @@ static struct rs_error *
_err_vcreate (unsigned int code, const char *file, int line, const char *fmt,
va_list args)
{
- struct rs_error *err;
+ struct rs_error *err = NULL;
err = malloc (sizeof(struct rs_error));
if (err)
@@ -67,15 +70,19 @@ _err_vcreate (unsigned int code, const char *file, int line, const char *fmt,
}
struct rs_error *
-_rs_err_create (unsigned int code, const char *file, int line, const char *fmt,
- ...)
+err_create (unsigned int code,
+ const char *file,
+ int line,
+ const char *fmt,
+ ...)
{
- struct rs_error *err;
+ struct rs_error *err = NULL;
va_list args;
va_start (args, fmt);
err = _err_vcreate (code, file, line, fmt, args);
va_end (args);
+
return err;
}
@@ -85,36 +92,52 @@ _ctx_err_vpush_fl (struct rs_context *ctx, int code, const char *file,
{
struct rs_error *err = _err_vcreate (code, file, line, fmt, args);
- if (err)
- ctx->err = err;
- return code;
+ if (!err)
+ return RSE_NOMEM;
+
+ /* TODO: Implement a stack. */
+ if (ctx->err)
+ rs_err_free (ctx->err);
+ ctx->err = err;
+
+ return err->code;
}
int
rs_err_ctx_push (struct rs_context *ctx, int code, const char *fmt, ...)
{
+ int r = 0;
va_list args;
+
va_start (args, fmt);
- _ctx_err_vpush_fl (ctx, code, NULL, 0, fmt, args);
+ r = _ctx_err_vpush_fl (ctx, code, NULL, 0, fmt, args);
va_end (args);
- return code;
+
+ return r;
}
int
rs_err_ctx_push_fl (struct rs_context *ctx, int code, const char *file,
int line, const char *fmt, ...)
{
+ int r = 0;
va_list args;
+
va_start (args, fmt);
- _ctx_err_vpush_fl (ctx, code, file, line, fmt, args);
+ r = _ctx_err_vpush_fl (ctx, code, file, line, fmt, args);
va_end (args);
- return code;
+
+ return r;
}
int
-_rs_err_conn_push_err (struct rs_connection *conn, struct rs_error *err)
+err_conn_push_err (struct rs_connection *conn, struct rs_error *err)
{
+
+ if (conn->err)
+ rs_err_free (conn->err);
conn->err = err; /* FIXME: use a stack */
+
return err->code;
}
@@ -124,30 +147,37 @@ _conn_err_vpush_fl (struct rs_connection *conn, int code, const char *file,
{
struct rs_error *err = _err_vcreate (code, file, line, fmt, args);
- if (err)
- _rs_err_conn_push_err (conn, err);
- return code;
+ if (!err)
+ return RSE_NOMEM;
+
+ return err_conn_push_err (conn, err);
}
int
rs_err_conn_push (struct rs_connection *conn, int code, const char *fmt, ...)
{
+ int r = 0;
+
va_list args;
va_start (args, fmt);
- _conn_err_vpush_fl (conn, code, NULL, 0, fmt, args);
+ r = _conn_err_vpush_fl (conn, code, NULL, 0, fmt, args);
va_end (args);
- return code;
+
+ return r;
}
int
rs_err_conn_push_fl (struct rs_connection *conn, int code, const char *file,
int line, const char *fmt, ...)
{
+ int r = 0;
+
va_list args;
va_start (args, fmt);
- _conn_err_vpush_fl (conn, code, file, line, fmt, args);
+ r = _conn_err_vpush_fl (conn, code, file, line, fmt, args);
va_end (args);
- return code;
+
+ return r;
}
struct rs_error *
@@ -159,6 +189,7 @@ rs_err_ctx_pop (struct rs_context *ctx)
return NULL; /* FIXME: RSE_INVALID_CTX. */
err = ctx->err;
ctx->err = NULL;
+
return err;
}
@@ -171,42 +202,35 @@ rs_err_conn_pop (struct rs_connection *conn)
return NULL; /* FIXME: RSE_INVALID_CONN */
err = conn->err;
conn->err = NULL;
+
return err;
}
int
rs_err_conn_peek_code (struct rs_connection *conn)
{
- if (conn && conn->err)
+ if (!conn)
+ return -1; /* FIXME: RSE_INVALID_CONN */
+ if (conn->err)
return conn->err->code;
- else
- return RSE_OK;
+
+ return RSE_OK;
}
void
rs_err_free (struct rs_error *err)
{
assert (err);
- if (err->msg)
- free (err->msg);
free (err);
}
char *
-rs_err_msg (struct rs_error *err, int dofree_flag)
+rs_err_msg (struct rs_error *err)
{
- char *msg;
-
if (!err)
return NULL;
- if (err->msg)
- msg = err->msg;
- else
- msg = strdup (err->buf);
- if (dofree_flag)
- rs_err_free (err);
- return msg;
+ return err->buf;
}
int
@@ -219,6 +243,7 @@ rs_err_code (struct rs_error *err, int dofree_flag)
code = err->code;
if (dofree_flag)
- rs_err_free(err);
+ rs_err_free (err);
+
return code;
}
diff --git a/lib/err.h b/lib/err.h
new file mode 100644
index 0000000..5e1c9c9
--- /dev/null
+++ b/lib/err.h
@@ -0,0 +1,9 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+struct rs_error *err_create (unsigned int code,
+ const char *file,
+ int line,
+ const char *fmt,
+ ...);
+int err_conn_push_err (struct rs_connection *conn, struct rs_error *err);
diff --git a/lib/event.c b/lib/event.c
new file mode 100644
index 0000000..5afba98
--- /dev/null
+++ b/lib/event.c
@@ -0,0 +1,255 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+#if defined (RS_ENABLE_TLS)
+#include <event2/bufferevent_ssl.h>
+#include <openssl/err.h>
+#endif
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+#include "tcp.h"
+#include "udp.h"
+#if defined (RS_ENABLE_TLS)
+#include "tls.h"
+#endif
+#include "event.h"
+#include "packet.h"
+#include "conn.h"
+#include "debug.h"
+
+static void
+_evlog_cb (int severity, const char *msg)
+{
+ const char *sevstr;
+ switch (severity)
+ {
+ case _EVENT_LOG_DEBUG:
+#if !defined (DEBUG_LEVENT)
+ return;
+#endif
+ sevstr = "debug";
+ break;
+ case _EVENT_LOG_MSG:
+ sevstr = "msg";
+ break;
+ case _EVENT_LOG_WARN:
+ sevstr = "warn";
+ break;
+ case _EVENT_LOG_ERR:
+ sevstr = "err";
+ break;
+ default:
+ sevstr = "???";
+ break;
+ }
+ fprintf (stderr, "libevent: [%s] %s\n", sevstr, msg); /* FIXME: stderr? */
+}
+
+void
+event_conn_timeout_cb (int fd, short event, void *data)
+{
+ struct rs_connection *conn = NULL;
+
+ assert (data);
+ conn = (struct rs_connection *) 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;
+ rs_err_conn_push_fl (conn, RSE_TIMEOUT_CONN, __FILE__, __LINE__, NULL);
+ event_loopbreak (conn);
+ }
+}
+
+void
+event_retransmit_timeout_cb (int fd, short event, void *data)
+{
+ struct rs_connection *conn = NULL;
+
+ assert (data);
+ conn = (struct rs_connection *) 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));
+ rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
+ event_loopbreak (conn);
+ }
+}
+
+int
+event_init_socket (struct rs_connection *conn, struct rs_peer *p)
+{
+ if (conn->fd != -1)
+ return RSE_OK;
+
+ assert (p->addr);
+ conn->fd = socket (p->addr->ai_family, p->addr->ai_socktype,
+ p->addr->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)
+ {
+ 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));
+ }
+ return RSE_OK;
+}
+
+int
+event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer)
+{
+ if (conn->bev)
+ return RSE_OK;
+
+ if (conn->realm->type == RS_CONN_TYPE_TCP)
+ {
+ conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0);
+ if (!conn->bev)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "bufferevent_socket_new");
+ }
+#if defined (RS_ENABLE_TLS)
+ else if (conn->realm->type == RS_CONN_TYPE_TLS)
+ {
+ 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)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "bufferevent_openssl_socket_new");
+ }
+#endif /* RS_ENABLE_TLS */
+ else
+ {
+ return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__,
+ "%s: unknown connection type: %d", __func__,
+ conn->realm->type);
+ }
+
+ return RSE_OK;
+}
+
+void
+event_do_connect (struct rs_connection *conn)
+{
+ struct rs_peer *p;
+ int err, sockerr;
+
+ assert (conn);
+ assert (conn->active_peer);
+ p = conn->active_peer;
+
+#if defined (DEBUG)
+ {
+ char host[80], serv[80];
+
+ getnameinfo (p->addr->ai_addr,
+ p->addr->ai_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 */
+ {
+ conn_activate_timeout (conn); /* Connect timeout. */
+ err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr,
+ p->addr->ai_addrlen);
+ if (err < 0)
+ rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
+ "bufferevent_socket_connect: %s",
+ evutil_gai_strerror (err));
+ else
+ p->conn->is_connecting = 1;
+ }
+ else /* UDP */
+ {
+ err = connect (p->conn->fd, p->addr->ai_addr, p->addr->ai_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_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));
+ }
+ }
+}
+
+int
+event_loopbreak (struct rs_connection *conn)
+{
+ int err = event_base_loopbreak (conn->evb);
+ if (err < 0)
+ rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "event_base_loopbreak: %s",
+ evutil_gai_strerror (err));
+ return err;
+}
+
+
+void
+event_on_disconnect (struct rs_connection *conn)
+{
+ conn->is_connecting = 0;
+ conn->is_connected = 0;
+ rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer));
+ if (conn->callbacks.disconnected_cb)
+ conn->callbacks.disconnected_cb (conn->user_data);
+}
+
+void
+event_on_connect (struct rs_connection *conn, struct rs_packet *pkt)
+{
+ assert (!conn->is_connecting);
+ conn->is_connected = 1;
+ rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
+
+ if (conn->callbacks.connected_cb)
+ conn->callbacks.connected_cb (conn->user_data);
+
+ if (pkt)
+ packet_do_send (pkt);
+}
+
+int
+event_init_eventbase (struct rs_connection *conn)
+{
+ assert (conn);
+ if (conn->evb)
+ return RSE_OK;
+
+#if defined (DEBUG)
+ event_enable_debug_mode ();
+#endif
+ event_set_log_callback (_evlog_cb);
+ conn->evb = event_base_new ();
+ if (!conn->evb)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "event_base_new");
+
+ return RSE_OK;
+}
diff --git a/lib/event.h b/lib/event.h
new file mode 100644
index 0000000..e042599
--- /dev/null
+++ b/lib/event.h
@@ -0,0 +1,12 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+void event_on_disconnect (struct rs_connection *conn);
+void event_on_connect (struct rs_connection *conn, struct rs_packet *pkt);
+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);
+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 49d3409..ee0787b 100644
--- a/lib/examples/Makefile.am
+++ b/lib/examples/Makefile.am
@@ -4,5 +4,5 @@ AM_CFLAGS = -Wall -g
bin_PROGRAMS = client
client_SOURCES = client-blocking.c
-client_LDADD = ../libradsec.la
+client_LDADD = ../libradsec.la #-lefence
client_CFLAGS = $(AM_CFLAGS) -DUSE_CONFIG_FILE
diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c
index 27f87ca..15152b6 100644
--- a/lib/examples/client-blocking.c
+++ b/lib/examples/client-blocking.c
@@ -8,6 +8,7 @@
#include <freeradius/libradius.h>
#include <radsec/radsec.h>
#include <radsec/request.h>
+#include "debug.h" /* For rs_dump_packet(). */
#define SECRET "sikrit"
#define USER_NAME "molgan"
@@ -16,9 +17,11 @@
struct rs_error *
blocking_client (const char *av1, const char *av2, int use_request_object_flag)
{
- struct rs_context *h;
- struct rs_connection *conn;
- struct rs_packet *req, *resp = NULL;
+ struct rs_context *h = NULL;
+ struct rs_connection *conn = NULL;
+ struct rs_request *request = NULL;
+ struct rs_packet *req = NULL, *resp = NULL;
+ struct rs_error *err = NULL;
if (rs_context_create (&h, "/usr/share/freeradius/dictionary"))
return NULL;
@@ -28,67 +31,60 @@ blocking_client (const char *av1, const char *av2, int use_request_object_flag)
struct rs_peer *server;
if (rs_conn_create (h, &conn, NULL))
- return rs_err_conn_pop (conn);
+ goto cleanup;
rs_conn_set_type (conn, RS_CONN_TYPE_UDP);
if (rs_peer_create (conn, &server))
- return rs_err_conn_pop (conn);
+ goto cleanup;
if (rs_peer_set_address (server, av1, av2))
- return rs_err_conn_pop (conn);
+ goto cleanup;
rs_peer_set_timeout (server, 1);
rs_peer_set_retries (server, 3);
if (rs_peer_set_secret (server, SECRET))
- return rs_err_conn_pop (conn);
+ goto cleanup;
}
#else
if (rs_context_read_config (h, av1))
- return rs_err_ctx_pop (h);
+ goto cleanup;
if (rs_conn_create (h, &conn, av2))
- return rs_err_conn_pop (conn);
+ goto cleanup;
#endif /* USE_CONFIG_FILE */
if (use_request_object_flag)
{
- struct rs_request *request;
-
if (rs_request_create_authn (conn, &request, USER_NAME, USER_PW))
- return rs_err_conn_pop (conn);
+ goto cleanup;
if (rs_request_send (request, &resp))
- return rs_err_conn_pop (conn);
- rs_request_destroy (request);
+ goto cleanup;
}
else
{
if (rs_packet_create_authn_request (conn, &req, USER_NAME, USER_PW))
- return rs_err_conn_pop (conn);
-
+ goto cleanup;
if (rs_packet_send (req, NULL))
- {
- rs_packet_destroy (req);
- return rs_err_conn_pop (conn);
- }
+ goto cleanup;
if (rs_conn_receive_packet (conn, req, &resp))
- {
- rs_packet_destroy (req);
- return rs_err_conn_pop (conn);
- }
- rs_packet_destroy (req);
+ goto cleanup;
}
if (resp)
- {
- RADIUS_PACKET *fr_pkt = NULL;
- VALUE_PAIR *fr_vp = NULL;
+ rs_dump_packet (resp);
+ else
+ fprintf (stderr, "%s: no response\n", __func__);
- fr_pkt = rs_packet_frpkt (resp);
- fr_vp = fr_pkt->vps; /* FIXME: Is there an accessor? */
- if (fr_vp)
- vp_printlist(stdout, fr_vp);
- rs_packet_destroy (resp);
- }
+ cleanup:
+ err = rs_err_conn_pop (conn);
+ if (resp)
+ rs_packet_destroy (resp);
+ if (req)
+ rs_packet_destroy (req);
+ if (conn)
+ rs_conn_destroy (conn);
+ if (request)
+ rs_request_destroy (request);
+ if (h)
+ rs_context_destroy (h);
- rs_conn_destroy (conn);
- rs_context_destroy (h);
- return NULL;
+ return err;
}
int
@@ -106,7 +102,7 @@ main (int argc, char *argv[])
err = blocking_client (argv[1], argv[2], use_request_object_flag);
if (err)
{
- fprintf (stderr, "%s\n", rs_err_msg (err, 0));
+ fprintf (stderr, "%s\n", rs_err_msg (err));
return rs_err_code (err, 1);
}
return 0;
diff --git a/lib/examples/client-udp.conf b/lib/examples/client-udp.conf
index a83fb26..8578e8b 100644
--- a/lib/examples/client-udp.conf
+++ b/lib/examples/client-udp.conf
@@ -1,10 +1,10 @@
config blocking-udp {
type = "UDP"
+ timeout = 2
+ retries = 2
server {
- hostname = "localhost"
+ hostname = "127.0.0.1"
service = "1820"
secret = "sikrit"
- timeout = 1 /* optional */
- tries = 10 /* optional */
}
}
diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h
index 963c821..9bcd208 100644
--- a/lib/include/radsec/radsec-impl.h
+++ b/lib/include/radsec/radsec-impl.h
@@ -32,7 +32,6 @@ struct rs_credentials {
struct rs_error {
int code;
- char *msg;
char buf[1024];
};
@@ -69,7 +68,6 @@ struct rs_connection {
struct rs_context *ctx;
struct rs_realm *realm; /* Owned by ctx. */
struct event_base *evb; /* Event base. */
- struct bufferevent *bev; /* Buffer event. */
struct event *tev; /* Timeout event. */
struct rs_credentials transport_credentials;
struct rs_conn_callbacks callbacks;
@@ -77,26 +75,37 @@ struct rs_connection {
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;
- int nextid;
- int user_dispatch_flag : 1; /* User does the dispatching. */
+ 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. */
#if defined(RS_ENABLE_TLS)
+ /* TLS specifics. */
SSL_CTX *tls_ctx;
SSL *tls_ssl;
#endif
};
+enum rs_packet_flags {
+ rs_packet_hdr_read_flag,
+ rs_packet_received_flag,
+ rs_packet_sent_flag,
+};
+
struct rs_packet {
struct rs_connection *conn;
- char hdr_read_flag;
- uint8_t hdr[4];
+ unsigned int flags;
+ uint8_t hdr[RS_HEADER_LEN];
RADIUS_PACKET *rpkt;
- struct rs_packet *original;
- char valid_flag;
- char written_flag;
+ struct rs_packet *next; /* Used for UDP output queue. */
};
struct rs_attr {
@@ -104,18 +113,11 @@ struct rs_attr {
VALUE_PAIR *vp;
};
-/* Nonpublic functions. */
-struct rs_error *_rs_resolv(struct evutil_addrinfo **addr,
- rs_conn_type_t type, const char *hostname,
+/* Nonpublic functions (in radsec.c -- FIXME: move?). */
+struct rs_error *rs_resolv (struct evutil_addrinfo **addr,
+ rs_conn_type_t type,
+ const char *hostname,
const char *service);
-struct rs_peer *_rs_peer_create(struct rs_context *ctx,
- struct rs_peer **rootp);
-struct rs_error *_rs_err_create(unsigned int code, const char *file,
- int line, const char *fmt, ...);
-int _rs_err_conn_push_err(struct rs_connection *conn,
- struct rs_error *err);
-
-
#if defined (__cplusplus)
}
#endif
diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h
index fcd391d..971fc17 100644
--- a/lib/include/radsec/radsec.h
+++ b/lib/include/radsec/radsec.h
@@ -3,8 +3,9 @@
/* See the file COPYING for licensing information. */
#include <unistd.h>
+#include <sys/time.h>
-enum rs_err_code {
+enum rs_error_code {
RSE_OK = 0,
RSE_NOMEM = 1,
RSE_NOSYS = 2,
@@ -21,9 +22,11 @@ enum rs_err_code {
RSE_INTERNAL = 13,
RSE_SSLERR = 14, /* OpenSSL error. */
RSE_INVALID_PKT = 15,
- RSE_TIMEOUT_CONN = 16,
- RSE_INVAL = 17,
- RSE_TIMEOUT_IO = 18,
+ RSE_TIMEOUT_CONN = 16, /* Connection timeout. */
+ RSE_INVAL = 17, /* Invalid argument. */
+ RSE_TIMEOUT_IO = 18, /* I/O timeout. */
+ RSE_TIMEOUT = 19, /* High level timeout. */
+ RSE_DISCO = 20,
};
enum rs_conn_type {
@@ -111,6 +114,7 @@ int rs_conn_receive_packet(struct rs_connection *conn,
struct rs_packet *request,
struct rs_packet **pkt_out);
int rs_conn_fd(struct rs_connection *conn);
+void rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv);
/* Peer -- client and server. */
int rs_peer_create(struct rs_connection *conn, struct rs_peer **peer_out);
@@ -164,7 +168,7 @@ int rs_err_conn_push_fl(struct rs_connection *conn,
struct rs_error *rs_err_conn_pop(struct rs_connection *conn);
int rs_err_conn_peek_code (struct rs_connection *conn);
void rs_err_free(struct rs_error *err);
-char *rs_err_msg(struct rs_error *err, int dofree_flag);
+char *rs_err_msg(struct rs_error *err);
int rs_err_code(struct rs_error *err, int dofree_flag);
#if defined (__cplusplus)
diff --git a/lib/include/radsec/request-impl.h b/lib/include/radsec/request-impl.h
index 4f50d44..19aef66 100644
--- a/lib/include/radsec/request-impl.h
+++ b/lib/include/radsec/request-impl.h
@@ -11,6 +11,7 @@ struct rs_request
struct rs_packet *req_msg;
struct rs_packet *resp_msg;
struct rs_conn_callbacks saved_cb;
+ void *saved_user_data;
};
#if defined (__cplusplus)
diff --git a/lib/packet.c b/lib/packet.c
index 89b1eca..48fb55e 100644
--- a/lib/packet.c
+++ b/lib/packet.c
@@ -1,60 +1,83 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
#endif
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/time.h>
#include <assert.h>
-#include <freeradius/libradius.h>
-#include <event2/event.h>
#include <event2/bufferevent.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
-#include "tls.h"
+#include "conn.h"
#include "debug.h"
-#if defined (RS_ENABLE_TLS)
-#include <event2/bufferevent_ssl.h>
-#include <openssl/err.h>
-#endif
+#include "packet.h"
+
#if defined (DEBUG)
#include <netdb.h>
#include <sys/socket.h>
#include <event2/buffer.h>
#endif
-static int
-_loopbreak (struct rs_connection *conn)
+int
+packet_verify_response (struct rs_connection *conn,
+ struct rs_packet *response,
+ struct rs_packet *request)
{
- int err = event_base_loopbreak (conn->evb);
- if (err < 0)
- rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
- "event_base_loopbreak: %s",
- evutil_gai_strerror (err));
- return err;
+ assert (conn);
+ assert (conn->active_peer);
+ assert (conn->active_peer->secret);
+ assert (response);
+ assert (response->rpkt);
+ assert (request);
+ assert (request->rpkt);
+
+ /* Verify header and message authenticator. */
+ if (rad_verify (response->rpkt, request->rpkt, conn->active_peer->secret))
+ {
+ conn_close (&conn);
+ return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__,
+ "rad_verify: %s", fr_strerror ());
+ }
+
+ /* Decode and decrypt. */
+ if (rad_decode (response->rpkt, request->rpkt, conn->active_peer->secret))
+ {
+ conn_close (&conn);
+ return rs_err_conn_push_fl (conn, RSE_FR, __FILE__, __LINE__,
+ "rad_decode: %s", fr_strerror ());
+ }
+
+ return RSE_OK;
}
-static int
-_do_send (struct rs_packet *pkt)
+
+/* Badly named function for preparing a RADIUS message and queue it.
+ FIXME: Rename. */
+int
+packet_do_send (struct rs_packet *pkt)
{
- int err;
- VALUE_PAIR *vp;
+ VALUE_PAIR *vp = NULL;
+ assert (pkt);
+ assert (pkt->conn);
+ assert (pkt->conn->active_peer);
+ assert (pkt->conn->active_peer->secret);
assert (pkt->rpkt);
- assert (!pkt->original);
+ /* Add a Message-Authenticator, RFC 2869, if not already present. */
+ /* FIXME: Make Message-Authenticator optional? */
vp = paircreate (PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
if (!vp)
- return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__,
+ return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
"paircreate: %s", fr_strerror ());
- pairadd (&pkt->rpkt->vps, vp);
+ pairreplace (&pkt->rpkt->vps, vp);
+ /* Encode message. */
if (rad_encode (pkt->rpkt, NULL, pkt->conn->active_peer->secret))
return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
"rad_encode: %s", fr_strerror ());
+ /* Sign message. */
if (rad_sign (pkt->rpkt, NULL, pkt->conn->active_peer->secret))
return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
"rad_sign: %s", fr_strerror ());
@@ -71,506 +94,28 @@ _do_send (struct rs_packet *pkt)
}
#endif
- err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data,
- pkt->rpkt->data_len);
- if (err < 0)
- return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
- "bufferevent_write: %s",
- evutil_gai_strerror (err));
- return RSE_OK;
-}
-
-static void
-_on_connect (struct rs_connection *conn)
-{
- conn->is_connected = 1;
- rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
- evtimer_del (conn->tev);
- if (conn->callbacks.connected_cb)
- conn->callbacks.connected_cb (conn->user_data);
-}
-
-static void
-_on_disconnect (struct rs_connection *conn)
-{
- conn->is_connecting = 0;
- conn->is_connected = 0;
- rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer));
- if (conn->callbacks.disconnected_cb)
- conn->callbacks.disconnected_cb (conn->user_data);
-}
-
-static void
-_event_cb (struct bufferevent *bev, short events, void *ctx)
-{
- struct rs_packet *pkt = (struct rs_packet *)ctx;
- struct rs_connection *conn = NULL;
- struct rs_peer *p = NULL;
- int sockerr = 0;
-#if defined (RS_ENABLE_TLS)
- unsigned long tlserr = 0;
-#endif
-
- assert (pkt);
- assert (pkt->conn);
- assert (pkt->conn->active_peer);
- conn = pkt->conn;
- p = conn->active_peer;
-
- conn->is_connecting = 0;
- if (events & BEV_EVENT_CONNECTED)
- {
- _on_connect (conn);
- if (_do_send (pkt))
- rs_debug (("%s: error sending\n", __func__));
- }
- else if (events & BEV_EVENT_EOF)
- {
- _on_disconnect (conn);
- }
- else if (events & BEV_EVENT_TIMEOUT)
+ /* Put message in output buffer. */
+ if (pkt->conn->bev) /* TCP. */
{
- rs_debug (("%s: %p times out on %s\n", __func__, p,
- (events & BEV_EVENT_READING) ? "read" : "write"));
- rs_err_conn_push_fl (pkt->conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
- }
- else if (events & BEV_EVENT_ERROR)
- {
- sockerr = evutil_socket_geterror (conn->active_peer->fd);
- if (sockerr == 0) /* FIXME: True that errno == 0 means closed? */
- {
- _on_disconnect (conn);
- }
- else
- {
- rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
- "%d: socket error %d (%s)",
- conn->fd,
- sockerr,
- evutil_socket_error_to_string (sockerr));
- rs_debug (("%s: socket error on fd %d: %s (%d)\n", __func__,
- conn->fd,
- evutil_socket_error_to_string (sockerr),
- sockerr));
- }
-#if defined (RS_ENABLE_TLS)
- if (conn->tls_ssl) /* FIXME: correct check? */
- {
- for (tlserr = bufferevent_get_openssl_error (conn->bev);
- tlserr;
- tlserr = bufferevent_get_openssl_error (conn->bev))
- {
- rs_debug (("%s: openssl error: %s\n", __func__,
- ERR_error_string (tlserr, NULL)));
- rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__,
- ERR_error_string (tlserr, NULL));
- }
- }
-#endif /* RS_ENABLE_TLS */
- _loopbreak (conn);
- }
-
-#if defined (DEBUG)
- if (events & BEV_EVENT_ERROR && events != BEV_EVENT_ERROR)
- rs_debug (("%s: BEV_EVENT_ERROR and more: 0x%x\n", __func__, events));
-#endif
-}
-
-static void
-_write_cb (struct bufferevent *bev, void *ctx)
-{
- struct rs_packet *pkt = (struct rs_packet *) ctx;
-
- assert (pkt);
- assert (pkt->conn);
-
- if (pkt->conn->callbacks.sent_cb)
- pkt->conn->callbacks.sent_cb (pkt->conn->user_data);
-}
-
-/* Read one RADIUS packet header. Return !0 on error. A return value
- of 0 means that we need more data. */
-static int
-_read_header (struct rs_packet *pkt)
-{
- size_t n = 0;
-
- n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN);
- if (n == RS_HEADER_LEN)
- {
- pkt->hdr_read_flag = 1;
- pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3];
- if (pkt->rpkt->data_len < 20 || pkt->rpkt->data_len > 4096)
- {
- bufferevent_free (pkt->conn->bev); /* Close connection. */
- return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
- "invalid packet length: %d",
+ int err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data,
pkt->rpkt->data_len);
- }
- pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len);
- if (!pkt->rpkt->data)
- {
- bufferevent_free (pkt->conn->bev); /* Close connection. */
- return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__,
- NULL);
- }
- memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN);
- bufferevent_setwatermark (pkt->conn->bev, EV_READ,
- pkt->rpkt->data_len - RS_HEADER_LEN, 0);
- rs_debug (("%s: packet header read, total pkt len=%d\n",
- __func__, pkt->rpkt->data_len));
- }
- else if (n < 0)
- {
- rs_debug (("%s: buffer frozen while reading header\n", __func__));
- }
- else /* Error: libevent gave us less than the low watermark. */
- {
- bufferevent_free (pkt->conn->bev); /* Close connection. */
- return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__,
- "got %d octets reading header", n);
- }
-
- return 0;
-}
-
-static int
-_read_packet (struct rs_packet *pkt)
-{
- size_t n = 0;
-
- rs_debug (("%s: trying to read %d octets of packet data\n", __func__,
- pkt->rpkt->data_len - RS_HEADER_LEN));
-
- n = bufferevent_read (pkt->conn->bev,
- pkt->rpkt->data + RS_HEADER_LEN,
- pkt->rpkt->data_len - RS_HEADER_LEN);
-
- rs_debug (("%s: read %ld octets of packet data\n", __func__, n));
-
- if (n == pkt->rpkt->data_len - RS_HEADER_LEN)
- {
- bufferevent_disable (pkt->conn->bev, EV_READ);
- rs_debug (("%s: complete packet read\n", __func__));
- pkt->hdr_read_flag = 0;
- memset (pkt->hdr, 0, sizeof(*pkt->hdr));
-
- /* Checks done by rad_packet_ok:
- - lenghts (FIXME: checks really ok for tcp?)
- - invalid code field
- - attribute lengths >= 2
- - attribute sizes adding up correctly */
- if (!rad_packet_ok (pkt->rpkt, 0) != 0)
- {
- bufferevent_free (pkt->conn->bev); /* Close connection. */
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "invalid packet: %s", fr_strerror ());
- }
-
- /* TODO: Verify that reception of an unsolicited response packet
- results in connection being closed. */
-
- /* If we have a request to match this response against, verify
- and decode the response. */
- if (pkt->original)
- {
- /* Verify header and message authenticator. */
- if (rad_verify (pkt->rpkt, pkt->original->rpkt,
- pkt->conn->active_peer->secret))
- {
- bufferevent_free (pkt->conn->bev); /* Close connection. */
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "rad_verify: %s", fr_strerror ());
- }
-
- /* Decode and decrypt. */
- if (rad_decode (pkt->rpkt, pkt->original->rpkt,
- pkt->conn->active_peer->secret))
- {
- bufferevent_free (pkt->conn->bev); /* Close connection. */
- return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
- "rad_decode: %s", fr_strerror ());
- }
- }
-
-#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));
- if (rest)
- rs_debug (("%s: returning with %d octets left in buffer\n", __func__,
- rest));
- }
-#endif
-
- /* Hand over message to user, changes ownership of pkt. 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);
- }
- else if (n < 0) /* Buffer frozen. */
- rs_debug (("%s: buffer frozen when reading packet\n", __func__));
- else /* Short packet. */
- rs_debug (("%s: waiting for another %d octets\n", __func__,
- pkt->rpkt->data_len - RS_HEADER_LEN - n));
-
- return 0;
-}
-
-/* Read callback for TCP.
-
- Read exactly one RADIUS message from BEV and store it in struct
- rs_packet passed in CTX (hereby called 'pkt').
-
- Verify the received packet against pkt->original, if !NULL.
-
- Inform upper layer about successful reception of valid RADIUS
- message by invoking conn->callbacks.recevied_cb(), if !NULL. */
-static void
-_read_cb (struct bufferevent *bev, void *ctx)
-{
- struct rs_packet *pkt = (struct rs_packet *) ctx;
-
- assert (pkt);
- assert (pkt->conn);
- assert (pkt->rpkt);
-
- pkt->rpkt->sockfd = pkt->conn->fd;
- pkt->rpkt->vps = NULL;
-
- if (!pkt->hdr_read_flag)
- if (_read_header (pkt))
- return;
- _read_packet (pkt);
-}
-
-static void
-_evlog_cb (int severity, const char *msg)
-{
- const char *sevstr;
- switch (severity)
- {
- case _EVENT_LOG_DEBUG:
-#if !defined (DEBUG_LEVENT)
- return;
-#endif
- sevstr = "debug";
- break;
- case _EVENT_LOG_MSG:
- sevstr = "msg";
- break;
- case _EVENT_LOG_WARN:
- sevstr = "warn";
- break;
- case _EVENT_LOG_ERR:
- sevstr = "err";
- break;
- default:
- sevstr = "???";
- break;
- }
- rs_debug (("libevent: [%s] %s\n", sevstr, msg));
-}
-
-static int
-_init_evb (struct rs_connection *conn)
-{
- if (conn->evb)
- return RSE_OK;
-
-#if defined (DEBUG)
- event_enable_debug_mode ();
-#endif
- event_set_log_callback (_evlog_cb);
- conn->evb = event_base_new ();
- if (!conn->evb)
- return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
- "event_base_new");
-
- return RSE_OK;
-}
-
-static int
-_init_socket (struct rs_connection *conn, struct rs_peer *p)
-{
- if (conn->fd != -1)
- return RSE_OK;
-
- assert (p->addr);
- conn->fd = socket (p->addr->ai_family, p->addr->ai_socktype,
- p->addr->ai_protocol);
- if (conn->fd < 0)
- return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
- strerror (errno));
- if (evutil_make_socket_nonblocking (conn->fd) < 0)
- {
- evutil_closesocket (conn->fd);
- conn->fd = -1;
- return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
- strerror (errno));
- }
- return RSE_OK;
-}
-
-static struct rs_peer *
-_pick_peer (struct rs_connection *conn)
-{
- assert (conn);
-
- if (conn->active_peer)
- conn->active_peer = conn->active_peer->next; /* Next. */
- if (!conn->active_peer)
- conn->active_peer = conn->peers; /* From the top. */
-
- return conn->active_peer;
-}
-
-static void
-_conn_timeout_cb (int fd, short event, void *data)
-{
- struct rs_connection *conn;
-
- assert (data);
- conn = (struct rs_connection *) 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;
- rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
- _loopbreak (conn);
+ if (err < 0)
+ return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
+ "bufferevent_write: %s",
+ evutil_gai_strerror (err));
}
-}
-static int
-_set_timeout (struct rs_connection *conn)
-{
- struct timeval tv;
-
- if (!conn->tev)
- conn->tev = evtimer_new (conn->evb, _conn_timeout_cb, conn);
- if (!conn->tev)
- return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
- "event_new");
- tv.tv_sec = conn->realm->timeout;
- tv.tv_usec = 0;
- evtimer_add (conn->tev, &tv);
-
- return RSE_OK;
-}
-
-static int
-_init_bev (struct rs_connection *conn, struct rs_peer *peer)
-{
- if (conn->bev)
- return RSE_OK;
-
- switch (conn->realm->type)
+ else /* UDP. */
{
- case RS_CONN_TYPE_UDP:
- /* Fall through. */
- /* NOTE: We know this is wrong for several reasons, most notably
- because libevent doesn't work as expected with UDP. The
- timeout handling is wrong too. */
- case RS_CONN_TYPE_TCP:
- conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0);
- if (!conn->bev)
- return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
- "bufferevent_socket_new");
- break;
-
-#if defined (RS_ENABLE_TLS)
- case RS_CONN_TYPE_TLS:
- 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)
- return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
- "bufferevent_openssl_socket_new");
- break;
-
- case RS_CONN_TYPE_DTLS:
- return rs_err_conn_push_fl (conn, RSE_NOSYS, __FILE__, __LINE__, NULL);
-#endif /* RS_ENABLE_TLS */
+ struct rs_packet **pp = &pkt->conn->out_queue;
- default:
- return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__,
- "%s: unknown connection type: %d", __func__,
- conn->realm->type);
+ while (*pp && (*pp)->next)
+ *pp = (*pp)->next;
+ *pp = pkt;
}
return RSE_OK;
}
-static void
-_do_connect (struct rs_connection *conn)
-{
- struct rs_peer *p;
- int err;
-
- assert (conn);
- assert (conn->active_peer);
- p = conn->active_peer;
-
-#if defined (DEBUG)
- {
- char host[80], serv[80];
-
- getnameinfo (p->addr->ai_addr,
- p->addr->ai_addrlen,
- host, sizeof(host), serv, sizeof(serv),
- 0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
- rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv));
- }
-#endif
-
- _set_timeout (conn);
- err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr,
- p->addr->ai_addrlen);
- if (err < 0)
- rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
- "bufferevent_socket_connect: %s",
- evutil_gai_strerror (err));
- else
- p->conn->is_connecting = 1;
-}
-
-static int
-_conn_open(struct rs_connection *conn, struct rs_packet *pkt)
-{
- if (_init_evb (conn))
- return -1;
-
- if (!conn->active_peer)
- _pick_peer (conn);
- if (!conn->active_peer)
- return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
-
- if (_init_socket (conn, conn->active_peer))
- return -1;
-
- if (_init_bev (conn, conn->active_peer))
- return -1;
-
- if (!conn->is_connected)
- if (!conn->is_connecting)
- _do_connect (conn);
-
- return RSE_OK;
-}
-
-static int
-_conn_is_open_p (struct rs_connection *conn)
-{
- return conn->active_peer && conn->is_connected;
-}
-
/* Public functions. */
int
rs_packet_create (struct rs_connection *conn, struct rs_packet **pkt_out)
@@ -629,153 +174,22 @@ rs_packet_create_authn_request (struct rs_connection *conn,
return RSE_OK;
}
-/* 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->written_flag = 1;
- bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
-}
-
-int
-rs_packet_send (struct rs_packet *pkt, void *user_data)
-{
- struct rs_connection *conn = NULL;
- int err = 0;
-
- assert (pkt);
- assert (pkt->conn);
- conn = pkt->conn;
-
- if (_conn_is_open_p (conn))
- _do_send (pkt);
- else
- if (_conn_open (conn, pkt))
- return -1;
-
- assert (conn->evb);
- assert (conn->bev);
- assert (conn->active_peer);
- assert (conn->fd >= 0);
-
- conn->user_data = user_data;
- bufferevent_setcb (conn->bev, NULL, _write_cb, _event_cb, pkt);
- bufferevent_enable (conn->bev, EV_WRITE);
-
- /* Do dispatch, unless the user wants to do it herself. */
- if (!conn->user_dispatch_flag)
- {
- conn->callbacks.sent_cb = _wcb;
- conn->user_data = pkt;
- rs_debug (("%s: entering event loop\n", __func__));
- err = event_base_dispatch (conn->evb);
- if (err < 0)
- return rs_err_conn_push_fl (pkt->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;
-
- if (!pkt->written_flag)
- return -1;
- }
-
- return RSE_OK;
-}
-
-static void
-_rcb (struct rs_packet *packet, void *user_data)
-{
- struct rs_packet *pkt = (struct rs_packet *) user_data;
- assert (pkt);
- pkt->valid_flag = 1;
- bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
-}
-
-/* Special function used in libradsec blocking dispatching mode,
- i.e. with socket set to block on read/write and with no libradsec
- callbacks registered.
-
- For any other use of libradsec, a the received_cb callback should
- be registered in the callbacks member of struct rs_connection.
-
- On successful reception, verification and decoding of a RADIUS
- message, PKT_OUT will upon return point at a pointer to a struct
- rs_packet containing the message.
-
- If anything goes wrong or if the read times out (TODO: explain),
- PKT_OUT will point at the NULL pointer and one or more errors are
- pushed on the connection (available through rs_err_conn_pop()). */
-
-int
-rs_conn_receive_packet (struct rs_connection *conn,
- struct rs_packet *request,
- struct rs_packet **pkt_out)
-{
- int err = 0;
- struct rs_packet *pkt = NULL;
-
- assert (conn);
- assert (conn->realm);
- assert (!conn->user_dispatch_flag); /* Dispatching mode only. */
-
- if (rs_packet_create (conn, pkt_out))
- return -1;
- pkt = *pkt_out;
- pkt->conn = conn;
- pkt->original = request;
-
- assert (conn->evb);
- assert (conn->bev);
- assert (conn->active_peer);
- assert (conn->fd >= 0);
-
- bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0);
- bufferevent_setcb (conn->bev, _read_cb, NULL, _event_cb, pkt);
- bufferevent_enable (conn->bev, EV_READ);
- conn->callbacks.received_cb = _rcb;
- conn->user_data = pkt;
-
- /* Dispatch. */
- rs_debug (("%s: entering event loop\n", __func__));
- err = event_base_dispatch (conn->evb);
- conn->callbacks.received_cb = NULL;
- if (err < 0)
- return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
- "event_base_dispatch: %s",
- evutil_gai_strerror (err));
- rs_debug (("%s: event loop done\n", __func__));
-
- if (!pkt->valid_flag)
- return -1;
-
-#if defined (DEBUG)
- rs_dump_packet (pkt);
-#endif
-
- pkt->original = NULL; /* FIXME: Why? */
- return RSE_OK;
-}
-
void
-rs_packet_add_attr(struct rs_packet *pkt, struct rs_attr *attr)
+rs_packet_add_attr (struct rs_packet *pkt, struct rs_attr *attr)
{
pairadd (&pkt->rpkt->vps, attr->vp);
attr->pkt = pkt;
}
struct radius_packet *
-rs_packet_frpkt(struct rs_packet *pkt)
+rs_packet_frpkt (struct rs_packet *pkt)
{
assert (pkt);
return pkt->rpkt;
}
void
-rs_packet_destroy(struct rs_packet *pkt)
+rs_packet_destroy (struct rs_packet *pkt)
{
if (pkt)
{
@@ -784,3 +198,4 @@ rs_packet_destroy(struct rs_packet *pkt)
rs_free (pkt->conn->ctx, pkt);
}
}
+
diff --git a/lib/packet.h b/lib/packet.h
new file mode 100644
index 0000000..edff9de
--- /dev/null
+++ b/lib/packet.h
@@ -0,0 +1,7 @@
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING 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 4fbb54f..bcd5c97 100644
--- a/lib/peer.c
+++ b/lib/peer.c
@@ -1,4 +1,6 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
#if defined HAVE_CONFIG_H
#include <config.h>
#endif
@@ -6,18 +8,54 @@
#include <assert.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
+#include "err.h"
+#include "peer.h"
+
+struct rs_peer *
+peer_pick_peer (struct rs_connection *conn)
+{
+ assert (conn);
+
+ if (conn->active_peer)
+ conn->active_peer = conn->active_peer->next; /* Next. */
+ if (!conn->active_peer)
+ conn->active_peer = conn->peers; /* From the top. */
+
+ return conn->active_peer;
+}
+
+struct rs_peer *
+peer_create (struct rs_context *ctx, struct rs_peer **rootp)
+{
+ struct rs_peer *p;
+
+ p = (struct rs_peer *) rs_malloc (ctx, sizeof(*p));
+ if (p)
+ {
+ memset (p, 0, sizeof(struct rs_peer));
+ if (*rootp)
+ {
+ p->next = (*rootp)->next;
+ (*rootp)->next = p;
+ }
+ else
+ *rootp = p;
+ }
+ return p;
+}
+/* Public functions. */
int
rs_peer_create (struct rs_connection *conn, struct rs_peer **peer_out)
{
struct rs_peer *peer;
- peer = _rs_peer_create (conn->ctx, &conn->peers);
+ peer = peer_create (conn->ctx, &conn->peers);
if (peer)
{
peer->conn = conn;
- peer->realm->timeout = 2;
- peer->realm->retries = 2;
+ 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);
@@ -35,9 +73,9 @@ rs_peer_set_address (struct rs_peer *peer, const char *hostname,
assert (peer);
assert (peer->realm);
- err = _rs_resolv (&peer->addr, peer->realm->type, hostname, service);
+ err = rs_resolv (&peer->addr, peer->realm->type, hostname, service);
if (err)
- return _rs_err_conn_push_err (peer->conn, err);
+ return err_conn_push_err (peer->conn, err);
return RSE_OK;
}
diff --git a/lib/peer.h b/lib/peer.h
new file mode 100644
index 0000000..a326325
--- /dev/null
+++ b/lib/peer.h
@@ -0,0 +1,5 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING 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.c b/lib/radsec.c
index b771dc8..a05a22b 100644
--- a/lib/radsec.c
+++ b/lib/radsec.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -16,16 +17,18 @@
#include <event2/util.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
+#include "err.h"
+#include "debug.h"
+#include "rsp_debug.h"
#if defined (RS_ENABLE_TLS)
#include <regex.h>
-#include "debug.h"
#include "rsp_list.h"
#include "../radsecproxy.h"
#endif
-#include "rsp_debug.h"
+/* Public functions. */
int
-rs_context_create(struct rs_context **ctx, const char *dict)
+rs_context_create (struct rs_context **ctx, const char *dict)
{
int err = RSE_OK;
struct rs_context *h;
@@ -88,44 +91,48 @@ rs_context_create(struct rs_context **ctx, const char *dict)
return err;
}
-struct rs_peer *
-_rs_peer_create (struct rs_context *ctx, struct rs_peer **rootp)
+struct rs_error * /* FIXME: Return int as all the others? */
+rs_resolv (struct evutil_addrinfo **addr,
+ rs_conn_type_t type,
+ const char *hostname,
+ const char *service)
{
- struct rs_peer *p;
+ int err;
+ struct evutil_addrinfo hints, *res = NULL;
- p = (struct rs_peer *) rs_malloc (ctx, sizeof(*p));
- if (p)
+ memset (&hints, 0, sizeof(struct evutil_addrinfo));
+ hints.ai_family = AF_INET; /* IPv4 only. TODO: Set AF_UNSPEC. */
+ hints.ai_flags = AI_ADDRCONFIG;
+ switch (type)
{
- memset (p, 0, sizeof(struct rs_peer));
- if (*rootp)
- {
- p->next = (*rootp)->next;
- (*rootp)->next = p;
- }
- else
- *rootp = p;
+ case RS_CONN_TYPE_NONE:
+ return err_create (RSE_INVALID_CONN, __FILE__, __LINE__, NULL, NULL);
+ case RS_CONN_TYPE_TCP:
+ /* Fall through. */
+ case RS_CONN_TYPE_TLS:
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ break;
+ case RS_CONN_TYPE_UDP:
+ /* Fall through. */
+ case RS_CONN_TYPE_DTLS:
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ break;
+ default:
+ return err_create (RSE_INVALID_CONN, __FILE__, __LINE__, NULL, NULL);
}
- return p;
+ err = evutil_getaddrinfo (hostname, service, &hints, &res);
+ if (err)
+ return err_create (RSE_BADADDR, __FILE__, __LINE__,
+ "%s:%s: bad host name or service name (%s)",
+ hostname, service, evutil_gai_strerror(err));
+ *addr = res; /* Simply use first result. */
+ return NULL;
}
-static void
-_rs_peer_destroy (struct rs_peer *p)
-{
- assert (p);
- assert (p->conn);
- assert (p->conn->ctx);
- /* NOTE: The peer object doesn't own its connection (conn), nor its
- realm. */
- /* NOTE: secret is owned by config object. */
- if (p->addr)
- {
- evutil_freeaddrinfo (p->addr);
- p->addr = NULL;
- }
- rs_free (p->conn->ctx, p);
-}
-
-void rs_context_destroy(struct rs_context *ctx)
+void
+rs_context_destroy (struct rs_context *ctx)
{
struct rs_realm *r = NULL;
struct rs_peer *p = NULL;
@@ -136,9 +143,12 @@ void rs_context_destroy(struct rs_context *ctx)
for (p = r->peers; p; )
{
struct rs_peer *tmp = p;
+ if (p->addr)
+ evutil_freeaddrinfo (p->addr);
p = p->next;
- _rs_peer_destroy (tmp);
+ rs_free (ctx, tmp);
}
+ rs_free (ctx, r->name);
r = r->next;
rs_free (ctx, tmp);
}
@@ -150,8 +160,10 @@ void rs_context_destroy(struct rs_context *ctx)
rs_free (ctx, ctx);
}
-int rs_context_set_alloc_scheme(struct rs_context *ctx,
- struct rs_alloc_scheme *scheme)
+int
+rs_context_set_alloc_scheme (struct rs_context *ctx,
+ struct rs_alloc_scheme *scheme)
{
return rs_err_ctx_push_fl (ctx, RSE_NOSYS, __FILE__, __LINE__, NULL);
}
+
diff --git a/lib/request.c b/lib/request.c
index 9aa29cb..f354382 100644
--- a/lib/request.c
+++ b/lib/request.c
@@ -1,16 +1,31 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
#endif
-#include <time.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <assert.h>
+#include <sys/time.h>
#include <event2/event.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
#include <radsec/request.h>
#include <radsec/request-impl.h>
+#include <freeradius/libradius.h>
+#include "debug.h"
+#include "conn.h"
+#include "tcp.h"
+#include "udp.h"
+
+/* RFC 5080 2.2.1. Retransmission Behavior. */
+#define IRT 2
+#define MRC 5
+#define MRT 16
+#define MRD 30
+#define RAND 100 /* Rand factor, milliseconds. */
int
rs_request_create (struct rs_connection *conn, struct rs_request **req_out)
@@ -59,68 +74,71 @@ rs_request_destroy (struct rs_request *request)
rs_free (request->conn->ctx, request);
}
-#if 0
static void
-_timer_cb(evutil_socket_t fd, short what, void *arg)
-
-{
-}
-#endif
-
-static void
-_rs_req_connected(void *user_data)
-{
- //struct rs_request *request = (struct rs_request *)user_data;
-}
-
-static void
-_rs_req_disconnected(void *user_data)
-{
- //struct rs_request *request = (struct rs_request *)user_data;
-}
-
-static void
-_rs_req_packet_received(struct rs_packet *msg, void *user_data)
+_rand_rt (struct timeval *res, uint32_t rtprev, uint32_t factor)
{
- //struct rs_request *request = (struct rs_request *)user_data;
-}
-
-static void
-_rs_req_packet_sent(void *user_data)
-{
- //struct rs_request *request = (struct rs_request *)user_data;
+ uint32_t ms = rtprev * (fr_rand () % factor);
+ res->tv_sec = rtprev + ms / 1000;
+ res->tv_usec = (ms % 1000) * 1000;
}
int
rs_request_send (struct rs_request *request, struct rs_packet **resp_msg)
{
- int err;
- struct rs_connection *conn;
-
- assert (request);
- assert (request->conn);
- assert (request->req_msg);
- conn = request->conn;
+ int r = 0;
+ struct rs_connection *conn = NULL;
+ int count = 0;
+ struct timeval rt = {0,0};
+ struct timeval end = {0,0};
+ struct timeval now = {0,0};
+ struct timeval tmp_tv = {0,0};
+ const struct timeval mrt_tv = {MRT,0};
if (!request || !request->conn || !request->req_msg || !resp_msg)
return rs_err_conn_push_fl (conn, RSE_INVAL, __FILE__, __LINE__, NULL);
-
- request->saved_cb = conn->callbacks;
-
- conn->callbacks.connected_cb = _rs_req_connected;
- conn->callbacks.disconnected_cb = _rs_req_disconnected;
- conn->callbacks.received_cb = _rs_req_packet_received;
- conn->callbacks.sent_cb = _rs_req_packet_sent;
-
- err = rs_packet_send(request->req_msg, request);
- if (err)
- goto cleanup;
-
- err = rs_conn_receive_packet(request->conn, request->req_msg, resp_msg);
- if (err)
- goto cleanup;
-
-cleanup:
- conn->callbacks = request->saved_cb;
- return err;
+ conn = request->conn;
+ assert (!conn_user_dispatch_p (conn)); /* This function is high level. */
+
+ gettimeofday (&end, NULL);
+ end.tv_sec += MRD;
+ _rand_rt (&rt, IRT, RAND);
+ while (1)
+ {
+ rs_conn_set_timeout (conn, &rt);
+
+ r = rs_packet_send (request->req_msg, NULL);
+ if (r == RSE_OK)
+ {
+ r = rs_conn_receive_packet (request->conn,
+ request->req_msg,
+ resp_msg);
+ if (r == RSE_OK)
+ break; /* Success. */
+
+ if (r != RSE_TIMEOUT_CONN && r != RSE_TIMEOUT_IO)
+ break; /* Error. */
+ }
+ else if (r != RSE_TIMEOUT_CONN && r != RSE_TIMEOUT_IO)
+ break; /* Error. */
+
+ gettimeofday (&now, NULL);
+ if (++count > MRC || timercmp (&now, &end, >))
+ {
+ r = RSE_TIMEOUT;
+ break; /* Timeout. */
+ }
+
+ /* rt = 2 * rt + rand_rt (rt, RAND); */
+ timeradd (&rt, &rt, &rt);
+ _rand_rt (&tmp_tv, IRT, RAND);
+ timeradd (&rt, &tmp_tv, &rt);
+ if (timercmp (&rt, &mrt_tv, >))
+ _rand_rt (&rt, MRT, RAND);
+ }
+
+ timerclear (&rt);
+ rs_conn_set_timeout (conn, &rt);
+
+ rs_debug (("%s: returning %d\n", __func__, r));
+ return r;
}
diff --git a/lib/send.c b/lib/send.c
new file mode 100644
index 0000000..a8ad1d5
--- /dev/null
+++ b/lib/send.c
@@ -0,0 +1,138 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+#include "debug.h"
+#include "packet.h"
+#include "event.h"
+#include "peer.h"
+#include "conn.h"
+#include "tcp.h"
+#include "udp.h"
+
+static int
+_conn_open (struct rs_connection *conn, struct rs_packet *pkt)
+{
+ if (event_init_eventbase (conn))
+ return -1;
+
+ if (!conn->active_peer)
+ peer_pick_peer (conn);
+ if (!conn->active_peer)
+ return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
+
+ if (event_init_socket (conn, conn->active_peer))
+ return -1;
+
+ if (conn->realm->type == RS_CONN_TYPE_TCP
+ || conn->realm->type == RS_CONN_TYPE_TLS)
+ {
+ if (tcp_init_connect_timer (conn))
+ return -1;
+ if (event_init_bufferevent (conn, conn->active_peer))
+ return -1;
+ }
+ else
+ {
+ if (udp_init (conn, pkt))
+ return -1;
+ if (udp_init_retransmit_timer (conn))
+ return -1;
+ }
+
+ if (!conn->is_connected)
+ if (!conn->is_connecting)
+ event_do_connect (conn);
+
+ return RSE_OK;
+}
+
+static int
+_conn_is_open_p (struct rs_connection *conn)
+{
+ return conn->active_peer && conn->is_connected;
+}
+
+/* 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_flag;
+ if (pkt->conn->bev)
+ bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+ else
+ event_del (pkt->conn->wev);
+}
+
+int
+rs_packet_send (struct rs_packet *pkt, void *user_data)
+{
+ struct rs_connection *conn = NULL;
+ int err = 0;
+
+ assert (pkt);
+ assert (pkt->conn);
+ conn = pkt->conn;
+
+ if (_conn_is_open_p (conn))
+ packet_do_send (pkt);
+ else
+ if (_conn_open (conn, pkt))
+ return -1;
+
+ assert (conn->evb);
+ assert (conn->active_peer);
+ assert (conn->fd >= 0);
+
+ conn->user_data = user_data;
+
+ if (conn->bev) /* TCP */
+ {
+ bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, pkt);
+ bufferevent_enable (conn->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);
+ if (err < 0)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "event_add: %s",
+ evutil_gai_strerror (err));
+ }
+
+ /* Do dispatch, unless the user wants to do it herself. */
+ if (!conn_user_dispatch_p (conn))
+ {
+ conn->callbacks.sent_cb = _wcb;
+ conn->user_data = pkt;
+ rs_debug (("%s: entering event loop\n", __func__));
+ err = event_base_dispatch (conn->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;
+
+ if ((pkt->flags & rs_packet_sent_flag) == 0)
+ {
+ assert (rs_err_conn_peek_code (conn));
+ return rs_err_conn_peek_code (conn);
+ }
+ }
+
+ return RSE_OK;
+}
diff --git a/lib/tcp.c b/lib/tcp.c
new file mode 100644
index 0000000..ce071cd
--- /dev/null
+++ b/lib/tcp.c
@@ -0,0 +1,254 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <event2/event.h>
+#include <event2/bufferevent.h>
+#if defined (RS_ENABLE_TLS)
+#include <event2/bufferevent_ssl.h>
+#include <openssl/err.h>
+#endif
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+#include "tcp.h"
+#include "packet.h"
+#include "conn.h"
+#include "debug.h"
+#include "event.h"
+
+#if defined (DEBUG)
+#include <event2/buffer.h>
+#endif
+
+/* Read one RADIUS packet header. Return !0 on error. A return value
+ of 0 means that we need more data. */
+static int
+_read_header (struct rs_packet *pkt)
+{
+ size_t n = 0;
+
+ n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN);
+ if (n == RS_HEADER_LEN)
+ {
+ pkt->flags |= rs_packet_hdr_read_flag;
+ pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3];
+ if (pkt->rpkt->data_len < 20 || pkt->rpkt->data_len > 4096)
+ {
+ conn_close (&pkt->conn);
+ return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
+ "invalid packet length: %d",
+ pkt->rpkt->data_len);
+ }
+ pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len);
+ if (!pkt->rpkt->data)
+ {
+ conn_close (&pkt->conn);
+ return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__,
+ NULL);
+ }
+ memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN);
+ bufferevent_setwatermark (pkt->conn->bev, EV_READ,
+ pkt->rpkt->data_len - RS_HEADER_LEN, 0);
+ rs_debug (("%s: packet header read, total pkt len=%d\n",
+ __func__, pkt->rpkt->data_len));
+ }
+ else if (n < 0)
+ {
+ rs_debug (("%s: buffer frozen while reading header\n", __func__));
+ }
+ else /* Error: libevent gave us less than the low watermark. */
+ {
+ conn_close (&pkt->conn);
+ return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__,
+ "got %d octets reading header", n);
+ }
+
+ return 0;
+}
+
+static int
+_read_packet (struct rs_packet *pkt)
+{
+ size_t n = 0;
+
+ rs_debug (("%s: trying to read %d octets of packet data\n", __func__,
+ pkt->rpkt->data_len - RS_HEADER_LEN));
+
+ n = bufferevent_read (pkt->conn->bev,
+ pkt->rpkt->data + RS_HEADER_LEN,
+ pkt->rpkt->data_len - RS_HEADER_LEN);
+
+ rs_debug (("%s: read %ld octets of packet data\n", __func__, n));
+
+ if (n == pkt->rpkt->data_len - RS_HEADER_LEN)
+ {
+ bufferevent_disable (pkt->conn->bev, EV_READ);
+ rs_debug (("%s: complete packet read\n", __func__));
+ pkt->flags &= ~rs_packet_hdr_read_flag;
+ memset (pkt->hdr, 0, sizeof(*pkt->hdr));
+
+ /* Checks done by rad_packet_ok:
+ - lenghts (FIXME: checks really ok for tcp?)
+ - invalid code field
+ - attribute lengths >= 2
+ - attribute sizes adding up correctly */
+ if (!rad_packet_ok (pkt->rpkt, 0))
+ {
+ conn_close (&pkt->conn);
+ return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
+ "invalid packet: %s", fr_strerror ());
+ }
+
+#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));
+ 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.
+ 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);
+ }
+ else if (n < 0) /* Buffer frozen. */
+ rs_debug (("%s: buffer frozen when reading packet\n", __func__));
+ else /* Short packet. */
+ rs_debug (("%s: waiting for another %d octets\n", __func__,
+ pkt->rpkt->data_len - 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.
+
+ Inform upper layer about successful reception of received RADIUS
+ message by invoking conn->callbacks.recevied_cb(), if !NULL. */
+void
+tcp_read_cb (struct bufferevent *bev, void *user_data)
+{
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+
+ assert (pkt);
+ assert (pkt->conn);
+ assert (pkt->rpkt);
+
+ pkt->rpkt->sockfd = pkt->conn->fd;
+ pkt->rpkt->vps = NULL;
+
+ if ((pkt->flags & rs_packet_hdr_read_flag) == 0)
+ if (_read_header (pkt))
+ return; /* Error. */
+ _read_packet (pkt);
+}
+
+void
+tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
+{
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+ struct rs_connection *conn = NULL;
+ struct rs_peer *p = NULL;
+ int sockerr = 0;
+#if defined (RS_ENABLE_TLS)
+ unsigned long tlserr = 0;
+#endif
+
+ assert (pkt);
+ assert (pkt->conn);
+ assert (pkt->conn->active_peer);
+ conn = pkt->conn;
+ p = conn->active_peer;
+
+ conn->is_connecting = 0;
+ if (events & BEV_EVENT_CONNECTED)
+ {
+ if (conn->tev)
+ evtimer_del (conn->tev); /* Cancel connect timer. */
+ event_on_connect (conn, pkt);
+ }
+ else if (events & BEV_EVENT_EOF)
+ {
+ event_on_disconnect (conn);
+ }
+ else if (events & BEV_EVENT_TIMEOUT)
+ {
+ rs_debug (("%s: %p times out on %s\n", __func__, p,
+ (events & BEV_EVENT_READING) ? "read" : "write"));
+ rs_err_conn_push_fl (pkt->conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
+ }
+ else if (events & BEV_EVENT_ERROR)
+ {
+ sockerr = evutil_socket_geterror (conn->active_peer->fd);
+ if (sockerr == 0) /* FIXME: True that errno == 0 means closed? */
+ {
+ event_on_disconnect (conn);
+ rs_err_conn_push_fl (pkt->conn, RSE_DISCO, __FILE__, __LINE__, NULL);
+ }
+ else
+ {
+ rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr,
+ evutil_socket_error_to_string (sockerr)));
+ rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
+ "%d: %d (%s)", conn->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);
+ tlserr;
+ tlserr = bufferevent_get_openssl_error (conn->bev))
+ {
+ rs_debug (("%s: openssl error: %s\n", __func__,
+ ERR_error_string (tlserr, NULL)));
+ rs_err_conn_push_fl (pkt->conn, RSE_SSLERR, __FILE__, __LINE__,
+ ERR_error_string (tlserr, NULL));
+ }
+ }
+#endif /* RS_ENABLE_TLS */
+ event_loopbreak (conn);
+ }
+
+#if defined (DEBUG)
+ if (events & BEV_EVENT_ERROR && events != BEV_EVENT_ERROR)
+ rs_debug (("%s: BEV_EVENT_ERROR and more: 0x%x\n", __func__, events));
+#endif
+}
+
+void
+tcp_write_cb (struct bufferevent *bev, void *ctx)
+{
+ struct rs_packet *pkt = (struct rs_packet *) ctx;
+
+ assert (pkt);
+ assert (pkt->conn);
+
+ if (pkt->conn->callbacks.sent_cb)
+ pkt->conn->callbacks.sent_cb (pkt->conn->user_data);
+}
+
+int
+tcp_init_connect_timer (struct rs_connection *conn)
+{
+ assert (conn);
+
+ if (conn->tev)
+ event_free (conn->tev);
+ conn->tev = evtimer_new (conn->evb, event_conn_timeout_cb, conn);
+ if (!conn->tev)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "evtimer_new");
+
+ return RSE_OK;
+}
diff --git a/lib/tcp.h b/lib/tcp.h
new file mode 100644
index 0000000..fc2c4df
--- /dev/null
+++ b/lib/tcp.h
@@ -0,0 +1,7 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+void tcp_event_cb (struct bufferevent *bev, short events, void *user_data);
+void tcp_read_cb (struct bufferevent *bev, void *user_data);
+void tcp_write_cb (struct bufferevent *bev, void *ctx);
+int tcp_init_connect_timer (struct rs_connection *conn);
diff --git a/lib/tls.c b/lib/tls.c
index f57c027..6fcf5a0 100644
--- a/lib/tls.c
+++ b/lib/tls.c
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010, 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
diff --git a/lib/tls.h b/lib/tls.h
index 5dcbc47..d457cfd 100644
--- a/lib/tls.h
+++ b/lib/tls.h
@@ -1,4 +1,5 @@
-/* See the file COPYING for licensing information. */
+/* Copyright 2010 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
#if defined (__cplusplus)
extern "C" {
diff --git a/lib/udp.c b/lib/udp.c
new file mode 100644
index 0000000..911616d
--- /dev/null
+++ b/lib/udp.c
@@ -0,0 +1,179 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <event2/event.h>
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+#include "debug.h"
+#include "event.h"
+#include "compat.h"
+#include "udp.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;
+
+ assert (pkt->rpkt);
+ assert (pkt->rpkt->data);
+
+ /* Send. */
+ r = compat_send (fd, pkt->rpkt->data, pkt->rpkt->data_len, 0);
+ if (r == -1)
+ {
+ int sockerr = evutil_socket_geterror (pkt->conn->fd);
+ if (sockerr != EAGAIN)
+ return rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
+ "%d: send: %d (%s)", fd, sockerr,
+ evutil_socket_error_to_string (sockerr));
+ }
+
+ assert (r == pkt->rpkt->data_len);
+ /* Unlink the packet. */
+ conn->out_queue = pkt->next;
+
+ /* If there are more packets in queue, add the write event again. */
+ if (pkt->conn->out_queue)
+ {
+ r = event_add (pkt->conn->wev, NULL);
+ if (r < 0)
+ return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
+ "event_add: %s", evutil_gai_strerror (r));
+ rs_debug (("%s: re-adding the write event\n", __func__));
+ }
+
+ return RSE_OK;
+}
+
+/* Callback for conn->wev and conn->rev. FIXME: Rename.
+
+ 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. */
+static void
+_evcb (evutil_socket_t fd, short what, void *user_data)
+{
+ rs_debug (("%s: fd=%d what =", __func__, fd));
+ if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT"));
+ if (what & EV_READ) rs_debug ((" READ"));
+ if (what & EV_WRITE) rs_debug ((" WRITE"));
+ rs_debug (("\n"));
+
+ if (what & EV_READ)
+ {
+ /* Read a single UDP packet and stick it in USER_DATA. */
+ /* TODO: Verify that unsolicited packets are dropped. */
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+ ssize_t r = 0;
+
+ assert (pkt);
+ assert (pkt->conn);
+
+ pkt->rpkt->data = rs_malloc (pkt->conn->ctx, 4096);
+ if (pkt->rpkt->data == NULL)
+ {
+ rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
+ return;
+ }
+ r = compat_recv (fd, pkt->rpkt->data, 4096, MSG_TRUNC);
+ if (r == -1)
+ {
+ int sockerr = evutil_socket_geterror (pkt->conn->fd);
+ if (sockerr == EAGAIN)
+ {
+ /* FIXME: Really shouldn't happen since we've been told
+ that fd is readable! */
+ rs_debug (("%s: EAGAIN reading UDP packet -- wot?"));
+ return;
+ }
+
+ /* 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);
+ return;
+ }
+ event_del (pkt->conn->tev);
+ if (r < 20 || r > 4096) /* Short or long packet. */
+ {
+ rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
+ "invalid packet length: %d",
+ pkt->rpkt->data_len);
+ return;
+ }
+ pkt->rpkt->data_len = (pkt->rpkt->data[2] << 8) + pkt->rpkt->data[3];
+ if (!rad_packet_ok (pkt->rpkt, 0))
+ {
+ rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
+ "invalid packet: %s", fr_strerror ());
+ return;
+ }
+ /* Hand over message to user. This changes ownership of pkt.
+ 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);
+ }
+ else if (what & EV_WRITE)
+ {
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+ assert (pkt);
+ assert (pkt->conn);
+
+ 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 defined (DEBUG)
+ if (what & EV_TIMEOUT)
+ rs_debug (("%s: timeout on UDP event, shouldn't happen\n", __func__));
+#endif
+}
+
+int
+udp_init (struct rs_connection *conn, struct rs_packet *pkt)
+{
+ 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)
+ {
+ if (conn->rev)
+ event_free (conn->rev);
+ /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at
+ least for now (libevent-2.0.5). */
+ return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
+ }
+ return RSE_OK;
+}
+
+int
+udp_init_retransmit_timer (struct rs_connection *conn)
+{
+ assert (conn);
+
+ if (conn->tev)
+ event_free (conn->tev);
+ conn->tev = evtimer_new (conn->evb, event_retransmit_timeout_cb, conn);
+ if (!conn->tev)
+ return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "evtimer_new");
+
+ return RSE_OK;
+}
diff --git a/lib/udp.h b/lib/udp.h
new file mode 100644
index 0000000..338e7c2
--- /dev/null
+++ b/lib/udp.h
@@ -0,0 +1,5 @@
+/* Copyright 2011 NORDUnet A/S. All rights reserved.
+ See the file COPYING for licensing information. */
+
+int udp_init (struct rs_connection *conn, struct rs_packet *pkt);
+int udp_init_retransmit_timer (struct rs_connection *conn);