WIP commit moving towards working server support.
authorLinus Nordberg <linus@nordberg.se>
Wed, 15 May 2013 12:20:52 +0000 (14:20 +0200)
committerLinus Nordberg <linus@nordberg.se>
Wed, 15 May 2013 12:20:52 +0000 (14:20 +0200)
29 files changed:
lib/HACKING
lib/Makefile.am
lib/README
lib/conf.c
lib/confutil.c
lib/conn.c
lib/conn.h
lib/err.c
lib/event.c
lib/event.h
lib/examples/Makefile.am
lib/examples/client-blocking.c
lib/examples/client-dispatch.c
lib/examples/client.conf
lib/examples/server.c
lib/include/radsec/radsec-impl.h
lib/include/radsec/radsec.h
lib/listener.c [new file with mode: 0644]
lib/listener.h [new file with mode: 0644]
lib/message.c
lib/peer.c
lib/peer.h
lib/radsec.sym
lib/request.c
lib/send.c
lib/tcp.c
lib/tests/Makefile.am
lib/tls.c
lib/udp.c

index d312ce3..62da414 100644 (file)
@@ -1,6 +1,6 @@
 HACKING file for libradsec (in Emacs -*- org -*- mode).
 
-Status as of libradsec-0.0.2.dev (2013-01-25).
+Status as of libradsec-0.2.0.dev (2013-05-02).
 
 * Build instructions
 sh autogen.sh
@@ -9,43 +9,6 @@ make
 
 examples/client -r examples/client.conf blocking-tls; echo $?
 
-* Design of the API
-- There are three usage modes:
-
-  - Application uses blocking send and receive calls (blocking
-    mode). This is typically fine for a simple client.
-
-  - Application registers callbacks with libradsec and runs the
-    libevent dispatch loop (a.k.a. user dispatch mode). This would
-    probably be how one would implement a server or a proxy.
-
-  - Application runs its own event loop, using fd's for select and
-    performs I/O using libradsec send/receive functions
-    (a.k.a. on-your-own mode). Might be useful for an application
-    which already has an event loop that wants to add RadSec
-    functionality.
-
-- Apart from configuration and error handling, an application
-  shouldn't need to handle TCP and UDP connections
-  differently. Similarly, the use of TLS/DTLS or not shouldn't
-  influence the libradsec calls made by the application.
-
-- Configuration is done either by using the API or by pointing at a
-  configuration file which is parsed by libradsec.
-
-- Fully reentrant.
-
-- Application chooses allocation regime.
-
-Note that as of 0.0.2.dev libradsec suffers from way too much focus on
-the behaviour of a blocking client and is totally useless as a server.
-Not only does it lack most of the functions needed for writing a
-server but it also contains at least one architectural mishap which
-kills the server idea -- a connection timeout (TCP) or a retransmit
-timeout (UDP) will result in the event loop being broken. The same
-thing will happen if there's an error on a TCP connection, f.ex. a
-failing certificate validation (TLS).
-
 * Dependencies
 Details (within parentheses) apply to Debian Wheezy.
 
@@ -66,6 +29,8 @@ Details (within parentheses) apply to Debian Wheezy.
 - [TLS] verification of CN
 
 ** Known issues
+- error handling when server can't bind to given listen_addr+port
+- configuration of listen_addr/service is per realm
 - error stack is only one entry deep
 - custom allocation scheme is not used in all places
 
@@ -89,3 +54,74 @@ a crash, catching the crash in gdb and providing a backtrace is highly
 valuable for debugging.
 
 Contact: mailto:linus+libradsec@nordu.net
+
+
+* Design of the API
+- There are three usage modes:
+
+  - Application uses blocking send and receive calls (blocking
+    mode). This is typically fine for a simple client.
+
+  - Application registers callbacks with libradsec and runs the
+    libevent dispatch loop (a.k.a. user dispatch mode). This would
+    probably be how one would implement a server or a proxy.
+
+  - Application runs its own event loop, using fd's for select and
+    performs I/O using libradsec send/receive functions
+    (a.k.a. on-your-own mode). Might be useful for an application
+    which already has an event loop that wants to add RadSec
+    functionality.
+
+- Apart from configuration and error handling, an application
+  shouldn't need to handle TCP and UDP connections
+  differently. Similarly, the use of TLS/DTLS or not shouldn't
+  influence the libradsec calls made by the application.
+
+- Configuration is done either by using the API or by pointing at a
+  configuration file which is parsed by libradsec.
+
+- Fully reentrant.
+
+- Application chooses allocation regime.
+
+Note that as of 0.0.2.dev libradsec suffers from way too much focus on
+the behaviour of a blocking client and is totally useless as a server.
+Not only does it lack most of the functions needed for writing a
+server but it also contains at least one architectural mishap which
+kills the server idea -- a connection timeout (TCP) or a retransmit
+timeout (UDP) will result in the event loop being broken. The same
+thing will happen if there's an error on a TCP connection, f.ex. a
+failing certificate validation (TLS).
+
+* Notes on internals
+** How to connect an outgoing connection?
+Connecting is not done explicitly by the application but implicitly by
+rs_message_send(). The application can treat connections in the same
+way regardless of whether they're connection-oriented (i.e. TCP) or
+not (UDP).
+
+rs_message_send(msg)
+  if msg->conn is open: mesasge_do_send(msg)
+  else: 
+    -> _conn_open(conn, msg)
+      pick configured peer
+      event_init_socket(peer)
+      if TCP or TLS:
+        init tcp timers
+        event_init_bufferevent(conn, peer)
+      else:
+        init udp timers
+      if not connected and not connecting:
+        event_do_connect(conn)    
+
+  if TCP:
+    bufferevent_setcb()
+    bufferevent_enable()
+  else:
+    event_assign(write_ev)         ; libevent func?
+    event_add(write_ev)
+  
+  if not in user-dispatch-mode:
+    event_base_dispatch()
+** How to bind a listener and start listening for incoming connections?
+
index 6d62c97..6a96d32 100644 (file)
@@ -53,10 +53,10 @@ else
 endif
 
 EXTRA_DIST = HACKING LICENSE
+EXTRA_libradsec_la_DEPENDENCIES = radsec.sym
 
 libradsec_la_CFLAGS = \
-       $(AM_CFLAGS) -DHAVE_CONFIG_H \
-       -DDEBUG -DDEBUG_LEVENT
+       $(AM_CFLAGS) -DHAVE_CONFIG_H -DDEBUG -DDEBUG_LEVENT
 libradsec_la_LDFLAGS = \
        -version-info $(VER_CUR):$(VER_REV):$(VER_AGE) \
        -export-symbols radsec.sym
index 8b8c25d..6401333 100644 (file)
@@ -19,7 +19,7 @@ http://git.nordu.net/?p=radsecproxy.git;a=shortlog;h=refs/heads/libradsec
 The source code is licensed under a 3-clause BSD license. See LICENSE.
 
 
-Libradsec depends on 
+Libradsec depends on
 - libconfuse
 - libevent2
 - openssl (if configured with --enable-tls)
index f75e9ed..d5a9d0d 100644 (file)
@@ -1,5 +1,5 @@
 /* Copyright 2010, 2011, 2013 NORDUnet A/S. All rights reserved.
-   See LICENSE for licensing information.  */
+   See LICENSE for licensing information. */
 
 #if defined HAVE_CONFIG_H
 #include <config.h>
@@ -23,7 +23,7 @@
       retries = INT
   }
 
