summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am39
-rw-r--r--lib/conn.c38
-rw-r--r--lib/err.c50
-rw-r--r--lib/err.h7
-rw-r--r--lib/event.c76
-rw-r--r--lib/examples/client-blocking.c40
-rw-r--r--lib/include/radsec/radsec-impl.h100
-rw-r--r--lib/include/radsec/radsec.h24
-rw-r--r--lib/message.c24
-rw-r--r--lib/peer.c98
-rw-r--r--lib/peer.h1
-rw-r--r--lib/radsec.sym5
-rw-r--r--lib/send.c19
-rw-r--r--lib/tcp.c10
-rw-r--r--lib/tls.c20
-rw-r--r--lib/udp.c2
-rw-r--r--lib/util.c9
17 files changed, 360 insertions, 202 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 06ea8d5..6d62c97 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -3,19 +3,22 @@ ACLOCAL_AMFLAGS = -I m4
# Shared library interface version, i.e. -version-info to Libtool,
# expressed as three integers CURRENT:REVISION:AGE.
-
-# CURRENT is the version number of the current interface. Increment
-# CURRENT when the library interface changes.
-
+#
+# CURRENT is the version number of the current interface.
+# Increment CURRENT when the library interface changes.
+#
# REVISION is the version number of the _implementation_ of the
-# CURRENT interface. Set REVISION to 0 when CURRENT changes,
-# else increment.
-
+# CURRENT interface.
+# Set REVISION to 0 when CURRENT changes, else increment.
+#
# AGE is the number of interfaces this library implements, i.e. how
-# many versions before CURRENT that are supported. Increment AGE
-# when the library interface is _extended_. Set AGE to 0 when the
-# library interface is _changed_.
+# many versions before CURRENT that are supported.
+# Increment AGE when the library interface is _extended_.
+# Set AGE to 0 when the library interface is _changed_.
+VER_CUR = 1
+VER_REV = 0
+VER_AGE = 0
SUBDIRS = radius radsecproxy . include examples
@@ -44,13 +47,19 @@ libradsec_la_SOURCES = \
util.c
if RS_ENABLE_TLS
-libradsec_la_SOURCES += tls.c
+ libradsec_la_SOURCES += tls.c
else
-libradsec_la_SOURCES += md5.c
+ libradsec_la_SOURCES += md5.c
endif
EXTRA_DIST = HACKING LICENSE
-libradsec_la_LIBADD = radsecproxy/libradsec-radsecproxy.la radius/libradsec-radius.la
-libradsec_la_LDFLAGS = -version-info 0:0:0 -export-symbols radsec.sym
-libradsec_la_CFLAGS = $(AM_CFLAGS) -DHAVE_CONFIG_H -Werror # -DDEBUG -DDEBUG_LEVENT
+libradsec_la_CFLAGS = \
+ $(AM_CFLAGS) -DHAVE_CONFIG_H \
+ -DDEBUG -DDEBUG_LEVENT
+libradsec_la_LDFLAGS = \
+ -version-info $(VER_CUR):$(VER_REV):$(VER_AGE) \
+ -export-symbols radsec.sym
+libradsec_la_LIBADD = \
+ radsecproxy/libradsec-radsecproxy.la \
+ radius/libradsec-radius.la
diff --git a/lib/conn.c b/lib/conn.c
index d8c1569..95e65a4 100644
--- a/lib/conn.c
+++ b/lib/conn.c
@@ -1,5 +1,5 @@
/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -25,7 +25,7 @@ conn_close (struct rs_connection **connp)
int r = 0;
assert (connp);
assert (*connp);
- if ((*connp)->is_connected)
+ if ((*connp)->state == RS_CONN_STATE_CONNECTED)
r = rs_conn_disconnect (*connp);
if (r == RSE_OK)
*connp = NULL;
@@ -53,7 +53,7 @@ conn_activate_timeout (struct rs_connection *conn)
if (conn->base_.timeout.tv_sec || conn->base_.timeout.tv_usec)
{
rs_debug (("%s: activating timer: %d.%d\n", __func__,
- conn->timeout.tv_sec, conn->timeout.tv_usec));
+ conn->base_.timeout.tv_sec, conn->base_.timeout.tv_usec));
if (evtimer_add (conn->tev, &conn->base_.timeout))
return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
"evtimer_add: %d", errno);
@@ -64,7 +64,7 @@ conn_activate_timeout (struct rs_connection *conn)
int
conn_type_tls (const struct rs_connection *conn)
{
- assert (conn->active_peer);
+ assert (conn->base_.active_peer);
return conn->base_.realm->type == RS_CONN_TYPE_TLS
|| conn->base_.realm->type == RS_CONN_TYPE_DTLS;
}
@@ -72,9 +72,9 @@ conn_type_tls (const struct rs_connection *conn)
int
conn_cred_psk (const struct rs_connection *conn)
{
- assert (conn->active_peer);
- return conn->active_peer->transport_cred &&
- conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK;
+ assert (conn->base_.active_peer);
+ return conn->base_.active_peer->transport_cred &&
+ conn->base_.active_peer->transport_cred->type == RS_CRED_TLS_PSK;
}
void
@@ -111,23 +111,21 @@ conn_configure (struct rs_context *ctx,
struct rs_realm *r = rs_conf_find_realm (ctx, config);
if (r)
{
- struct rs_peer *p;
-
connbase->realm = r;
connbase->peers = r->peers; /* FIXME: Copy instead? */
+#if 0
for (p = connbase->peers; p != NULL; p = p->next)
- p->conn = TO_GENERIC_CONN(connbase);
+ p->connbase = connbase;
+#endif
connbase->timeout.tv_sec = r->timeout;
connbase->tryagain = r->retries;
}
- else
- {
- connbase->realm = rs_malloc (ctx, sizeof (struct rs_realm));
- if (!connbase->realm)
- return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
- NULL);
- memset (connbase->realm, 0, sizeof (struct rs_realm));
- }
+ }
+ if (connbase->realm == NULL)
+ {
+ connbase->realm = rs_calloc (ctx, 1, sizeof (struct rs_realm));
+ if (connbase->realm == NULL)
+ return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
}
return RSE_OK;
}
@@ -200,7 +198,7 @@ rs_conn_destroy (struct rs_connection *conn)
/* NOTE: conn->realm is owned by context. */
/* NOTE: conn->peers is owned by context. */
- if (conn->is_connected)
+ if (conn->state == RS_CONN_STATE_CONNECTED)
err = rs_conn_disconnect (conn);
#if defined (RS_ENABLE_TLS)
@@ -284,7 +282,7 @@ struct event_base
int rs_conn_get_fd (struct rs_connection *conn)
{
assert (conn);
- assert (conn->active_peer);
+ assert (conn->base_.active_peer);
return conn->base_.fd;
}
diff --git a/lib/err.c b/lib/err.c
index e0bcea9..413ab3e 100644
--- a/lib/err.c
+++ b/lib/err.c
@@ -1,5 +1,5 @@
/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -156,26 +156,41 @@ rs_err_ctx_push_fl (struct rs_context *ctx, int code, const char *file,
}
int
-err_conn_push_err (struct rs_connection *conn, struct rs_error *err)
+err_connbase_push_err (struct rs_conn_base *connbase, struct rs_error *err)
{
- if (conn->base_.err)
- rs_err_free (conn->base_.err);
- conn->base_.err = err; /* FIXME: use a stack */
+ if (connbase->err)
+ rs_err_free (connbase->err);
+ connbase->err = err; /* FIXME: use a stack */
return err->code;
}
static int
-_conn_err_vpush_fl (struct rs_connection *conn, int code, const char *file,
- int line, const char *fmt, va_list args)
+_connbase_err_vpush_fl (struct rs_conn_base *connbase, int code,
+ const char *file, int line, const char *fmt,
+ va_list args)
{
struct rs_error *err = _err_vcreate (code, file, line, fmt, args);
if (!err)
return RSE_NOMEM;
- return err_conn_push_err (conn, err);
+ return err_connbase_push_err (connbase, err);
+}
+
+int
+rs_err_connbase_push (struct rs_conn_base *connbase, int code,
+ const char *fmt, ...)
+{
+ int r = 0;
+
+ va_list args;
+ va_start (args, fmt);
+ r = _connbase_err_vpush_fl (connbase, code, NULL, 0, fmt, args);
+ va_end (args);
+
+ return r;
}
int
@@ -185,7 +200,22 @@ rs_err_conn_push (struct rs_connection *conn, int code, const char *fmt, ...)
va_list args;
va_start (args, fmt);
- r = _conn_err_vpush_fl (conn, code, NULL, 0, fmt, args);
+ r = _connbase_err_vpush_fl (TO_BASE_CONN (conn), code, NULL, 0, fmt, args);
+ va_end (args);
+
+ return r;
+}
+
+int
+rs_err_connbase_push_fl (struct rs_conn_base *connbase, int code,
+ const char *file,
+ int line, const char *fmt, ...)
+{
+ int r = 0;
+
+ va_list args;
+ va_start (args, fmt);
+ r = _connbase_err_vpush_fl (connbase, code, file, line, fmt, args);
va_end (args);
return r;
@@ -199,7 +229,7 @@ rs_err_conn_push_fl (struct rs_connection *conn, int code, const char *file,
va_list args;
va_start (args, fmt);
- r = _conn_err_vpush_fl (conn, code, file, line, fmt, args);
+ r = _connbase_err_vpush_fl (TO_BASE_CONN (conn), code, file, line, fmt, args);
va_end (args);
return r;
diff --git a/lib/err.h b/lib/err.h
index 6615ac8..de5851f 100644
--- a/lib/err.h
+++ b/lib/err.h
@@ -1,9 +1,10 @@
-/* Copyright 2011 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
+ See LICENSE for licensing information. */
struct rs_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);
+int err_connbase_push_err (struct rs_conn_base *, struct rs_error *);
+int rs_err_connbase_push (struct rs_conn_base *, int, const char *, ...);
diff --git a/lib/event.c b/lib/event.c
index f7b936a..c651e4b 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -1,5 +1,5 @@
/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -72,8 +72,8 @@ event_conn_timeout_cb (int fd, short event, void *data)
if (event & EV_TIMEOUT)
{
rs_debug (("%s: connection timeout on %p (fd %d) connecting to %p\n",
- __func__, conn, conn->fd, conn->active_peer));
- conn->is_connecting = 0;
+ __func__, conn, conn->base_.fd, conn->base_.active_peer));
+ conn->state = RS_CONN_STATE_UNDEFINED;
rs_err_conn_push_fl (conn, RSE_TIMEOUT_CONN, __FILE__, __LINE__, NULL);
event_loopbreak (conn);
}
@@ -90,7 +90,7 @@ event_retransmit_timeout_cb (int fd, short event, void *data)
if (event & EV_TIMEOUT)
{
rs_debug (("%s: retransmission timeout on %p (fd %d) sending to %p\n",
- __func__, conn, conn->fd, conn->active_peer));
+ __func__, conn, conn->base_.fd, conn->base_.active_peer));
rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
event_loopbreak (conn);
}
@@ -107,7 +107,7 @@ event_init_socket (struct rs_connection *conn, struct rs_peer *p)
struct rs_error *err =
rs_resolve (&p->addr_cache, p->realm->type, p->hostname, p->service);
if (err != NULL)
- return err_conn_push_err (conn, err);
+ return err_connbase_push_err (TO_BASE_CONN (conn), err);
}
conn->base_.fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype,
@@ -171,59 +171,67 @@ event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer)
void
event_do_connect (struct rs_connection *conn)
{
- struct rs_peer *p;
int err, sockerr;
+ struct sockaddr *peer_addr;
+ size_t peer_addrlen;
assert (conn);
- assert (conn->active_peer);
- p = conn->active_peer;
+ assert (conn->base_.active_peer);
+ assert (conn->base_.active_peer->addr_cache);
+ peer_addr = conn->base_.active_peer->addr_cache->ai_addr;
+ peer_addrlen = conn->base_.active_peer->addr_cache->ai_addrlen;
+
+ /* We don't connect listeners. */
+ assert (conn->base_.magic == RS_CONN_MAGIC_GENERIC);
#if defined (DEBUG)
{
char host[80], serv[80];
- getnameinfo (p->addr_cache->ai_addr,
- p->addr_cache->ai_addrlen,
- host, sizeof(host), serv, sizeof(serv),
+ getnameinfo (peer_addr, peer_addrlen,
+ host, sizeof(host),
+ serv, sizeof(serv),
0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
rs_debug (("%s: connecting to %s:%s\n", __func__, host, serv));
}
#endif
- if (p->conn->base_.bev) /* TCP */
+ if (conn->base_.bev) /* TCP */
{
conn_activate_timeout (conn); /* Connect timeout. */
- err = bufferevent_socket_connect (p->conn->base_.bev,
- p->addr_cache->ai_addr,
- p->addr_cache->ai_addrlen);
+ err = bufferevent_socket_connect (conn->base_.bev,
+ peer_addr, peer_addrlen);
if (err < 0)
- rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
- "bufferevent_socket_connect: %s",
- evutil_gai_strerror (err));
+ rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
+ "bufferevent_socket_connect: %s",
+ evutil_gai_strerror (err));
else
- p->conn->is_connecting = 1;
+ conn->state = RS_CONN_STATE_CONNECTING;
}
else /* UDP */
{
- err = connect (p->conn->base_.fd,
- p->addr_cache->ai_addr,
- p->addr_cache->ai_addrlen);
+ err = connect (conn->base_.fd, peer_addr, peer_addrlen);
if (err < 0)
{
- sockerr = evutil_socket_geterror (p->conn->fd);
- rs_debug (("%s: %d: connect: %d (%s)\n", __func__, p->conn->fd,
+ sockerr = evutil_socket_geterror (conn->base_.fd);
+ rs_debug (("%s: %d: connect: %d (%s)\n", __func__,
+ conn->base_.fd,
sockerr, evutil_socket_error_to_string (sockerr)));
- rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__,
- "%d: connect: %d (%s)", p->conn->base_.fd, sockerr,
+ rs_err_conn_push_fl (conn, RSE_SOCKERR,
+ __FILE__, __LINE__,
+ "%d: connect: %d (%s)", conn->base_.fd,
+ sockerr,
evutil_socket_error_to_string (sockerr));
}
+ else
+ conn->state = RS_CONN_STATE_CONNECTING;
}
}
int
event_loopbreak (struct rs_connection *conn)
{
- int err = event_base_loopbreak (conn->base_.ctx->evb);
+ int err = event_base_loopbreak (TO_BASE_CONN(conn)->ctx->evb);
if (err < 0)
rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
"event_base_loopbreak: %s",
@@ -235,9 +243,9 @@ event_loopbreak (struct rs_connection *conn)
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));
+ conn->state = RS_CONN_STATE_UNDEFINED;
+ rs_debug (("%s: %p disconnected\n", __func__,
+ TO_BASE_CONN(conn)->active_peer));
if (conn->callbacks.disconnected_cb)
conn->callbacks.disconnected_cb (conn->base_.user_data);
}
@@ -246,8 +254,8 @@ event_on_disconnect (struct rs_connection *conn)
int
event_on_connect (struct rs_connection *conn, struct rs_message *msg)
{
- assert (!conn->is_connecting);
- assert (conn->active_peer);
+ assert (conn->state == RS_CONN_STATE_CONNECTING);
+ assert (conn->base_.active_peer);
#if defined (RS_ENABLE_TLS)
if (conn_type_tls(conn) && !conn_cred_psk(conn))
@@ -258,8 +266,8 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg)
}
#endif /* RS_ENABLE_TLS */
- conn->is_connected = 1;
- rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
+ conn->state = RS_CONN_STATE_CONNECTED;
+ rs_debug (("%s: %p connected\n", __func__, TO_BASE_CONN(conn)->active_peer));
if (conn->callbacks.connected_cb)
conn->callbacks.connected_cb (conn->base_.user_data);
diff --git a/lib/examples/client-blocking.c b/lib/examples/client-blocking.c
index 3ea4b51..d2ee9f4 100644
--- a/lib/examples/client-blocking.c
+++ b/lib/examples/client-blocking.c
@@ -1,7 +1,11 @@
/* RADIUS/RadSec client using libradsec in blocking mode. */
+/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
+ See LICENSE for licensing information. */
+
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <assert.h>
#include <radsec/radsec.h>
#include <radsec/request.h>
@@ -13,7 +17,7 @@
#define USER_PW "password"
struct rs_error *
-blocking_client (const char *config_fn, const char *configuration,
+blocking_client (const char *av1, const char *av2, const char *av3,
int use_request_object_flag)
{
struct rs_context *h = NULL;
@@ -22,6 +26,15 @@ blocking_client (const char *config_fn, const char *configuration,
struct rs_message *req = NULL, *resp = NULL;
struct rs_error *err = NULL;
int r;
+#if defined (USE_CONFIG_FILE)
+ const char *config_fn= av1;
+ const char *configuration = av2;
+#else
+ const char *host = av1;
+ const char *service = av2;
+ const char *proto = av3;
+ struct rs_peer *server;
+#endif
r = rs_context_create (&h);
if (r)
@@ -31,15 +44,25 @@ blocking_client (const char *config_fn, const char *configuration,
}
#if !defined (USE_CONFIG_FILE)
+ /* Do it without a configuration file by setting all stuff "by
+ hand". Doesn't work for TLS at the moment because we don't have an
+ API for setting the X509 cert file names and such. */
{
- struct rs_peer *server;
+ int conn_type = RS_CONN_TYPE_UDP;
if (rs_conn_create (h, &conn, NULL))
goto cleanup;
- rs_conn_set_type (conn, RS_CONN_TYPE_UDP);
- if (rs_peer_create (conn, &server))
+ if (proto)
+ {
+ if (!strncmp (proto, "udp", strlen ("udp")))
+ conn_type = RS_CONN_TYPE_UDP;
+ else if (!strncmp (proto, "tls", strlen ("tls")))
+ conn_type = RS_CONN_TYPE_TLS;
+ }
+ rs_conn_set_type (conn, conn_type);
+ if (rs_peer_create_for_conn (conn, &server))
goto cleanup;
- if (rs_peer_set_address (server, av1, av2))
+ if (rs_peer_set_address (server, host, service))
goto cleanup;
rs_peer_set_timeout (server, 1);
rs_peer_set_retries (server, 3);
@@ -85,6 +108,10 @@ blocking_client (const char *config_fn, const char *configuration,
err = rs_err_ctx_pop (h);
if (err == RSE_OK)
err = rs_err_conn_pop (conn);
+#if !defined (USE_CONFIG_FILE)
+ rs_peer_free_address (server);
+ rs_peer_free_secret (server);
+#endif
if (resp)
rs_message_destroy (resp);
if (request)
@@ -118,7 +145,8 @@ main (int argc, char *argv[])
}
if (argc < 3)
usage (argc, argv);
- err = blocking_client (argv[1], argv[2], use_request_object_flag);
+ err = blocking_client (argv[1], argv[2], argc >= 3 ? argv[3] : NULL,
+ use_request_object_flag);
if (err)
{
fprintf (stderr, "error: %s: %d\n", rs_err_msg (err), rs_err_code (err, 0));
diff --git a/lib/include/radsec/radsec-impl.h b/lib/include/radsec/radsec-impl.h
index fecf8f2..45ce7f6 100644
--- a/lib/include/radsec/radsec-impl.h
+++ b/lib/include/radsec/radsec-impl.h
@@ -1,5 +1,5 @@
/** @file libradsec-impl.h
- @brief Libraray internal header file for libradsec. */
+ @brief Libraray internal header file for libradsec. */
/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
@@ -15,13 +15,18 @@
#endif
#include "compat.h"
-/* Constants. */
+/**************/
+/* Constants. */
#define RS_HEADER_LEN 4
+#define RS_CONN_MAGIC_BASE 0xAE004711u
+#define RS_CONN_MAGIC_GENERIC 0x843AEF47u
+#define RS_CONN_MAGIC_LISTENER 0xDCB04783u
-/* Data types. */
+/***************/
+/* Data types. */
enum rs_cred_type {
RS_CRED_NONE = 0,
- /* TLS pre-shared keys, RFC 4279. */
+ /* TLS pre-shared keys, RFC 4279. */
RS_CRED_TLS_PSK,
/* RS_CRED_TLS_DH_PSK, */
/* RS_CRED_TLS_RSA_PSK, */
@@ -34,6 +39,17 @@ enum rs_key_encoding {
};
typedef unsigned int rs_key_encoding_t;
+enum rs_peer_type {
+ RS_PEER_TYPE_CLIENT = 1,
+ RS_PEER_TYPE_SERVER = 2
+};
+
+enum rs_conn_subtype {
+ RS_CONN_OBJTYPE_BASE = 1,
+ RS_CONN_OBJTYPE_GENERIC,
+ RS_CONN_OBJTYPE_LISTENER,
+};
+
#if defined (__cplusplus)
extern "C" {
#endif
@@ -50,19 +66,14 @@ struct rs_error {
char buf[1024];
};
-enum rs_peer_type {
- RS_PEER_TYPE_CLIENT = 1,
- RS_PEER_TYPE_SERVER = 2
-};
-
-/** Configuration object for a connection. */
+/** Configuration object for a connection. */
struct rs_peer {
enum rs_peer_type type;
- struct rs_connection *conn;
+ struct rs_conn_base *connbase; /**< For error handling. */
struct rs_realm *realm;
char *hostname;
char *service;
- char *secret; /* RADIUS secret. */
+ char *secret; /* RADIUS secret. */
struct evutil_addrinfo *addr_cache;
char *cacertfile;
char *cacertpath;
@@ -72,17 +83,18 @@ struct rs_peer {
struct rs_peer *next;
};
-/** Configuration object for a RADIUS realm. */
+/** Configuration object for a RADIUS realm. */
struct rs_realm {
char *name;
enum rs_conn_type type;
int timeout;
int retries;
+ struct rs_listener *listeners;
struct rs_peer *peers;
struct rs_realm *next;
};
-/** Top configuration object. */
+/** Top configuration object. */
struct rs_config {
struct rs_realm *realms;
cfg_t *cfg;
@@ -93,47 +105,51 @@ struct rs_context {
struct rs_config *config;
struct rs_alloc_scheme alloc_scheme;
struct rs_error *err;
- struct event_base *evb; /* Event base. */
-};
-
-enum rs_conn_subtype {
- RS_CONN_OBJTYPE_BASE = 1,
- RS_CONN_OBJTYPE_GENERIC,
- RS_CONN_OBJTYPE_LISTENER,
+ struct event_base *evb; /* Event base. */
};
-#define RS_CONN_MAGIC_BASE 0xAE004711u
-#define RS_CONN_MAGIC_GENERIC 0x843AEF47u
-#define RS_CONN_MAGIC_LISTENER 0xDCB04783u
/** Base class for a connection. */
struct rs_conn_base {
uint32_t magic; /* Must be one of RS_CONN_MAGIC_*. */
struct rs_context *ctx;
- struct rs_realm *realm; /* Owned by ctx. */
- struct rs_peer *peers; /*< Configured peers. */
+ struct rs_realm *realm; /* Owned by ctx. */
+ /** For a listener, allowed client addr/port pairs.
+ For an outgoing connection, set of servers.
+ For an incoming connection, the peer (as the only entry). */
+ struct rs_peer *peers; /**< Configured peers. */
+ struct rs_peer *active_peer; /**< The other end of the connection. */
struct timeval timeout;
- int tryagain; /* For server failover. */
+ int tryagain; /* For server failover. */
void *user_data;
struct rs_error *err;
- int fd; /* Socket. */
- /* TCP transport specifics. */
- struct bufferevent *bev; /* Buffer event. */
- /* UDP transport specifics. */
- struct event *wev; /* Write event (for UDP). */
- struct event *rev; /* Read event (for UDP). */
+ int fd; /* Socket. */
+ /* TCP transport specifics. */
+ struct bufferevent *bev; /* Buffer event. */
+ /* UDP transport specifics. */
+ struct event *wev; /* Write event (for UDP). */
+ struct event *rev; /* Read event (for UDP). */
+};
+
+
+enum rs_conn_state {
+ RS_CONN_STATE_UNDEFINED = 0,
+ RS_CONN_STATE_CONNECTING,
+ RS_CONN_STATE_CONNECTED,
};
/** A "generic" connection. */
struct rs_connection {
struct rs_conn_base base_;
- struct event *tev; /* Timeout event. */
+ struct event *tev; /* Timeout event. */
struct rs_conn_callbacks callbacks;
- struct rs_peer *active_peer;
+ enum rs_conn_state state;
+#if 0
char is_connecting; /* FIXME: replace with a single state member */
char is_connected; /* FIXME: replace with a single state member */
- struct rs_message *out_queue; /* Queue for outgoing UDP packets. */
+#endif /* 0 */
+ struct rs_message *out_queue; /* Queue for outgoing UDP packets. */
#if defined(RS_ENABLE_TLS)
- /* TLS specifics. */
+ /* TLS specifics. */
SSL_CTX *tls_ctx;
SSL *tls_ssl;
#endif
@@ -145,6 +161,7 @@ struct rs_listener {
struct rs_conn_base base_;
struct evconnlistener *evlistener;
struct rs_listener_callbacks callbacks;
+ struct rs_listener *next;
};
enum rs_message_flags {
@@ -159,16 +176,16 @@ struct rs_message {
struct rs_connection *conn;
unsigned int flags;
uint8_t hdr[RS_HEADER_LEN];
- struct radius_packet *rpkt; /* FreeRADIUS object. */
- struct rs_message *next; /* Used for UDP output queue. */
+ struct radius_packet *rpkt; /* FreeRADIUS object. */
+ struct rs_message *next; /* Used for UDP output queue. */
};
#if defined (__cplusplus)
}
#endif
-/************************/
-/* Convenience macros. */
+/***********************/
+/* Convenience macros. */
/* Memory allocation. */
#define rs_calloc(h, nmemb, size) ((h)->alloc_scheme.calloc != NULL \
@@ -192,6 +209,7 @@ struct rs_message {
#define SUBTYPE_P(p, subtype, basemember) \
((void*) (((char*)(p)) - STRUCT_OFFSET(subtype, basemember)))
#define DOWNCAST(to, ptr) ((to*)SUBTYPE_P(ptr, to, base_))
+#define TO_BASE_CONN(c) (&((c)->base_))
static struct rs_connection *TO_GENERIC_CONN (struct rs_conn_base *);
static struct rs_listener *TO_LISTENER_CONN (struct rs_conn_base *);
static INLINE struct rs_connection *TO_GENERIC_CONN (struct rs_conn_base *b)
diff --git a/lib/include/radsec/radsec.h b/lib/include/radsec/radsec.h
index 021f677..cb98db7 100644
--- a/lib/include/radsec/radsec.h
+++ b/lib/include/radsec/radsec.h
@@ -1,5 +1,5 @@
/** \file radsec.h
- \brief Public interface for libradsec. */
+ \brief Public interface for libradsec. */
/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
See LICENSE for licensing information. */
@@ -136,6 +136,7 @@ extern "C" {
/* Data types. */
struct rs_context; /* radsec-impl.h */
+struct rs_conn_base; /* radsec-impl.h */
struct rs_connection; /* radsec-impl.h */
struct rs_listener; /* radsec-impl.h */
struct rs_message; /* radsec-impl.h */
@@ -175,8 +176,10 @@ struct rs_conn_callbacks {
typedef void (*rs_listener_new_conn_cb) (struct rs_connection *conn,
void *user_data);
+typedef void (*rs_listener_error_cb) (void *user_data);
struct rs_listener_callbacks {
rs_listener_new_conn_cb new_conn_cb;
+ rs_listener_error_cb error_cb;
};
typedef struct value_pair rs_avp;
@@ -327,11 +330,20 @@ int rs_conn_get_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);
+/** Create a peer and add it to list of peers held by \a conn. */
+int rs_peer_create_for_conn (struct rs_connection *conn,
+ struct rs_peer **peer_out);
+/** Create a peer and add it to list of peers held by \a listener. */
+int rs_peer_create_for_listener (struct rs_listener *listener,
+ struct rs_peer **peer_out);
+/** Set RADIUS secret for \a peer. Free resurces with \a rs_peer_free_secret. */
+int rs_peer_set_secret(struct rs_peer *peer, const char *secret);
+/** Free resources allocated by \a rs_peer_set_secret. */
+void rs_peer_free_secret (struct rs_peer *peer);
int rs_peer_set_address(struct rs_peer *peer,
const char *hostname,
const char *service);
-int rs_peer_set_secret(struct rs_peer *peer, const char *secret);
+void rs_peer_free_address (struct rs_peer *peer);
void rs_peer_set_timeout(struct rs_peer *peer, int timeout);
void rs_peer_set_retries(struct rs_peer *peer, int retries);
@@ -427,6 +439,12 @@ int rs_err_conn_push_fl(struct rs_connection *conn,
int line,
const char *fmt,
...);
+int rs_err_connbase_push_fl (struct rs_conn_base *connbase,
+ int code,
+ const char *file,
+ int line,
+ const char *fmt,
+ ...);
/** Pop the first error from the error FIFO associated with connection
\a conn or NULL if there are no errors in the FIFO. */
struct rs_error *rs_err_conn_pop(struct rs_connection *conn);
diff --git a/lib/message.c b/lib/message.c
index e010f94..47590ca 100644
--- a/lib/message.c
+++ b/lib/message.c
@@ -1,5 +1,5 @@
/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -28,21 +28,21 @@ message_verify_response (struct rs_connection *conn,
int err;
assert (conn);
- assert (conn->active_peer);
- assert (conn->active_peer->secret);
+ assert (conn->base_.active_peer);
+ assert (conn->base_.active_peer->secret);
assert (response);
assert (response->rpkt);
assert (request);
assert (request->rpkt);
- response->rpkt->secret = conn->active_peer->secret;
- response->rpkt->sizeof_secret = strlen (conn->active_peer->secret);
+ response->rpkt->secret = conn->base_.active_peer->secret;
+ response->rpkt->sizeof_secret = strlen (conn->base_.active_peer->secret);
/* Verify header and message authenticator. */
err = nr_packet_verify (response->rpkt, request->rpkt);
if (err)
{
- if (conn->is_connected)
+ if (conn->state == RS_CONN_STATE_CONNECTED)
rs_conn_disconnect(conn);
return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__,
"nr_packet_verify");
@@ -52,7 +52,7 @@ message_verify_response (struct rs_connection *conn,
err = nr_packet_decode (response->rpkt, request->rpkt);
if (err)
{
- if (conn->is_connected)
+ if (conn->state == RS_CONN_STATE_CONNECTED)
rs_conn_disconnect(conn);
return rs_err_conn_push_fl (conn, -err, __FILE__, __LINE__,
"nr_packet_decode");
@@ -71,11 +71,11 @@ message_do_send (struct rs_message *msg)
assert (msg);
assert (msg->conn);
- assert (msg->conn->active_peer);
- assert (msg->conn->active_peer->secret);
+ assert (msg->conn->base_.active_peer);
+ assert (msg->conn->base_.active_peer->secret);
assert (msg->rpkt);
- msg->rpkt->secret = msg->conn->active_peer->secret;
+ msg->rpkt->secret = msg->conn->base_.active_peer->secret;
msg->rpkt->sizeof_secret = strlen (msg->rpkt->secret);
/* Encode message. */
@@ -92,8 +92,8 @@ message_do_send (struct rs_message *msg)
{
char host[80], serv[80];
- getnameinfo (msg->conn->active_peer->addr_cache->ai_addr,
- msg->conn->active_peer->addr_cache->ai_addrlen,
+ getnameinfo (msg->conn->base_.active_peer->addr_cache->ai_addr,
+ msg->conn->base_.active_peer->addr_cache->ai_addrlen,
host, sizeof(host), serv, sizeof(serv),
0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv));
diff --git a/lib/peer.c b/lib/peer.c
index 3e0069b..b6ec167 100644
--- a/lib/peer.c
+++ b/lib/peer.c
@@ -1,5 +1,5 @@
/* Copyright 2010,2011,2013 NORDUnet A/S. All rights reserved.
- See LICENSE for licensing information. */
+ See LICENSE for licensing information. */
#if defined HAVE_CONFIG_H
#include <config.h>
@@ -20,12 +20,12 @@ 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->base_.peers; /* From the top. */
+ if (conn->base_.active_peer)
+ conn->base_.active_peer = conn->base_.active_peer->next; /* Next. */
+ if (!conn->base_.active_peer)
+ conn->base_.active_peer = conn->base_.peers; /* From the top. */
- return conn->active_peer;
+ return conn->base_.active_peer;
}
struct rs_peer *
@@ -33,10 +33,9 @@ peer_create (struct rs_context *ctx, struct rs_peer **rootp)
{
struct rs_peer *p;
- p = (struct rs_peer *) rs_malloc (ctx, sizeof(*p));
+ p = (struct rs_peer *) rs_calloc (ctx, 1, sizeof(*p));
if (p)
{
- memset (p, 0, sizeof(struct rs_peer));
if (*rootp)
{
p->next = (*rootp)->next;
@@ -48,36 +47,49 @@ peer_create (struct rs_context *ctx, struct rs_peer **rootp)
return p;
}
-/* Public functions. */
int
-rs_peer_create (struct rs_connection *conn, struct rs_peer **peer_out)
+peer_create_for_connbase (struct rs_conn_base *connbase,
+ struct rs_peer **peer_out)
{
struct rs_peer *peer;
- peer = peer_create (conn->base_.ctx, &conn->base_.peers);
- if (peer)
- {
- peer->conn = conn;
- peer->realm->timeout = 2; /* FIXME: Why? */
- peer->realm->retries = 2; /* FIXME: Why? */
- }
- else
- return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
+ peer = peer_create (connbase->ctx, &connbase->peers);
+ if (peer == NULL)
+ return rs_err_connbase_push_fl (connbase, RSE_NOMEM, __FILE__, __LINE__,
+ NULL);
+ peer->connbase = connbase;
+ peer->realm = connbase->realm;
+
if (*peer_out)
*peer_out = peer;
return RSE_OK;
}
+/* Public functions. */
+int
+rs_peer_create_for_conn (struct rs_connection *conn, struct rs_peer **peer_out)
+{
+ return peer_create_for_connbase (TO_BASE_CONN (conn), peer_out);
+}
+
int
-rs_peer_set_address (struct rs_peer *peer, const char *hostname,
+rs_peer_create_for_listener (struct rs_listener *listener,
+ struct rs_peer **peer_out)
+{
+ return peer_create_for_connbase (TO_BASE_CONN (listener), peer_out);
+}
+
+int
+rs_peer_set_address (struct rs_peer *peer,
+ const char *hostname,
const char *service)
{
assert (peer);
- assert (peer->conn);
- assert (peer->conn->base_.ctx);
+ assert (peer->connbase);
+ assert (peer->connbase->ctx);
- peer->hostname = rs_strdup (peer->conn->base_.ctx, hostname);
- peer->service = rs_strdup (peer->conn->base_.ctx, service);
+ peer->hostname = rs_strdup (peer->connbase->ctx, hostname);
+ peer->service = rs_strdup (peer->connbase->ctx, service);
if (peer->hostname == NULL || peer->service == NULL)
return RSE_NOMEM;
@@ -85,12 +97,28 @@ rs_peer_set_address (struct rs_peer *peer, const char *hostname,
}
void
+rs_peer_free_address (struct rs_peer *peer)
+{
+ assert (peer);
+ assert (peer->connbase);
+ assert (peer->connbase->ctx);
+
+ if (peer->hostname)
+ rs_free (peer->connbase->ctx, peer->hostname);
+ peer->hostname = NULL;
+ if (peer->service)
+ rs_free (peer->connbase->ctx, peer->service);
+ peer->service = NULL;
+}
+
+void
rs_peer_set_timeout (struct rs_peer *peer, int timeout)
{
assert (peer);
assert (peer->realm);
peer->realm->timeout = timeout;
}
+
void
rs_peer_set_retries (struct rs_peer *peer, int retries)
{
@@ -102,12 +130,26 @@ rs_peer_set_retries (struct rs_peer *peer, int retries)
int
rs_peer_set_secret (struct rs_peer *peer, const char *secret)
{
- if (peer->secret)
- free (peer->secret);
- peer->secret = (char *) malloc (strlen(secret) + 1);
+ assert (peer);
+ assert (peer->connbase);
+ assert (peer->connbase->ctx);
+
+ rs_peer_free_secret (peer);
+ peer->secret = rs_calloc (peer->connbase->ctx, 1, strlen(secret) + 1);
if (!peer->secret)
- return rs_err_conn_push (peer->conn, RSE_NOMEM, NULL);
+ return rs_err_connbase_push (peer->connbase, RSE_NOMEM, NULL);
strcpy (peer->secret, secret);
return RSE_OK;
}
+void
+rs_peer_free_secret (struct rs_peer *peer)
+{
+ assert (peer);
+ assert (peer->connbase);
+ assert (peer->connbase->ctx);
+
+ if (peer->secret)
+ rs_free (peer->connbase->ctx, peer->secret);
+ peer->secret = NULL;
+}
diff --git a/lib/peer.h b/lib/peer.h
index 4e976c5..95be0b0 100644
--- a/lib/peer.h
+++ b/lib/peer.h
@@ -3,3 +3,4 @@
struct rs_peer *peer_create (struct rs_context *ctx, struct rs_peer **rootp);
struct rs_peer *peer_pick_peer (struct rs_connection *conn);
+int peer_create_for_connbase (struct rs_conn_base *, struct rs_peer **);
diff --git a/lib/radsec.sym b/lib/radsec.sym
index 7e64560..6ad1361 100644
--- a/lib/radsec.sym
+++ b/lib/radsec.sym
@@ -77,7 +77,10 @@ rs_message_create
rs_message_create_authn_request
rs_message_destroy
rs_message_send
-rs_peer_create
+rs_peer_create_for_conn
+rs_peer_create_for_listener
+rs_peer_free_address
+rs_peer_free_secret
rs_peer_set_address
rs_peer_set_retries
rs_peer_set_secret
diff --git a/lib/send.c b/lib/send.c
index 6b76958..fab89a9 100644
--- a/lib/send.c
+++ b/lib/send.c
@@ -24,12 +24,12 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg)
if (event_init_eventbase (conn))
return -1;
- if (!conn->active_peer)
+ if (!conn->base_.active_peer)
peer_pick_peer (conn);
- if (!conn->active_peer)
+ if (!conn->base_.active_peer)
return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
- if (event_init_socket (conn, conn->active_peer))
+ if (event_init_socket (conn, conn->base_.active_peer))
return -1;
if (conn->base_.realm->type == RS_CONN_TYPE_TCP
@@ -37,7 +37,7 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg)
{
if (tcp_init_connect_timer (conn))
return -1;
- if (event_init_bufferevent (conn, conn->active_peer))
+ if (event_init_bufferevent (conn, conn->base_.active_peer))
return -1;
}
else
@@ -48,9 +48,9 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg)
return -1;
}
- if (!conn->is_connected)
- if (!conn->is_connecting)
- event_do_connect (conn);
+ if (conn->state != RS_CONN_STATE_CONNECTED
+ && conn->state != RS_CONN_STATE_CONNECTING)
+ event_do_connect (conn);
return RSE_OK;
}
@@ -58,7 +58,8 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg)
static int
_conn_is_open_p (struct rs_connection *conn)
{
- return conn->active_peer && conn->is_connected;
+ return conn->state == RS_CONN_STATE_CONNECTED
+ && conn->base_.active_peer != NULL;
}
/* User callback used when we're dispatching for user. */
@@ -92,7 +93,7 @@ rs_message_send (struct rs_message *msg, void *user_data)
assert (conn->base_.ctx);
assert (conn->base_.ctx->evb);
- assert (conn->active_peer);
+ assert (conn->base_.active_peer);
assert (conn->base_.fd >= 0);
conn->base_.user_data = user_data;
diff --git a/lib/tcp.c b/lib/tcp.c
index b8d7906..f5673f5 100644
--- a/lib/tcp.c
+++ b/lib/tcp.c
@@ -109,7 +109,8 @@ _read_message (struct rs_message *msg)
/* Find out what happens if there's data left in the buffer. */
{
size_t rest = 0;
- rest = evbuffer_get_length (bufferevent_get_input (msg->conn->bev));
+ rest =
+ evbuffer_get_length (bufferevent_get_input (msg->conn->base_.bev));
if (rest)
rs_debug (("%s: returning with %d octets left in buffer\n", __func__,
rest));
@@ -178,11 +179,10 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
assert (msg->conn);
conn = msg->conn;
#if defined (DEBUG)
- assert (msg->conn->active_peer);
- p = conn->active_peer;
+ assert (msg->conn->base_.active_peer);
+ p = conn->base_.active_peer;
#endif
- conn->is_connecting = 0;
if (events & BEV_EVENT_CONNECTED)
{
if (conn->tev)
@@ -213,7 +213,7 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
}
else
{
- rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr,
+ rs_debug (("%s: %d: %d (%s)\n", __func__, conn->base_.fd, sockerr,
evutil_socket_error_to_string (sockerr)));
rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
"%d: %d (%s)", conn->base_.fd, sockerr,
diff --git a/lib/tls.c b/lib/tls.c
index 979ee3c..b9fb3cf 100644
--- a/lib/tls.c
+++ b/lib/tls.c
@@ -26,14 +26,14 @@ _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
{
memset (c, 0, sizeof (struct tls));
/* _conn_open() should've picked a peer by now. */
- assert (conn->active_peer);
+ assert (conn->base_.active_peer);
/* TODO: Make sure old radsecproxy code doesn't free these all
of a sudden, or strdup them. */
c->name = realm->name;
- c->cacertfile = conn->active_peer->cacertfile;
+ c->cacertfile = conn->base_.active_peer->cacertfile;
c->cacertpath = NULL; /* NYI */
- c->certfile = conn->active_peer->certfile;
- c->certkeyfile = conn->active_peer->certkeyfile;
+ c->certfile = conn->base_.active_peer->certfile;
+ c->certkeyfile = conn->base_.active_peer->certkeyfile;
c->certkeypwd = NULL; /* NYI */
c->cacheexpiry = 0; /* NYI */
c->crlcheck = 0; /* NYI */
@@ -60,7 +60,7 @@ psk_client_cb (SSL *ssl,
conn = SSL_get_ex_data (ssl, 0);
assert (conn != NULL);
- cred = conn->active_peer->transport_cred;
+ cred = conn->base_.active_peer->transport_cred;
assert (cred != NULL);
/* NOTE: Ignoring identity hint from server. */
@@ -126,7 +126,7 @@ rs_tls_init (struct rs_connection *conn)
assert (conn->base_.ctx);
ctx = conn->base_.ctx;
- tlsconf = _get_tlsconf (conn, conn->active_peer->realm);
+ tlsconf = _get_tlsconf (conn, conn->base_.active_peer->realm);
if (!tlsconf)
return -1;
ssl_ctx = tlsgetctx (RAD_TLS, tlsconf);
@@ -147,7 +147,7 @@ rs_tls_init (struct rs_connection *conn)
}
#if defined RS_ENABLE_TLS_PSK
- if (conn->active_peer->transport_cred != NULL)
+ if (conn->base_.active_peer->transport_cred != NULL)
{
SSL_set_psk_client_callback (ssl, psk_client_cb);
SSL_set_ex_data (ssl, 0, conn);
@@ -203,9 +203,9 @@ tls_verify_cert (struct rs_connection *conn)
struct in6_addr addr;
const char *hostname = NULL;
- assert (conn->active_peer->conn == conn);
- assert (conn->active_peer->hostname != NULL);
- hostname = conn->active_peer->hostname;
+ assert (conn->base_.active_peer != NULL);
+ assert (conn->base_.active_peer->hostname != NULL);
+ hostname = conn->base_.active_peer->hostname;
/* verifytlscert() performs basic verification as described by
OpenSSL VERIFY(1), i.e. verification of the certificate chain. */
diff --git a/lib/udp.c b/lib/udp.c
index 71d7003..22a8375 100644
--- a/lib/udp.c
+++ b/lib/udp.c
@@ -129,7 +129,7 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
assert (msg);
assert (msg->conn);
- if (!msg->conn->is_connected)
+ if (msg->conn->state == RS_CONN_STATE_CONNECTING)
event_on_connect (msg->conn, msg);
if (msg->conn->out_queue)
diff --git a/lib/util.c b/lib/util.c
index eceaec9..671fcde 100644
--- a/lib/util.c
+++ b/lib/util.c
@@ -11,9 +11,10 @@ rs_strdup (struct rs_context *ctx, const char *s)
{
char *buf = rs_calloc (ctx, 1, strlen (s) + 1);
- if (buf != NULL)
- return strcpy (buf, s);
+ if (buf)
+ strcpy (buf, s);
+ else
+ rs_err_ctx_push (ctx, RSE_NOMEM, NULL);
- rs_err_ctx_push (ctx, RSE_NOMEM, NULL);
- return NULL;
+ return buf;
}