f169ff903584fd7d88f16d22db0f63fff51b3e5d
[radsecproxy.git] / lib / send.c
1 /* Copyright 2011 NORDUnet A/S. All rights reserved.
2    See the file COPYING 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 "packet.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_packet *pkt)
23 {
24   if (event_init_eventbase (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, conn->active_peer))
33     return -1;
34
35   if (conn->realm->type == RS_CONN_TYPE_TCP
36       || conn->realm->type == RS_CONN_TYPE_TLS)
37     {
38       if (event_init_bufferevent (conn, conn->active_peer))
39         return -1;
40     }
41   else
42     {
43       if (udp_init (conn, pkt))
44         return -1;
45     }
46
47   if (!conn->is_connected)
48     if (!conn->is_connecting)
49       event_do_connect (conn);
50
51   return RSE_OK;
52 }
53
54 static int
55 _conn_is_open_p (struct rs_connection *conn)
56 {
57   return conn->active_peer && conn->is_connected;
58 }
59
60 /* User callback used when we're dispatching for user.  */
61 static void
62 _wcb (void *user_data)
63 {
64   struct rs_packet *pkt = (struct rs_packet *) user_data;
65   assert (pkt);
66   pkt->flags |= rs_packet_sent_flag;
67   if (pkt->conn->bev)
68     bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
69   else
70     event_del (pkt->conn->wev);
71 }
72
73 int
74 rs_packet_send (struct rs_packet *pkt, void *user_data)
75 {
76   struct rs_connection *conn = NULL;
77   int err = 0;
78
79   assert (pkt);
80   assert (pkt->conn);
81   conn = pkt->conn;
82
83   if (_conn_is_open_p (conn))
84     packet_do_send (pkt);
85   else
86     if (_conn_open (conn, pkt))
87       return -1;
88
89   assert (conn->evb);
90   assert (conn->active_peer);
91   assert (conn->fd >= 0);
92
93   conn->user_data = user_data;
94
95   if (conn->bev)                /* TCP */
96     {
97       bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, pkt);
98       bufferevent_enable (conn->bev, EV_WRITE);
99     }
100   else                          /* UDP */
101     {
102       err = event_add (conn->wev, NULL);
103       if (err < 0)
104         return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
105                                     "event_add: %s",
106                                     evutil_gai_strerror (err));
107     }
108
109   /* Do dispatch, unless the user wants to do it herself.  */
110   if (!conn_user_dispatch_p (conn))
111     {
112       conn->callbacks.sent_cb = _wcb;
113       conn->user_data = pkt;
114       rs_debug (("%s: entering event loop\n", __func__));
115       err = event_base_dispatch (conn->evb);
116       if (err < 0)
117         return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
118                                     "event_base_dispatch: %s",
119                                     evutil_gai_strerror (err));
120       rs_debug (("%s: event loop done\n", __func__));
121       conn->callbacks.sent_cb = NULL;
122       conn->user_data = NULL;
123
124       if ((pkt->flags & rs_packet_sent_flag) == 0)
125         {
126           assert (rs_err_conn_peek_code (conn));
127           return rs_err_conn_peek_code (conn);
128         }
129     }
130
131   return RSE_OK;
132 }