summaryrefslogtreecommitdiff
path: root/lib/udp.c
diff options
context:
space:
mode:
authorLinus Nordberg <linus@nordu.net>2011-03-09 23:09:26 +0100
committerLinus Nordberg <linus@nordu.net>2011-03-09 23:09:26 +0100
commit0a7d803b9aa40512cf0f0c574d397ccba3ff1d13 (patch)
treec0f375e500c48e0b9aa983e02e8931a42dcd4d2c /lib/udp.c
parent11cf984f611e835c394deede450af9fd69434e30 (diff)
Get UDP working.
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().
Diffstat (limited to 'lib/udp.c')
-rw-r--r--lib/udp.c114
1 files changed, 84 insertions, 30 deletions
diff --git a/lib/udp.c b/lib/udp.c
index ac4e487..19968b3 100644
--- 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)
{