UDP w/o bufferevents, part 1.
authorLinus Nordberg <linus@nordu.net>
Sun, 6 Mar 2011 13:46:57 +0000 (14:46 +0100)
committerLinus Nordberg <linus@nordu.net>
Sun, 6 Mar 2011 13:46:57 +0000 (14:46 +0100)
Sending, no retransmitting and no receiving.

lib/Makefile.am
lib/compat.c
lib/conn.c
lib/examples/client-udp.conf
lib/include/radsec/radsec-impl.h
lib/packet.c
lib/radsec.c

index 311d3cc..69c1c27 100644 (file)
@@ -10,6 +10,7 @@ lib_LTLIBRARIES = libradsec.la
 
 libradsec_la_SOURCES = \
        attr.c \
+       compat.c \
        conf.c \
        conn.c \
        debug.c \
@@ -32,4 +33,4 @@ libradsec_la_SOURCES += \
 endif
 
 libradsec_la_LDFLAGS = -version-info 0:0:0
-libradsec_la_CFLAGS = $(AM_CFLAGS) #-DDEBUG -DDEBUG_LEVENT -Werror
+libradsec_la_CFLAGS = $(AM_CFLAGS) -DDEBUG -DDEBUG_LEVENT -Werror # -ansi
index 731c071..b00bea1 100644 (file)
@@ -1,8 +1,9 @@
 #include <sys/types.h>
 #include <sys/socket.h>
+#include "compat.h"
 
 ssize_t
 compat_send (int sockfd, const void *buf, size_t len, int flags)
 {
-  compat_send (int sockfd, const void *buf, size_t len, int flags);
+  return send (sockfd, buf, len, flags);
 }
index 904596b..48d2fe5 100644 (file)
@@ -8,6 +8,7 @@
 #include <assert.h>
 #include <debug.h>
 #include <event2/event.h>
+#include <event2/bufferevent.h>
 #include <radsec/radsec.h>
 #include <radsec/radsec-impl.h>
 
@@ -126,25 +127,22 @@ rs_conn_destroy (struct rs_connection *conn)
 
   assert (conn);
 
-  if (conn->is_connected)
-    {
-      err = rs_conn_disconnect (conn);
-      if (err)
-       return err;
-    }
-
   /* NOTE: conn->realm is owned by context.  */
   /* NOTE: conn->peers is owned by context.  */
 
+  if (conn->is_connected)
+    err = rs_conn_disconnect (conn);
   if (conn->tev)
     event_free (conn->tev);
+  if (conn->bev)
+    bufferevent_free (conn->bev);
   if (conn->evb)
     event_base_free (conn->evb);
 
   /* TODO: free tls_ctx  */
   /* TODO: free tls_ssl  */
 
-  return 0;
+  return err;
 }
 
 int
index a83fb26..8578e8b 100644 (file)
@@ -1,10 +1,10 @@
 config blocking-udp {
     type = "UDP"
+    timeout = 2
+    retries = 2
     server {
-        hostname = "localhost"
+        hostname = "127.0.0.1"
        service = "1820"
        secret = "sikrit"
-       timeout = 1         /* optional */
-       tries = 10            /* optional */
     }
 }
