1 |
|
|
/* $OpenBSD: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $ */ |
2 |
|
|
|
3 |
|
|
/*- |
4 |
|
|
* Copyright (c) 2009 Internet Initiative Japan Inc. |
5 |
|
|
* All rights reserved. |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer in the |
14 |
|
|
* documentation and/or other materials provided with the distribution. |
15 |
|
|
* |
16 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
17 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
20 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
21 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
22 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
23 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
24 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
25 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
26 |
|
|
* SUCH DAMAGE. |
27 |
|
|
*/ |
28 |
|
|
/**@file |
29 |
|
|
* This file provides functions for RADIUS request using radius(3) and event(3). |
30 |
|
|
* @author Yasuoka Masahiko |
31 |
|
|
* $Id: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $ |
32 |
|
|
*/ |
33 |
|
|
#include <sys/types.h> |
34 |
|
|
#include <sys/time.h> |
35 |
|
|
#include <sys/socket.h> |
36 |
|
|
#include <netinet/in.h> |
37 |
|
|
#include <unistd.h> |
38 |
|
|
#include <stdio.h> |
39 |
|
|
#include <syslog.h> |
40 |
|
|
#include <stdlib.h> |
41 |
|
|
#include <debugutil.h> |
42 |
|
|
#include <time.h> |
43 |
|
|
#include <event.h> |
44 |
|
|
#include <string.h> |
45 |
|
|
#include <errno.h> |
46 |
|
|
|
47 |
|
|
#include "radius_req.h" |
48 |
|
|
#include <radius.h> |
49 |
|
|
|
50 |
|
|
#ifndef nitems |
51 |
|
|
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) |
52 |
|
|
#endif |
53 |
|
|
|
54 |
|
|
struct overlapped { |
55 |
|
|
struct event ev_sock; |
56 |
|
|
int socket; |
57 |
|
|
int ntry; |
58 |
|
|
int max_tries; |
59 |
|
|
int failovers; |
60 |
|
|
int acct_delay_time; |
61 |
|
|
int response_fn_calling; |
62 |
|
|
struct sockaddr_storage ss; |
63 |
|
|
struct timespec req_time; |
64 |
|
|
void *context; |
65 |
|
|
radius_response *response_fn; |
66 |
|
|
char secret[MAX_RADIUS_SECRET]; |
67 |
|
|
RADIUS_PACKET *pkt; |
68 |
|
|
radius_req_setting *setting; |
69 |
|
|
}; |
70 |
|
|
|
71 |
|
|
static int radius_request0 (struct overlapped *, int); |
72 |
|
|
static int radius_prepare_socket(struct overlapped *); |
73 |
|
|
static void radius_request_io_event (int, short, void *); |
74 |
|
|
static void radius_on_response(RADIUS_REQUEST_CTX, RADIUS_PACKET *, int, int); |
75 |
|
|
static int select_srcaddr(struct sockaddr const *, struct sockaddr *, socklen_t *); |
76 |
|
|
static void radius_req_setting_ref(radius_req_setting *); |
77 |
|
|
static void radius_req_setting_unref(radius_req_setting *); |
78 |
|
|
|
79 |
|
|
#ifdef RADIUS_REQ_DEBUG |
80 |
|
|
#define RADIUS_REQ_DBG(x) log_printf x |
81 |
|
|
#define RADIUS_REQ_ASSERT(cond) \ |
82 |
|
|
if (!(cond)) { \ |
83 |
|
|
fprintf(stderr, \ |
84 |
|
|
"\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ |
85 |
|
|
, __func__, __FILE__, __LINE__); \ |
86 |
|
|
abort(); \ |
87 |
|
|
} |
88 |
|
|
#else |
89 |
|
|
#define RADIUS_REQ_ASSERT(cond) |
90 |
|
|
#define RADIUS_REQ_DBG(x) |
91 |
|
|
#endif |
92 |
|
|
|
93 |
|
|
/** |
94 |
|
|
* Send RADIUS request message. The pkt(RADIUS packet) will be released |
95 |
|
|
* by this implementation. |
96 |
|
|
*/ |
97 |
|
|
void |
98 |
|
|
radius_request(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt) |
99 |
|
|
{ |
100 |
|
|
uint32_t ival; |
101 |
|
|
struct overlapped *lap; |
102 |
|
|
|
103 |
|
|
RADIUS_REQ_ASSERT(pkt != NULL); |
104 |
|
|
RADIUS_REQ_ASSERT(ctx != NULL); |
105 |
|
|
lap = ctx; |
106 |
|
|
lap->pkt = pkt; |
107 |
|
|
if (radius_get_uint32_attr(pkt, RADIUS_TYPE_ACCT_DELAY_TIME, &ival) |
108 |
|
|
== 0) |
109 |
|
|
lap->acct_delay_time = 1; |
110 |
|
|
radius_request0(lap, 0); |
111 |
|
|
} |
112 |
|
|
|
113 |
|
|
/** |
114 |
|
|
* Prepare NAS-IP-Address or NAS-IPv6-Address. If |
115 |
|
|
* setting->server[setting->curr_server].sock is not initialized, address |
116 |
|
|
* will be selected automatically. |
117 |
|
|
*/ |
118 |
|
|
int |
119 |
|
|
radius_prepare_nas_address(radius_req_setting *setting, |
120 |
|
|
RADIUS_PACKET *pkt) |
121 |
|
|
{ |
122 |
|
|
int af; |
123 |
|
|
struct sockaddr_in *sin4; |
124 |
|
|
struct sockaddr_in6 *sin6; |
125 |
|
|
socklen_t socklen; |
126 |
|
|
|
127 |
|
|
/* See RFC 2765, 3162 */ |
128 |
|
|
RADIUS_REQ_ASSERT(setting != NULL); |
129 |
|
|
|
130 |
|
|
af = setting->server[setting->curr_server].peer.sin6.sin6_family; |
131 |
|
|
RADIUS_REQ_ASSERT(af == AF_INET6 || af == AF_INET); |
132 |
|
|
|
133 |
|
|
sin4 = &setting->server[setting->curr_server].sock.sin4; |
134 |
|
|
sin6 = &setting->server[setting->curr_server].sock.sin6; |
135 |
|
|
|
136 |
|
|
switch (af) { |
137 |
|
|
case AF_INET: |
138 |
|
|
socklen = sizeof(*sin4); |
139 |
|
|
if (sin4->sin_addr.s_addr == INADDR_ANY) { |
140 |
|
|
if (select_srcaddr((struct sockaddr const *) |
141 |
|
|
&setting->server[setting->curr_server].peer, |
142 |
|
|
(struct sockaddr *)sin4, &socklen) != 0) { |
143 |
|
|
RADIUS_REQ_ASSERT("NOTREACHED" == NULL); |
144 |
|
|
goto fail; |
145 |
|
|
} |
146 |
|
|
} |
147 |
|
|
if (radius_put_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, |
148 |
|
|
sin4->sin_addr) != 0) |
149 |
|
|
goto fail; |
150 |
|
|
break; |
151 |
|
|
case AF_INET6: |
152 |
|
|
socklen = sizeof(*sin6); |
153 |
|
|
if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { |
154 |
|
|
if (select_srcaddr((struct sockaddr const *) |
155 |
|
|
&setting->server[setting->curr_server].peer, |
156 |
|
|
(struct sockaddr *)sin4, &socklen) != 0) { |
157 |
|
|
RADIUS_REQ_ASSERT("NOTREACHED" == NULL); |
158 |
|
|
goto fail; |
159 |
|
|
} |
160 |
|
|
} |
161 |
|
|
if (radius_put_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, |
162 |
|
|
sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)) |
163 |
|
|
!= 0) |
164 |
|
|
goto fail; |
165 |
|
|
break; |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
return 0; |
169 |
|
|
fail: |
170 |
|
|
return 1; |
171 |
|
|
} |
172 |
|
|
|
173 |
|
|
|
174 |
|
|
/** Checks whether the request can fail over to another server */ |
175 |
|
|
int |
176 |
|
|
radius_request_can_failover(RADIUS_REQUEST_CTX ctx) |
177 |
|
|
{ |
178 |
|
|
struct overlapped *lap; |
179 |
|
|
radius_req_setting *setting; |
180 |
|
|
|
181 |
|
|
lap = ctx; |
182 |
|
|
setting = lap->setting; |
183 |
|
|
|
184 |
|
|
if (lap->failovers >= setting->max_failovers) |
185 |
|
|
return 0; |
186 |
|
|
if (memcmp(&lap->ss, &setting->server[setting->curr_server].peer, |
187 |
|
|
setting->server[setting->curr_server].peer.sin6.sin6_len) == 0) |
188 |
|
|
/* flagged server doesn't differ from the last server. */ |
189 |
|
|
return 0; |
190 |
|
|
|
191 |
|
|
return 1; |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
/** Send RADIUS request failing over to another server. */ |
195 |
|
|
int |
196 |
|
|
radius_request_failover(RADIUS_REQUEST_CTX ctx) |
197 |
|
|
{ |
198 |
|
|
struct overlapped *lap; |
199 |
|
|
|
200 |
|
|
lap = ctx; |
201 |
|
|
RADIUS_REQ_ASSERT(lap != NULL); |
202 |
|
|
RADIUS_REQ_ASSERT(lap->socket >= 0) |
203 |
|
|
|
204 |
|
|
if (!radius_request_can_failover(lap)) |
205 |
|
|
return -1; |
206 |
|
|
|
207 |
|
|
if (radius_prepare_socket(lap) != 0) |
208 |
|
|
return -1; |
209 |
|
|
|
210 |
|
|
if (radius_request0(lap, 1) != 0) |
211 |
|
|
return -1; |
212 |
|
|
|
213 |
|
|
lap->failovers++; |
214 |
|
|
|
215 |
|
|
return 0; |
216 |
|
|
} |
217 |
|
|
|
218 |
|
|
static int |
219 |
|
|
radius_prepare_socket(struct overlapped *lap) |
220 |
|
|
{ |
221 |
|
|
int sock; |
222 |
|
|
radius_req_setting *setting; |
223 |
|
|
struct sockaddr *sa; |
224 |
|
|
|
225 |
|
|
setting = lap->setting; |
226 |
|
|
if (lap->socket >= 0) |
227 |
|
|
close(lap->socket); |
228 |
|
|
lap->socket = -1; |
229 |
|
|
|
230 |
|
|
sa = (struct sockaddr *)&setting->server[setting->curr_server].peer; |
231 |
|
|
|
232 |
|
|
if ((sock = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) { |
233 |
|
|
log_printf(LOG_ERR, "socket() failed in %s: %m", __func__); |
234 |
|
|
return -1; |
235 |
|
|
} |
236 |
|
|
if (connect(sock, sa, sa->sa_len) != 0) { |
237 |
|
|
log_printf(LOG_ERR, "connect() failed in %s: %m", __func__); |
238 |
|
|
close(sock); |
239 |
|
|
return -1; |
240 |
|
|
} |
241 |
|
|
memcpy(&lap->ss, sa, sa->sa_len); |
242 |
|
|
lap->socket = sock; |
243 |
|
|
memcpy(lap->secret, setting->server[setting->curr_server].secret, |
244 |
|
|
sizeof(lap->secret)); |
245 |
|
|
lap->ntry = lap->max_tries; |
246 |
|
|
|
247 |
|
|
return 0; |
248 |
|
|
} |
249 |
|
|
|
250 |
|
|
/** |
251 |
|
|
* Prepare sending RADIUS request. This implementation will call back to |
252 |
|
|
* notice that it receives the response or it fails for timeouts to the |
253 |
|
|
* The context that is set as 'pctx' and response packet that is given |
254 |
|
|
* by the callback function will be released by this implementation internally. |
255 |
|
|
* @param setting Setting for RADIUS server or request. |
256 |
|
|
* @param context Context for the caller. |
257 |
|
|
* @param pctx Pointer to the space for context of RADIUS request |
258 |
|
|
* (RADIUS_REQUEST_CTX). This will be used for canceling. |
259 |
|
|
* NULL can be specified when you don't need. |
260 |
|
|
* @param response_fn Specify callback function as a pointer. The function |
261 |
|
|
* will be called when it receives a response or when |
262 |
|
|
* request fails for timeouts. |
263 |
|
|
* @param timeout response timeout in second. |
264 |
|
|
*/ |
265 |
|
|
int |
266 |
|
|
radius_prepare(radius_req_setting *setting, void *context, |
267 |
|
|
RADIUS_REQUEST_CTX *pctx, radius_response response_fn) |
268 |
|
|
{ |
269 |
|
|
struct overlapped *lap; |
270 |
|
|
|
271 |
|
|
RADIUS_REQ_ASSERT(setting != NULL); |
272 |
|
|
lap = NULL; |
273 |
|
|
|
274 |
|
|
if (setting->server[setting->curr_server].enabled == 0) |
275 |
|
|
return 1; |
276 |
|
|
if ((lap = calloc(1, sizeof(struct overlapped))) == NULL) { |
277 |
|
|
log_printf(LOG_ERR, "calloc() failed in %s: %m", __func__); |
278 |
|
|
goto fail; |
279 |
|
|
} |
280 |
|
|
lap->context = context; |
281 |
|
|
lap->response_fn = response_fn; |
282 |
|
|
lap->socket = -1; |
283 |
|
|
lap->setting = setting; |
284 |
|
|
|
285 |
|
|
lap->max_tries = setting->max_tries; |
286 |
|
|
if (lap->max_tries <= 0) |
287 |
|
|
lap->max_tries = 3; /* default max tries */ |
288 |
|
|
|
289 |
|
|
if (radius_prepare_socket(lap) != 0) |
290 |
|
|
goto fail; |
291 |
|
|
|
292 |
|
|
if (pctx != NULL) |
293 |
|
|
*pctx = lap; |
294 |
|
|
|
295 |
|
|
radius_req_setting_ref(setting); |
296 |
|
|
|
297 |
|
|
return 0; |
298 |
|
|
fail: |
299 |
|
|
free(lap); |
300 |
|
|
|
301 |
|
|
return 1; |
302 |
|
|
} |
303 |
|
|
|
304 |
|
|
/** |
305 |
|
|
* Cancel the RADIUS request. |
306 |
|
|
* @param The context received by {@link radius_request()} |
307 |
|
|
*/ |
308 |
|
|
void |
309 |
|
|
radius_cancel_request(RADIUS_REQUEST_CTX ctx) |
310 |
|
|
{ |
311 |
|
|
struct overlapped *lap = ctx; |
312 |
|
|
|
313 |
|
|
/* |
314 |
|
|
* Don't call this function from the callback function. |
315 |
|
|
* The context will be freed after the callback function is called. |
316 |
|
|
*/ |
317 |
|
|
RADIUS_REQ_ASSERT(lap->response_fn_calling == 0); |
318 |
|
|
if (lap->response_fn_calling != 0) |
319 |
|
|
return; |
320 |
|
|
|
321 |
|
|
if (lap->socket >= 0) { |
322 |
|
|
event_del(&lap->ev_sock); |
323 |
|
|
close(lap->socket); |
324 |
|
|
lap->socket = -1; |
325 |
|
|
} |
326 |
|
|
if (lap->pkt != NULL) { |
327 |
|
|
radius_delete_packet(lap->pkt); |
328 |
|
|
lap->pkt = NULL; |
329 |
|
|
} |
330 |
|
|
radius_req_setting_unref(lap->setting); |
331 |
|
|
|
332 |
|
|
memset(lap->secret, 0x41, sizeof(lap->secret)); |
333 |
|
|
|
334 |
|
|
free(lap); |
335 |
|
|
} |
336 |
|
|
|
337 |
|
|
/** Return the shared secret for RADIUS server that is used by this context. */ |
338 |
|
|
const char * |
339 |
|
|
radius_get_server_secret(RADIUS_REQUEST_CTX ctx) |
340 |
|
|
{ |
341 |
|
|
struct overlapped *lap; |
342 |
|
|
|
343 |
|
|
lap = ctx; |
344 |
|
|
RADIUS_REQ_ASSERT(lap != NULL); |
345 |
|
|
|
346 |
|
|
return lap->secret; |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
/** Return the address of RADIUS server that is used by this context. */ |
350 |
|
|
struct sockaddr * |
351 |
|
|
radius_get_server_address(RADIUS_REQUEST_CTX ctx) |
352 |
|
|
{ |
353 |
|
|
struct overlapped *lap; |
354 |
|
|
|
355 |
|
|
lap = ctx; |
356 |
|
|
RADIUS_REQ_ASSERT(lap != NULL); |
357 |
|
|
|
358 |
|
|
return (struct sockaddr *)&lap->ss; |
359 |
|
|
} |
360 |
|
|
|
361 |
|
|
static int |
362 |
|
|
radius_request0(struct overlapped *lap, int new_message) |
363 |
|
|
{ |
364 |
|
|
struct timeval tv0; |
365 |
|
|
|
366 |
|
|
RADIUS_REQ_ASSERT(lap->ntry > 0); |
367 |
|
|
|
368 |
|
|
if (lap->acct_delay_time != 0) { |
369 |
|
|
struct timespec curr, delta; |
370 |
|
|
|
371 |
|
|
if (clock_gettime(CLOCK_MONOTONIC, &curr) != 0) { |
372 |
|
|
log_printf(LOG_CRIT, |
373 |
|
|
"clock_gettime(CLOCK_MONOTONIC,) failed: %m"); |
374 |
|
|
RADIUS_REQ_ASSERT(0); |
375 |
|
|
} |
376 |
|
|
if (!timespecisset(&lap->req_time)) |
377 |
|
|
lap->req_time = curr; |
378 |
|
|
else { |
379 |
|
|
timespecsub(&curr, &lap->req_time, &delta); |
380 |
|
|
if (radius_set_uint32_attr(lap->pkt, |
381 |
|
|
RADIUS_TYPE_ACCT_DELAY_TIME, delta.tv_sec) == 0) { |
382 |
|
|
radius_update_id(lap->pkt); |
383 |
|
|
new_message = 1; |
384 |
|
|
} |
385 |
|
|
} |
386 |
|
|
} |
387 |
|
|
if (new_message) { |
388 |
|
|
radius_set_accounting_request_authenticator(lap->pkt, |
389 |
|
|
radius_get_server_secret(lap)); |
390 |
|
|
} |
391 |
|
|
|
392 |
|
|
lap->ntry--; |
393 |
|
|
if (radius_send(lap->socket, lap->pkt, 0) != 0) { |
394 |
|
|
log_printf(LOG_ERR, "sendto() failed in %s: %m", |
395 |
|
|
__func__); |
396 |
|
|
radius_on_response(lap, NULL, RADIUS_REQUEST_ERROR, 1); |
397 |
|
|
return 1; |
398 |
|
|
} |
399 |
|
|
tv0.tv_usec = 0; |
400 |
|
|
tv0.tv_sec = lap->setting->timeout; |
401 |
|
|
|
402 |
|
|
event_set(&lap->ev_sock, lap->socket, EV_READ | EV_PERSIST, |
403 |
|
|
radius_request_io_event, lap); |
404 |
|
|
event_add(&lap->ev_sock, &tv0); |
405 |
|
|
|
406 |
|
|
return 0; |
407 |
|
|
} |
408 |
|
|
|
409 |
|
|
static void |
410 |
|
|
radius_request_io_event(int fd, short evmask, void *context) |
411 |
|
|
{ |
412 |
|
|
struct overlapped *lap; |
413 |
|
|
struct sockaddr_storage ss; |
414 |
|
|
int flags; |
415 |
|
|
socklen_t len; |
416 |
|
|
RADIUS_PACKET *respkt; |
417 |
|
|
|
418 |
|
|
RADIUS_REQ_ASSERT(context != NULL); |
419 |
|
|
|
420 |
|
|
lap = context; |
421 |
|
|
respkt = NULL; |
422 |
|
|
flags = 0; |
423 |
|
|
if ((evmask & EV_READ) != 0) { |
424 |
|
|
RADIUS_REQ_ASSERT(lap->socket >= 0); |
425 |
|
|
if (lap->socket < 0) |
426 |
|
|
return; |
427 |
|
|
RADIUS_REQ_ASSERT(lap->pkt != NULL); |
428 |
|
|
memset(&ss, 0, sizeof(ss)); |
429 |
|
|
len = sizeof(ss); |
430 |
|
|
if ((respkt = radius_recv(lap->socket, 0)) == NULL) { |
431 |
|
|
RADIUS_REQ_DBG((LOG_DEBUG, |
432 |
|
|
"radius_recv() on %s(): %m", __func__)); |
433 |
|
|
/* |
434 |
|
|
* Ignore error by icmp. Wait a response from the |
435 |
|
|
* server anyway, it may eventually become ready. |
436 |
|
|
*/ |
437 |
|
|
switch (errno) { |
438 |
|
|
case EHOSTDOWN: case EHOSTUNREACH: case ECONNREFUSED: |
439 |
|
|
return; /* sleep the rest of timeout time */ |
440 |
|
|
} |
441 |
|
|
flags |= RADIUS_REQUEST_ERROR; |
442 |
|
|
} else if (lap->secret[0] == '\0') { |
443 |
|
|
flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK; |
444 |
|
|
} else { |
445 |
|
|
radius_set_request_packet(respkt, lap->pkt); |
446 |
|
|
if (!radius_check_response_authenticator(respkt, |
447 |
|
|
lap->secret)) |
448 |
|
|
flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK; |
449 |
|
|
} |
450 |
|
|
radius_on_response(lap, respkt, flags, 0); |
451 |
|
|
radius_delete_packet(respkt); |
452 |
|
|
} else if ((evmask & EV_TIMEOUT) != 0) { |
453 |
|
|
if (lap->ntry > 0) { |
454 |
|
|
RADIUS_REQ_DBG((LOG_DEBUG, |
455 |
|
|
"%s() timed out retry", __func__)); |
456 |
|
|
radius_request0(lap, 0); |
457 |
|
|
return; |
458 |
|
|
} |
459 |
|
|
RADIUS_REQ_DBG((LOG_DEBUG, "%s() timed out", __func__)); |
460 |
|
|
flags |= RADIUS_REQUEST_TIMEOUT; |
461 |
|
|
radius_on_response(lap, NULL, flags, 1); |
462 |
|
|
} |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
static void |
466 |
|
|
radius_on_response(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt, int flags, |
467 |
|
|
int server_failure) |
468 |
|
|
{ |
469 |
|
|
struct overlapped *lap; |
470 |
|
|
int failovers; |
471 |
|
|
|
472 |
|
|
lap = ctx; |
473 |
|
|
if (server_failure) { |
474 |
|
|
int i, n; |
475 |
|
|
struct sockaddr *sa_curr; |
476 |
|
|
|
477 |
|
|
sa_curr = (struct sockaddr *)&lap->setting->server[ |
478 |
|
|
lap->setting->curr_server].peer; |
479 |
|
|
if (sa_curr->sa_len == lap->ss.ss_len && |
480 |
|
|
memcmp(sa_curr, &lap->ss, sa_curr->sa_len) == 0) { |
481 |
|
|
/* |
482 |
|
|
* The server on failure is flagged as the current. |
483 |
|
|
* change the current |
484 |
|
|
*/ |
485 |
|
|
for (i = 1; i < nitems(lap->setting->server); i++) { |
486 |
|
|
n = (lap->setting->curr_server + i) % |
487 |
|
|
nitems(lap->setting->server); |
488 |
|
|
if (lap->setting->server[n].enabled) { |
489 |
|
|
lap->setting->curr_server = n; |
490 |
|
|
break; |
491 |
|
|
} |
492 |
|
|
} |
493 |
|
|
} |
494 |
|
|
} |
495 |
|
|
|
496 |
|
|
failovers = lap->failovers; |
497 |
|
|
if (lap->response_fn != NULL) { |
498 |
|
|
lap->response_fn_calling++; |
499 |
|
|
lap->response_fn(lap->context, pkt, flags, ctx); |
500 |
|
|
lap->response_fn_calling--; |
501 |
|
|
} |
502 |
|
|
if (failovers == lap->failovers) |
503 |
|
|
radius_cancel_request(lap); |
504 |
|
|
} |
505 |
|
|
|
506 |
|
|
static int |
507 |
|
|
select_srcaddr(struct sockaddr const *dst, struct sockaddr *src, |
508 |
|
|
socklen_t *srclen) |
509 |
|
|
{ |
510 |
|
|
int sock; |
511 |
|
|
|
512 |
|
|
sock = -1; |
513 |
|
|
if ((sock = socket(dst->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) |
514 |
|
|
goto fail; |
515 |
|
|
if (connect(sock, dst, dst->sa_len) != 0) |
516 |
|
|
goto fail; |
517 |
|
|
if (getsockname(sock, src, srclen) != 0) |
518 |
|
|
goto fail; |
519 |
|
|
|
520 |
|
|
close(sock); |
521 |
|
|
|
522 |
|
|
return 0; |
523 |
|
|
fail: |
524 |
|
|
if (sock >= 0) |
525 |
|
|
close(sock); |
526 |
|
|
|
527 |
|
|
return 1; |
528 |
|
|
} |
529 |
|
|
|
530 |
|
|
radius_req_setting * |
531 |
|
|
radius_req_setting_create(void) |
532 |
|
|
{ |
533 |
|
|
return calloc(1, sizeof(radius_req_setting)); |
534 |
|
|
} |
535 |
|
|
|
536 |
|
|
int |
537 |
|
|
radius_req_setting_has_server(radius_req_setting *setting) |
538 |
|
|
{ |
539 |
|
|
return setting->server[setting->curr_server].enabled; |
540 |
|
|
} |
541 |
|
|
|
542 |
|
|
void |
543 |
|
|
radius_req_setting_destroy(radius_req_setting *setting) |
544 |
|
|
{ |
545 |
|
|
setting->destroyed = 1; |
546 |
|
|
|
547 |
|
|
if (setting->refcnt == 0) |
548 |
|
|
free(setting); |
549 |
|
|
} |
550 |
|
|
|
551 |
|
|
static void |
552 |
|
|
radius_req_setting_ref(radius_req_setting *setting) |
553 |
|
|
{ |
554 |
|
|
setting->refcnt++; |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
static void |
558 |
|
|
radius_req_setting_unref(radius_req_setting *setting) |
559 |
|
|
{ |
560 |
|
|
setting->refcnt--; |
561 |
|
|
if (setting->destroyed) |
562 |
|
|
radius_req_setting_destroy(setting); |
563 |
|
|
} |