e2e9feb0abbd876169d6e15f4bd150e1e01b7ed4
[libradsec.git] / lib / tcp.c
1 /* Copyright 2011 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 #if defined (RS_ENABLE_TLS)
12 #include <event2/bufferevent_ssl.h>
13 #include <openssl/err.h>
14 #endif
15 #include <radius/client.h>
16 #include <radsec/radsec.h>
17 #include <radsec/radsec-impl.h>
18 #include "tcp.h"
19 #include "packet.h"
20 #include "conn.h"
21 #include "debug.h"
22 #include "event.h"
23
24 #if defined (DEBUG)
25 #include <event2/buffer.h>
26 #endif
27
28 /** Read one RADIUS packet header. Return !0 on error. */
29 static int
30 _read_header (struct rs_packet *pkt)
31 {
32   size_t n = 0;
33
34   n = bufferevent_read (pkt->conn->bev, pkt->hdr, RS_HEADER_LEN);
35   if (n == RS_HEADER_LEN)
36     {
37       pkt->flags |= RS_PACKET_HEADER_READ;
38       pkt->rpkt->length = (pkt->hdr[2] << 8) + pkt->hdr[3];
39       if (pkt->rpkt->length < 20 || pkt->rpkt->length > RS_MAX_PACKET_LEN)
40         {
41           conn_close (&pkt->conn);
42           return rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
43                                    "invalid packet length: %d",
44                                    pkt->rpkt->length);
45         }
46       memcpy (pkt->rpkt->data, pkt->hdr, RS_HEADER_LEN);
47       bufferevent_setwatermark (pkt->conn->bev, EV_READ,
48                                 pkt->rpkt->length - RS_HEADER_LEN, 0);
49       rs_debug (("%s: packet header read, total pkt len=%d\n",
50                  __func__, pkt->rpkt->length));
51     }
52   else if (n < 0)
53     {
54       rs_debug (("%s: buffer frozen while reading header\n", __func__));
55     }
56   else      /* Error: libevent gave us less than the low watermark. */
57     {
58       conn_close (&pkt->conn);
59       return rs_err_conn_push_fl (pkt->conn, RSE_INTERNAL, __FILE__, __LINE__,
60                                   "got %d octets reading header", n);
61     }
62
63   return 0;
64 }
65
66 /** Read a message, check that it's valid RADIUS and hand it off to
67     registered user callback.
68
69     The packet is read from the bufferevent associated with \a pkt and
70     the data is stored in \a pkt->rpkt.
71
72     Return 0 on success and !0 on failure. */
73 static int
74 _read_packet (struct rs_packet *pkt)
75 {
76   size_t n = 0;
77   int err;
78
79   rs_debug (("%s: trying to read %d octets of packet data\n", __func__,
80              pkt->rpkt->length - RS_HEADER_LEN));
81
82   n = bufferevent_read (pkt->conn->bev,
83                         pkt->rpkt->data + RS_HEADER_LEN,
84                         pkt->rpkt->length - RS_HEADER_LEN);
85
86   rs_debug (("%s: read %ld octets of packet data\n", __func__, n));
87
88   if (n == pkt->rpkt->length - RS_HEADER_LEN)
89     {
90       bufferevent_disable (pkt->conn->bev, EV_READ);
91       rs_debug (("%s: complete packet read\n", __func__));
92       pkt->flags &= ~RS_PACKET_HEADER_READ;
93       memset (pkt->hdr, 0, sizeof(*pkt->hdr));
94
95       /* Checks done by rad_packet_ok:
96          - lenghts (FIXME: checks really ok for tcp?)
97          - invalid code field
98          - attribute lengths >= 2
99          - attribute sizes adding up correctly  */
100       err = nr_packet_ok (pkt->rpkt);
101       if (err != RSE_OK)
102         {
103           conn_close (&pkt->conn);
104           return rs_err_conn_push_fl (pkt->conn, err, __FILE__, __LINE__,
105                                       "invalid packet");
106         }
107
108 #if defined (DEBUG)
109       /* Find out what happens if there's data left in the buffer.  */
110       {
111         size_t rest = 0;
112         rest = evbuffer_get_length (bufferevent_get_input (pkt->conn->bev));
113         if (rest)
114           rs_debug (("%s: returning with %d octets left in buffer\n", __func__,
115                      rest));
116       }
117 #endif
118
119       /* Hand over message to user.  This changes ownership of pkt.
120          Don't touch it afterwards -- it might have been freed.  */
121       if (pkt->conn->callbacks.received_cb)
122         pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data);
123     }
124   else if (n < 0)               /* Buffer frozen.  */
125     rs_debug (("%s: buffer frozen when reading packet\n", __func__));
126   else                          /* Short packet.  */
127     rs_debug (("%s: waiting for another %d octets\n", __func__,
128                pkt->rpkt->length - RS_HEADER_LEN - n));
129
130   return 0;
131 }
132
133 /* The read callback for TCP.
134
135    Read exactly one RADIUS message from BEV and store it in struct
136    rs_packet passed in USER_DATA.
137
138    Inform upper layer about successful reception of received RADIUS
139    message by invoking conn->callbacks.recevied_cb(), if !NULL.  */
140 void
141 tcp_read_cb (struct bufferevent *bev, void *user_data)
142 {
143   struct rs_packet *pkt = (struct rs_packet *) user_data;
144
145   assert (pkt);
146   assert (pkt->conn);
147   assert (pkt->rpkt);
148
149   pkt->rpkt->sockfd = pkt->conn->fd;
150   pkt->rpkt->vps = NULL;        /* FIXME: can this be done when initializing pkt? */
151
152   /* Read a message header if not already read, return if that
153      fails. Read a message and have it dispatched to the user
154      registered callback.
155
156      Room for improvement: Peek inside buffer (evbuffer_copyout()) to
157      avoid the extra copying. */
158   if ((pkt->flags & RS_PACKET_HEADER_READ) == 0)
159     if (_read_header (pkt))
160       return;                   /* Error.  */
161   _read_packet (pkt);
162 }
163
164 void
165 tcp_event_cb (struct bufferevent *bev, short events, void *user_data)
166 {
167   struct rs_packet *pkt = (struct rs_packet *) user_data;
168   struct rs_connection *conn = NULL;
169   int sockerr = 0;
170 #if defined (RS_ENABLE_TLS)
171   unsigned long tlserr = 0;
172 #endif
173 #if defined (DEBUG)
174   struct rs_peer *p = NULL;
175 #endif
176
177   assert (pkt);
178   assert (pkt->conn);
179   conn = pkt->conn;
180 #if defined (DEBUG)
181   assert (pkt->conn->active_peer);
182   p = conn->active_peer;
183 #endif
184
185   conn->is_connecting = 0;
186   if (events & BEV_EVENT_CONNECTED)
187     {
188       if (conn->tev)
189         evtimer_del (conn->tev); /* Cancel connect timer.  */
190       if (event_on_connect (conn, pkt))
191         {
192           event_on_disconnect (conn);
193           event_loopbreak (conn);
194         }
195     }
196   else if (events & BEV_EVENT_EOF)
197     {
198       event_on_disconnect (conn);
199     }
200   else if (events & BEV_EVENT_TIMEOUT)
201     {
202       rs_debug (("%s: %p times out on %s\n", __func__, p,
203                  (events & BEV_EVENT_READING) ? "read" : "write"));
204       rs_err_conn_push_fl (conn, RSE_TIMEOUT_IO, __FILE__, __LINE__, NULL);
205     }
206   else if (events & BEV_EVENT_ERROR)
207     {
208       sockerr = evutil_socket_geterror (conn->active_peer->fd);
209       if (sockerr == 0) /* FIXME: True that errno == 0 means closed? */
210         {
211           event_on_disconnect (conn);
212           rs_err_conn_push_fl (conn, RSE_DISCO, __FILE__, __LINE__, NULL);
213         }
214       else
215         {
216           rs_debug (("%s: %d: %d (%s)\n", __func__, conn->fd, sockerr,
217                      evutil_socket_error_to_string (sockerr)));
218           rs_err_conn_push_fl (conn, RSE_SOCKERR, __FILE__, __LINE__,
219                                "%d: %d (%s)", conn->fd, sockerr,
220                                evutil_socket_error_to_string (sockerr));
221         }
222 #if defined (RS_ENABLE_TLS)
223       if (conn->tls_ssl)        /* FIXME: correct check?  */
224         {
225           for (tlserr = bufferevent_get_openssl_error (conn->bev);
226                tlserr;
227                tlserr = bufferevent_get_openssl_error (conn->bev))
228             {
229               rs_debug (("%s: openssl error: %s\n", __func__,
230                          ERR_error_string (tlserr, NULL)));
231               rs_err_conn_push_fl (conn, RSE_SSLERR, __FILE__, __LINE__,
232                                    ERR_error_string (tlserr, NULL));
233             }
234         }
235 #endif  /* RS_ENABLE_TLS */
236       event_loopbreak (conn);
237     }
238
239 #if defined (DEBUG)
240   if (events & BEV_EVENT_ERROR && events != BEV_EVENT_ERROR)
241     rs_debug (("%s: BEV_EVENT_ERROR and more: 0x%x\n", __func__, events));
242 #endif
243 }
244
245 void
246 tcp_write_cb (struct bufferevent *bev, void *ctx)
247 {
248   struct rs_packet *pkt = (struct rs_packet *) ctx;
249
250   assert (pkt);
251   assert (pkt->conn);
252
253   if (pkt->conn->callbacks.sent_cb)
254     pkt->conn->callbacks.sent_cb (pkt->conn->user_data);
255 }
256
257 int
258 tcp_init_connect_timer (struct rs_connection *conn)
259 {
260   assert (conn);
261
262   if (conn->tev)
263     event_free (conn->tev);
264   conn->tev = evtimer_new (conn->evb, event_conn_timeout_cb, conn);
265   if (!conn->tev)
266     return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
267                                 "evtimer_new");
268
269   return RSE_OK;
270 }