-  # realm configuration inherited by clients and servers
+  # realm configuration inherited (and overwritable) by clients and servers
   realm STRING {
       cacertfile = STRING
       #cacertpath = STRING
@@ -46,6 +46,8 @@
 
   # server configuration
   realm STRING {
+      listen_addr = STRING
+      listen_service = STRING
       client {
           hostname = STRING
          service = STRING      # name or port number
@@ -82,6 +84,8 @@ confload_peers (struct rs_context *ctx,
   const char *peer_type_str[] = {"<no type>", "client", "server"};
   cfg_t *cfg_peer = NULL;
   int j;
+  char *def_listen_addr = cfg_getstr (cfg_realm, "listen_addr");
+  char *def_listen_service = cfg_getstr (cfg_realm, "listen_service");
   char *def_cacertfile = cfg_getstr (cfg_realm, "cacertfile");
   /*char *def_cacertpath = cfg_getstr (cfg_realm, "cacertpath");*/
   char *def_certfile = cfg_getstr (cfg_realm, "certfile");
@@ -105,6 +109,24 @@ confload_peers (struct rs_context *ctx,
       p->service = cfg_getstr (cfg_peer, "service");
       p->secret = cfg_getstr (cfg_peer, "secret");
 
+      if (type == RS_PEER_TYPE_CLIENT)
+        {
+          struct rs_peer *lp = peer_create (ctx, &r->local_addr); /* FIXME: this should be saved per peer, not realm */
+          if (lp == NULL)
+            return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__,
+                                       NULL);
+          lp->realm = r;
+          fprintf (stderr, " *** %s %s ***\n", def_listen_addr, def_listen_service);
+#if 0
+          CONFGET_STR (lp->hostname, cfg_peer, "listen_addr", def_listen_addr);
+          CONFGET_STR (lp->service, cfg_peer, "listen_service",
+                       def_listen_service);
+#else
+          lp->hostname = "127.0.0.1";
+          lp->service = "4711";
+#endif
+        }
+
       CONFGET_STR (p->cacertfile, cfg_peer, "cacertfile", def_cacertfile);
       CONFGET_STR (p->certfile, cfg_peer, "certfile", def_certfile);
       CONFGET_STR (p->certkeyfile, cfg_peer, "certkeyfile", def_certkeyfile);
@@ -161,7 +183,7 @@ confload_peers (struct rs_context *ctx,
 #endif  /* RS_ENABLE_TLS_PSK */
         }
 
-
+#if defined (RS_ENABLE_TLS)
       /* For a TLS or DTLS client or server, validate that we have either of CA
          cert file/path or PSK.  */
       if ((r->type == RS_CONN_TYPE_TLS || r->type == RS_CONN_TYPE_DTLS)
@@ -170,6 +192,7 @@ confload_peers (struct rs_context *ctx,
         return rs_err_ctx_push (ctx, RSE_CONFIG,
                                 "%s: missing both CA file/path and PSK",
                                 r->name);
+#endif
     }
 
   return RSE_OK;
@@ -189,6 +212,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
     {
       CFG_STR ("hostname", NULL, CFGF_NONE),
       CFG_STR ("service", "2083", CFGF_NONE),
+      CFG_STR ("listen_addr", "127.0.0.1", CFGF_NONE), /* Clients only. */
+      CFG_STR ("listen_service", "0", CFGF_NONE), /* Clients only. */
       CFG_STR ("secret", "radsec", CFGF_NONE),
       CFG_STR ("cacertfile", NULL, CFGF_NONE),
       /*CFG_STR ("cacertpath", NULL, CFGF_NONE),*/
@@ -204,6 +229,8 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
   cfg_opt_t realm_opts[] =
     {
       CFG_STR ("type", "UDP", CFGF_NONE),
+      CFG_STR ("listen_addr", "127.0.0.1", CFGF_NONE),
+      CFG_STR ("listen_service", "0", CFGF_NONE),
       CFG_INT ("timeout", 2, CFGF_NONE), /* FIXME: Remove?  */
       CFG_INT ("retries", 2, CFGF_NONE), /* FIXME: Remove?  */
       CFG_STR ("cacertfile", NULL, CFGF_NONE),
@@ -283,10 +310,12 @@ rs_context_read_config(struct rs_context *ctx, const char *config_file)
        r->type = RS_CONN_TYPE_UDP;
       else if (strcmp (typestr, "TCP") == 0)
        r->type = RS_CONN_TYPE_TCP;
+#if defined (RS_ENABLE_TLS)
       else if (strcmp (typestr, "TLS") == 0)
        r->type = RS_CONN_TYPE_TLS;
       else if (strcmp (typestr, "DTLS") == 0)
        r->type = RS_CONN_TYPE_DTLS;
+#endif
       else
        return rs_err_ctx_push (ctx, RSE_CONFIG,
                                 "%s: invalid connection type: %s",
index 3a1d639..7bd4a37 100644 (file)
@@ -94,10 +94,17 @@ rs_context_print_config (struct rs_context *ctx, char **buf_out)
   for (r = cfg->realms; r != NULL; r = r->next)
     {
       if (pp (&out, &len, "realm %s {\n", r->name)
-          || pp (&out, &len, "\ttype = \"%s\"\n\ttimeout = %d\n\tretries = %d\n",
+          || pp (&out, &len,
+                 "\ttype = \"%s\"\n"
+                 "\ttimeout = %d\n"
+                 "\tretries = %d\n"
+                 "\tlisten_addr = \"%s\"\n"
+                 "\tlisten_service = \"%s\"\n",
                  realm_type[r->type],
                  r->timeout,
-                 r->retries))
+                 r->retries,
+                 r->local_addr->hostname,
+                 r->local_addr->service))
         return -RSE_INTERNAL;
       for (p = r->peers; p != NULL; p = p->next)
         {
index 95e65a4..4c9158c 100644 (file)
@@ -13,6 +13,7 @@
 #include <event2/bufferevent.h>
 #include <radsec/radsec.h>
 #include <radsec/radsec-impl.h>
+#include "err.h"
 #include "debug.h"
 #include "conn.h"
 #include "event.h"
 #include "tcp.h"
 
 int
-conn_close (struct rs_connection **connp)
-{
-  int r = 0;
-  assert (connp);
-  assert (*connp);
-  if ((*connp)->state == RS_CONN_STATE_CONNECTED)
-    r = rs_conn_disconnect (*connp);
-  if (r == RSE_OK)
-    *connp = NULL;
-  return r;
-}
-
-int
 conn_user_dispatch_p (const struct rs_connection *conn)
 {
   assert (conn);
@@ -43,7 +31,6 @@ conn_user_dispatch_p (const struct rs_connection *conn)
          conn->callbacks.sent_cb);
 }
 
-
 int
 conn_activate_timeout (struct rs_connection *conn)
 {
@@ -62,23 +49,37 @@ conn_activate_timeout (struct rs_connection *conn)
 }
 
 int
-conn_type_tls (const struct rs_connection *conn)
+conn_type_tls_p (const struct rs_connection *conn)
+{
+  return TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_TLS
+    || TO_BASE_CONN(conn)->transport == RS_CONN_TYPE_DTLS;
+}
+
+int
+baseconn_type_datagram_p (const struct rs_conn_base *connbase)
 {
-  assert (conn->base_.active_peer);
-  return conn->base_.realm->type == RS_CONN_TYPE_TLS
-    || conn->base_.realm->type == RS_CONN_TYPE_DTLS;
+  return connbase->transport == RS_CONN_TYPE_UDP
+    || connbase->transport == RS_CONN_TYPE_DTLS;
+}
+
+int
+baseconn_type_stream_p (const struct rs_conn_base *connbase)
+{
+  return connbase->transport == RS_CONN_TYPE_TCP
+    || connbase->transport == RS_CONN_TYPE_TLS;
 }
 
 int
 conn_cred_psk (const struct rs_connection *conn)
 {
-  assert (conn->base_.active_peer);
-  return conn->base_.active_peer->transport_cred &&
-    conn->base_.active_peer->transport_cred->type == RS_CRED_TLS_PSK;
+  assert (conn);
+  assert (conn->active_peer);
+  return conn->active_peer->transport_cred &&
+    conn->active_peer->transport_cred->type == RS_CRED_TLS_PSK;
 }
 
 void
-conn_init (struct rs_context *ctx,
+conn_init (struct rs_context *ctx, /* FIXME: rename connbase_init? */
            struct rs_conn_base *connbase,
            enum rs_conn_subtype type)
 {
@@ -102,7 +103,7 @@ conn_init (struct rs_context *ctx,
 }
 
 int
-conn_configure (struct rs_context *ctx,
+conn_configure (struct rs_context *ctx, /* FIXME: rename conbbase_configure? */
                 struct rs_conn_base *connbase,
                 const char *config)
 {
@@ -121,15 +122,84 @@ conn_configure (struct rs_context *ctx,
          connbase->tryagain = r->retries;
        }
     }
+#if 0  /* incoming connections don't have a realm (a config object), update: they do, but "somebody else" is setting this up <-- FIXME */
   if (connbase->realm == NULL)
     {
-      connbase->realm = rs_calloc (ctx, 1, sizeof (struct rs_realm));
-      if (connbase->realm == NULL)
+      struct rs_realm *r = rs_calloc (ctx, 1, sizeof (struct rs_realm));
+      if (r == NULL)
         return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
+      r->next = ctx->realms;
+      ctx->realms = connbase->realm = r;
+    }
+#else
+  if (connbase->realm)
+    connbase->transport = connbase->realm->type;
+#endif
+
+  return RSE_OK;
+}
+
+int
+conn_add_read_event (struct rs_connection *conn, void *user_data)
+{
+  struct rs_conn_base *connbase = TO_BASE_CONN(conn);
+  int err;
+
+  assert(connbase);
+
+  if (connbase->bev)           /* TCP (including TLS).  */
+    {
+      bufferevent_setwatermark (connbase->bev, EV_READ, RS_HEADER_LEN, 0);
+      bufferevent_setcb (connbase->bev, tcp_read_cb, NULL, tcp_event_cb,
+                         user_data);
+      bufferevent_enable (connbase->bev, EV_READ);
+    }
+  else                         /* UDP.  */
+    {
+      /* Put fresh message in user_data for the callback and enable the
+        read event.  */
+      event_assign (connbase->rev, connbase->ctx->evb,
+                    event_get_fd (connbase->rev), EV_READ,
+                    event_get_callback (connbase->rev),
+                    user_data);
+      err = event_add (connbase->rev, NULL);
+      if (err < 0)
+       return rs_err_connbase_push_fl (connbase, RSE_EVENT, __FILE__, __LINE__,
+                                        "event_add: %s",
+                                        evutil_gai_strerror (err));
+
+      /* Activate retransmission timer.  */
+      conn_activate_timeout (conn);
     }
+
   return RSE_OK;
 }
 
+/** Return !=0 if \a conn is an originating connection, i.e. if its
+    peer is a server. */
+int
+conn_originating_p (const struct rs_connection *conn)
+{
+  return conn->active_peer->type == RS_PEER_TYPE_SERVER;
+}
+
+int
+baseconn_close (struct rs_conn_base *connbase)
+{
+  int err = 0;
+  assert (connbase);
+
+  rs_debug (("%s: closing fd %d\n", __func__, connbase->fd));
+
+  err = evutil_closesocket (connbase->fd);
+  if (err)
+    err = rs_err_connbase_push (connbase, RSE_EVENT,
+                                "evutil_closesocket: %d (%s)",
+                                errno, strerror (errno));
+  connbase->fd = -1;
+  return err;
+}
+
 /* Public functions. */
 int
 rs_conn_create (struct rs_context *ctx,
@@ -179,12 +249,8 @@ rs_conn_add_listener (struct rs_connection *conn,
 int
 rs_conn_disconnect (struct rs_connection *conn)
 {
-  int err = 0;
-
-  assert (conn);
-
-  err = evutil_closesocket (conn->base_.fd);
-  conn->base_.fd = -1;
+  int err = baseconn_close (TO_BASE_CONN (conn));
+  conn->state = RS_CONN_STATE_UNDEFINED;
   return err;
 }
 
@@ -229,9 +295,12 @@ rs_conn_set_eventbase (struct rs_connection *conn, struct event_base *eb)
 }
 
 void
-rs_conn_set_callbacks (struct rs_connection *conn, struct rs_conn_callbacks *cb)
+rs_conn_set_callbacks (struct rs_connection *conn,
+                       struct rs_conn_callbacks *cb,
+                       void *user_data)
 {
   assert (conn);
+  TO_BASE_CONN(conn)->user_data = user_data;
   memcpy (&conn->callbacks, cb, sizeof (conn->callbacks));
 }
 
@@ -243,7 +312,7 @@ rs_conn_del_callbacks (struct rs_connection *conn)
 }
 
 struct rs_conn_callbacks *
-rs_conn_get_callbacks(struct rs_connection *conn)
+rs_conn_get_callbacks (struct rs_connection *conn)
 {
   assert (conn);
   return &conn->callbacks;
@@ -282,7 +351,6 @@ struct event_base
 int rs_conn_get_fd (struct rs_connection *conn)
 {
   assert (conn);
-  assert (conn->base_.active_peer);
   return conn->base_.fd;
 }
 
@@ -294,9 +362,9 @@ _rcb (struct rs_message *message, void *user_data)
   assert (msg->conn);
 
   msg->flags |= RS_MESSAGE_RECEIVED;
-  if (msg->conn->base_.bev)
+  if (msg->conn->base_.bev)     /* TCP -- disable bufferevent. */
     bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ);
-  else
+  else                          /* UDP -- remove read event. */
     event_del (msg->conn->base_.rev);
 }
 
@@ -322,46 +390,29 @@ rs_conn_receive_message (struct rs_connection *conn,
   conn->base_.user_data = msg;
   msg->flags &= ~RS_MESSAGE_RECEIVED;
 
-  if (conn->base_.bev)         /* TCP.  */
-    {
-      bufferevent_setwatermark (conn->base_.bev, EV_READ, RS_HEADER_LEN, 0);
-      bufferevent_setcb (conn->base_.bev, tcp_read_cb, NULL, tcp_event_cb, msg);
-      bufferevent_enable (conn->base_.bev, EV_READ);
-    }
-  else                         /* UDP.  */
-    {
-      /* Put fresh message in user_data for the callback and enable the
-        read event.  */
-      event_assign (conn->base_.rev, conn->base_.ctx->evb,
-                    event_get_fd (conn->base_.rev), EV_READ,
-                    event_get_callback (conn->base_.rev), msg);
-      err = event_add (conn->base_.rev, NULL);
-      if (err < 0)
-       return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__,
-                                   "event_add: %s",
-                                   evutil_gai_strerror (err));
-
-      /* Activate retransmission timer.  */
-      conn_activate_timeout (msg->conn);
-    }
+  err = conn_add_read_event (conn, msg);
+  if (err)
+    return err;
 
   rs_debug (("%s: entering event loop\n", __func__));
   err = event_base_dispatch (conn->base_.ctx->evb);
   conn->callbacks.received_cb = NULL;
   if (err < 0)
-    return rs_err_conn_push_fl (msg->conn, RSE_EVENT, __FILE__, __LINE__,
+    return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
                                "event_base_dispatch: %s",
                                evutil_gai_strerror (err));
   rs_debug (("%s: event loop done\n", __func__));
 
-  if ((msg->flags & RS_MESSAGE_RECEIVED) == 0
+  if ((msg->flags & RS_MESSAGE_RECEIVED) == 0 /* No message. */
       || (req_msg
-         && message_verify_response (msg->conn, msg, req_msg) != RSE_OK))
+         && message_verify_response (conn, msg, req_msg) != RSE_OK))
     {
-      if (rs_err_conn_peek_code (msg->conn) == RSE_OK)
-        /* No message and no error on the stack _should_ mean that the
-           server hung up on us.  */
-        rs_err_conn_push (msg->conn, RSE_DISCO, "no response");
+      if (rs_err_conn_peek_code (conn) == RSE_OK)
+        {
+          /* No message and no error on the stack _should_ mean that the
+             server hung up on us. */
+          rs_err_conn_push (conn, RSE_DISCO, "no response");
+        }
       return rs_err_conn_peek_code (conn);
     }
 
@@ -377,3 +428,10 @@ rs_conn_set_timeout(struct rs_connection *conn, struct timeval *tv)
   assert (tv);
   conn->base_.timeout = *tv;
 }
+
+struct rs_peer *
+connbase_get_peers (const struct rs_conn_base *connbase)
+{
+  assert (connbase);
+  return connbase->peers;
+}
index c1543d9..3e34453 100644 (file)
@@ -1,10 +1,8 @@
 /* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
    See LICENSE for licensing information.  */
 
-int conn_close (struct rs_connection **connp);
 int conn_user_dispatch_p (const struct rs_connection *conn);
 int conn_activate_timeout (struct rs_connection *conn);
-int conn_type_tls (const struct rs_connection *conn);
 int conn_cred_psk (const struct rs_connection *conn);
 int conn_configure (struct rs_context *ctx,
                     struct rs_conn_base *connbase,
@@ -12,3 +10,11 @@ int conn_configure (struct rs_context *ctx,
 void conn_init (struct rs_context *ctx,
                 struct rs_conn_base *connbase,
                 enum rs_conn_subtype type);
+int conn_type_tls_p (const struct rs_connection *conn);
+int baseconn_type_datagram_p (const struct rs_conn_base *connbase);
+int baseconn_type_stream_p (const struct rs_conn_base *connbase);
+struct rs_peer *connbase_get_peers (const struct rs_conn_base *connbase);
+int conn_add_read_event (struct rs_connection *conn, void *user_data);
+int conn_originating_p (const struct rs_connection *conn);
+int baseconn_close (struct rs_conn_base *connbase);
+struct bufferevent *baseconn_get_bev (struct rs_conn_base *connbase);
index 413ab3e..a0fe8a0 100644 (file)
--- a/lib/err.c
+++ b/lib/err.c
@@ -121,6 +121,7 @@ _ctx_err_vpush_fl (struct rs_context *ctx, int code, const char *file,
     return RSE_NOMEM;
 
   /* TODO: Implement a stack.  */
+  assert (ctx);
   if (ctx->err)
     rs_err_free (ctx->err);
   ctx->err = err;
@@ -158,11 +159,12 @@ rs_err_ctx_push_fl (struct rs_context *ctx, int code, const char *file,
 int
 err_connbase_push_err (struct rs_conn_base *connbase, struct rs_error *err)
 {
-
+  assert (connbase);
   if (connbase->err)
     rs_err_free (connbase->err);
   connbase->err = err;         /* FIXME: use a stack */
 
+  assert (err);
   return err->code;
 }
 
@@ -185,6 +187,7 @@ rs_err_connbase_push (struct rs_conn_base *connbase, int code,
 {
   int r = 0;
 
+  assert (connbase);
   va_list args;
   va_start (args, fmt);
   r = _connbase_err_vpush_fl (connbase, code, NULL, 0, fmt, args);
@@ -198,6 +201,7 @@ rs_err_conn_push (struct rs_connection *conn, int code, const char *fmt, ...)
 {
   int r = 0;
 
+  assert (conn);
   va_list args;
   va_start (args, fmt);
   r = _connbase_err_vpush_fl (TO_BASE_CONN (conn), code, NULL, 0, fmt, args);
@@ -249,18 +253,30 @@ rs_err_ctx_pop (struct rs_context *ctx)
 }
 
 struct rs_error *
-rs_err_conn_pop (struct rs_connection *conn)
+rs_err_connbase_pop (struct rs_conn_base *connbase)
 {
   struct rs_error *err;
 
-  if (!conn)
+  if (!connbase)
     return NULL;               /* FIXME: RSE_INVALID_CONN */
-  err = conn->base_.err;
-  conn->base_.err = NULL;
+  err = connbase->err;
+  connbase->err = NULL;
 
   return err;
 }
 
+struct rs_error *
+rs_err_conn_pop (struct rs_connection *conn)
+{
+  return rs_err_connbase_pop (TO_BASE_CONN (conn));
+}
+
+struct rs_error *
+rs_err_listener_pop (struct rs_listener *l)
+{
+  return rs_err_connbase_pop (TO_BASE_CONN (l));
+}
+
 int
 rs_err_conn_peek_code (struct rs_connection *conn)
 {
index c651e4b..fadcc14 100644 (file)
@@ -10,6 +10,7 @@
 #include <errno.h>
 
 #include <event2/event.h>
+#include <event2/listener.h>
 #include <event2/bufferevent.h>
 #if defined (RS_ENABLE_TLS)
 #include <event2/bufferevent_ssl.h>
@@ -27,6 +28,7 @@
 #include "event.h"
 #include "message.h"
 #include "conn.h"
+#include "listener.h"
 #include "debug.h"
 
 #if defined (DEBUG)
@@ -72,7 +74,7 @@ event_conn_timeout_cb (int fd, short event, void *data)
   if (event & EV_TIMEOUT)
     {
       rs_debug (("%s: connection timeout on %p (fd %d) connecting to %p\n",
-                __func__, conn, conn->base_.fd, conn->base_.active_peer));
+                __func__, conn, conn->base_.fd, conn->active_peer));
       conn->state = RS_CONN_STATE_UNDEFINED;
       rs_err_conn_push_fl (conn, RSE_TIMEOUT_CONN, __FILE__, __LINE__, NULL);
       event_loopbreak (conn);
@@ -90,70 +92,118 @@ event_retransmit_timeout_cb (int fd, short event, void *data)
   if (event & EV_TIMEOUT)
     {
       rs_debug (("%s: retransmission timeout on %p (fd %d) sending to %p\n",
-                __func__, conn, conn->base_.fd, conn->base_.active_peer));
+                __func__, conn, conn->base_.fd, conn->active_peer));
       rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
       event_loopbreak (conn);
     }
 }
 
+/* FIXME: event_ is actually not such a great prefix given that we
+   link with libevent which exports 113 symbols prefixed 'event_'. */
 int
-event_init_socket (struct rs_connection *conn, struct rs_peer *p)
+event_init_socket (struct rs_conn_base *connbase, struct rs_peer *p)
 {
-  if (conn->base_.fd != -1)
+  if (connbase->fd != -1)
     return RSE_OK;
 
+  assert (p);
+  assert (p->realm);
+
+  /* Resolve potential DNS name for peer. */
   if (p->addr_cache == NULL)
     {
       struct rs_error *err =
         rs_resolve (&p->addr_cache, p->realm->type, p->hostname, p->service);
       if (err != NULL)
-        return err_connbase_push_err (TO_BASE_CONN (conn), err);
+        return err_connbase_push_err (connbase, err);
     }
 
-  conn->base_.fd = socket (p->addr_cache->ai_family, p->addr_cache->ai_socktype,
-                    p->addr_cache->ai_protocol);
-  if (conn->base_.fd < 0)
-    return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
-                               "socket: %d (%s)",
-                               errno, strerror (errno));
-  if (evutil_make_socket_nonblocking (conn->base_.fd) < 0)
+  /* Create the socket and make it non-blocking. */
+  connbase->fd = socket (p->addr_cache->ai_family,
+                         p->addr_cache->ai_socktype,
+                         p->addr_cache->ai_protocol);
+  if (connbase->fd < 0)
+    return rs_err_connbase_push_fl (connbase, RSE_SOCKERR, __FILE__, __LINE__,
+                                    "socket: %d (%s)",
+                                    errno, strerror (errno));
+  if (evutil_make_socket_nonblocking (connbase->fd) < 0)
     {
-      evutil_closesocket (conn->base_.fd);
-      conn->base_.fd = -1;
-      return rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
-                                 "evutil_make_socket_nonblocking: %d (%s)",
-                                 errno, strerror (errno));
+      evutil_closesocket (connbase->fd);
+      connbase->fd = -1;
+      return rs_err_connbase_push_fl (connbase, RSE_SOCKERR, __FILE__, __LINE__,
+                                      "evutil_make_socket_nonblocking: %d (%s)",
+                                      errno, strerror (errno));
+    }
+
+  /* If we're inititalising the socket for a listener, bind to the
+     peer address. */
+  if (connbase->magic == RS_CONN_MAGIC_LISTENER)
+    {
+      assert (p->realm->type == connbase->transport);
+      if (p->realm->type == RS_CONN_TYPE_TLS
+          || p->realm->type == RS_CONN_TYPE_TCP)
+        {
+          struct rs_listener *listener = TO_LISTENER_CONN (connbase);
+          listener->evlistener =
+            evconnlistener_new_bind (listener->base_.ctx->evb,
+                                     listener_accept_cb_,
+                                     listener, LEV_OPT_REUSEABLE,
+                                     LISTENER_BACKLOG,
+                                     p->addr_cache->ai_addr,
+                                     p->addr_cache->ai_addrlen);
+          if (listener->evlistener == NULL)
+            return rs_err_connbase_push (connbase, RSE_EVENT,
+                                         "evconnlistener_new_bind: %d (%s)",
+                                         errno, strerror (errno));
+
+          evconnlistener_set_error_cb (listener->evlistener, listener_err_cb_);
+        }
+      else
+        {
+          return rs_err_connbase_push_fl (connbase, RSE_NOSYS,
+                                          __FILE__, __LINE__, NULL);
+        }
     }
+
   return RSE_OK;
 }
 
 int
-event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer)
+event_init_bufferevent (struct rs_connection *conn)
 {
-  if (conn->base_.bev)
+  struct rs_conn_base *connbase = NULL;
+  assert (conn);
+  connbase = TO_BASE_CONN(conn);
+
+  if (connbase->bev)
     return RSE_OK;
 
-  if (conn->base_.realm->type == RS_CONN_TYPE_TCP)
+  if (connbase->transport == RS_CONN_TYPE_TCP)
     {
-      conn->base_.bev = bufferevent_socket_new (conn->base_.ctx->evb,
-                                              conn->base_.fd, 0);
-      if (!conn->base_.bev)
+      connbase->bev = bufferevent_socket_new (connbase->ctx->evb,
+                                              connbase->fd, 0);
+      if (!connbase->bev)
        return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
-                                   "bufferevent_socket_new");
+                                    "bufferevent_socket_new");
     }
 #if defined (RS_ENABLE_TLS)
-  else if (conn->base_.realm->type == RS_CONN_TYPE_TLS)
+  else if (connbase->transport == RS_CONN_TYPE_TLS)
     {
+      enum bufferevent_ssl_state bev_ssl_state;
+
       if (rs_tls_init (conn))
        return -1;
-      /* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things
-        seem to break when be_openssl_ctrl() (in libevent) calls
-        SSL_set_bio() after BIO_new_socket() with flag=1.  */
-      conn->base_.bev =
-       bufferevent_openssl_socket_new (conn->base_.ctx->evb, conn->base_.fd,
-                                        conn->tls_ssl,
-                                       BUFFEREVENT_SSL_CONNECTING, 0);
-      if (!conn->base_.bev)
+      bev_ssl_state = conn_originating_p (conn)
+        ? BUFFEREVENT_SSL_CONNECTING : BUFFEREVENT_SSL_ACCEPTING;
+
+      /* It would be convenient to pass BEV_OPT_CLOSE_ON_FREE in last
+        argument (options) but things seem to break when
+        be_openssl_ctrl() (in libevent) calls SSL_set_bio() after
+        BIO_new_socket() with flag=1. */
+      connbase->bev =
+       bufferevent_openssl_socket_new (connbase->ctx->evb, connbase->fd,
+                                        conn->tls_ssl, bev_ssl_state, 0);
+      if (!connbase->bev)
        return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
                                    "bufferevent_openssl_socket_new");
     }
@@ -162,7 +212,7 @@ event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer)
     {
       return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__,
                                  "%s: unknown connection type: %d", __func__,
-                                 conn->base_.realm->type);
+                                 connbase->transport);
     }
 
   return RSE_OK;
@@ -176,10 +226,10 @@ event_do_connect (struct rs_connection *conn)
   size_t peer_addrlen;
 
   assert (conn);
-  assert (conn->base_.active_peer);
-  assert (conn->base_.active_peer->addr_cache);
-  peer_addr = conn->base_.active_peer->addr_cache->ai_addr;
-  peer_addrlen = conn->base_.active_peer->addr_cache->ai_addrlen;
+  assert (conn->active_peer);
+  assert (conn->active_peer->addr_cache);
+  peer_addr = conn->active_peer->addr_cache->ai_addr;
+  peer_addrlen = conn->active_peer->addr_cache->ai_addrlen;
 
   /* We don't connect listeners. */
   assert (conn->base_.magic == RS_CONN_MAGIC_GENERIC);
@@ -217,11 +267,9 @@ event_do_connect (struct rs_connection *conn)
          rs_debug (("%s: %d: connect: %d (%s)\n", __func__,
                      conn->base_.fd,
                     sockerr, evutil_socket_error_to_string (sockerr)));
-         rs_err_conn_push_fl (conn, RSE_SOCKERR,
-                               __FILE__, __LINE__,
-                               "%d: connect: %d (%s)", conn->base_.fd,
-                               sockerr,
-                              evutil_socket_error_to_string (sockerr));
+         rs_err_conn_push (conn, RSE_SOCKERR,
+                            "%d: connect: %d (%s)", conn->base_.fd,
+                            sockerr, evutil_socket_error_to_string (sockerr));
        }
       else
        conn->state = RS_CONN_STATE_CONNECTING;
@@ -235,7 +283,7 @@ event_loopbreak (struct rs_connection *conn)
   if (err < 0)
     rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
                         "event_base_loopbreak: %s",
-                        evutil_gai_strerror (err));
+                        evutil_gai_strerror (err)); /* FIXME: really gai_strerror? */
   return err;
 }
 
