diff options
-rw-r--r-- | lib/compat.c | 6 | ||||
-rw-r--r-- | lib/compat.h | 1 | ||||
-rw-r--r-- | lib/conn.c | 14 | ||||
-rw-r--r-- | lib/packet.c | 2 | ||||
-rw-r--r-- | lib/udp.c | 114 |
5 files changed, 100 insertions, 37 deletions
diff --git a/lib/compat.c b/lib/compat.c index fa075a1..ccc6388 100644 --- a/lib/compat.c +++ b/lib/compat.c @@ -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); +} diff --git a/lib/compat.h b/lib/compat.h index 50ae22e..125f651 100644 --- a/lib/compat.h +++ b/lib/compat.h @@ -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); @@ -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); diff --git a/lib/packet.c b/lib/packet.c index 7b5ae2d..48fb55e 100644 --- a/lib/packet.c +++ b/lib/packet.c @@ -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; @@ -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) { |