index 963c821..f8904ac 100644 (file)
@@ -69,7 +69,6 @@ struct rs_connection {
     struct rs_context *ctx;
     struct rs_realm *realm;    /* Owned by ctx.  */
     struct event_base *evb;    /* Event base.  */
-    struct bufferevent *bev;   /* Buffer event.  */
     struct event *tev;         /* Timeout event.  */
     struct rs_credentials transport_credentials;
     struct rs_conn_callbacks callbacks;
@@ -80,10 +79,17 @@ struct rs_connection {
     char is_connecting;                /* FIXME: replace with a single state member */
     char is_connected;         /* FIXME: replace with a single state member */
     int fd;                    /* Socket.  */
-    int tryagain;
-    int nextid;
+    int tryagain;              /* For server failover.  */
+    int nextid;                        /* Next RADIUS packet identifier.  */
     int user_dispatch_flag : 1;        /* User does the dispatching.  */
+    /* TCP transport specifics.  */
+    struct bufferevent *bev;   /* Buffer event.  */
+    /* UDP transport specifics.  */
+    struct event *wev;         /* Write event (for UDP).  */
+    struct event *rev;         /* Read event (for UDP).  */
+    struct rs_packet *out_queue; /* Queue for outgoing UDP packets.  */
 #if defined(RS_ENABLE_TLS)
+    /* TLS specifics.  */
     SSL_CTX *tls_ctx;
     SSL *tls_ssl;
 #endif
@@ -97,6 +103,7 @@ struct rs_packet {
     struct rs_packet *original;
     char valid_flag;
     char written_flag;
+    struct rs_packet *next;    /* Used for UDP output queue.  */
 };
 
 struct rs_attr {
index 9874dee..afe2725 100644 (file)
@@ -8,17 +8,19 @@
 #include <string.h>
 #include <errno.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #include <assert.h>
 #include <freeradius/libradius.h>
 #include <event2/event.h>
 #include <event2/bufferevent.h>
 #include <radsec/radsec.h>
 #include <radsec/radsec-impl.h>
-#include "tls.h"
+#include "compat.h"
 #include "debug.h"
 #if defined (RS_ENABLE_TLS)
 #include <event2/bufferevent_ssl.h>
 #include <openssl/err.h>
+#include "tls.h"
 #endif
 #if defined (DEBUG)
 #include <netdb.h>
 #endif
 
 static int
+_close_conn (struct rs_connection **connp)
+{
+  int r;
+  assert (connp);
+  assert (*connp);
+  r = rs_conn_destroy (*connp);
+  if (!r)
+    *connp = NULL;
+  return r;
+}
+
+static int
 _loopbreak (struct rs_connection *conn)
 {
   int err = event_base_loopbreak (conn->evb);
@@ -37,11 +51,12 @@ _loopbreak (struct rs_connection *conn)
   return err;
 }
 
+/* Badly named helper function for preparing a RADIUS message and
+   queue it.  FIXME: Rename.  */
 static int
 _do_send (struct rs_packet *pkt)
 {
-  int err;
-  VALUE_PAIR *vp;
+  VALUE_PAIR *vp = NULL;
 
   assert (pkt->rpkt);
   assert (!pkt->original);
@@ -73,23 +88,39 @@ _do_send (struct rs_packet *pkt)
   }
 #endif
 
-  err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data,
-                          pkt->rpkt->data_len);
-  if (err < 0)
-    return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
-                               "bufferevent_write: %s",
-                               evutil_gai_strerror (err));
+  if (pkt->conn->bev)
+    {
+      int err = bufferevent_write (pkt->conn->bev, pkt->rpkt->data,
+                                  pkt->rpkt->data_len);
+      if (err < 0)
+       return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
+                                   "bufferevent_write: %s",
+                                   evutil_gai_strerror (err));
+    }
+  else
+    {
+      struct rs_packet **pp = &pkt->conn->out_queue;
+
+      while (*pp && (*pp)->next)
+       *pp = (*pp)->next;
+      *pp = pkt;
+    }
+
   return RSE_OK;
 }
 
 static void
-_on_connect (struct rs_connection *conn)
+_on_connect (struct rs_connection *conn, struct rs_packet *pkt)
 {
+  assert (!conn->is_connecting);
   conn->is_connected = 1;
   rs_debug (("%s: %p connected\n", __func__, conn->active_peer));
-  evtimer_del (conn->tev);
+  if (conn->tev)
+    evtimer_del (conn->tev);
   if (conn->callbacks.connected_cb)
     conn->callbacks.connected_cb (conn->user_data);
+  if (pkt)
+    _do_send (pkt);
 }
 
 static void
@@ -103,9 +134,9 @@ _on_disconnect (struct rs_connection *conn)
 }
 
 static void
-_event_cb (struct bufferevent *bev, short events, void *ctx)
+_event_cb (struct bufferevent *bev, short events, void *user_data)
 {
-  struct rs_packet *pkt = (struct rs_packet *)ctx;
+  struct rs_packet *pkt = (struct rs_packet *) user_data;
   struct rs_connection *conn = NULL;
   struct rs_peer *p = NULL;
   int sockerr = 0;
@@ -122,9 +153,7 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
   conn->is_connecting = 0;
   if (events & BEV_EVENT_CONNECTED)
     {
-      _on_connect (conn);
-      if (_do_send (pkt))
-       rs_debug (("%s: error sending\n", __func__));
+      _on_connect (conn, pkt);
     }
   else if (events & BEV_EVENT_EOF)
     {
@@ -145,15 +174,11 @@ _event_cb (struct bufferevent *bev, short events, void *ctx)
        }
       else
        {
+         rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr,
+                    evutil_socket_error_to_string (sockerr)));
          rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
