diff options
author | Linus Nordberg <linus@nordu.net> | 2011-03-12 12:41:19 +0100 |
---|---|---|
committer | Linus Nordberg <linus@nordu.net> | 2011-03-12 12:41:19 +0100 |
commit | 2af84872cb78becf43f7bf4a654418fb7fc532d5 (patch) | |
tree | 7c3f33df5316fd67431fc4e70356d7a62da4543f /lib | |
parent | 319c2bf1b2886b2e5cd1c5d60a8950493d2d4d75 (diff) | |
parent | efce8db03af505f76c0c579f2439757bd6998dc9 (diff) |
Merge branch 'udp' into libradsec.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/COPYING | 2 | ||||
-rw-r--r-- | lib/Makefile.am | 9 | ||||
-rw-r--r-- | lib/attr.c | 8 | ||||
-rw-r--r-- | lib/compat.c | 22 | ||||
-rw-r--r-- | lib/compat.h | 5 | ||||
-rw-r--r-- | lib/conf.c | 20 | ||||
-rw-r--r-- | lib/conn.c | 211 | ||||
-rw-r--r-- | lib/conn.h | 6 | ||||
-rw-r--r-- | lib/debug.c | 3 | ||||
-rw-r--r-- | lib/debug.h | 3 | ||||
-rw-r--r-- | lib/err.c | 97 | ||||
-rw-r--r-- | lib/err.h | 9 | ||||
-rw-r--r-- | lib/event.c | 255 | ||||
-rw-r--r-- | lib/event.h | 12 | ||||
-rw-r--r-- | lib/examples/Makefile.am | 2 | ||||
-rw-r--r-- | lib/examples/client-blocking.c | 72 | ||||
-rw-r--r-- | lib/examples/client-udp.conf | 6 | ||||
-rw-r--r-- | lib/include/radsec/radsec-impl.h | 44 | ||||
-rw-r--r-- | lib/include/radsec/radsec.h | 14 | ||||
-rw-r--r-- | lib/include/radsec/request-impl.h | 1 | ||||
-rw-r--r-- | lib/packet.c | 717 | ||||
-rw-r--r-- | lib/packet.h | 7 | ||||
-rw-r--r-- | lib/peer.c | 50 | ||||
-rw-r--r-- | lib/peer.h | 5 | ||||
-rw-r--r-- | lib/radsec.c | 90 | ||||
-rw-r--r-- | lib/request.c | 130 | ||||
-rw-r--r-- | lib/send.c | 138 | ||||
-rw-r--r-- | lib/tcp.c | 254 | ||||
-rw-r--r-- | lib/tcp.h | 7 | ||||
-rw-r--r-- | lib/tls.c | 3 | ||||
-rw-r--r-- | lib/tls.h | 3 | ||||
-rw-r--r-- | lib/udp.c | 179 | ||||
-rw-r--r-- | lib/udp.h | 5 |
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 @@ -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); @@ -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"); } } @@ -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++) { \ @@ -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); @@ -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); @@ -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> @@ -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); |