@@ -244,21 +292,21 @@ void
 event_on_disconnect (struct rs_connection *conn)
 {
   conn->state = RS_CONN_STATE_UNDEFINED;
-  rs_debug (("%s: %p disconnected\n", __func__,
-             TO_BASE_CONN(conn)->active_peer));
+  rs_debug (("%s: %p disconnected\n", __func__, conn->active_peer));
   if (conn->callbacks.disconnected_cb)
     conn->callbacks.disconnected_cb (conn->base_.user_data);
 }
 
-/** Internal connect event returning 0 on success or -1 on error.  */
+/** Internal connect event for originating connections. Returns 0 on
+    success and -1 on TLS certificate verification failure.  */
 int
-event_on_connect (struct rs_connection *conn, struct rs_message *msg)
+event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg)
 {
   assert (conn->state == RS_CONN_STATE_CONNECTING);
-  assert (conn->base_.active_peer);
+  assert (conn->active_peer);
 
 #if defined (RS_ENABLE_TLS)
-  if (conn_type_tls(conn) && !conn_cred_psk(conn))
+  if (conn_type_tls_p (conn) && !conn_cred_psk (conn))
     if (tls_verify_cert (conn) != RSE_OK)
       {
         rs_debug (("%s: server cert verification failed\n", __func__));
@@ -267,7 +315,7 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg)
 #endif /* RS_ENABLE_TLS */
 
   conn->state = RS_CONN_STATE_CONNECTED;
-  rs_debug (("%s: %p connected\n", __func__, TO_BASE_CONN(conn)->active_peer));
+  rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
 
   if (conn->callbacks.connected_cb)
     conn->callbacks.connected_cb (conn->base_.user_data);
@@ -278,12 +326,24 @@ event_on_connect (struct rs_connection *conn, struct rs_message *msg)
   return 0;
 }
 