-                              "%d: socket error %d (%s)",
-                              conn->fd,
-                              sockerr,
+                              "%d: %d (%s)", conn->fd, sockerr,
                               evutil_socket_error_to_string (sockerr));
-         rs_debug (("%s: socket error on fd %d: %s (%d)\n", __func__,
-                    conn->fd,
-                    evutil_socket_error_to_string (sockerr),
-                    sockerr));
        }
 #if defined (RS_ENABLE_TLS)
       if (conn->tls_ssl)       /* FIXME: correct check?  */
@@ -204,7 +229,7 @@ _read_header (struct rs_packet *pkt)
       pkt->rpkt->data_len = (pkt->hdr[2] << 8) + pkt->hdr[3];
       if (pkt->rpkt->data_len < 20 || pkt->rpkt->data_len > 4096)
        {
-         bufferevent_free (pkt->conn->bev); /* Close connection.  */
+         _close_conn (&pkt->conn);
          return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
                                   "invalid packet length: %d",
                                   pkt->rpkt->data_len);
@@ -212,7 +237,7 @@ _read_header (struct rs_packet *pkt)
       pkt->rpkt->data = rs_malloc (pkt->conn->ctx, pkt->rpkt->data_len);
       if (!pkt->rpkt->data)
        {
-         bufferevent_free (pkt->conn->bev); /* Close connection.  */
+         _close_conn (&pkt->conn);
          return rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__,
                                      NULL);
        }
@@ -228,7 +253,7 @@ _read_header (struct rs_packet *pkt)
     }
   else     /* Error: libevent gave us less than the low watermark. */
     {
-      bufferevent_free (pkt->conn->bev); /* Close connection.  */
+      _close_conn (&pkt->conn);
       return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__,
                                  "got %d octets reading header", n);
     }
@@ -264,7 +289,7 @@ _read_packet (struct rs_packet *pkt)
         - attribute sizes adding up correctly  */
       if (!rad_packet_ok (pkt->rpkt, 0) != 0)
        {
-         bufferevent_free (pkt->conn->bev); /* Close connection.  */
+         _close_conn (&pkt->conn);
          return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
                                      "invalid packet: %s", fr_strerror ());
        }
@@ -280,7 +305,7 @@ _read_packet (struct rs_packet *pkt)
          if (rad_verify (pkt->rpkt, pkt->original->rpkt,
                          pkt->conn->active_peer->secret))
            {
-             bufferevent_free (pkt->conn->bev); /* Close connection.  */
+             _close_conn (&pkt->conn);
              return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
                                          "rad_verify: %s", fr_strerror ());
            }
@@ -289,7 +314,7 @@ _read_packet (struct rs_packet *pkt)
          if (rad_decode (pkt->rpkt, pkt->original->rpkt,
                          pkt->conn->active_peer->secret))
            {
-             bufferevent_free (pkt->conn->bev); /* Close connection.  */
+             _close_conn (&pkt->conn);
              return rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
                                          "rad_decode: %s", fr_strerror ());
            }
@@ -376,7 +401,7 @@ _evlog_cb (int severity, const char *msg)
 }
 
 static int
-_init_evb (struct rs_connection *conn)
+_init_eventbase (struct rs_connection *conn)
 {
   if (conn->evb)
     return RSE_OK;
@@ -465,27 +490,21 @@ _set_timeout (struct rs_connection *conn)
 }
 
 static int
-_init_bev (struct rs_connection *conn, struct rs_peer *peer)
+_init_bufferevent (struct rs_connection *conn, struct rs_peer *peer)
 {
   if (conn->bev)
     return RSE_OK;
 
-  switch (conn->realm->type)
+  if (conn->realm->type == RS_CONN_TYPE_TCP)
     {
-    case RS_CONN_TYPE_UDP:
-      /* Fall through.  */
-      /* NOTE: We know this is wrong for several reasons, most notably
-        because libevent doesn't work as expected with UDP.  The
-        timeout handling is wrong too.  */
-    case RS_CONN_TYPE_TCP:
       conn->bev = bufferevent_socket_new (conn->evb, conn->fd, 0);
       if (!conn->bev)
        return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
                                    "bufferevent_socket_new");
-      break;
-
+    }
 #if defined (RS_ENABLE_TLS)
