Whitespace changes in license headers.
[radsecproxy.git] / lib / udp.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 <sys/types.h>
10 #include <sys/socket.h>
11 #include <event2/event.h>
12 #include <radius/client.h>
13 #include <radsec/radsec.h>
14 #include <radsec/radsec-impl.h>
15 #include "debug.h"
16 #include "event.h"
17 #include "compat.h"
18 #include "udp.h"
19
20 /* Send one packet, the first in queue.  */
21 static int
22 _send (struct rs_connection *conn, int fd)
23 {
24   ssize_t r = 0;
25   struct rs_packet *pkt = conn->out_queue;
26
27   assert (pkt->rpkt);
28   assert (pkt->rpkt->data);
29
30   /* Send.  */
31   r = compat_send (fd, pkt->rpkt->data, pkt->rpkt->length, 0);
32   if (r == -1)
33     {
34       int sockerr = evutil_socket_geterror (pkt->conn->fd);
35       if (sockerr != EAGAIN)
36         return rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
37                                     "%d: send: %d (%s)", fd, sockerr,
38                                     evutil_socket_error_to_string (sockerr));
39     }
40
41   assert (r == pkt->rpkt->length);
42   /* Unlink the packet.  */
43   conn->out_queue = pkt->next;
44
45   /* If there are more packets in queue, add the write event again.  */
46   if (pkt->conn->out_queue)
47     {
48       r = event_add (pkt->conn->wev, NULL);
49       if (r < 0)
50         return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__,
51                                     "event_add: %s", evutil_gai_strerror (r));
52       rs_debug (("%s: re-adding the write event\n", __func__));
53     }
54
55   return RSE_OK;
56 }
57
58 /* Callback for conn->wev and conn->rev.  FIXME: Rename.
59
60    USER_DATA contains connection for EV_READ and a packet for
61    EV_WRITE.  This is because we don't have a connect/establish entry
62    point at the user level -- send implies connect so when we're
63    connected we need the packet to send.  */
64 static void
65 _evcb (evutil_socket_t fd, short what, void *user_data)
66 {
67   int err;
68
69   rs_debug (("%s: fd=%d what =", __func__, fd));
70   if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT"));
71   if (what & EV_READ) rs_debug ((" READ"));
72   if (what & EV_WRITE) rs_debug ((" WRITE"));
73   rs_debug (("\n"));
74
75   if (what & EV_READ)
76     {
77       /* Read a single UDP packet and stick it in USER_DATA.  */
78       /* TODO: Verify that unsolicited packets are dropped.  */
79       struct rs_packet *pkt = (struct rs_packet *) user_data;
80       ssize_t r = 0;
81
82       assert (pkt);
83       assert (pkt->conn);
84       assert (pkt->rpkt->data);
85
86       r = compat_recv (fd, pkt->rpkt->data, RS_MAX_PACKET_LEN, MSG_TRUNC);
87       if (r == -1)
88         {
89           int sockerr = evutil_socket_geterror (pkt->conn->fd);
90           if (sockerr == EAGAIN)
91             {
92               /* FIXME: Really shouldn't happen since we've been told
93                  that fd is readable!  */
94               rs_debug (("%s: EAGAIN reading UDP packet -- wot?"));
95               return;
96             }
97
98           /* Hard error.  */
99           rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__,
100                                "%d: recv: %d (%s)", fd, sockerr,
101                                evutil_socket_error_to_string (sockerr));
102           event_del (pkt->conn->tev);
103           return;
104         }
105       event_del (pkt->conn->tev);
106       if (r < 20 || r > RS_MAX_PACKET_LEN)      /* Short or long packet.  */
107         {
108           rs_err_conn_push (pkt->conn, RSE_INVALID_PKT,
109                             "invalid packet length: %d",
110                             pkt->rpkt->length);
111           return;
112         }
113       pkt->rpkt->length = (pkt->rpkt->data[2] << 8) + pkt->rpkt->data[3];
114       err = nr_packet_ok (pkt->rpkt);
115       if (err)
116         {
117           rs_err_conn_push_fl (pkt->conn, err, __FILE__, __LINE__,
118                                "invalid packet");
119           return;
120         }
121       /* Hand over message to user.  This changes ownership of pkt.
122          Don't touch it afterwards -- it might have been freed.  */
123       if (pkt->conn->callbacks.received_cb)
124         pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data);
125     }
126   else if (what & EV_WRITE)
127     {
128       struct rs_packet *pkt = (struct rs_packet *) user_data;
129       assert (pkt);
130       assert (pkt->conn);
131
132       if (!pkt->conn->is_connected)
133         event_on_connect (pkt->conn, pkt);
134
135       if (pkt->conn->out_queue)
136         if (_send (pkt->conn, fd) == RSE_OK)
137           if (pkt->conn->callbacks.sent_cb)
138             pkt->conn->callbacks.sent_cb (pkt->conn->user_data);
139     }
140
141 #if defined (DEBUG)
142   if (what & EV_TIMEOUT)
143     rs_debug (("%s: timeout on UDP event, shouldn't happen\n", __func__));
144 #endif
145 }
146
147 int
148 udp_init (struct rs_connection *conn, struct rs_packet *pkt)
149 {
150   assert (!conn->bev);
151
152   conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, NULL);
153   conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, NULL);
154   if (!conn->rev || !conn->wev)
155     {
156       if (conn->rev)
157         {
158           event_free (conn->rev);
159           conn->rev = NULL;
160         }
161       /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at
162          least for now (libevent-2.0.5).  */
163       return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
164     }
165   return RSE_OK;
166 }
167
168 int
169 udp_init_retransmit_timer (struct rs_connection *conn)
170 {
171   assert (conn);
172
173   if (conn->tev)
174     event_free (conn->tev);
175   conn->tev = evtimer_new (conn->evb, event_retransmit_timeout_cb, conn);
176   if (!conn->tev)
177     return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__,
178                                 "evtimer_new");
179
180   return RSE_OK;
181 }