+/** FIXME: DOC */
 int
-event_init_eventbase (struct rs_connection *conn)
+event_on_connect_term (struct rs_connection *conn, struct rs_message *msg)
 {
-  assert (conn);
-  assert (conn->base_.ctx);
-  if (conn->base_.ctx->evb)
+  /* TODO: verify client */
+  conn->state = RS_CONN_STATE_CONNECTED;
+  rs_debug (("%s: WARNING: not checking client cert!!!\n", __func__));
+  if (conn->callbacks.connected_cb)
+    conn->callbacks.connected_cb (conn->base_.user_data);
+  return 0;
+}
+
+int
+event_init_eventbase (struct rs_conn_base *connbase)
+{
+  assert (connbase);
+  assert (connbase->ctx);
+  if (connbase->ctx->evb)
     return RSE_OK;
 
 #if defined (DEBUG)
@@ -291,10 +351,10 @@ event_init_eventbase (struct rs_connection *conn)
     event_enable_debug_mode ();
 #endif
   event_set_log_callback (_evlog_cb);
-  conn->base_.ctx->evb = event_base_new ();
-  if (!conn->base_.ctx->evb)
-    return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
-                               "event_base_new");
+  connbase->ctx->evb = event_base_new ();
+  if (!connbase->ctx->evb)
+    return rs_err_connbase_push_fl (connbase, RSE_EVENT, __FILE__, __LINE__,
+                                    "event_base_new");
 
   return RSE_OK;
 }
index 3fcea61..63fccc5 100644 (file)
@@ -1,12 +1,13 @@
-/* Copyright 2011 NORDUnet A/S. All rights reserved.
-   See LICENSE for licensing information.  */
+/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
+   See LICENSE for licensing information. */
 
 void event_on_disconnect (struct rs_connection *conn);
-int event_on_connect (struct rs_connection *conn, struct rs_message *msg);
+int event_on_connect_orig (struct rs_connection *conn, struct rs_message *msg);
+int event_on_connect_term (struct rs_connection *conn, struct rs_message *msg);
 int event_loopbreak (struct rs_connection *conn);
-int event_init_eventbase (struct rs_connection *conn);
-int event_init_socket (struct rs_connection *conn, struct rs_peer *p);
-int event_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer);
+int event_init_eventbase (struct rs_conn_base *connbase);
+int event_init_socket (struct rs_conn_base *connbase, struct rs_peer *p);
+int event_init_bufferevent (struct rs_connection *conn);
 void event_do_connect (struct rs_connection *conn);
 void event_conn_timeout_cb (int fd, short event, void *data);
 void event_retransmit_timeout_cb (int fd, short event, void *data);
index 8dc4f58..d86f4f8 100644 (file)
@@ -1,6 +1,6 @@
 AUTOMAKE_OPTIONS = foreign
 INCLUDES = -I$(top_srcdir)/include
-AM_CFLAGS = -Wall -Werror -g
+AM_CFLAGS = -Wall -Werror -g -DDEBUG -DDEBUG_LEVENT
 
 LDADD = ../libradsec.la #-lefence
 CFLAGS = $(AM_CFLAGS) -DUSE_CONFIG_FILE
index d2ee9f4..82a4453 100644 (file)
@@ -85,9 +85,9 @@ blocking_client (const char *av1, const char *av2, const char *av3,
     }
   else
     {
-      if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW, SECRET))
+      if (rs_message_create_authn_request (conn, &req, USER_NAME, USER_PW))
        goto cleanup;
-      if (rs_message_send (req, NULL))
+      if (rs_message_send (req))
        goto cleanup;
       if (rs_conn_receive_message (conn, req, &resp))
        goto cleanup;
index e007654..8a80ec6 100644 (file)
@@ -67,14 +67,13 @@ dispatching_client (struct rs_context *ctx)
 
   if (rs_conn_create(ctx, &conn, CONFIG))
     goto out;
-  rs_conn_set_callbacks (conn, &cb);
-  if (rs_packet_create_authn_request (conn, &req_msg,
-                                      USER_NAME, USER_PW, SECRET))
+  rs_conn_set_callbacks (conn, &cb, &state);
+  if (rs_packet_create_authn_request (conn, &req_msg, USER_NAME, USER_PW))
     goto out;
   /* Doesn't really send the message but rather queues it for sending.
      msg_received_cb() will be invoked with user_data = &state when
      the message has been sent.  */
-  if (rs_packet_send (req_msg, &state))
+  if (rs_packet_send (req_msg))
     goto out;
 
   while (1)
index 32af3c0..288a084 100644 (file)
@@ -9,6 +9,17 @@ realm blocking-udp {
     }
 }
 