-    case RS_CONN_TYPE_TLS:
+  else if (conn->realm->type == RS_CONN_TYPE_TLS)
+    {
       if (rs_tls_init (conn))
        return -1;
       /* Would be convenient to pass BEV_OPT_CLOSE_ON_FREE but things
@@ -497,13 +516,10 @@ _init_bev (struct rs_connection *conn, struct rs_peer *peer)
       if (!conn->bev)
        return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
                                    "bufferevent_openssl_socket_new");
-      break;
-
-    case RS_CONN_TYPE_DTLS:
-      return rs_err_conn_push_fl (conn, RSE_NOSYS, __FILE__, __LINE__, NULL);
+    }
 #endif /* RS_ENABLE_TLS */
-
-    default:
+  else
+    {
       return rs_err_conn_push_fl (conn, RSE_INTERNAL, __FILE__, __LINE__,
                                  "%s: unknown connection type: %d", __func__,
                                  conn->realm->type);
@@ -512,11 +528,81 @@ _init_bev (struct rs_connection *conn, struct rs_peer *peer)
   return RSE_OK;
 }
 
+/* Callback for conn->wev and conn->rev.  FIXME: Rename.  */
+static void
+_evcb (evutil_socket_t fd, short what, void *user_data)
+{
+  //rs_debug (("%s: fd=%d what=0x%x\n", __func__, fd, what));
+  if (what & EV_TIMEOUT)
+    {
+      struct rs_connection *conn = (struct rs_connection *) user_data;
+      assert (conn);
+      conn->is_connecting = 0;
+      rs_debug (("%s: UDP timeout NYI", __func__));
+    }
+  else if (what & EV_READ)
+    {
+      struct rs_connection *conn = (struct rs_connection *) user_data;
+      assert (conn);
+      /* read a single UDP packet and stick it in a new struct
+        rs_packet */
+
+      rs_debug (("%s: UDP read NYI", __func__));
+    }
+  else if (what & EV_WRITE)
+    {
+      struct rs_packet *pkt = (struct rs_packet *) user_data;
+      assert (pkt);
+      /* Socket ready for writing, possibly as a result of a
+        successful connect.  */
+      if (!pkt->conn->is_connected)
+       _on_connect (pkt->conn, pkt);
+      if (pkt->conn->out_queue)
+       {
+         /* Send one packet, the first.  */
+         ssize_t r = 0;
+         struct rs_packet *p = pkt->conn->out_queue;
+
+         assert (p->rpkt);
+         assert (p->rpkt->data);
+         r = compat_send (fd, p->rpkt->data, p->rpkt->data_len, 0);
+         if (r == -1)
+           {
+             int sockerr = evutil_socket_geterror (p->conn->fd);
+             if (sockerr != EAGAIN)
+               rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__,
+                                    "%d: send: %d (%s)", fd, sockerr,
+                                    evutil_socket_error_to_string (sockerr));
+             return;           /* Don't unlink packet. */
+           }
+         pkt->conn->out_queue = p->next;
+       }
+    }
+}
+
+static int
+_init_udp (struct rs_connection *conn, struct rs_packet *pkt)
+{
+  assert (!conn->bev);
+
+  conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn);
+  conn->wev = event_new (conn->evb, conn->fd, EV_WRITE|EV_PERSIST, _evcb, pkt);
+  if (!conn->rev || !conn->wev)
+    {
+      if (conn->rev)
+       event_free (conn->rev);
+      /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at
+        least for now (libevent-2.0.5).  */
+      return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
+    }
+  return RSE_OK;
+}
+
 static void
 _do_connect (struct rs_connection *conn)
 {
   struct rs_peer *p;
-  int err;
+  int err, sockerr;
 
   assert (conn);
   assert (conn->active_peer);
@@ -534,21 +620,37 @@ _do_connect (struct rs_connection *conn)
   }
 #endif
 
-  _set_timeout (conn);
-  err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr,
-                                   p->addr->ai_addrlen);
-  if (err < 0)
-    rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
-                        "bufferevent_socket_connect: %s",
-                        evutil_gai_strerror (err));
-  else
-    p->conn->is_connecting = 1;
+  if (p->conn->bev)            /* TCP */
+    {
+      _set_timeout (conn);
+      err = bufferevent_socket_connect (p->conn->bev, p->addr->ai_addr,
+                                       p->addr->ai_addrlen);
+      if (err < 0)
+       rs_err_conn_push_fl (p->conn, RSE_EVENT, __FILE__, __LINE__,
+                            "bufferevent_socket_connect: %s",
+                            evutil_gai_strerror (err));
+      else
+       p->conn->is_connecting = 1;
+    }
+  else                         /* UDP */
+    {
+      err = connect (p->conn->fd, p->addr->ai_addr, p->addr->ai_addrlen);
+      if (err < 0)
+       {
+         sockerr = evutil_socket_geterror (p->conn->fd);
+         rs_debug (("%s: %d: connect: %d (%s)\n", __func__, p->conn->fd,
+                    sockerr, evutil_socket_error_to_string (sockerr)));
+         rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__,
+                              "%d: connect: %d (%s)", p->conn->fd, sockerr,
+                              evutil_socket_error_to_string (sockerr));
+       }
+    }
 }
 
 static int
