Move verification of response packets up to a level where it makes sense.
[libradsec.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 /* RFC 5080 2.2.1.  Retransmission Behavior */
22 #define IRT 2
23 #define MRC 5
24 #define MRT 16
25 #define MRD 30
26
27 static int
28 _conn_open (struct rs_connection *conn, struct rs_packet *pkt)
29 {
30   if (event_init_eventbase (conn))
31     return -1;
32
33   if (!conn->active_peer)
34     peer_pick_peer (conn);
35   if (!conn->active_peer)
36     return rs_err_conn_push_fl (conn, RSE_NOPEER, __FILE__, __LINE__, NULL);
37
38   if (event_init_socket (conn, conn->active_peer))
39     return -1;
40
41   if (conn->realm->type == RS_CONN_TYPE_TCP
42       || conn->realm->type == RS_CONN_TYPE_TLS)
43     {
44       if (event_init_bufferevent (conn, conn->active_peer))
45         return -1;
46     }
47   else
48     {
49       if (udp_init (conn, pkt))
50         return -1;
51     }
52
53   if (!conn->is_connected)
54     if (!conn->is_connecting)
55       event_do_connect (conn);
56
57   return RSE_OK;
58 }
59
60 static int
61 _conn_is_open_p (struct rs_connection *conn)
62 {
63   return conn->active_peer && conn->is_connected;
64 }
65
66 /* User callback used when we're dispatching for user.  */
67 static void
68 _wcb (void *user_data)
69 {
70   struct rs_packet *pkt = (struct rs_packet *) user_data;
71   assert (pkt);
72   pkt->flags |= rs_packet_sent_flag;
73   if (pkt->conn->bev)
74     bufferevent_disable (pkt->conn->bev, EV_WRITE|EV_READ);
75   else
76     event_del (pkt->conn->wev);
77 }
78
79 int
80 rs_packet_send (struct rs_packet *pkt, void *user_data)
81 {
82   struct rs_connection *conn = NULL;
83   int err = 0;
84
85   assert (pkt);
86   assert (pkt->conn);
87   conn = pkt->conn;
88
89   if (_conn_is_open_p (conn))
90     packet_do_send (pkt);
91   else
92     if (_conn_open (conn, pkt))
93       return -1;
94
95   assert (conn->evb);
96   assert (conn->active_peer);
97   assert (conn->fd >= 0);
98
99   conn->user_data = user_data;
100
101   if (conn->bev)                /* TCP */
102     {
103       bufferevent_setcb (conn->bev, NULL, tcp_write_cb, tcp_event_cb, pkt);
104       bufferevent_enable (conn->bev, EV_WRITE);
105     }
106   else                          /* UDP */
107     {
108       err = event_add (conn->wev, NULL);
109       if (err < 0)
110         return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
111                                     "event_add: %s",
112                                     evutil_gai_strerror (err));
113     }
114
115   /* Do dispatch, unless the user wants to do it herself.  */
116   if (!conn_user_dispatch_p (conn))
117     {
118       conn->callbacks.sent_cb = _wcb;
119       conn->user_data = pkt;
120       rs_debug (("%s: entering event loop\n", __func__));
121       err = event_base_dispatch (conn->evb);
122       if (err < 0)
123         return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
124                                     "event_base_dispatch: %s",
125                                     evutil_gai_strerror (err));
126       rs_debug (("%s: event loop done\n", __func__));
127       conn->callbacks.sent_cb = NULL;
128       conn->user_data = NULL;
129
130       if ((pkt->flags & rs_packet_sent_flag) == 0)
131         return -1;
132     }
133
134   return RSE_OK;
135 }