WIP commit moving towards working server support.
[libradsec.git] / lib / send.c
1 /* Copyright 2011,2013 NORDUnet A/S. All rights reserved.
2    See LICENSE for licensing information.  */
3
4 #if defined HAVE_CONFIG_H
5 #include <config.h>
6 #endif
7
8 #include <assert.h>
9 #include <event2/event.h>
10 #include <event2/bufferevent.h>
11 #include <radsec/radsec.h>
12 #include <radsec/radsec-impl.h>
13 #include "debug.h"
14 #include "message.h"
15 #include "event.h"
16 #include "peer.h"
17 #include "conn.h"
18 #include "tcp.h"
19 #include "udp.h"
20
21 static int
22 _conn_open (struct rs_connection *conn, struct rs_message *msg)
23 {
24   if (event_init_eventbase (TO_BASE_CONN (conn)))
25     return -1;
26
27   if (!conn->active_peer)
28     peer_pick_peer (conn);
29   if (!conn->active_peer)
30     return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
31
32   if (event_init_socket (&conn->base_, conn->active_peer))
33     return -1;
34
35   if (conn->base_.realm->type == RS_CONN_TYPE_TCP
36       || conn->base_.realm->type == RS_CONN_TYPE_TLS)
37     {
38       if (tcp_init_connect_timer (conn))
39         return -1;
40       if (event_init_bufferevent (conn))
41         return -1;
42     }
43   else
44     {
45       if (udp_init (conn, msg))
46         return -1;
47       if (udp_init_retransmit_timer (conn))
48         return -1;
49     }
50
51   if (conn->state != RS_CONN_STATE_CONNECTED
52       && conn->state != RS_CONN_STATE_CONNECTING)
53     event_do_connect (conn);
54
55   return RSE_OK;
56 }
57
58 static int
59 _conn_is_open_p (struct rs_connection *conn)
60 {
61   return conn->state == RS_CONN_STATE_CONNECTED
62     && conn->active_peer != NULL;
63 }
64
65 /* User callback used when we're dispatching for user.  */
66 static void
67 _wcb (void *user_data)
68 {
69   struct rs_message *msg = (struct rs_message *) user_data;
70   assert (msg);
71   msg->flags |= RS_MESSAGE_SENT;
72   if (msg->conn->base_.bev)
73     bufferevent_disable (msg->conn->base_.bev, EV_WRITE|EV_READ);
74   else
75     event_del (msg->conn->base_.wev);
76 }
77
78 int
79 rs_message_send (struct rs_message *msg)
80 {
81   struct rs_connection *conn = NULL;
82   int err = 0;
83
84   assert (msg);
85   assert (msg->conn);
86   conn = msg->conn;
87
88   if (_conn_is_open_p (conn))
89     message_do_send (msg);
90   else
91     if (_conn_open (conn, msg))
92       return -1;
93
94   assert (conn->base_.ctx);
95   assert (conn->base_.ctx->evb);
96   assert (conn->active_peer);
97   assert (conn->base_.fd >= 0);
98
99   if (conn->base_.bev)          /* TCP */
100     {
101       bufferevent_setcb (conn->base_.bev, NULL, tcp_write_cb, tcp_event_cb,
102                          msg);
103       bufferevent_enable (conn->base_.bev, EV_WRITE);
104     }
105   else                          /* UDP */
106     {
107       event_assign (conn->base_.wev, conn->base_.ctx->evb,
108                     event_get_fd (conn->base_.wev),
109                     EV_WRITE, event_get_callback (conn->base_.wev), msg);
110       err = event_add (conn->base_.wev, NULL);
111       if (err < 0)
112         return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
113                                     "event_add: %s",
114                                     evutil_gai_strerror (err));
115     }
116
117   /* Do dispatch, unless the user wants to do it herself.  */
118   if (!conn_user_dispatch_p (conn))
119     {
120       /* Blocking mode. */
121       conn->callbacks.sent_cb = _wcb;
122       conn->base_.user_data = msg;
123       rs_debug (("%s: entering event loop\n", __func__));
124       err = event_base_dispatch (conn->base_.ctx->evb);
125       if (err < 0)
126         return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
127                                     "event_base_dispatch: %s",
128                                     evutil_gai_strerror (err));
129       rs_debug (("%s: event loop done\n", __func__));
130       conn->callbacks.sent_cb = NULL;
131       conn->base_.user_data = NULL;
132
133       if ((msg->flags & RS_MESSAGE_SENT) == 0)
134         {
135           assert (rs_err_conn_peek_code (conn));
136           return rs_err_conn_peek_code (conn);
137         }
138     }
139
140   return RSE_OK;
141 }