b0a8eefe3dfa80da7fb78a204b9db54f2415f3f2
[radsecproxy.git] / lib / request.c
1 /* Copyright 2010, 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 <stdint.h>
9 #include <stdlib.h>
10 #include <assert.h>
11 #include <sys/time.h>
12 #include <event2/event.h>
13 #include <radsec/radsec.h>
14 #include <radsec/radsec-impl.h>
15 #include <radsec/request.h>
16 #include <radsec/request-impl.h>
17 #include <freeradius/libradius.h>
18 #include "debug.h"
19 #include "conn.h"
20 #include "tcp.h"
21
22 /* RFC 5080 2.2.1.  Retransmission Behavior.  */
23 #define IRT 2
24 #define MRC 5
25 #define MRT 16
26 #define MRD 30
27 #define RAND 100                /* Rand factor, milliseconds. */
28
29 int
30 rs_request_create (struct rs_connection *conn, struct rs_request **req_out)
31 {
32   struct rs_request *req = rs_malloc (conn->ctx, sizeof(*req));
33   assert (req_out);
34   if (!req)
35     return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL);
36   memset (req, 0, sizeof(*req));
37   req->conn = conn;
38   *req_out = req;
39   return RSE_OK;
40 }
41
42 void
43 rs_request_add_reqpkt (struct rs_request *req, struct rs_packet *reqpkt)
44 {
45   assert (req);
46   req->req_msg = reqpkt;
47 }
48
49 int
50 rs_request_create_authn (struct rs_connection *conn,
51                          struct rs_request **req_out,
52                          const char *user_name,
53                          const char *user_pw)
54 {
55   struct rs_request *req;
56   assert (req_out);
57   if (rs_request_create (conn, &req))
58     return -1;
59
60   if (rs_packet_create_authn_request (conn, &req->req_msg, user_name, user_pw))
61     return -1;
62
63   *req_out = req;
64   return RSE_OK;
65 }
66
67 void
68 rs_request_destroy (struct rs_request *request)
69 {
70   assert (request);
71   rs_packet_destroy (request->req_msg);
72   rs_packet_destroy (request->resp_msg);
73   rs_free (request->conn->ctx, request);
74 }
75
76 static void
77 _rand_rt (struct timeval *res, uint32_t rtprev, uint32_t factor)
78 {
79   uint32_t ms = rtprev * (fr_rand () % factor);
80   res->tv_sec = rtprev + ms / 1000;
81   res->tv_usec = (ms % 1000) * 1000;
82 }
83
84 int
85 rs_request_send (struct rs_request *request, struct rs_packet **resp_msg)
86 {
87   int r = 0;
88   struct rs_connection *conn = NULL;
89   int count = 0;
90   struct timeval rt = {0,0};
91   struct timeval end = {0,0};
92   struct timeval now = {0,0};
93   struct timeval tmp_tv = {0,0};
94   struct timeval mrt_tv = {MRT,0};
95
96   if (!request || !request->conn || !request->req_msg || !resp_msg)
97     return rs_err_conn_push_fl (conn, RSE_INVAL, __FILE__, __LINE__, NULL);
98   conn = request->conn;
99   assert (!conn_user_dispatch_p (conn)); /* This function is high level.  */
100
101   gettimeofday (&end, NULL);
102   end.tv_sec += MRD;
103   _rand_rt (&rt, IRT, RAND);
104   while (1)
105     {
106       rs_conn_set_timeout (conn, &rt);
107       r = rs_packet_send (request->req_msg, NULL);
108       if (r == RSE_OK)
109         {
110           r = rs_conn_receive_packet (request->conn,
111                                       request->req_msg,
112                                       resp_msg);
113           if (r == RSE_OK)
114             break;              /* Success.  */
115
116           /* Timing out on receiving data or reconnecting a broken TCP
117              connection is ok.  */
118           if (r != RSE_TIMEOUT_CONN && r != RSE_TIMEOUT_IO)
119             break;              /* Error.  */
120         }
121       else if (r != RSE_TIMEOUT_CONN) /* Timeout on TCP connect is ok.  */
122         break;                        /* Error.  */
123
124       gettimeofday (&now, NULL);
125       if (++count > MRC || timercmp (&now, &end, >))
126         {
127           r = RSE_TIMEOUT;
128           break;                /* Timeout.  */
129         }
130
131       /* rt = 2 * rt + _rand_rt (rt, RAND); */
132       timeradd (&rt, &rt, &rt); /* rt = 2 * rt */
133       _rand_rt (&tmp_tv, IRT, RAND);
134       timeradd (&rt, &tmp_tv, &rt);
135       if (timercmp (&rt, &mrt_tv, >))
136         _rand_rt (&rt, MRT, RAND);
137     }
138
139   rs_debug (("%s: returning %d\n", __func__, r));
140   return r;
141 }