The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
client.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 7f8aab3f8a75c54077bed48ae18e2d3647dc3f3f $
19 *
20 * @file protocols/radius/client.c
21 * @brief Functions to support RADIUS bio handlers for client sockets
22 *
23 * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: 7f8aab3f8a75c54077bed48ae18e2d3647dc3f3f $")
26
27#include <freeradius-devel/radius/client.h>
28#include <freeradius-devel/radius/client_udp.h>
29#include <freeradius-devel/radius/client_tcp.h>
30#include <freeradius-devel/radius/client_priv.h>
31
32#include <freeradius-devel/protocol/radius/rfc2865.h>
33#include <freeradius-devel/protocol/radius/rfc2866.h>
34
35typedef struct {
36 uint32_t initial; //!< initial value
37 uint32_t start; //!< Unix time we started sending this packet
38
39 size_t offset; //!< offset to Acct-Delay-Time value
41
42static void radius_client_retry_sent(fr_bio_t *bio, void *packet_ctx, const void *buffer, UNUSED size_t size,
43 fr_bio_retry_entry_t *retry_ctx);
44static bool radius_client_retry_response(fr_bio_t *bio, fr_bio_retry_entry_t **retry_ctx_p, UNUSED void *packet_ctx, const void *buffer, UNUSED size_t size);
46static ssize_t radius_client_retry(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, UNUSED const void *buffer, NDEBUG_UNUSED size_t size);
47
49
51{
53
54 if (fd_cfg->path || fd_cfg->filename) {
55 fr_strerror_const("Domain sockets and files are not supported");
56 return NULL;
57 }
58
59 if (fd_cfg->socket_type == SOCK_DGRAM) return fr_radius_client_udp_bio_alloc(ctx, cfg, fd_cfg);
60
61 return fr_radius_client_tcp_bio_alloc(ctx, cfg, fd_cfg);
62}
63
65{
66 if (fr_bio_shutdown(my->common.bio) < 0) return -1;
67
68 if (fr_bio_free(my->common.bio) < 0) return -1;
69
70 return 0;
71}
72
73
75{
76 int i;
78 fr_bio_retry_rewrite_t rewrite = NULL;
79
81
82 my = talloc_zero(ctx, fr_radius_client_fd_bio_t);
83 if (!my) return NULL;
84
85 /*
86 * Allocate tracking for all of the packets.
87 */
88 for (i = 1; i < FR_RADIUS_CODE_MAX; i++) {
89 if (!cfg->outgoing[i]) continue;
90
91 my->codes[i] = fr_radius_id_alloc(my);
92 if (!my->codes[i]) goto fail;
93 }
94
95 my->fd = fr_bio_fd_alloc(my, fd_cfg, 0);
96 if (!my->fd) {
97 fail:
99 return NULL;
100 }
101
102 /*
103 * So that read / write pause / resume callbacks can find us
104 */
105 my->fd->uctx = my;
106
107 my->info.fd_info = fr_bio_fd_info(my->fd);
108 fr_assert(my->info.fd_info != NULL);
109
110 my->reply_socket = my->info.fd_info->socket;
111 if ((my->reply_socket.af == AF_INET) || (my->reply_socket.af == AF_INET6)) {
112 fr_socket_addr_swap(&my->reply_socket, &my->info.fd_info->socket);
113 }
114
115 my->mem = fr_bio_mem_alloc(my, read_size, 2 * 4096, my->fd);
116 if (!my->mem) goto fail;
117
118 my->mem->uctx = my;
119
120 if (cfg->packet_cb_cfg.retry) rewrite = radius_client_retry;
121
122 /*
123 * We allocate a retry BIO even for TCP, as we want to be able to timeout the packets.
124 */
125 if (cfg->retry_cfg.el) {
127 rewrite, radius_client_retry_release, &cfg->retry_cfg, my->mem);
128 if (!my->retry) goto fail;
129
130 my->retry->uctx = my;
131
132 my->info.retry_info = fr_bio_retry_info(my->retry);
133 fr_assert(my->info.retry_info != NULL);
134
135 my->common.bio = my->retry;
136
137 } else {
138 /*
139 * No timers for retries, we just use a memory buffer for outbound packets.
140 */
141 my->common.bio = my->mem;
142 }
143
144 my->cfg = *cfg;
145
146 /*
147 * Inform the packet BIO about our application callbacks.
148 */
149 my->common.cb = cfg->packet_cb_cfg;
150
151 /*
152 * Initialize the packet handlers in each BIO.
153 */
154 fr_bio_packet_init(&my->common);
155
156 talloc_set_destructor(my, _radius_client_fd_bio_free);
157
158 /*
159 * Set up the connected status.
160 */
161 my->info.connected = false;
162
163 /*
164 * If we're supposed to be connected (but aren't), then ensure that we don't keep trying to
165 * connect forever.
166 */
167 if ((my->info.fd_info->type == FR_BIO_FD_CONNECTED) && !my->info.connected &&
171 return NULL;
172 }
173 }
174
175 my->proto_ctx = (fr_radius_ctx_t) {
176 .secret = (char const *) my->cfg.verify.secret,
177 .secret_length = my->cfg.verify.secret_len,
178 .secure_transport = false,
179 .proxy_state = my->cfg.proxy_state,
180 };
181
182 my->info.last_idle = fr_time();
183
184 return my;
185}
186
188{
189 ssize_t slen;
190 fr_radius_id_ctx_t *id_ctx;
191
192 fr_assert(!packet->data);
193
194 fr_assert(packet->code > 0);
196 fr_assert(!my->common.write_blocked);
197
198 if (!my->codes[packet->code]) {
199 fr_strerror_printf("Outgoing packet code %s is disallowed by the configuration",
200 fr_radius_packet_name[packet->code]);
201 return fr_bio_error(GENERIC);
202 }
203
204 id_ctx = fr_radius_code_id_pop(my->codes, packet);
205 if (!id_ctx) {
206 /*
207 * Try to cancel the oldest one.
208 */
209 if (!my->retry || fr_bio_retry_entry_cancel(my->retry, NULL) < 1) {
210 all_ids_used:
211 my->all_ids_used = true;
212
213 /*
214 * Tell the application to stop writing data to the BIO.
215 */
216 if (my->common.cb.write_blocked) my->common.cb.write_blocked(&my->common);
217
218 fr_strerror_const("All IDs are in use");
219 return fr_bio_error(GENERIC);
220 }
221
222 id_ctx = fr_radius_code_id_pop(my->codes, packet);
223 if (!id_ctx) goto all_ids_used;
224 }
225 id_ctx->request_ctx = pctx;
226 fr_assert(id_ctx->packet == packet);
227
228 /*
229 * @todo - just create the random auth vector here?
230 */
231 if ((packet->code == FR_RADIUS_CODE_ACCESS_REQUEST) ||
232 (packet->code == FR_RADIUS_CODE_STATUS_SERVER)) {
233 memcpy(my->buffer + 4, packet->vector, sizeof(packet->vector));
234 }
235
236 /*
237 * Encode the packet.
238 */
239 slen = fr_radius_encode(&FR_DBUFF_TMP(my->buffer, sizeof(my->buffer)), list, &(fr_radius_encode_ctx_t) {
240 .common = &my->proto_ctx,
241 .request_authenticator = NULL,
242 .rand_ctx = (fr_fast_rand_t) {
243 .a = fr_rand(),
244 .b = fr_rand(),
245 },
246 .code = packet->code,
247 .id = packet->id,
248 .add_proxy_state = my->cfg.add_proxy_state,
249 });
250 if (slen < 0) {
251 fail:
252 fr_radius_code_id_push(my->codes, packet);
253 return fr_bio_error(GENERIC);
254 }
255
257 packet->data_len = slen;
258
259 slen = fr_radius_sign(my->buffer, NULL,
260 (uint8_t const *) my->cfg.verify.secret, my->cfg.verify.secret_len);
261 if (slen < 0) goto fail;
262
263 /*
264 * The other BIOs will take care of calling fr_radius_client_bio_write_blocked() when the write
265 * is blocked.
266 *
267 * The "next" BIO is a memory one, which can store the entire packet. So write() never returns a
268 * partial packet.
269 */
270 slen = fr_bio_write(my->common.bio, &packet->socket, my->buffer, packet->data_len);
271 if (slen < 0) {
272 fr_assert((slen != fr_bio_error(IO_WOULD_BLOCK)) || my->common.write_blocked);
273
274 fr_radius_code_id_push(my->codes, packet);
275 return slen;
276 }
277
278 fr_assert((size_t) slen == packet->data_len);
279
280 /*
281 * We only allocate packet data after writing it to the socket. If the write fails, we avoid a
282 * memory alloc / free.
283 */
284 packet->data = talloc_array(packet, uint8_t, packet->data_len);
285 if (!packet->data) goto fail;
286
287 /*
288 * Only after successful write do we copy the data back to the packet structure.
289 */
290 memcpy(packet->data, my->buffer, packet->data_len);
291 memcpy(packet->vector, packet->data + 4, RADIUS_AUTH_VECTOR_LENGTH);
292
293 return 0;
294}
295
311
312static ssize_t radius_client_retry(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, UNUSED const void *buffer, NDEBUG_UNUSED size_t size)
313{
314 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
315 fr_radius_id_ctx_t *id_ctx = retry_ctx->uctx;
316 fr_packet_t *packet = id_ctx->packet;
317
318 fr_assert(packet->data_len == size);
319
320 fr_assert(my->cfg.packet_cb_cfg.retry);
321
322 my->cfg.packet_cb_cfg.retry(&my->common, id_ctx->packet);
323
324 /*
325 * Note do do NOT ball fr_bio_write(), because that will treat the packet as a new one!
326 */
327 return fr_bio_retry_rewrite(bio, retry_ctx, packet->data, packet->data_len);
328}
329
330
332{
333 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
334 fr_radius_client_bio_retry_t *acct = retry_ctx->rewrite_ctx;
335 fr_radius_id_ctx_t *id_ctx = retry_ctx->uctx;
336 fr_radius_id_ctx_t my_id_ctx = *id_ctx;
337 fr_packet_t *packet = id_ctx->packet;
338 uint8_t *ptr = packet->data + acct->offset;
339 uint32_t now, delay;
340
341 fr_assert(packet->data_len == size);
342
343 /*
344 * Change IDs, since we're changing the value of Acct-Delay-Time
345 */
346 fr_radius_code_id_push(my->codes, packet);
347
348 id_ctx = fr_radius_code_id_pop(my->codes, packet);
349 if (!id_ctx) return fr_bio_error(GENERIC); /* at the minimum, we should get the ID we just pushed */
350
351 /*
352 * Update the ID context, and tell the retry context that the ID context has changed.
353 */
354 id_ctx->request_ctx = my_id_ctx.request_ctx;
355 retry_ctx->uctx = id_ctx;
356
357 now = fr_time_to_sec(retry_ctx->retry.updated);
358 fr_assert(now >= acct->start);
359 fr_assert((now - acct->start) < (1 << 20)); /* just for pairanoia */
360
361 delay = acct->initial + (now - acct->start);
362
363 fr_nbo_from_uint32(ptr, delay);
364
365 /*
366 * Sign the updated packet.
367 */
368 (void) fr_radius_sign(packet->data, NULL,
369 (uint8_t const *) my->cfg.verify.secret, my->cfg.verify.secret_len);
370
371 /*
372 * Signal that the packet has been retried.
373 */
374 if (my->cfg.packet_cb_cfg.retry) my->cfg.packet_cb_cfg.retry(&my->common, id_ctx->packet);
375
376 /*
377 * Note do do NOT ball fr_bio_write(), because that will treat the packet as a new one!
378 */
379 return fr_bio_retry_rewrite(bio, retry_ctx, packet->data, packet->data_len);
380}
381
382
383static void radius_client_retry_sent(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size,
384 fr_bio_retry_entry_t *retry_ctx)
385{
386 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
387 fr_radius_id_ctx_t *id_ctx;
388 uint8_t const *data = buffer;
389 uint8_t const *end;
391
392 id_ctx = fr_radius_code_id_find(my->codes, data[0], data[1]);
393 fr_assert(id_ctx != NULL);
394
395 id_ctx->packet = packet_ctx;
396 id_ctx->retry_ctx = retry_ctx;
397
398 retry_ctx->uctx = id_ctx;
399
400 (void) fr_bio_retry_entry_init(bio, retry_ctx, &my->cfg.retry[data[0]]);
401
402 my->info.outstanding++;
403 my->info.last_sent = retry_ctx->retry.start;
404 if (fr_time_lteq(my->info.first_sent, my->info.last_idle)) my->info.first_sent = my->info.last_sent;
405
406 /*
407 * For Accounting-Request packets which have Acct-Delay-Time, we need to track where the
408 * Acct-Delay-Time is in the packet, along with its original value, and then we can use the
409 * #fr_retry_t to discover how many seconds to add to Acct-Delay-Time.
410 */
411 retry_ctx->rewrite = NULL;
412
413 if ((data[0] != FR_RADIUS_CODE_ACCOUNTING_REQUEST) || (my->cfg.retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrc == 1)) return;
414
415 end = data + size;
417
418 /*
419 * Find the Acct-Delay-Time attribute. If it doesn't exist, we don't update it on retransmits.
420 *
421 * @todo - maybe if it doesn't exist, we look for Event-Timestamp? And add one if necessary?
422 */
423 while (data < end) {
424 if (data[0] == FR_ACCT_DELAY_TIME) break;
425
426 data += data[1];
427 }
428
429 if ((data == end) || (data[1] != 6)) return;
430
431 acct = retry_ctx->rewrite_ctx = talloc_zero(my, fr_radius_client_bio_retry_t);
432 if (!acct) return;
433
434 /*
435 * Set up the retry handler with initial data.
436 */
438
439 data += 2;
440
442 acct->start = fr_time_to_sec(retry_ctx->retry.start);
443 acct->offset = (size_t) (data - (uint8_t const *) buffer);
444}
445
446
447static bool radius_client_retry_response(fr_bio_t *bio, fr_bio_retry_entry_t **retry_ctx_p, void *packet_ctx, const void *buffer, UNUSED size_t size)
448{
449 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
450 unsigned int code;
451 uint8_t *data = UNCONST(uint8_t *, buffer); /* @todo - for verify() */
452 fr_radius_id_ctx_t *id_ctx;
453 fr_packet_t *response = packet_ctx;
454
455 /*
456 * We now have a complete packet in our buffer. Check if it is one we expect.
457 */
458 fr_assert(data[0] > 0);
460
461 /*
462 * Is the code an allowed reply code?
463 */
464 code = allowed_replies[data[0]];
465 if (!code) return false;
466
467 /*
468 * It's a reply, but not a permitted reply to a particular request.
469 *
470 * @todo - Status-Server. And for protocol error, look up original packet code
471 */
472 id_ctx = fr_radius_code_id_find(my->codes, code, data[1]);
473 if (!id_ctx) return false;
474
475 /*
476 * No reply yet, verify the response packet, and save it for later.
477 */
478 if (!id_ctx->response) {
480
481 if (fr_radius_verify(data, id_ctx->packet->data + 4,
482 my->cfg.verify.secret, my->cfg.verify.secret_len,
483 my->cfg.verify.require_message_authenticator,
484 my->cfg.verify.limit_proxy_state) < 0) {
485 return false;
486 }
487
488 retry = *retry_ctx_p = id_ctx->retry_ctx;
489
490 if (fr_time_gt(retry->retry.start, my->info.mrs_time)) my->info.mrs_time = retry->retry.start;
491 my->info.last_reply = fr_time(); /* @todo - cache this so read() doesn't call time? */
492
493 *retry_ctx_p = id_ctx->retry_ctx;
494 response->uctx = id_ctx->packet;
495 return true;
496 }
497
498 /*
499 * The reply has the correct ID / Code, but it's not the
500 * same as our previous reply: ignore it.
501 */
502 if (memcmp(buffer, id_ctx->response->data, RADIUS_HEADER_LENGTH) != 0) return false;
503
504 /*
505 * Tell the caller that it's a duplicate reply.
506 */
507 *retry_ctx_p = id_ctx->retry_ctx;
508 my->info.last_reply = fr_time(); /* @todo - cache this so read() doesn't call time? */
509 return false;
510}
511
513{
514 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
515 fr_radius_id_ctx_t *id_ctx = retry_ctx->uctx;
516 fr_packet_t *packet = id_ctx->packet;
517
518 fr_assert(id_ctx->packet == retry_ctx->packet_ctx);
519
520 fr_radius_code_id_push(my->codes, id_ctx->packet);
521
522 /*
523 * Free any pending rewrite CTX.
524 */
525 TALLOC_FREE(retry_ctx->rewrite_ctx);
526
527 /*
528 * We're no longer retrying this packet.
529 *
530 * However, we leave id_ctx->request_ctx and id_ctx->packet around, because the other code still
531 * needs it.
532 */
533 id_ctx->request_ctx = NULL;
534 id_ctx->retry_ctx = NULL;
535
536 /*
537 * Tell the application that this packet did not see a reply/
538 */
539 if (my->cfg.packet_cb_cfg.release && (reason == FR_BIO_RETRY_NO_REPLY)) my->cfg.packet_cb_cfg.release(&my->common, packet);
540
541 fr_assert(my->info.outstanding > 0);
542 my->info.outstanding--;
543
544 /*
545 * IO was blocked due to IDs. We now have a free ID, so we resume the normal write process.
546 */
547 if (my->all_ids_used) {
548 my->all_ids_used = false;
549
550 /*
551 * Tell the application to resume writing to the BIO.
552 */
553 if (my->common.cb.write_resume) my->common.cb.write_resume(&my->common);
554
555 } else if (!my->info.outstanding) {
556 my->info.last_idle = fr_time();
557 }
558}
559
560/** Cancel one packet.
561 *
562 * The packet can have a reply, or not. It doesn't matter.
563 *
564 * This also frees any IDs associated with the packet.
565 */
567{
568 fr_radius_id_ctx_t *id_ctx;
569 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
570
571 id_ctx = fr_radius_code_id_find(my->codes, packet->code, packet->id);
572
573 if (!id_ctx || !id_ctx->retry_ctx) return 0;
574 fr_assert(id_ctx->packet == packet);
575
576 if (!my->retry) goto done;
577
578 if (fr_bio_retry_entry_cancel(my->retry, id_ctx->retry_ctx) < 0) return -1;
579
580done:
581 id_ctx->retry_ctx = NULL;
582 id_ctx->packet = NULL;
583
584 return 0;
585}
586
587int fr_radius_client_fd_bio_read(fr_bio_packet_t *bio, void **pctx_p, fr_packet_t **packet_p,
588 UNUSED TALLOC_CTX *out_ctx, fr_pair_list_t *out)
589{
590 ssize_t slen;
591 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
592 fr_packet_t *reply, *original;
593
594 /*
595 * We don't need to set up response.socket for connected bios.
596 */
597 fr_packet_t response = {};
598
599 /*
600 * We read the response packet ctx into our local structure. If we have a real response, we will
601 * swap to using the request context, and not the response context.
602 */
603 slen = fr_bio_read(my->common.bio, &response, &my->buffer, sizeof(my->buffer));
604 if (!slen) return 0;
605
606 if (slen < 0) {
607 fr_assert(slen != fr_bio_error(IO_WOULD_BLOCK));
608 return slen;
609 }
610
611 original = response.uctx;
612
613 /*
614 * Allocate the new request data structure
615 */
616 reply = fr_packet_alloc(original, false);
617 if (!reply) return -1;
618
619 /*
620 * This is for connected sockets.
621 */
622 reply->socket = my->reply_socket;
623 reply->timestamp = fr_time();
624
625 reply->data = talloc_memdup(reply, my->buffer, slen);
626 if (!reply->data) {
627 talloc_free(reply);
628 return -1;
629 }
630 reply->data_len = slen;
631
632 reply->code = reply->data[0];
633 reply->id = reply->data[1];
634 memcpy(reply->vector, reply->data + 4, sizeof(reply->vector));
635
636 /*
637 * If this fails, we're out of memory.
638 */
639 if (fr_radius_decode_simple(original, out, reply->data, reply->data_len,
640 original->vector, (char const *) my->cfg.verify.secret) < 0) {
641 talloc_free(reply);
642 return -1;
643 }
644
645 *pctx_p = original->uctx;
646 *packet_p = reply;
647
648 return 1;
649}
650
652{
653 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
654
655 /*
656 * @todo - add API for ID allocation to track this.
657 */
658 if (!my->retry) return 0;
659
660 return fr_bio_retry_outstanding(my->retry);
661}
662
664{
665 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
666
667 return &my->info;
668}
669
670
672{
673 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
674
675 if (!code || (code >= FR_RADIUS_CODE_MAX)) {
676 fr_strerror_const("Invalid packet code");
677 return -1;
678 }
679
680 if ((id < 0) || (id > 256)) {
681 fr_strerror_const("Invalid ID");
682 return -1;
683 }
684
685 return fr_radius_code_id_force(my->codes, code, id);
686}
687
688/** We failed to connect in the given timeout, the connection is dead.
689 *
690 */
692{
693 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(uctx, fr_radius_client_fd_bio_t);
694
695 fr_assert(!my->retry || (my->info.retry_info->el == el));
696
697 if (my->common.cb.failed) my->common.cb.failed(&my->common);
698}
699
700
702{
703 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(uctx, fr_radius_client_fd_bio_t);
704
705 fr_assert(my->common.cb.connected);
706 fr_assert(!my->retry || (my->info.retry_info->el == el));
707 fr_assert(my->info.fd_info->socket.fd == fd);
708
709 /*
710 * The socket is already connected, tell the application. This happens when the FD bio opens an
711 * unconnected socket. It calls our connected routine before the application has a chance to
712 * call our connect routine.
713 */
714 if (my->info.connected) return;
715
716 /*
717 * We don't pass the callbacks to fr_bio_fd_alloc(), so it can't call our connected routine.
718 * As a result, we have to check if the FD is open, and then call it ourselves.
719 */
720 if (my->info.fd_info->state == FR_BIO_FD_STATE_OPEN) {
722 return;
723 }
724
725 fr_assert(my->info.fd_info->type == FR_BIO_FD_CONNECTED);
726 fr_assert(my->info.fd_info->state == FR_BIO_FD_STATE_CONNECTING);
727
728 /*
729 * Try to connect it. Any magic handling is done in the callbacks.
730 */
731 (void) fr_bio_fd_connect(my->fd);
732}
static int const char char buffer[256]
Definition acutest.h:576
static ssize_t fr_bio_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
Write raw data to a bio.
Definition base.h:172
static ssize_t fr_bio_read(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
Read raw data from a bio.
Definition base.h:149
void * uctx
user ctx, caller can manually set it.
Definition base.h:113
#define fr_bio_error(_x)
Definition base.h:192
fr_bio_packet_signal_t retry
Definition packet.h:82
int fr_bio_retry_entry_init(UNUSED fr_bio_t *bio, fr_bio_retry_entry_t *item, fr_retry_config_t const *cfg)
Set a per-packet retry config.
Definition retry.c:1025
fr_bio_t * fr_bio_retry_alloc(TALLOC_CTX *ctx, size_t max_saved, fr_bio_retry_sent_t sent, fr_bio_retry_response_t response, fr_bio_retry_rewrite_t rewrite, fr_bio_retry_release_t release, fr_bio_retry_config_t const *cfg, fr_bio_t *next)
Allocate a fr_bio_retry_t.
Definition retry.c:1087
size_t fr_bio_retry_outstanding(fr_bio_t *bio)
Definition retry.c:1165
int fr_bio_retry_entry_cancel(fr_bio_t *bio, fr_bio_retry_entry_t *item)
Cancel one item.
Definition retry.c:993
ssize_t fr_bio_retry_rewrite(fr_bio_t *bio, fr_bio_retry_entry_t *item, const void *buffer, size_t size)
Resend a packet.
Definition retry.c:554
fr_bio_retry_info_t const * fr_bio_retry_info(fr_bio_t *bio)
Definition retry.c:1158
void * rewrite_ctx
context specifically for rewriting this packet
Definition retry.c:68
fr_retry_t retry
retry timers and counters
Definition retry.c:70
fr_bio_retry_release_reason_t
Definition retry.h:79
@ FR_BIO_RETRY_NO_REPLY
Definition retry.h:81
ssize_t(* fr_bio_retry_rewrite_t)(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, const void *buffer, size_t size)
Definition retry.h:66
fr_event_list_t * el
event list
Definition retry.h:45
void * packet_ctx
packet_ctx from the write() call
Definition retry.c:66
fr_bio_retry_rewrite_t rewrite
per-packet rewrite callback
Definition retry.c:67
void * uctx
user-writable context
Definition retry.c:65
Definition retry.c:64
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:167
#define RCSID(id)
Definition build.h:483
#define NDEBUG_UNUSED
Definition build.h:326
#define UNUSED
Definition build.h:315
fr_bio_packet_t * fr_radius_client_tcp_bio_alloc(TALLOC_CTX *ctx, fr_radius_client_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
Allocate a RADIUS bio for writing client packets.
Definition client_tcp.c:50
fr_bio_packet_t * fr_radius_client_udp_bio_alloc(TALLOC_CTX *ctx, fr_radius_client_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
Allocate a RADIUS bio for writing client packets.
Definition client_udp.c:51
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition dbuff.h:514
fr_radius_packet_code_t
RADIUS packet codes.
Definition defs.h:31
@ FR_RADIUS_CODE_ACCESS_CHALLENGE
RFC2865 - Access-Challenge.
Definition defs.h:43
@ FR_RADIUS_CODE_ACCESS_REQUEST
RFC2865 - Access-Request.
Definition defs.h:33
@ FR_RADIUS_CODE_DISCONNECT_REQUEST
RFC3575/RFC5176 - Disconnect-Request.
Definition defs.h:46
@ FR_RADIUS_CODE_MAX
Maximum possible protocol code.
Definition defs.h:53
@ FR_RADIUS_CODE_DISCONNECT_ACK
RFC3575/RFC5176 - Disconnect-Ack (positive)
Definition defs.h:47
@ FR_RADIUS_CODE_STATUS_SERVER
RFC2865/RFC5997 - Status Server (request)
Definition defs.h:44
@ FR_RADIUS_CODE_COA_REQUEST
RFC3575/RFC5176 - CoA-Request.
Definition defs.h:49
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
Definition defs.h:34
@ FR_RADIUS_CODE_ACCOUNTING_RESPONSE
RFC2866 - Accounting-Response.
Definition defs.h:37
@ FR_RADIUS_CODE_COA_NAK
RFC3575/RFC5176 - CoA-Nak (not willing to perform)
Definition defs.h:51
@ FR_RADIUS_CODE_COA_ACK
RFC3575/RFC5176 - CoA-Ack (positive)
Definition defs.h:50
@ FR_RADIUS_CODE_DISCONNECT_NAK
RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
Definition defs.h:48
@ FR_RADIUS_CODE_PROTOCOL_ERROR
RFC7930 - Protocol-Error (generic NAK)
Definition defs.h:52
@ FR_RADIUS_CODE_ACCOUNTING_REQUEST
RFC2866 - Accounting-Request.
Definition defs.h:36
@ FR_RADIUS_CODE_ACCESS_REJECT
RFC2865 - Access-Reject.
Definition defs.h:35
#define fr_event_timer_in(...)
Definition event.h:255
fr_bio_t * fr_bio_fd_alloc(TALLOC_CTX *ctx, fr_bio_fd_config_t const *cfg, size_t offset)
Allocate a FD bio.
Definition fd.c:1018
fr_bio_fd_info_t const * fr_bio_fd_info(fr_bio_t *bio)
Returns a pointer to the bio-specific information.
Definition fd.c:1338
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
Definition fd.h:68
@ FR_BIO_FD_STATE_CONNECTING
Definition fd.h:60
@ FR_BIO_FD_STATE_OPEN
error states must be before this
Definition fd.h:59
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
Definition fd.h:82
char const * path
for Unix domain sockets
Definition fd.h:99
char const * filename
for files
Definition fd.h:105
#define fr_bio_fd_connect(_x)
Definition fd.h:149
int socket_type
SOCK_STREAM or SOCK_DGRAM.
Definition fd.h:84
Configuration for sockets.
Definition fd.h:81
fr_bio_shutdown & my
Definition fd_errno.h:59
int fr_bio_free(fr_bio_t *bio)
Free this bio, and everything it calls.
Definition base.c:105
int fr_bio_shutdown(fr_bio_t *bio)
Shut down a set of BIOs.
Definition base.c:141
fr_bio_t * fr_bio_mem_alloc(TALLOC_CTX *ctx, size_t read_size, size_t write_size, fr_bio_t *next)
Allocate a memory buffer bio.
Definition mem.c:727
void fr_bio_packet_init(fr_bio_packet_t *my)
Definition packet.c:177
void fr_bio_packet_connected(fr_bio_t *bio)
Called when a particular BIO is connected.
Definition packet.c:116
talloc_free(reap)
Stores all information relating to an event list.
Definition event.c:411
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition packet.c:38
unsigned int uint32_t
long int ssize_t
unsigned char uint8_t
unsigned long int size_t
static uint32_t fr_nbo_to_uint32(uint8_t const data[static sizeof(uint32_t)])
Read an unsigned 32bit integer from wire format (big endian)
Definition nbo.h:167
static void fr_nbo_from_uint32(uint8_t out[static sizeof(uint32_t)], uint32_t num)
Write out an unsigned 32bit integer in wire format (big endian)
Definition nbo.h:61
#define RADIUS_HEADER_LENGTH
Definition net.h:80
#define RADIUS_AUTH_VECTOR_LENGTH
Definition net.h:89
int fr_radius_sign(uint8_t *packet, uint8_t const *vector, uint8_t const *secret, size_t secret_len)
Sign a previously encoded packet.
Definition base.c:358
int fr_radius_verify(uint8_t *packet, uint8_t const *vector, uint8_t const *secret, size_t secret_len, bool require_message_authenticator, bool limit_proxy_state)
Verify a request / response packet.
Definition base.c:778
ssize_t fr_radius_decode_simple(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t *packet, size_t packet_len, uint8_t const *vector, char const *secret)
Simple wrapper for callers who just need a shared secret.
Definition base.c:1196
ssize_t fr_radius_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_radius_encode_ctx_t *packet_ctx)
Definition base.c:953
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
Definition base.c:112
fr_event_list_t * el
Definition client.h:35
fr_bio_retry_config_t retry_cfg
Definition client.h:40
fr_bio_packet_cb_funcs_t packet_cb_cfg
Definition client.h:42
bool outgoing[FR_RADIUS_CODE_MAX]
allowed outgoing packet types
Definition client.h:49
fr_time_delta_t connection_timeout
Definition client.h:44
fr_radius_id_t * fr_radius_id_alloc(TALLOC_CTX *ctx)
Allocate a tracking structure for one packet code.
Definition id.c:45
static void fr_radius_code_id_push(fr_radius_code_id_t codes, fr_packet_t const *packet)
Definition id.h:74
void * retry_ctx
to find the retry information
Definition id.h:38
void * request_ctx
for the application to track
Definition id.h:35
fr_packet_t * packet
outgoing packet
Definition id.h:36
fr_packet_t * response
response to outgoing packet
Definition id.h:37
static fr_radius_id_ctx_t * fr_radius_code_id_pop(fr_radius_code_id_t codes, fr_packet_t *packet)
Definition id.h:64
static fr_radius_id_ctx_t * fr_radius_code_id_find(fr_radius_code_id_t codes, int code, int id)
Definition id.h:84
static int fr_radius_code_id_force(fr_radius_code_id_t codes, int code, int id)
Definition id.h:94
#define fr_assert(_expr)
Definition rad_assert.h:38
static bool done
Definition radclient.c:80
static int _radius_client_fd_bio_free(fr_radius_client_fd_bio_t *my)
Definition client.c:64
static void radius_client_retry_release(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, UNUSED fr_bio_retry_release_reason_t reason)
static ssize_t radius_client_retry(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, UNUSED const void *buffer, NDEBUG_UNUSED size_t size)
Definition client.c:312
static bool radius_client_retry_response(fr_bio_t *bio, fr_bio_retry_entry_t **retry_ctx_p, UNUSED void *packet_ctx, const void *buffer, UNUSED size_t size)
static void radius_client_retry_sent(fr_bio_t *bio, void *packet_ctx, const void *buffer, UNUSED size_t size, fr_bio_retry_entry_t *retry_ctx)
void fr_radius_client_bio_connect(NDEBUG_UNUSED fr_event_list_t *el, NDEBUG_UNUSED int fd, UNUSED int flags, void *uctx)
Definition client.c:701
static ssize_t radius_client_rewrite_acct(fr_bio_t *bio, fr_bio_retry_entry_t *retry_ctx, UNUSED const void *buffer, NDEBUG_UNUSED size_t size)
Definition client.c:331
static const fr_radius_packet_code_t allowed_replies[FR_RADIUS_CODE_MAX]
Definition client.c:296
int fr_radius_client_fd_bio_cancel(fr_bio_packet_t *bio, fr_packet_t *packet)
Cancel one packet.
Definition client.c:566
size_t offset
offset to Acct-Delay-Time value
Definition client.c:39
fr_bio_packet_t * fr_radius_client_bio_alloc(TALLOC_CTX *ctx, fr_radius_client_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
Definition client.c:50
int fr_radius_client_bio_force_id(fr_bio_packet_t *bio, int code, int id)
Definition client.c:671
uint32_t initial
initial value
Definition client.c:36
static void fr_radius_client_bio_connect_timer(fr_event_list_t *el, fr_time_t now, void *uctx)
int fr_radius_client_fd_bio_read(fr_bio_packet_t *bio, void **pctx_p, fr_packet_t **packet_p, UNUSED TALLOC_CTX *out_ctx, fr_pair_list_t *out)
Definition client.c:587
size_t fr_radius_client_bio_outstanding(fr_bio_packet_t *bio)
Definition client.c:651
fr_radius_client_bio_info_t const * fr_radius_client_bio_info(fr_bio_packet_t *bio)
Definition client.c:663
uint32_t start
Unix time we started sending this packet.
Definition client.c:37
int fr_radius_client_fd_bio_write(fr_radius_client_fd_bio_t *my, void *pctx, fr_packet_t *packet, fr_pair_list_t *list)
Definition client.c:187
fr_radius_client_fd_bio_t * fr_radius_client_fd_bio_alloc(TALLOC_CTX *ctx, size_t read_size, fr_radius_client_config_t *cfg, fr_bio_fd_config_t const *fd_cfg)
Definition client.c:74
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
static int64_t fr_time_to_sec(fr_time_t when)
Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time)
Definition time.h:731
#define fr_time_lteq(_a, _b)
Definition time.h:240
#define fr_time_delta_ispos(_a)
Definition time.h:290
#define fr_time_gt(_a, _b)
Definition time.h:237
"server local" time.
Definition time.h:69
static fr_event_list_t * el
void * uctx
Definition packet.h:79
unsigned int code
Packet code (type).
Definition packet.h:61
fr_socket_t socket
This packet was received on.
Definition packet.h:57
int id
Packet ID (used to link requests/responses).
Definition packet.h:60
uint8_t * data
Packet data (body).
Definition packet.h:63
size_t data_len
Length of packet data.
Definition packet.h:64
uint8_t vector[RADIUS_AUTH_VECTOR_LENGTH]
RADIUS authentication vector.
Definition packet.h:69
fr_time_t timestamp
When we received the packet.
Definition packet.h:58
fr_time_t start
when we started the retransmission
Definition retry.h:53
fr_time_t updated
last update, really a cached "now".
Definition retry.h:56
static void fr_socket_addr_swap(fr_socket_t *dst, fr_socket_t const *src)
Swap src/dst information of a fr_socket_t.
Definition socket.h:121
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_const(_msg)
Definition strerror.h:223
static fr_slen_t data
Definition value.h:1265
static size_t char ** out
Definition value.h:997