summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/Makefile.am3
-rw-r--r--lib/compat.c3
-rw-r--r--lib/conn.c14
-rw-r--r--lib/examples/client-udp.conf6
-rw-r--r--lib/include/radsec/radsec-impl.h13
-rw-r--r--lib/packet.c277
-rw-r--r--lib/radsec.c3
7 files changed, 233 insertions, 86 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 311d3cc..69c1c27 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -10,6 +10,7 @@ lib_LTLIBRARIES = libradsec.la
libradsec_la_SOURCES = \
attr.c \
+ compat.c \
conf.c \
conn.c \
debug.c \
@@ -32,4 +33,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 # -ansi
diff --git a/lib/compat.c b/lib/compat.c
index 731c071..b00bea1 100644
--- a/lib/compat.c
+++ b/lib/compat.c
@@ -1,8 +1,9 @@
#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)
{
- compat_send (int sockfd, const void *buf, size_t len, int flags);
+ return send (sockfd, buf, len, flags);
}
diff --git a/lib/conn.c b/lib/conn.c
index 904596b..48d2fe5 100644
--- a/lib/conn.c
+++ b/lib/conn.c
@@ -8,6 +8,7 @@
#include <assert.h>
#include <debug.h>
#include <event2/event.h>
+#include <event2/bufferevent.h>
#include <radsec/radsec.h>
#include <radsec/radsec-impl.h>
@@ -126,25 +127,22 @@ 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;
+ return err;
}
int
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..f8904ac 100644
--- a/lib/include/radsec/radsec-impl.h
+++ b/lib/include/radsec/radsec-impl.h
@@ -69,7 +69,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;
@@ -80,10 +79,17 @@ struct rs_connection {
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 tryagain; /* For server failover. */
+ int nextid; /* Next RADIUS packet identifier. */
int user_dispatch_flag : 1; /* User does the dispatching. */
+ /* 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
@@ -97,6 +103,7 @@ struct rs_packet {
struct rs_packet *original;
char valid_flag;
char written_flag;
+ struct rs_packet *next; /* Used for UDP output queue. */
};
struct rs_attr {
diff --git a/lib/packet.c b/lib/packet.c
index 9874dee..afe2725 100644
--- a/lib/packet.c
+++ b/lib/packet.c
@@ -8,17 +8,19 @@
#include <string.h>
#include <errno.h>
#include <sys/time.h>
+#include <sys/types.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 "compat.h"
#include "debug.h"
#if defined (RS_ENABLE_TLS)
#include <event2/bufferevent_ssl.h>
#include <openssl/err.h>
+#include "tls.h"
#endif
#if defined (DEBUG)
#include <netdb.h>
@@ -27,6 +29,18 @@
#endif
static int
+_close_conn (struct rs_connection **connp)
+{
+ int r;
+ assert (connp);
+ assert (*connp);
+ r = rs_conn_destroy (*connp);
+ if (!r)
+ *connp = NULL;
+ return r;
+}
+
+static int
_loopbreak (struct rs_connection *conn)
{
int err = event_base_loopbreak (conn->evb);
@@ -37,11 +51,12 @@ _loopbreak (struct rs_connection *conn)
return err;
}
+/* Badly named helper function for preparing a RADIUS message and
+ queue it. FIXME: Rename. */
static int
_do_send (struct rs_packet *pkt)
{
- int err;
- VALUE_PAIR *vp;
+ VALUE_PAIR *vp = NULL;
assert (pkt->rpkt);
assert (!pkt->original);
@@ -73,23 +88,39 @@ _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));
+ if (pkt->conn->bev)
+ {
+ int 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));
+ }
+ else
+ {
+ struct rs_packet **pp = &pkt->conn->out_queue;
+
+ while (*pp && (*pp)->next)
+ *pp = (*pp)->next;
+ *pp = pkt;
+ }
+
return RSE_OK;
}
static void
-_on_connect (struct rs_connection *conn)
+_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));
- evtimer_del (conn->tev);
+ if (conn->tev)
+ evtimer_del (conn->tev);
if (conn->callbacks.connected_cb)
conn->callbacks.connected_cb (conn->user_data);
+ if (pkt)
+ _do_send (pkt);
}
static void
@@ -103,9 +134,9 @@ _on_disconnect (struct rs_connection *conn)
}
static void
-_event_cb (struct bufferevent *bev, short events, void *ctx)
+_event_cb (struct bufferevent *bev, short events, void *user_data)
{
- struct rs_packet *pkt = (struct rs_packet *)ctx;
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
struct rs_connection *conn = NULL;
struct rs_peer *p = NULL;
int sockerr = 0;
@@ -122,9 +153,7 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
conn->is_connecting = 0;
if (events & BEV_EVENT_CONNECTED)
{
- _on_connect (conn);
- if (_do_send (pkt))
- rs_debug (("%s: error sending\n", __func__));
+ _on_connect (conn, pkt);
}
else if (events & BEV_EVENT_EOF)
{
@@ -145,15 +174,11 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
}
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: socket error %d (%s)",
- conn->fd,
- sockerr,
+ "%d: %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? */
@@ -204,7 +229,7 @@ _read_header (struct rs_packet *pkt)
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. */
+ _close_conn (&pkt->conn);
return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
"invalid packet length: %d",
pkt->rpkt->data_len);
@@ -212,7 +237,7 @@ _read_header (struct rs_packet *pkt)
pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len);
if (!pkt->rpkt->data)
{
- bufferevent_free (pkt->conn->bev); /* Close connection. */
+ _close_conn (&pkt->conn);
return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__,
NULL);
}
@@ -228,7 +253,7 @@ _read_header (struct rs_packet *pkt)
}
else /* Error: libevent gave us less than the low watermark. */
{
- bufferevent_free (pkt->conn->bev); /* Close connection. */
+ _close_conn (&pkt->conn);
return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__,
"got %d octets reading header", n);
}
@@ -264,7 +289,7 @@ _read_packet (struct rs_packet *pkt)
- attribute sizes adding up correctly */
if (!rad_packet_ok (pkt->rpkt, 0) != 0)
{
- bufferevent_free (pkt->conn->bev); /* Close connection. */
+ _close_conn (&pkt->conn);
return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
"invalid packet: %s", fr_strerror ());
}
@@ -280,7 +305,7 @@ _read_packet (struct rs_packet *pkt)
if (rad_verify (pkt->rpkt, pkt->original->rpkt,
pkt->conn->active_peer->secret))
{
- bufferevent_free (pkt->conn->bev); /* Close connection. */
+ _close_conn (&pkt->conn);
return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
"rad_verify: %s", fr_strerror ());
}
@@ -289,7 +314,7 @@ _read_packet (struct rs_packet *pkt)
if (rad_decode (pkt->rpkt, pkt->original->rpkt,
pkt->conn->active_peer->secret))
{
- bufferevent_free (pkt->conn->bev); /* Close connection. */
+ _close_conn (&pkt->conn);
return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
"rad_decode: %s", fr_strerror ());
}
@@ -376,7 +401,7 @@ _evlog_cb (int severity, const char *msg)
}
static int
-_init_evb (struct rs_connection *conn)
+_init_eventbase (struct rs_connection *conn)
{
if (conn->evb)
return RSE_OK;
@@ -465,27 +490,21 @@ _set_timeout (struct rs_connection *conn)
}
static int
-_init_bev (struct rs_connection *conn, struct rs_peer *peer)
+_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer)
{
if (conn->bev)
return RSE_OK;
- switch (conn->realm->type)
+ if (conn->realm->type == RS_CONN_TYPE_TCP)
{
- 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:
+ 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
@@ -497,13 +516,10 @@ _init_bev (struct rs_connection *conn, struct rs_peer *peer)
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 */
-
- default:
+ else
+ {
return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__,
"%s: unknown connection type: %d", __func__,
conn->realm->type);
@@ -512,11 +528,81 @@ _init_bev (struct rs_connection *conn, struct rs_peer *peer)
return RSE_OK;
}
+/* Callback for conn->wev and conn->rev. FIXME: Rename. */
+static void
+_evcb (evutil_socket_t fd, short what, void *user_data)
+{
+ //rs_debug (("%s: fd=%d what=0x%x\n", __func__, fd, what));
+ if (what & EV_TIMEOUT)
+ {
+ struct rs_connection *conn = (struct rs_connection *) user_data;
+ assert (conn);
+ conn->is_connecting = 0;
+ rs_debug (("%s: UDP timeout NYI", __func__));
+ }
+ else if (what & EV_READ)
+ {
+ struct rs_connection *conn = (struct rs_connection *) user_data;
+ assert (conn);
+ /* read a single UDP packet and stick it in a new struct
+ rs_packet */
+
+ rs_debug (("%s: UDP read NYI", __func__));
+ }
+ else if (what & EV_WRITE)
+ {
+ struct rs_packet *pkt = (struct rs_packet *) user_data;
+ assert (pkt);
+ /* Socket ready for writing, possibly as a result of a
+ successful connect. */
+ if (!pkt->conn->is_connected)
+ _on_connect (pkt->conn, pkt);
+ if (pkt->conn->out_queue)
+ {
+ /* Send one packet, the first. */
+ ssize_t r = 0;
+ struct rs_packet *p = pkt->conn->out_queue;
+
+ assert (p->rpkt);
+ assert (p->rpkt->data);
+ r = compat_send (fd, p->rpkt->data, p->rpkt->data_len, 0);
+ if (r == -1)
+ {
+ int sockerr = evutil_socket_geterror (p->conn->fd);
+ if (sockerr != EAGAIN)
+ rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__,
+ "%d: send: %d (%s)", fd, sockerr,
+ evutil_socket_error_to_string (sockerr));
+ return; /* Don't unlink packet. */
+ }
+ pkt->conn->out_queue = p->next;
+ }
+ }
+}
+
+static int
+_init_udp (struct rs_connection *conn, struct rs_packet *pkt)
+{
+ assert (!conn->bev);
+
+ conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn);
+ conn->wev = event_new (conn->evb, conn->fd, EV_WRITE|EV_PERSIST, _evcb, pkt);
+ 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;
+}
+
static void
_do_connect (struct rs_connection *conn)
{
struct rs_peer *p;
- int err;
+ int err, sockerr;
assert (conn);
assert (conn->active_peer);
@@ -534,21 +620,37 @@ _do_connect (struct rs_connection *conn)
}
#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;
+ if (p->conn->bev) /* TCP */
+ {
+ _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;
+ }
+ 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));
+ }
+ }
}
static int
-_conn_open(struct rs_connection *conn, struct rs_packet *pkt)
+_conn_open (struct rs_connection *conn, struct rs_packet *pkt)
{
- if (_init_evb (conn))
+ if (_init_eventbase (conn))
return -1;
if (!conn->active_peer)
@@ -559,8 +661,17 @@ _conn_open(struct rs_connection *conn, struct rs_packet *pkt)
if (_init_socket (conn, conn->active_peer))
return -1;
- if (_init_bev (conn, conn->active_peer))
- return -1;
+ if (conn->realm->type == RS_CONN_TYPE_TCP
+ || conn->realm->type == RS_CONN_TYPE_TLS)
+ {
+ if (_init_bufferevent (conn, conn->active_peer))
+ return -1;
+ }
+ else
+ {
+ if (_init_udp (conn, pkt))
+ return -1;
+ }
if (!conn->is_connected)
if (!conn->is_connecting)
@@ -640,7 +751,10 @@ _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);
+ if (pkt->conn->bev)
+ bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+ else
+ event_del (pkt->conn->wev);
}
int
@@ -660,13 +774,24 @@ rs_packet_send (struct rs_packet *pkt, void *user_data)
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);
+
+ if (conn->bev) /* TCP */
+ {
+ bufferevent_setcb (conn->bev, NULL, _write_cb, _event_cb, pkt);
+ bufferevent_enable (conn->bev, EV_WRITE);
+ }
+ else /* UDP */
+ {
+ err = event_add (conn->wev, NULL);
+ if (err < 0)
+ return rs_err_conn_push_fl (pkt->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_flag)
@@ -696,7 +821,10 @@ _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);
+ 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,
@@ -737,11 +865,22 @@ rs_conn_receive_packet (struct rs_connection *conn,
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;
+ if (conn->bev)
+ {
+ 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);
+ }
+ else
+ {
+ 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));
+ }
/* Dispatch. */
rs_debug (("%s: entering event loop\n", __func__));
diff --git a/lib/radsec.c b/lib/radsec.c
index afb871e..f191e73 100644
--- a/lib/radsec.c
+++ b/lib/radsec.c
@@ -125,7 +125,8 @@ _rs_peer_destroy (struct rs_peer *p)
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;