+realm testcli-udp {
+    type = "UDP"
+    timeout = 2
+    retries = 2
+    server {
+        hostname = "srv1"
+       service = "4711"
+       secret = "sikrit"
+    }
+}
+
 realm blocking-tls {
     type = "TLS"
     timeout = 1
@@ -22,7 +33,22 @@ realm blocking-tls {
     #pskex = "PSK"
     server {
         hostname = "srv1"
+        # test setup: radsecproxy fronting freeradius on 2083
        service = "2083"
+        # test setup: examples/server on 4711
+       #service = "4711"
+       secret = "sikrit"
+    }
+}
+
+realm testcli {
+    type = "TLS"
+    cacertfile = "/home/linus/p/radsecproxy/demoCA/newcerts/01.pem"
+    certfile = "/home/linus/p/radsecproxy/demoCA/newcerts/03.pem"
+    certkeyfile = "/home/linus/p/radsecproxy/demoCA/private/cli1.key"
+    server {
+        hostname = "srv1"
+       service = "4711"
        secret = "sikrit"
     }
 }
index 8c304a0..fb51866 100644 (file)
@@ -1,7 +1,7 @@
 /* RADIUS/RadSec server using libradsec. */
 
 /* Copyright 2013 NORDUnet A/S. All rights reserved.
-   See LICENSE for licensing information.  */
+   See LICENSE for licensing information. */
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <event2/event.h>
 #include "debug.h"             /* For rs_dump_message(). */
 
-#define CONFIG_FILE "examples/test.conf"
+#define CONFIG_FILE "examples/server.conf"
 #define CONFIG "tls"
 
 #define SECRET "sikrit"
 #define USER_NAME "molgan@PROJECT-MOONSHOT.ORG"
 #define USER_PW "password"
 
-void
+static struct rs_peer *
+client_filter_cb (const struct rs_listener *listener,
+                  void *user_data)
+{
+  printf ("DEBUG: listener %p (user_data=%p) asking for a client filter list\n",
+          listener, user_data);
+  return NULL;
+}
+
+static void
+disco_cb (void *user_data)
+{
+  struct rs_connection *conn = user_data;
+  assert (conn);
+  printf ("DEBUG: conn %p disconnected\n", conn);
+}
+
+static void
+read_cb (struct rs_message *message, void *user_data)
+{
+  struct rs_connection *conn = user_data;
+  assert (conn);
+  printf ("DEBUG: msg received on connection %p\n", conn);
+  rs_dump_message (message);
+  //if (message_verify_response (conn, fixme)) error;
+}
+
+static void
 new_conn_cb (struct rs_connection *conn, void *user_data)
 {
-  printf ("new connection: fd=%d\n", -1); /* conn->fd */
+  const struct rs_listener *l = user_data;
+  struct rs_conn_callbacks cb = {NULL, /* connected */
+                                 disco_cb,
+                                 read_cb,
+                                 NULL}; /* msg sent */
+
+  printf ("DEBUG: new connection on listener %p: %p, fd=%d\n",
+          l, conn, rs_conn_get_fd (conn));
+  rs_conn_set_callbacks (conn, &cb, conn);
 }
 
+void
+err_cb (struct rs_connection *conn, void *user_data)
+{
+  struct rs_listener *listener = user_data;
+  struct rs_error *err = NULL;
+  assert (conn);
+  err = rs_err_conn_pop (conn);
+
+  printf ("DEBUG: error on conn %p, listener %p: %d (%s)\n", conn, listener,
+          rs_err_code (err, 0), rs_err_msg (err));
+}
+
+#if 0
+void
+stdin_cb (evutil_socket_t s, short flags, void *user_data)
+{
+  struct rs_listener *l = user_data;
+
+  printf ("DEBUG: got data on stdin, quitting\n");
+  assert (event_base_loopbreak (rs_listener_get_eventbase (l)) == 0);
+}
+#endif
+
 struct rs_error *
 server (struct rs_context *ctx)
 {
   int r = 0;
   struct rs_error *err = NULL;
-  struct rs_connection *conn = NULL;
   struct rs_listener *listener = NULL;
-  const struct rs_listener_callbacks cbs = {};
+  const struct rs_listener_callbacks cbs =
+    {client_filter_cb, new_conn_cb, err_cb};
+  struct event *read_event = NULL;
 
   if (rs_listener_create (ctx, &listener, CONFIG))
     goto out;
-  rs_listener_set_callbacks (listener, &cbs);
+  rs_listener_set_callbacks (listener, &cbs, listener);
+  if (rs_listener_listen (listener))
+    goto out;
+
+#if 0
+  /* Listen on stdin too, for quitting the server nicely without
+     having to trap SIGKILL. */
+  read_event = event_new (rs_listener_get_eventbase (listener),
+                          fileno (stdin),
+                          EV_READ,
+                          stdin_cb,
+                          listener);
+  assert (read_event != NULL);
+  assert (event_add (read_event, NULL) == 0);
+#endif
 
   do
-    {
-      r = rs_listener_dispatch (listener);
-      printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r);
-    }
+    r = rs_listener_dispatch (listener);
   while (r == 0);
 
+  printf ("DEBUG: rs_listener_dispatch done (r=%d)\n", r);
+  if (r < 0)
+    printf ("DEBUG: libevent signals error: %s\n", evutil_gai_strerror (r));
+  if (r == 1)
+    printf ("DEBUG: no events registered, exiting\n");
+
  out:
   err = rs_err_ctx_pop (ctx);
   if (err == NULL)
-    err = rs_err_conn_pop (conn);
+    err = rs_err_listener_pop (listener);
 
-#if 0
+  if (read_event)
+    event_free (read_event);
+  read_event = NULL;
   if (listener)
-    rs_listener_destroy (listener);
+    {
+      assert (rs_listener_close (listener) == RSE_OK);
+      //rs_listener_destroy (listener);
+    }
   listener = NULL;
-#endif
 
   return err;
 }
@@ -80,13 +160,15 @@ main (int argc, char *argv[])
   err = server (ctx);
 
  out:
-  if (ctx)
-    rs_context_destroy (ctx);
-
   if (err)
     {
-      fprintf (stderr, "error: %s: %d\n", rs_err_msg (err), rs_err_code (err, 0));
+      fprintf (stderr, "%s: error: %s: %d\n",
+               argv[0], rs_err_msg (err), rs_err_code (err, 0));
       return rs_err_code (err, 1);
     }
+
+  if (ctx)
+    rs_context_destroy (ctx);
+
   return 0;
 }
index 45ce7f6..28e9e4c 100644 (file)
@@ -90,6 +90,7 @@ struct rs_realm {
     int timeout;
     int retries;
     struct rs_listener *listeners;
+    struct rs_peer *local_addr;
     struct rs_peer *peers;
     struct rs_realm *next;
 };