-_conn_open(struct rs_connection *conn, struct rs_packet *pkt)
+_conn_open (struct rs_connection *conn, struct rs_packet *pkt)
 {
-  if (_init_evb (conn))
+  if (_init_eventbase (conn))
     return -1;
 
   if (!conn->active_peer)
@@ -559,8 +661,17 @@ _conn_open(struct rs_connection *conn, struct rs_packet *pkt)
   if (_init_socket (conn, conn->active_peer))
     return -1;
 
-  if (_init_bev (conn, conn->active_peer))
-    return -1;
+  if (conn->realm->type == RS_CONN_TYPE_TCP
+      || conn->realm->type == RS_CONN_TYPE_TLS)
+    {
+      if (_init_bufferevent (conn, conn->active_peer))
+       return -1;
+    }
+  else
+    {
+      if (_init_udp (conn, pkt))
+       return -1;
+    }
 
   if (!conn->is_connected)
     if (!conn->is_connecting)
@@ -640,7 +751,10 @@ _wcb (void *user_data)
   struct rs_packet *pkt = (struct rs_packet *) user_data;
   assert (pkt);
   pkt->written_flag = 1;
-  bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+  if (pkt->conn->bev)
+    bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+  else
+    event_del (pkt->conn->wev);
 }
 
 int
@@ -660,13 +774,24 @@ rs_packet_send (struct rs_packet *pkt, void *user_data)
       return -1;
 
   assert (conn->evb);
-  assert (conn->bev);
   assert (conn->active_peer);
   assert (conn->fd >= 0);
 
   conn->user_data = user_data;
-  bufferevent_setcb (conn->bev, NULL, _write_cb, _event_cb, pkt);
-  bufferevent_enable (conn->bev, EV_WRITE);
+
+  if (conn->bev)               /* TCP */
+    {
+      bufferevent_setcb (conn->bev, NULL, _write_cb, _event_cb, pkt);
+      bufferevent_enable (conn->bev, EV_WRITE);
+    }
+  else                 /* UDP */
+    {
+      err = event_add (conn->wev, NULL);
+      if (err < 0)
+       return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
+                                   "event_add: %s",
+                                   evutil_gai_strerror (err));
+    }
 
   /* Do dispatch, unless the user wants to do it herself.  */
   if (!conn->user_dispatch_flag)
@@ -696,7 +821,10 @@ _rcb (struct rs_packet *packet, void *user_data)
   struct rs_packet *pkt = (struct rs_packet *) user_data;
   assert (pkt);
   pkt->valid_flag = 1;
-  bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+  if (pkt->conn->bev)
+    bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
+  else
+    event_del (pkt->conn->rev);
 }
 
 /* Special function used in libradsec blocking dispatching mode,
@@ -737,11 +865,22 @@ rs_conn_receive_packet (struct rs_connection *conn,
   assert (conn->active_peer);
   assert (conn->fd >= 0);
 
-  bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0);
-  bufferevent_setcb (conn->bev, _read_cb, NULL, _event_cb, pkt);
-  bufferevent_enable (conn->bev, EV_READ);
   conn->callbacks.received_cb = _rcb;
   conn->user_data = pkt;
+  if (conn->bev)
+    {
+      bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0);
+      bufferevent_setcb (conn->bev, _read_cb, NULL, _event_cb, pkt);
+      bufferevent_enable (conn->bev, EV_READ);
+    }
+  else
+    {
+      err = event_add (conn->rev, NULL);
+      if (err < 0)
+       return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
+                                   "event_add: %s",
+                                   evutil_gai_strerror (err));
+    }
 
   /* Dispatch.  */
   rs_debug (("%s: entering event loop\n", __func__));
index afb871e..f191e73 100644 (file)
@@ -125,7 +125,8 @@ _rs_peer_destroy (struct rs_peer *p)
   rs_free (p->conn->ctx, p);
 }
 
-void rs_context_destroy(struct rs_context *ctx)
+void
+rs_context_destroy (struct rs_context *ctx)
 {
   struct rs_realm *r = NULL;
   struct rs_peer *p = NULL;