Get UDP working.
authorLinus Nordberg <linus@nordu.net>
Wed, 9 Mar 2011 22:09:26 +0000 (23:09 +0100)
committerLinus Nordberg <linus@nordu.net>
Wed, 9 Mar 2011 22:09:26 +0000 (23:09 +0100)
For UDP, activate retransmit timer before receiving rather than
sending makes the event loop break nicely after sending a message
(which is important for blocking mode).  Not quite sure that this is
really accurate wrt to retransmission timing though but it should do
for now.

For UDP, set the user_data member for the read callback in
rs_conn_receive_packet -- the one from udp_init() doesn't do much good
now.

For UDP, implement receiving message.

Add compat_recv().

lib/compat.c
lib/compat.h
lib/conn.c
lib/packet.c
lib/udp.c

index fa075a1..ccc6388 100644 (file)
@@ -14,3 +14,9 @@ compat_send (int sockfd, const void *buf, size_t len, int flags)
 {
   return send (sockfd, buf, len, flags);
 }
+
+ssize_t
+compat_recv (int sockfd, void *buf, size_t len, int flags)
+{
+  return recv (sockfd, buf, len, flags);
+}
index 50ae22e..125f651 100644 (file)
@@ -2,3 +2,4 @@
    See the file COPYING for licensing information.  */
 
 ssize_t compat_send (int sockfd, const void *buf, size_t len, int flags);
+ssize_t compat_recv (int sockfd, void *buf, size_t len, int flags);
index 8d369ab..5382888 100644 (file)
@@ -228,29 +228,33 @@ rs_conn_receive_packet (struct rs_connection *conn,
     return -1;
 
   assert (conn->evb);
-  assert (conn->bev);
-  assert (conn->active_peer);
   assert (conn->fd >= 0);
 
   conn->callbacks.received_cb = _rcb;
   conn->user_data = pkt;
   pkt->flags &= ~rs_packet_received_flag;
 
-  if (conn->bev)
+  if (conn->bev)               /* TCP.  */
     {
       bufferevent_setwatermark (conn->bev, EV_READ, RS_HEADER_LEN, 0);
       bufferevent_setcb (conn->bev, tcp_read_cb, NULL, tcp_event_cb, pkt);
       bufferevent_enable (conn->bev, EV_READ);
     }
-  else
+  else                         /* UDP.  */
     {
+      /* Put fresh packet in user_data for the callback and enable the
+        read event.  */
+      event_assign (conn->rev, conn->evb, event_get_fd (conn->rev),
+                   EV_READ, event_get_callback (conn->rev), pkt);
       err = event_add (conn->rev, NULL);
       if (err < 0)
        return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
                                    "event_add: %s",
                                    evutil_gai_strerror (err));
-    }
 
+      /* Activae retransmission timer.  */
+      conn_activate_timeout (pkt->conn);
+    }
 
   rs_debug (("%s: entering event loop\n", __func__));
   err = event_base_dispatch (conn->evb);
index 7b5ae2d..48fb55e 100644 (file)
@@ -111,8 +111,6 @@ packet_do_send (struct rs_packet *pkt)
       while (*pp && (*pp)->next)
        *pp = (*pp)->next;
       *pp = pkt;
-
-      conn_activate_timeout (pkt->conn); /* Retransmission timer.  */
     }
 
   return RSE_OK;
index ac4e487..19968b3 100644 (file)
--- a/lib/udp.c
+++ b/lib/udp.c
@@ -6,6 +6,8 @@
 #endif
 
 #include <assert.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <event2/event.h>
 #include <radsec/radsec.h>
 #include <radsec/radsec-impl.h>
@@ -15,7 +17,7 @@
 #include "udp.h"
 
 /* Send one packet, the first in queue.  */
-static void
+static int
 _send (struct rs_connection *conn, int fd)
 {
   ssize_t r = 0;
@@ -30,12 +32,12 @@ _send (struct rs_connection *conn, int fd)
     {
       int sockerr = evutil_socket_geterror (pkt->conn->fd);
       if (sockerr != EAGAIN)
-       rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
-                            "%d: send: %d (%s)", fd, sockerr,
-                            evutil_socket_error_to_string (sockerr));
-      return;          /* Don't unlink packet. */
+       return rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
+                                   "%d: send: %d (%s)", fd, sockerr,
+                                   evutil_socket_error_to_string (sockerr));
     }
 
+  assert (r == pkt->rpkt->data_len);
   /* Unlink the packet.  */
   conn->out_queue = pkt->next;
 
