diff options
Diffstat (limited to 'lib/send.c')
-rw-r--r-- | lib/send.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/lib/send.c b/lib/send.c new file mode 100644 index 0000000..a8ad1d5 --- /dev/null +++ b/lib/send.c @@ -0,0 +1,138 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. + See the file COPYING for licensing information. */ + +#if defined HAVE_CONFIG_H +#include <config.h> +#endif + +#include <assert.h> +#include <event2/event.h> +#include <event2/bufferevent.h> +#include <radsec/radsec.h> +#include <radsec/radsec-impl.h> +#include "debug.h" +#include "packet.h" +#include "event.h" +#include "peer.h" +#include "conn.h" +#include "tcp.h" +#include "udp.h" + +static int +_conn_open (struct rs_connection *conn, struct rs_packet *pkt) +{ + if (event_init_eventbase (conn)) + return -1; + + if (!conn->active_peer) + peer_pick_peer (conn); + if (!conn->active_peer) + return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL); + + if (event_init_socket (conn, conn->active_peer)) + return -1; + + if (conn->realm->type == RS_CONN_TYPE_TCP + || conn->realm->type == RS_CONN_TYPE_TLS) + { + if (tcp_init_connect_timer (conn)) + return -1; + if (event_init_bufferevent (conn, conn->active_peer)) + return -1; + } + else + { + if (udp_init (conn, pkt)) + return -1; + if (udp_init_retransmit_timer (conn)) + return -1; + } + + if (!conn->is_connected) + if (!conn->is_connecting) + event_do_connect (conn); + + return RSE_OK; +} + +static int +_conn_is_open_p (struct rs_connection *conn) +{ + return conn->active_peer && conn->is_connected; +} + +/* User callback used when we're dispatching for user. */ +static void +_wcb (void *user_data) +{ + struct rs_packet *pkt = (struct rs_packet *) user_data; + assert (pkt); + pkt->flags |= rs_packet_sent_flag; + if (pkt->conn->bev) + bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ); + else + event_del (pkt->conn->wev); +} + +int +rs_packet_send (struct rs_packet *pkt, void *user_data) +{ + struct rs_connection *conn = NULL; + int err = 0; + + assert (pkt); + assert (pkt->conn); + conn = pkt->conn; + + if (_conn_is_open_p (conn)) + packet_do_send (pkt); + else + if (_conn_open (conn, pkt)) + return -1; + + assert (conn->evb); + assert (conn->active_peer); + assert (conn->fd >= 0); + + conn->user_data = user_data; + + if (conn->bev) /* TCP */ + { + bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, pkt); + bufferevent_enable (conn->bev, EV_WRITE); + } + else /* UDP */ + { + event_assign (conn->wev, conn->evb, event_get_fd (conn->wev), + EV_WRITE, event_get_callback (conn->wev), pkt); + err = event_add (conn->wev, NULL); + if (err < 0) + return rs_err_conn_push_fl (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_p (conn)) + { + conn->callbacks.sent_cb = _wcb; + conn->user_data = pkt; + rs_debug (("%s: entering event loop\n", __func__)); + err = event_base_dispatch (conn->evb); + if (err < 0) + 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__)); + conn->callbacks.sent_cb = NULL; + conn->user_data = NULL; + + if ((pkt->flags & rs_packet_sent_flag) == 0) + { + assert (rs_err_conn_peek_code (conn)); + return rs_err_conn_peek_code (conn); + } + } + + return RSE_OK; +} |