@@ -103,9 +104,10 @@ struct rs_config {
 /** Libradsec context. */
 struct rs_context {
     struct rs_config *config;
+    struct rs_realm *realms;
     struct rs_alloc_scheme alloc_scheme;
     struct rs_error *err;
-    struct event_base *evb;    /* Event base. */
+    struct event_base *evb;
 };
 
 /** Base class for a connection. */
@@ -113,11 +115,11 @@ struct rs_conn_base {
     uint32_t magic;             /* Must be one of RS_CONN_MAGIC_*. */
     struct rs_context *ctx;
     struct rs_realm *realm;    /* Owned by ctx. */
+    enum rs_conn_type transport;
     /** For a listener, allowed client addr/port pairs.
-     For an outgoing connection, set of servers.
+     For an outgoing connection, set of configured servers.
      For an incoming connection, the peer (as the only entry). */
-    struct rs_peer *peers;      /**< Configured peers. */
-    struct rs_peer *active_peer; /**< The other end of the connection. */
+    struct rs_peer *peers;
     struct timeval timeout;
     int tryagain;              /* For server failover. */
     void *user_data;
@@ -137,16 +139,13 @@ enum rs_conn_state {
     RS_CONN_STATE_CONNECTED,
 };
 
-/** A "generic" connection. */
+/** A generic connection. */
 struct rs_connection {
     struct rs_conn_base base_;
     struct event *tev;         /* Timeout event. */
     struct rs_conn_callbacks callbacks;
     enum rs_conn_state state;
-#if 0
-    char is_connecting;                /* FIXME: replace with a single state member */
-    char is_connected;         /* FIXME: replace with a single state member */
-#endif                          /* 0 */
+    struct rs_peer *active_peer;
     struct rs_message *out_queue; /* Queue for outgoing UDP packets. */
 #if defined(RS_ENABLE_TLS)
     /* TLS specifics. */
@@ -156,7 +155,7 @@ struct rs_connection {
 };
 
 /** A listening connection. Spawns generic connections when peers
* connect to it. */
   connect to it. */
 struct rs_listener {
     struct rs_conn_base base_;
     struct evconnlistener *evlistener;
index cb98db7..2858f9e 100644 (file)
@@ -22,6 +22,7 @@
 #ifdef HAVE_STDINT_H
 #include <stdint.h>
 #endif
+#include "compat.h"
 
 enum rs_error_code {
     RSE_OK = 0,
@@ -72,7 +73,7 @@ enum rs_error_code {
     RSE_MAX = RSE_CERT
 };
 
-enum rs_conn_type {
+enum rs_conn_type {             /* FIXME: Rename rs_transport_type? */
     RS_CONN_TYPE_NONE = 0,
     RS_CONN_TYPE_UDP,
     RS_CONN_TYPE_TCP,
@@ -174,10 +175,14 @@ struct rs_conn_callbacks {
     rs_conn_message_sent_cb sent_cb;
 };
 
-typedef void (*rs_listener_new_conn_cb) (struct rs_connection *conn,
-                                         void *user_data);
-typedef void (*rs_listener_error_cb) (void *user_data);
+typedef struct rs_peer *(*rs_listener_client_filter_cb)
+    (const struct rs_listener *listener, void *user_data);
+typedef void (*rs_listener_new_conn_cb)
+    (struct rs_connection *conn, void *user_data);
+typedef void (*rs_listener_error_cb)
+    (struct rs_connection *conn, void *user_data);
 struct rs_listener_callbacks {
+    rs_listener_client_filter_cb client_filter_cb;
     rs_listener_new_conn_cb new_conn_cb;
     rs_listener_error_cb error_cb;
 };
@@ -228,8 +233,13 @@ int rs_listener_create (struct rs_context *ctx,
                         struct rs_listener **listener,
                         const char *config);
 void rs_listener_set_callbacks (struct rs_listener *listener,
-                                const struct rs_listener_callbacks *cb);
+                                const struct rs_listener_callbacks *cb,
+                                void *user_data);
+int rs_listener_listen (struct rs_listener *listener);
 int rs_listener_dispatch (const struct rs_listener *listener);
+int rs_listener_close (struct rs_listener *l);
+struct event_base *rs_listener_get_eventbase (const struct rs_listener *l);
+int rs_listener_get_fd (const struct rs_listener *l);
 
 /****************/
 /* Connection.  */
@@ -277,7 +287,8 @@ int rs_conn_set_eventbase(struct rs_connection *conn, struct event_base *eb);
 
 /** Register callbacks \a cb for connection \a conn.  */
 void rs_conn_set_callbacks(struct rs_connection *conn,
-                          struct rs_conn_callbacks *cb);
+                          struct rs_conn_callbacks *cb,
+                           void *user_data);
 
 /** Remove callbacks for connection \a conn.  */
 void rs_conn_del_callbacks(struct rs_connection *conn);
@@ -356,28 +367,27 @@ int rs_message_create(struct rs_connection *conn, struct rs_message **pkt_out);
 /** Free all memory allocated for message \a msg.  */
 void rs_message_destroy(struct rs_message *msg);
 
-/** Send message \a msg on the connection associated with \a msg.
-    \a user_data is sent to the \a rs_conn_message_received_cb callback
-    registered with the connection. If no callback is registered with
-    the connection, the event loop is run by \a rs_message_send and it
-    blocks until the message has been succesfully sent.
+/** Send \a msg on the connection associated with \a msg.
+    If no callback is registered with the connection
+    (\a rs_conn_set_callbacks), the event loop is run by
+    \a rs_message_send and it blocks until the message has been
+    succesfully sent.
 
-    \return On success, RSE_OK (0) is returned.  On error, !0 is
+    \return On success, RSE_OK (0) is returned. On error, !0 is
     returned and a struct \a rs_error is pushed on the error stack for
-    the connection.  The error can be accessed using \a
-    rs_err_conn_pop.  */
-int rs_message_send(struct rs_message *msg, void *user_data);
+    the connection. The error can be accessed using \a
+    rs_err_conn_pop. */
+int rs_message_send(struct rs_message *msg);
 
 /** Create a RADIUS authentication request message associated with
-    connection \a conn.  Optionally, User-Name and User-Password
-    attributes are added to the message using the data in \a user_name,
-    \a user_pw and \a secret where \secret is the RADIUS shared
-    secret. */
+    connection \a conn. Optionally, User-Name and User-Password
+    attributes are added to the message using the data in \a user_name
+    and \a user_pw.
+    FIXME: describe what RADIUS shared secret is being used */
 int rs_message_create_authn_request(struct rs_connection *conn,
                                     struct rs_message **msg,
                                     const char *user_name,
-                                    const char *user_pw,
-                                    const char *secret);
+                                    const char *user_pw);
 
 /*** Append \a tail to message \a msg.  */
 int
@@ -439,15 +449,17 @@ int rs_err_conn_push_fl(struct rs_connection *conn,
                        int line,
                        const char *fmt,
                        ...);
-int rs_err_connbase_push_fl (struct rs_conn_base *connbase,
-                             int code,
-                             const char *file,
-                             int line,
-                             const char *fmt,
-                             ...);
+int rs_err_connbase_push_fl(struct rs_conn_base *connbase,
+                            int code,
+                            const char *file,
+                            int line,
+                            const char *fmt,
+                            ...);
 /** Pop the first error from the error FIFO associated with connection
-    \a conn or NULL if there are no errors in the FIFO.  */
+    \a conn or NULL if there are no errors in the FIFO. */
 struct rs_error *rs_err_conn_pop(struct rs_connection *conn);
+struct rs_error *rs_err_connbase_pop(struct rs_conn_base *connbase);
+struct rs_error *rs_err_listener_pop(struct rs_listener *l);
 
 int rs_err_conn_peek_code (struct rs_connection *conn);
 void rs_err_free(struct rs_error *err);
diff --git a/lib/listener.c b/lib/listener.c
new file mode 100644 (file)
index 0000000..877c162
--- /dev/null
@@ -0,0 +1,236 @@
+/* Copyright 2013 NORDUnet A/S. All rights reserved.
+   See LICENSE for licensing information. */
+
+#if defined HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <assert.h>
+#include <event2/listener.h>
+#include <radsec/radsec.h>
+#include <radsec/radsec-impl.h>
+#include "listener.h"
+#include "conn.h"
+#include "peer.h"
+#include "event.h"
+#include "debug.h"
+
+struct rs_listener *
+listener_create (struct rs_context *ctx, struct rs_listener **rootp)
+{
+  struct rs_listener *listener;
+
+  listener = rs_calloc (ctx, 1, sizeof (*listener));
+  if (listener)
+    {
+      if (*rootp == NULL)
+        *rootp = listener;
+      else
+        {
+          listener->next = (*rootp)->next;
+          (*rootp)->next = listener;
+        }
+    }
+  return listener;
+}
+
+void
+listener_accept_cb_ (struct evconnlistener *evconnlistener,
+                     evutil_socket_t newfd,
+                     struct sockaddr *srcaddr_sa,
+                     int srcaddr_len,
+                     void *user_data)
+{
+  int err = RSE_OK;
+  struct rs_listener *l = NULL;
+  struct rs_connection *newconn = NULL;
+  struct rs_context *ctx = NULL;
+  struct rs_peer *clients = NULL;
+
+  l = (struct rs_listener *) user_data;
+  assert (l);
+  assert (l->base_.magic == RS_CONN_MAGIC_LISTENER);
+  assert (l->evlistener == evconnlistener);
+  ctx = l->base_.ctx;
+  assert (ctx);
+
+#if defined (DEBUG)
+  {
+    char host[80], port[80];
+    getnameinfo (srcaddr_sa, srcaddr_len, host, sizeof(host),
+                 port, sizeof(port), 0);
+    rs_debug (("%s: incoming connection from %s:%s\n", __func__, host, port));
+  }
+#endif
+
+/*
+Application needs to specify acceptable clients -- we need to verify
+src addr in the UDP case and x509 client cert in the TLS case. A list
+of peers with proper pointers to realms should be a good way of doing
+this.
+
+Ask the application for a list of acceptable clients. Default to
+accepting any potential configured client block in the realm of the
+listener.  Note that this for this to be an opption, the application
+must have read a config file. If there is no configuration, reject the
+client.
+*/
+  if (l->callbacks.client_filter_cb)
+    clients = l->callbacks.client_filter_cb (l, TO_BASE_CONN(l)->user_data);
+  if (clients == NULL)
+    clients = connbase_get_peers (TO_BASE_CONN(l));
+  if (clients == NULL)
+    {
+      rs_debug (("%s: didn't get a client list for listener %p\n",
+                 __func__, l));
+      return;
+    }
+  rs_debug (("%s: using client list %p\n", __func__, clients));
+
+  err = rs_conn_create (ctx, &newconn, NULL);
+  if (err)
+    {
+      rs_debug (("%s: failed creating a new struct rs_connection: %d\n",
+                __func__, err));
+      return;                /* FIXME: Verify that this is handled. */
+    }
+
+  assert(clients);
+  /* TODO: Picking the very first peer is not really what we want to
+     do. For UDP, we can look at src ip and try to find a matching
+     peer. For TLS, it's worse because we don't have the certificate
+     until we've accepted the TCP connection. */
+  TO_BASE_CONN(newconn)->realm = clients->realm;
+  newconn->active_peer = clients;
+
+  TO_BASE_CONN(newconn)->fd = newfd;
+  TO_BASE_CONN(newconn)->transport = TO_BASE_CONN(l)->realm->type;
+  err = event_init_bufferevent (newconn);
+  if (err)
+    {
+      rs_debug (("%s: failed init bev: %d\n", __func__, err));
+      goto errout;
+    }
+
+  /* Create a message and set up a read event. This installs the
+     callback performing the TLS verification. */
+  {                             /* FIXME */
+    struct rs_message *msg = NULL;
+    err = rs_message_create (newconn, &msg);
+    if (err)
+      abort ();                 /* FIXME */
+    conn_add_read_event (newconn, msg);
+  }
+
+  if (l->callbacks.new_conn_cb)
+    l->callbacks.new_conn_cb (newconn, TO_BASE_CONN(l)->user_data);
+  return;                       /* Success. */
+
+ errout:
+  rs_conn_destroy (newconn);
+  if (l->callbacks.error_cb)
+    l->callbacks.error_cb (newconn, TO_BASE_CONN(l)->user_data);
+}
+
+void
+listener_err_cb_ (struct evconnlistener *listener, void *user_data)
+{
+  rs_debug (("%s: FIXME: handle error\n", __func__));
+}
+
+/* Public functions. */
+int
+rs_listener_create (struct rs_context *ctx,
+                    struct rs_listener **listener_out,
+                    const char *config)
+{
+  int err = RSE_OK;
+  struct rs_listener *listener = NULL;
+
+  assert (ctx);
+
+  listener = rs_calloc (ctx, 1, sizeof (*listener));
+  if (listener == NULL)
+    return rs_err_ctx_push_fl (ctx, RSE_NOMEM, __FILE__, __LINE__, NULL);
+  conn_init (ctx, TO_BASE_CONN (listener), RS_CONN_OBJTYPE_LISTENER);
+  err = conn_configure (ctx, TO_BASE_CONN (listener), config);
+  if (err)
+    goto errout;
+
+  if (listener_out)
+    *listener_out = listener;
+  return RSE_OK;
+
+ errout:
+  if (listener)
+    rs_free (ctx, listener);
+  return err;
+}
+
+void
+rs_listener_set_callbacks (struct rs_listener *listener,
+                           const struct rs_listener_callbacks *cb,
+                           void *user_data)
+{
+  assert (listener);
+  TO_BASE_CONN(listener)->user_data = user_data;
+  memcpy (&listener->callbacks, cb, sizeof (listener->callbacks));
+}
+
+int
+rs_listener_listen (struct rs_listener *listener)
+{
+  int err = RSE_OK;
+  struct rs_conn_base *connbase = NULL;
+  assert (listener);
+  connbase = TO_BASE_CONN (listener);
+
+  err = event_init_eventbase (connbase);
+  if (err)
+    return err;
+  err = event_init_socket (connbase, connbase->realm->local_addr);
+  if (err)
+    return err;
+#if 0
+  {
+    struct linger l;
+    l.l_onoff = 1;
+    l.l_linger = 0;
+    rs_debug (("%s: setting SO_LINGER 0s on fd %d\n", __func__, connbase->fd));
+    assert (0 == setsockopt (connbase->fd, SOL_SOCKET, SO_LINGER,
+                             (void*)&l, sizeof(l)));
+  }
+#endif
+  return err;
+}
+
+int
+rs_listener_dispatch (const struct rs_listener *listener)
+{
+  assert (listener);
+  assert (TO_BASE_CONN(listener)->ctx);
+  return event_base_dispatch (TO_BASE_CONN(listener)->ctx->evb);
+}
+
+int
+rs_listener_close (struct rs_listener *l)
+{
+  int err = baseconn_close (TO_BASE_CONN (l));
+  return err;
+}
+
+struct event_base *
+rs_listener_get_eventbase (const struct rs_listener *l)
+{
+  assert (TO_BASE_CONN (l));
+  assert (TO_BASE_CONN (l)->ctx);
+  return TO_BASE_CONN (l)->ctx->evb;
+}
+
+int
+rs_listener_get_fd (const struct rs_listener *l)
+{
+  assert (TO_BASE_CONN (l));
+  return TO_BASE_CONN (l)->fd;
+}
diff --git a/lib/listener.h b/lib/listener.h
new file mode 100644 (file)
index 0000000..8f7c543
--- /dev/null
@@ -0,0 +1,14 @@
+/* Copyright 2013 NORDUnet A/S. All rights reserved.
+   See LICENSE for licensing information. */
+
+/** Maximum number of pending connection requests. */
+#define LISTENER_BACKLOG -1
+
+void listener_accept_cb_(struct evconnlistener *evconnlistener,
+                         evutil_socket_t fd,
+                         struct sockaddr *sa,
+                         int socklen,
+                         void *data);
+void listener_err_cb_(struct evconnlistener *listener, void *user_data);
+struct rs_listener *listener_create(struct rs_context *ctx,
+                                    struct rs_listener **rootp);
index 47590ca..a40e00b 100644 (file)
@@ -28,15 +28,15 @@ message_verify_response (struct rs_connection *conn,
   int err;
 
   assert (conn);
-  assert (conn->base_.active_peer);
-  assert (conn->base_.active_peer->secret);
+  assert (conn->active_peer);
+  assert (conn->active_peer->secret);
   assert (response);
   assert (response->rpkt);
   assert (request);
   assert (request->rpkt);
 
-  response->rpkt->secret = conn->base_.active_peer->secret;
-  response->rpkt->sizeof_secret = strlen (conn->base_.active_peer->secret);
+  response->rpkt->secret = conn->active_peer->secret;
+  response->rpkt->sizeof_secret = strlen (conn->active_peer->secret);
 
   /* Verify header and message authenticator.  */
   err = nr_packet_verify (response->rpkt, request->rpkt);
@@ -71,11 +71,11 @@ message_do_send (struct rs_message *msg)
 
   assert (msg);
   assert (msg->conn);
-  assert (msg->conn->base_.active_peer);
-  assert (msg->conn->base_.active_peer->secret);
+  assert (msg->conn->active_peer);
+  assert (msg->conn->active_peer->secret);
   assert (msg->rpkt);
 
-  msg->rpkt->secret = msg->conn->base_.active_peer->secret;
+  msg->rpkt->secret = msg->conn->active_peer->secret;
   msg->rpkt->sizeof_secret = strlen (msg->rpkt->secret);
 
   /* Encode message.  */
@@ -89,16 +89,16 @@ message_do_send (struct rs_message *msg)
     return rs_err_conn_push_fl (msg->conn, -err, __FILE__, __LINE__,
                                "nr_packet_sign");
 #if defined (DEBUG)
-  {
-    char host[80], serv[80];
-
-    getnameinfo (msg->conn->base_.active_peer->addr_cache->ai_addr,
-                msg->conn->base_.active_peer->addr_cache->ai_addrlen,
-                host, sizeof(host), serv, sizeof(serv),
-                0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
-    rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv));
-    rs_dump_message (msg);
-  }
+  if (msg->conn->active_peer->addr_cache)
+    {
+      char host[80], serv[80];
+      getnameinfo (msg->conn->active_peer->addr_cache->ai_addr,
+                   msg->conn->active_peer->addr_cache->ai_addrlen,
+                   host, sizeof(host), serv, sizeof(serv),
+                   0 /* NI_NUMERICHOST|NI_NUMERICSERV*/);
+      rs_debug (("%s: about to send this to %s:%s:\n", __func__, host, serv));
+      rs_dump_message (msg);
+    }
 #endif
 
   /* Put message in output buffer.  */
@@ -160,8 +160,7 @@ int
 rs_message_create_authn_request (struct rs_connection *conn,
                                  struct rs_message **msg_out,
                                  const char *user_name,
-                                 const char *user_pw,
-                                 const char *secret)
+                                 const char *user_pw)
 {
   struct rs_message *msg;
   int err;
@@ -174,6 +173,7 @@ rs_message_create_authn_request (struct rs_connection *conn,
 
   if (user_name)
     {
+      /* FIXME: use new rs_message_add_avp() */
       err = rs_message_append_avp (msg, PW_USER_NAME, 0, user_name, 0);
       if (err)
        return err;
@@ -181,7 +181,9 @@ rs_message_create_authn_request (struct rs_connection *conn,
 
   if (user_pw)
     {
-      msg->rpkt->secret = secret;
+      /* FIXME: msg->rpkt->secret = secret;
+         FIXME: use new rs_message_add_avp() */
+      msg->rpkt->secret = "sikrit"; /* FIXME */
       err = rs_message_append_avp (msg, PW_USER_PASSWORD, 0, user_pw, 0);
       if (err)
        return err;
index b6ec167..bbc3bf5 100644 (file)
@@ -20,12 +20,12 @@ peer_pick_peer (struct rs_connection *conn)
 {
   assert (conn);
 
-  if (conn->base_.active_peer)
-    conn->base_.active_peer = conn->base_.active_peer->next; /* Next.  */
-  if (!conn->base_.active_peer)
-    conn->base_.active_peer = conn->base_.peers; /* From the top.  */
+  if (conn->active_peer)
+    conn->active_peer = conn->active_peer->next; /* Next.  */
+  if (conn->active_peer == NULL)
+    conn->active_peer = TO_BASE_CONN (conn)->peers; /* From the top.  */
 
-  return conn->base_.active_peer;
+  return conn->active_peer;
 }
 
 struct rs_peer *
index 95be0b0..2a8b43c 100644 (file)
@@ -1,5 +1,5 @@
-/* Copyright 2011 NORDUnet A/S. All rights reserved.
-   See LICENSE for licensing information.  */
+/* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
+   See LICENSE for licensing information. */
 
 struct rs_peer *peer_create (struct rs_context *ctx, struct rs_peer **rootp);
 struct rs_peer *peer_pick_peer (struct rs_connection *conn);
index 6ad1361..9dd7186 100644 (file)
@@ -42,7 +42,7 @@ rs_conn_del_callbacks
 rs_conn_destroy
 rs_conn_disconnect
 rs_conn_dispatch
-rs_conn_fd
+rs_conn_get_fd
 rs_conn_get_callbacks
 rs_conn_get_current_peer
 rs_conn_receive_message
@@ -66,9 +66,14 @@ rs_err_ctx_pop
 rs_err_ctx_push
 rs_err_ctx_push_fl
 rs_err_free
+rs_err_listener_pop
 rs_err_msg
 rs_listener_create
+rs_listener_close
 rs_listener_dispatch
+rs_listener_get_eventbase
+rs_listener_get_fd
+rs_listener_listen
 rs_listener_set_callbacks
 rs_message_append_avp
 rs_message_avps
index 17b36de..611cbdf 100644 (file)
@@ -60,7 +60,7 @@ rs_request_create_authn (struct rs_connection *conn,
   if (rs_request_create (conn, &req))
     return -1;
 
-  if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw, secret))
+  if (rs_message_create_authn_request (conn, &req->req_msg, user_name, user_pw))
     return -1;
 
   if (req_out)
@@ -103,7 +103,7 @@ rs_request_send (struct rs_request *request, struct rs_message **resp_msg)
   if (!request || !request->conn || !request->req_msg || !resp_msg)
     return rs_err_conn_push_fl (conn, RSE_INVAL, __FILE__, __LINE__, NULL);
   conn = request->conn;
-  assert (!conn_user_dispatch_p (conn)); /* This function is high level.  */
+  assert (!conn_user_dispatch_p (conn)); /* This function is high level. */
 
   gettimeofday (&end, NULL);
   end.tv_sec += MRD;
@@ -112,7 +112,7 @@ rs_request_send (struct rs_request *request, struct rs_message **resp_msg)
     {
       rs_conn_set_timeout (conn, &rt);
 
-      r = rs_message_send (request->req_msg, NULL);
+      r = rs_message_send (request->req_msg);
       if (r == RSE_OK)
        {
          r = rs_conn_receive_message (request->conn,
index fab89a9..3ed6c93 100644 (file)
 static int
 _conn_open (struct rs_connection *conn, struct rs_message *msg)
 {
-  if (event_init_eventbase (conn))
+  if (event_init_eventbase (TO_BASE_CONN (conn)))
     return -1;
 
-  if (!conn->base_.active_peer)
+  if (!conn->active_peer)
     peer_pick_peer (conn);
-  if (!conn->base_.active_peer)
+  if (!conn->active_peer)
     return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
 
-  if (event_init_socket (conn, conn->base_.active_peer))
+  if (event_init_socket (&conn->base_, conn->active_peer))
     return -1;
 
   if (conn->base_.realm->type == RS_CONN_TYPE_TCP
@@ -37,7 +37,7 @@ _conn_open (struct rs_connection *conn, struct rs_message *msg)
     {
       if (tcp_init_connect_timer (conn))
        return -1;
-      if (event_init_bufferevent (conn, conn->base_.active_peer))
+      if (event_init_bufferevent (conn))
        return -1;
     }
   else
@@ -59,7 +59,7 @@ static int
 _conn_is_open_p (struct rs_connection *conn)
 {
   return conn->state == RS_CONN_STATE_CONNECTED
-    && conn->base_.active_peer != NULL;
+    && conn->active_peer != NULL;
 }
 
 /* User callback used when we're dispatching for user.  */
@@ -76,7 +76,7 @@ _wcb (void *user_data)
 }
 
 int
-rs_message_send (struct rs_message *msg, void *user_data)
+rs_message_send (struct rs_message *msg)
 {
   struct rs_connection *conn = NULL;
   int err = 0;
@@ -93,14 +93,13 @@ rs_message_send (struct rs_message *msg, void *user_data)
 
   assert (conn->base_.ctx);
   assert (conn->base_.ctx->evb);
-  assert (conn->base_.active_peer);
+  assert (conn->active_peer);
   assert (conn->base_.fd >= 0);
 
-  conn->base_.user_data = user_data;
-
   if (conn->base_.bev)         /* TCP */
     {
-      bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb, msg);
+      bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb,
+                         msg);
       bufferevent_enable (conn->base_.bev, EV_WRITE);
     }
   else                         /* UDP */
@@ -118,6 +117,7 @@ rs_message_send (struct rs_message *msg, void *user_data)
   /* Do dispatch, unless the user wants to do it herself.  */
   if (!conn_user_dispatch_p (conn))
     {
+      /* Blocking mode. */
       conn->callbacks.sent_cb = _wcb;
       conn->base_.user_data = msg;
       rs_debug (("%s: entering event loop\n", __func__));
index f5673f5..7264244 100644 (file)
--- a/lib/tcp.c
+++ b/lib/tcp.c
@@ -31,36 +31,27 @@ _read_header (struct rs_message *msg)
 {
   size_t n = 0;
 
-  n = bufferevent_read (msg->conn->base_.bev, msg->hdr, RS_HEADER_LEN);
+  n = bufferevent_read (TO_BASE_CONN(msg->conn)->bev, msg->hdr, RS_HEADER_LEN);
   if (n == RS_HEADER_LEN)
     {
       msg->flags |= RS_MESSAGE_HEADER_READ;
       msg->rpkt->length = (msg->hdr[2] << 8) + msg->hdr[3];
       if (msg->rpkt->length < 20 || msg->rpkt->length > RS_MAX_PACKET_LEN)
-       {
-         conn_close (&msg->conn);
-         return rs_err_conn_push (msg->conn, RSE_INVALID_MSG,
-                                  "invalid message length: %d",
-                                  msg->rpkt->length);
-       }
+        return  rs_err_conn_push (msg->conn, RSE_INVALID_MSG,
+                                  "invalid message length: %d",
+                                  msg->rpkt->length);
       memcpy (msg->rpkt->data, msg->hdr, RS_HEADER_LEN);
-      bufferevent_setwatermark (msg->conn->base_.bev, EV_READ,
+      bufferevent_setwatermark (TO_BASE_CONN(msg->conn)->bev, EV_READ,
                                msg->rpkt->length - RS_HEADER_LEN, 0);
       rs_debug (("%s: message header read, total msg len=%d\n",
                 __func__, msg->rpkt->length));
     }
   else if (n < 0)
-    {
-      rs_debug (("%s: buffer frozen while reading header\n", __func__));
-    }
+    rs_debug (("%s: buffer frozen while reading header\n", __func__));
   else     /* Error: libevent gave us less than the low watermark. */
-    {
-      conn_close (&msg->conn);
-      return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__,
-                                 "got %d octets reading header", n);
-    }
-
-  return 0;
+    return rs_err_conn_push_fl (msg->conn, RSE_INTERNAL, __FILE__, __LINE__,
+                                "got %d octets reading header", n);
+  return RSE_OK;
 }
 
 /** Read a message, check that it's valid RADIUS and hand it off to
@@ -98,12 +89,9 @@ _read_message (struct rs_message *msg)
         - attribute lengths >= 2
         - attribute sizes adding up correctly  */
       err = nr_packet_ok (msg->rpkt);
-      if (err != RSE_OK)
-       {
-         conn_close (&msg->conn);
-         return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__,
-                                     "invalid message");
-       }
+      if (err)
+        return rs_err_conn_push_fl (msg->conn, err, __FILE__, __LINE__,
+                                    "invalid message");
 
 #if defined (DEBUG)
       /* Find out what happens if there's data left in the buffer.  */
@@ -133,11 +121,11 @@ _read_message (struct rs_message *msg)
 
 /* The read callback for TCP.
 
-   Read exactly one RADIUS message from BEV and store it in struct
-   rs_message passed in USER_DATA.
+   Read exactly one RADIUS message from \a bev and store it in the
+   struct rs_message passed in \a user_data.
 
    Inform upper layer about successful reception of received RADIUS
-   message by invoking conn->callbacks.recevied_cb(), if !NULL.  */
+   message by invoking conn->callbacks.recevied_cb(), if not NULL. */
 void
 tcp_read_cb (struct bufferevent *bev, void *user_data)
 {
@@ -148,7 +136,7 @@ tcp_read_cb (struct bufferevent *bev, void *user_data)
   assert (msg->rpkt);
 
   msg->rpkt->sockfd = msg->conn->base_.fd;
-  msg->rpkt->vps = NULL;        /* FIXME: can this be done when initializing msg? */
+  msg->rpkt->vps = NULL; /* FIXME: can this be done when initializing msg? */
 
   /* Read a message header if not already read, return if that
      fails. Read a message and have it dispatched to the user
@@ -158,8 +146,9 @@ tcp_read_cb (struct bufferevent *bev, void *user_data)
      avoid the extra copying. */
   if ((msg->flags & RS_MESSAGE_HEADER_READ) == 0)
     if (_read_header (msg))
-      return;                  /* Error.  */
-  _read_message (msg);
+      return;                   /* Invalid header. */
+  if (_read_message (msg))
+    return;                     /* Invalid message. */
 }
 
 void
@@ -179,15 +168,27 @@ tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
   assert (msg->conn);
   conn = msg->conn;
 #if defined (DEBUG)
-  assert (msg->conn->base_.active_peer);
-  p = conn->base_.active_peer;
+  assert (conn->active_peer);
+  p = conn->active_peer;
 #endif
 
   if (events & BEV_EVENT_CONNECTED)
     {
-      if (conn->tev)
-       evtimer_del (conn->tev); /* Cancel connect timer.  */
-      if (event_on_connect (conn, msg))
+      int err = -1;
+
+      if (conn_originating_p (conn)) /* We're a client. */
+        {
+          assert (conn->tev);
+          if (conn->tev)
+            evtimer_del (conn->tev); /* Cancel connect timer.  */
+          err = event_on_connect_orig (conn, msg);
+        }
+      else                      /* We're a server. */
+        {
+          assert (conn->tev == NULL);
+          err = event_on_connect_term (conn, msg);
+        }
+      if (err)
         {
           event_on_disconnect (conn);
           event_loopbreak (conn);
index 33ddb51..30f5e0f 100644 (file)
@@ -4,9 +4,13 @@ AM_CFLAGS = -Wall -Werror -g
 
 TESTS = test-udp
 
-check_PROGRAMS = test-udp udp-server
+check_PROGRAMS = test-udp udp-server tls-server
 
 test_udp_SOURCES = test-udp.c udp.c
 test_udp_LDADD = ../libradsec.la -lcgreen -lm
 
 udp_server_SOURCES = udp-server.c udp.c
+
+tls_server_SOURCES = server.c
+tls_server_LDADD = ../libradsec.la
+
index b9fb3cf..788bf5c 100644 (file)
--- a/lib/tls.c
+++ b/lib/tls.c
 static struct tls *
 _get_tlsconf (struct rs_connection *conn, const struct rs_realm *realm)
 {
-  struct tls *c = rs_malloc (conn->base_.ctx, sizeof (struct tls));
+  struct tls *c = rs_calloc (conn->base_.ctx, 1, sizeof (struct tls));
 
   if (c)
     {
-      memset (c, 0, sizeof (struct tls));
+      assert (realm);
       /* _conn_open() should've picked a peer by now. */
-      assert (conn->base_.active_peer);
+      assert (conn->active_peer);
       /* TODO: Make sure old radsecproxy code doesn't free these all
         of a sudden, or strdup them.  */
       c->name = realm->name;
-      c->cacertfile = conn->base_.active_peer->cacertfile;
+      c->cacertfile = conn->active_peer->cacertfile;
       c->cacertpath = NULL;    /* NYI */
-      c->certfile = conn->base_.active_peer->certfile;
-      c->certkeyfile = conn->base_.active_peer->certkeyfile;
+      c->certfile = conn->active_peer->certfile;
+      c->certkeyfile = conn->active_peer->certkeyfile;
       c->certkeypwd = NULL;    /* NYI */
       c->cacheexpiry = 0;      /* NYI */
       c->crlcheck = 0;         /* NYI */
@@ -60,7 +60,7 @@ psk_client_cb (SSL *ssl,
 
   conn = SSL_get_ex_data (ssl, 0);
   assert (conn != NULL);
-  cred = conn->base_.active_peer->transport_cred;
+  cred = conn->active_peer->transport_cred;
   assert (cred != NULL);
   /* NOTE: Ignoring identity hint from server.  */
 
@@ -125,8 +125,9 @@ rs_tls_init (struct rs_connection *conn)
 
   assert (conn->base_.ctx);
   ctx = conn->base_.ctx;
+  assert (conn->active_peer);
 
-  tlsconf = _get_tlsconf (conn, conn->base_.active_peer->realm);
+  tlsconf = _get_tlsconf (conn, conn->active_peer->realm);
   if (!tlsconf)
     return -1;
   ssl_ctx = tlsgetctx (RAD_TLS, tlsconf);
@@ -147,7 +148,7 @@ rs_tls_init (struct rs_connection *conn)
     }
 
 #if defined RS_ENABLE_TLS_PSK
-  if (conn->base_.active_peer->transport_cred != NULL)
+  if (conn->active_peer->transport_cred != NULL)
     {
       SSL_set_psk_client_callback (ssl, psk_client_cb);
       SSL_set_ex_data (ssl, 0, conn);
@@ -203,9 +204,9 @@ tls_verify_cert (struct rs_connection *conn)
   struct in6_addr addr;
   const char *hostname = NULL;
 
-  assert (conn->base_.active_peer != NULL);
-  assert (conn->base_.active_peer->hostname != NULL);
-  hostname = conn->base_.active_peer->hostname;
+  assert (conn->active_peer != NULL);
+  assert (conn->active_peer->hostname != NULL);
+  hostname = conn->active_peer->hostname;
 
   /* verifytlscert() performs basic verification as described by
      OpenSSL VERIFY(1), i.e. verification of the certificate chain.  */
index 22a8375..de78fed 100644 (file)
--- a/lib/udp.c
+++ b/lib/udp.c
@@ -16,6 +16,7 @@
 #include "event.h"
 #include "compat.h"
 #include "udp.h"
+#include "conn.h"
 
 /* Send one packet, the first in queue.  */
 static int
@@ -55,16 +56,16 @@ _send (struct rs_connection *conn, int fd)
   return RSE_OK;
 }
 
-/* Callback for conn->wev and conn->rev.  FIXME: Rename.
+/** Callback for conn->wev and conn->rev.  FIXME: Rename.
 
-   USER_DATA contains connection for EV_READ and a message for
-   EV_WRITE.  This is because we don't have a connect/establish entry
-   point at the user level -- send implies connect so when we're
-   connected we need the message to send.  */
+    \a user_data holds a message. */
 static void
 _evcb (evutil_socket_t fd, short what, void *user_data)
 {
   int err;
+  struct rs_message *msg = (struct rs_message *) user_data;
+  assert (msg);
+  assert (msg->conn);
 
   rs_debug (("%s: fd=%d what =", __func__, fd));
   if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT"));
@@ -74,13 +75,11 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
 
   if (what & EV_READ)
     {
-      /* Read a single UDP packet and stick it in USER_DATA.  */
+      /* Read a single UDP packet and stick it in the struct
+         rs_message passed in user_data. */
       /* TODO: Verify that unsolicited packets are dropped.  */
-      struct rs_message *msg = (struct rs_message *) user_data;
       ssize_t r = 0;
-
-      assert (msg);
-      assert (msg->conn);
+      assert (msg->rpkt);
       assert (msg->rpkt->data);
 
       r = compat_recv (fd, msg->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC);
@@ -96,9 +95,9 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
            }
 
          /* Hard error.  */
-         rs_err_conn_push_fl (msg->conn, RSE_SOCKERR, __FILE__, __LINE__,
-                              "%d: recv: %d (%s)", fd, sockerr,
-                              evutil_socket_error_to_string (sockerr));
+         rs_err_conn_push (msg->conn, RSE_SOCKERR,
+                            "%d: recv: %d (%s)", fd, sockerr,
+                            evutil_socket_error_to_string (sockerr));
          event_del (msg->conn->tev);
          return;
        }
@@ -125,12 +124,18 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
     }
   else if (what & EV_WRITE)
     {
-      struct rs_message *msg = (struct rs_message *) user_data;
-      assert (msg);
-      assert (msg->conn);
-
-      if (msg->conn->state == RS_CONN_STATE_CONNECTING)
-       event_on_connect (msg->conn, msg);
+      if (conn_originating_p (msg->conn))
+        {
+          /* We're a client. */
+          if (msg->conn->state == RS_CONN_STATE_CONNECTING)
+            event_on_connect_orig (msg->conn, msg);
+        }
+      else
+        {
+          /* We're a server. */
+          rs_debug (("%s: write event on terminating conn %p\n",
+                     __func__, msg->conn));
+        }
 
       if (msg->conn->out_queue)
        if (_send (msg->conn, fd) == RSE_OK)
@@ -149,10 +154,12 @@ udp_init (struct rs_connection *conn, struct rs_message *msg)
 {
   assert (!conn->base_.bev);
 
+  /* FIXME: Explain why we set EV_PERSIST on the read event but not on
+     the write event. */
   conn->base_.rev = event_new (conn->base_.ctx->evb, conn->base_.fd,
-                             EV_READ|EV_PERSIST, _evcb, NULL);
+                               EV_READ|EV_PERSIST, _evcb, NULL);
   conn->base_.wev = event_new (conn->base_.ctx->evb, conn->base_.fd,
-                             EV_WRITE, _evcb, NULL);
+                               EV_WRITE, _evcb, NULL);
   if (!conn->base_.rev || !conn->base_.wev)
     {
       if (conn->base_.rev)