@@ -44,38 +46,83 @@ _send (struct rs_connection *conn, int fd)
     {
       r = event_add (pkt->conn->wev, NULL);
       if (r < 0)
-       {
-         rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
-                              "event_add: %s", evutil_gai_strerror (r));
-         return;
-       }
+       return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
+                                   "event_add: %s", evutil_gai_strerror (r));
+      rs_debug (("%s: re-adding the write event\n", __func__));
     }
+
+  return RSE_OK;
 }
 
-/* Callback for conn->wev and conn->rev.  FIXME: Rename.  */
+/* Callback for conn->wev and conn->rev.  FIXME: Rename.
+
+   USER_DATA contains connection for EV_READ and a packet for
+   EV_WRITE.  This is because we don't have a connect/establish entry
+   point at the user level -- send implies connect so when we're
+   connected we need the packet to send.  */
 static void
 _evcb (evutil_socket_t fd, short what, void *user_data)
 {
-  //rs_debug (("%s: fd=%d what=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)
+  rs_debug (("%s: fd=%d what =", __func__, fd));
+  if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT"));
+  if (what & EV_READ) rs_debug ((" READ"));
+  if (what & EV_WRITE) rs_debug ((" WRITE"));
+  rs_debug (("\n"));
+
+  if (what & EV_READ)
     {
-      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 */
+      /* Read a single UDP packet and stick it in USER_DATA.  */
+      /* TODO: Verify that unsolicited packets are dropped.  */
+      struct rs_packet *pkt = (struct rs_packet *) user_data;
+      ssize_t r = 0;
 
-      /* TODO: Verify that reception of an unsolicited response packet
-        results in connection being closed.  */
-      rs_debug (("%s: UDP read NYI", __func__));
+      assert (pkt);
+      assert (pkt->conn);
 
-      /* TODO: delete retransmit timer */
+      pkt->rpkt->data = rs_malloc (pkt->conn->ctx, 4096);
+      if (pkt->rpkt->data == NULL)
+       {
+         rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
+         return;
+       }
+      r = compat_recv (fd, pkt->rpkt->data, 4096, MSG_TRUNC);
+      if (r == -1)
+       {
+         int sockerr = evutil_socket_geterror (pkt->conn->fd);
+         if (sockerr == EAGAIN)
+           {
+             /* FIXME: Really shouldn't happen since we've been told
+                that fd is readable!  */
+             rs_debug (("%s: EAGAIN reading UDP packet -- wot?"));
+             return;
+           }
+
+         /* Hard error.  */
+         rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
+                              "%d: recv: %d (%s)", fd, sockerr,
+                              evutil_socket_error_to_string (sockerr));
+         event_del (pkt->conn->tev);
+         return;
+       }
+      event_del (pkt->conn->tev);
+      if (r < 20 || r > 4096)  /* Short or long packet.  */
+       {
+         rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
+                           "invalid packet length: %d",
+                           pkt->rpkt->data_len);
+         return;
+       }
+      pkt->rpkt->data_len = (pkt->rpkt->data[2] << 8) + pkt->rpkt->data[3];
+      if (!rad_packet_ok (pkt->rpkt, 0))
+       {
+         rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__,
+                              "invalid packet: %s", fr_strerror ());
+         return;
+       }
+      /* Hand over message to user.  This changes ownership of pkt.
+        Don't touch it afterwards -- it might have been freed.  */
+      if (pkt->conn->callbacks.received_cb)
+       pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data);
     }
   else if (what & EV_WRITE)
     {
@@ -86,8 +133,15 @@ _evcb (evutil_socket_t fd, short what, void *user_data)
        event_on_connect (pkt->conn, pkt);
 
       if (pkt->conn->out_queue)
-       _send (pkt->conn, fd);
+       if (_send (pkt->conn, fd) == RSE_OK)
+         if (pkt->conn->callbacks.sent_cb)
+           pkt->conn->callbacks.sent_cb (pkt->conn->user_data);
     }
+
+#if defined (DEBUG)
+  if (what & EV_TIMEOUT)
+    rs_debug (("%s: timeout on UDP event, shouldn't happen\n", __func__));
+#endif
 }
 
 int
@@ -95,7 +149,7 @@ udp_init (struct rs_connection *conn, struct rs_packet *pkt)
 {
   assert (!conn->bev);
 
-  conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn);
+  conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, NULL);
   conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, pkt);
   if (!conn->rev || !conn->wev)
     {