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: 33e7a9a90ce1d8b356562a109b1dbd093f191ab5 $
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: 33e7a9a90ce1d8b356562a109b1dbd093f191ab5 $")
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 int i;
68 fr_bio_retry_rewrite_t rewrite = NULL;
69
71
72 my = talloc_zero(ctx, fr_radius_client_fd_bio_t);
73 if (!my) return NULL;
74
75 /*
76 * Allocate tracking for all of the packets.
77 */
78 for (i = 1; i < FR_RADIUS_CODE_MAX; i++) {
79 if (!cfg->outgoing[i]) continue;
80
81 my->codes[i] = fr_radius_id_alloc(my);
82 if (!my->codes[i]) goto fail;
83 }
84
85 my->fd = fr_bio_fd_alloc(my, fd_cfg, 0);
86 if (!my->fd) {
87 fail:
89 return NULL;
90 }
91
92 /*
93 * So that read / write pause / resume callbacks can find us
94 */
95 my->fd->uctx = my;
96
97 my->info.fd_info = fr_bio_fd_info(my->fd);
98 fr_assert(my->info.fd_info != NULL);
99
100 my->reply_socket = my->info.fd_info->socket;
101 if ((my->reply_socket.af == AF_INET) || (my->reply_socket.af == AF_INET6)) {
102 fr_socket_addr_swap(&my->reply_socket, &my->info.fd_info->socket);
103 }
104
105 my->mem = fr_bio_mem_alloc(my, read_size, 2 * 4096, my->fd);
106 if (!my->mem) goto fail;
107
108 my->mem->uctx = my;
109
110 if (cfg->packet_cb_cfg.retry) rewrite = radius_client_retry;
111
112 /*
113 * We allocate a retry BIO even for TCP, as we want to be able to timeout the packets.
114 */
115 if (cfg->retry_cfg.el) {
117 rewrite, radius_client_retry_release, &cfg->retry_cfg, my->mem);
118 if (!my->retry) goto fail;
119
120 my->retry->uctx = my;
121
122 my->info.retry_info = fr_bio_retry_info(my->retry);
123 fr_assert(my->info.retry_info != NULL);
124
125 my->common.bio = my->retry;
126
127 } else {
128 /*
129 * No timers for retries, we just use a memory buffer for outbound packets.
130 */
131 my->common.bio = my->mem;
132 }
133
134 my->cfg = *cfg;
135
136 /*
137 * Inform the packet BIO about our application callbacks.
138 */
139 my->common.cb = cfg->packet_cb_cfg;
140
141 /*
142 * Initialize the packet handlers in each BIO.
143 */
144 fr_bio_packet_init(&my->common);
145
146 /*
147 * Set up the connected status.
148 */
149 my->info.connected = false;
150
151 /*
152 * If we're supposed to be connected (but aren't), then ensure that we don't keep trying to
153 * connect forever.
154 */
155 if ((my->info.fd_info->type == FR_BIO_FD_CONNECTED) && !my->info.connected &&
157 if (fr_timer_in(my, cfg->el->tl, &my->common.ev, cfg->connection_timeout, false,
160 return NULL;
161 }
162 }
163
164 my->proto_ctx = (fr_radius_ctx_t) {
165 .secret = (char const *) my->cfg.verify.secret,
166 .secret_length = my->cfg.verify.secret_len,
167 .secure_transport = false,
168 .proxy_state = my->cfg.proxy_state,
169 };
170
171 my->info.last_idle = fr_time();
172
173 return my;
174}
175
177{
178 ssize_t slen;
179 fr_radius_id_ctx_t *id_ctx;
180
181 fr_assert(!packet->data);
182
183 fr_assert(packet->code > 0);
185 fr_assert(!my->common.write_blocked);
186
187 if (!my->codes[packet->code]) {
188 fr_strerror_printf("Outgoing packet code %s is disallowed by the configuration",
189 fr_radius_packet_name[packet->code]);
190 return fr_bio_error(GENERIC);
191 }
192
193 id_ctx = fr_radius_code_id_pop(my->codes, packet);
194 if (!id_ctx) {
195 /*
196 * Try to cancel the oldest one.
197 */
198 if (!my->retry || fr_bio_retry_entry_cancel(my->retry, NULL) < 1) {
199 all_ids_used:
200 my->all_ids_used = true;
201
202 /*
203 * Tell the application to stop writing data to the BIO.
204 */
205 if (my->common.cb.write_blocked) my->common.cb.write_blocked(&my->common);
206
207 fr_strerror_const("All IDs are in use");
208 return fr_bio_error(GENERIC);
209 }
210
211 id_ctx = fr_radius_code_id_pop(my->codes, packet);
212 if (!id_ctx) goto all_ids_used;
213 }
214 id_ctx->request_ctx = pctx;
215 fr_assert(id_ctx->packet == packet);
216
217 /*
218 * @todo - just create the random auth vector here?
219 */
220 if ((packet->code == FR_RADIUS_CODE_ACCESS_REQUEST) ||
221 (packet->code == FR_RADIUS_CODE_STATUS_SERVER)) {
222 memcpy(my->buffer + 4, packet->vector, sizeof(packet->vector));
223 }
224
225 /*
226 * Encode the packet.
227 */
228 slen = fr_radius_encode(&FR_DBUFF_TMP(my->buffer, sizeof(my->buffer)), list, &(fr_radius_encode_ctx_t) {
229 .common = &my->proto_ctx,
230 .request_authenticator = NULL,
231 .rand_ctx = (fr_fast_rand_t) {
232 .a = fr_rand(),
233 .b = fr_rand(),
234 },
235 .code = packet->code,
236 .id = packet->id,
237 .add_proxy_state = my->cfg.add_proxy_state,
238 });
239 if (slen < 0) {
240 fail:
241 fr_radius_code_id_push(my->codes, packet);
242 return fr_bio_error(GENERIC);
243 }
244
246 packet->data_len = slen;
247
248 slen = fr_radius_sign(my->buffer, NULL,
249 (uint8_t const *) my->cfg.verify.secret, my->cfg.verify.secret_len);
250 if (slen < 0) goto fail;
251
252 /*
253 * The other BIOs will take care of calling fr_radius_client_bio_write_blocked() when the write
254 * is blocked.
255 *
256 * The "next" BIO is a memory one, which can store the entire packet. So write() never returns a
257 * partial packet.
258 */
259 slen = fr_bio_write(my->common.bio, &packet->socket, my->buffer, packet->data_len);
260 if (slen < 0) {
261 fr_assert((slen != fr_bio_error(IO_WOULD_BLOCK)) || my->common.write_blocked);
262
263 fr_radius_code_id_push(my->codes, packet);
264 return slen;
265 }
266
267 fr_assert((size_t) slen == packet->data_len);
268
269 /*
270 * We only allocate packet data after writing it to the socket. If the write fails, we avoid a
271 * memory alloc / free.
272 */
273 packet->data = talloc_array(packet, uint8_t, packet->data_len);
274 if (!packet->data) goto fail;
275
276 /*
277 * Only after successful write do we copy the data back to the packet structure.
278 */
279 memcpy(packet->data, my->buffer, packet->data_len);
280 memcpy(packet->vector, packet->data + 4, RADIUS_AUTH_VECTOR_LENGTH);
281
282 return 0;
283}
284
300
301static 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)
302{
303 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
304 fr_radius_id_ctx_t *id_ctx = retry_ctx->uctx;
305 fr_packet_t *packet = id_ctx->packet;
306
307 fr_assert(packet->data_len == size);
308
309 fr_assert(my->cfg.packet_cb_cfg.retry);
310
311 my->cfg.packet_cb_cfg.retry(&my->common, id_ctx->packet);
312
313 /*
314 * Note do do NOT ball fr_bio_write(), because that will treat the packet as a new one!
315 */
316 return fr_bio_retry_rewrite(bio, retry_ctx, packet->data, packet->data_len);
317}
318
319
321{
322 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
323 fr_radius_client_bio_retry_t *acct = retry_ctx->rewrite_ctx;
324 fr_radius_id_ctx_t *id_ctx = retry_ctx->uctx;
325 fr_radius_id_ctx_t my_id_ctx = *id_ctx;
326 fr_packet_t *packet = id_ctx->packet;
327 uint8_t *ptr = packet->data + acct->offset;
328 uint32_t now, delay;
329
330 fr_assert(packet->data_len == size);
331
332 /*
333 * Change IDs, since we're changing the value of Acct-Delay-Time
334 */
335 fr_radius_code_id_push(my->codes, packet);
336
337 id_ctx = fr_radius_code_id_pop(my->codes, packet);
338 if (!id_ctx) return fr_bio_error(GENERIC); /* at the minimum, we should get the ID we just pushed */
339
340 /*
341 * Update the ID context, and tell the retry context that the ID context has changed.
342 */
343 id_ctx->request_ctx = my_id_ctx.request_ctx;
344 retry_ctx->uctx = id_ctx;
345
346 now = fr_time_to_sec(retry_ctx->retry.updated);
347 fr_assert(now >= acct->start);
348 fr_assert((now - acct->start) < (1 << 20)); /* just for pairanoia */
349
350 delay = acct->initial + (now - acct->start);
351
352 fr_nbo_from_uint32(ptr, delay);
353
354 /*
355 * Sign the updated packet.
356 */
357 (void) fr_radius_sign(packet->data, NULL,
358 (uint8_t const *) my->cfg.verify.secret, my->cfg.verify.secret_len);
359
360 /*
361 * Signal that the packet has been retried.
362 */
363 if (my->cfg.packet_cb_cfg.retry) my->cfg.packet_cb_cfg.retry(&my->common, id_ctx->packet);
364
365 /*
366 * Note do do NOT ball fr_bio_write(), because that will treat the packet as a new one!
367 */
368 return fr_bio_retry_rewrite(bio, retry_ctx, packet->data, packet->data_len);
369}
370
371
372static void radius_client_retry_sent(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size,
373 fr_bio_retry_entry_t *retry_ctx)
374{
375 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
376 fr_radius_id_ctx_t *id_ctx;
377 uint8_t const *data = buffer;
378 uint8_t const *end;
380
381 id_ctx = fr_radius_code_id_find(my->codes, data[0], data[1]);
382 fr_assert(id_ctx != NULL);
383
384 id_ctx->packet = packet_ctx;
385 id_ctx->retry_ctx = retry_ctx;
386
387 retry_ctx->uctx = id_ctx;
388
389 (void) fr_bio_retry_entry_init(bio, retry_ctx, &my->cfg.retry[data[0]]);
390
391 my->info.outstanding++;
392 my->info.last_sent = retry_ctx->retry.start;
393 if (fr_time_lteq(my->info.first_sent, my->info.last_idle)) my->info.first_sent = my->info.last_sent;
394
395 /*
396 * For Accounting-Request packets which have Acct-Delay-Time, we need to track where the
397 * Acct-Delay-Time is in the packet, along with its original value, and then we can use the
398 * #fr_retry_t to discover how many seconds to add to Acct-Delay-Time.
399 */
400 retry_ctx->rewrite = NULL;
401
402 if ((data[0] != FR_RADIUS_CODE_ACCOUNTING_REQUEST) || (my->cfg.retry[FR_RADIUS_CODE_ACCOUNTING_REQUEST].mrc == 1)) return;
403
404 end = data + size;
406
407 /*
408 * Find the Acct-Delay-Time attribute. If it doesn't exist, we don't update it on retransmits.
409 *
410 * @todo - maybe if it doesn't exist, we look for Event-Timestamp? And add one if necessary?
411 */
412 while (data < end) {
413 if (data[0] == FR_ACCT_DELAY_TIME) break;
414
415 data += data[1];
416 }
417
418 if ((data == end) || (data[1] != 6)) return;
419
420 acct = retry_ctx->rewrite_ctx = talloc_zero(my, fr_radius_client_bio_retry_t);
421 if (!acct) return;
422
423 /*
424 * Set up the retry handler with initial data.
425 */
427
428 data += 2;
429
431 acct->start = fr_time_to_sec(retry_ctx->retry.start);
432 acct->offset = (size_t) (data - (uint8_t const *) buffer);
433}
434
435
436static 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)
437{
438 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
439 unsigned int code;
440 uint8_t *data = UNCONST(uint8_t *, buffer); /* @todo - for verify() */
441 fr_radius_id_ctx_t *id_ctx;
442 fr_packet_t *response = packet_ctx;
443
444 /*
445 * We now have a complete packet in our buffer. Check if it is one we expect.
446 */
447 fr_assert(data[0] > 0);
449
450 /*
451 * Is the code an allowed reply code?
452 */
453 code = allowed_replies[data[0]];
454 if (!code) return false;
455
456 /*
457 * It's a reply, but not a permitted reply to a particular request.
458 *
459 * @todo - Status-Server. And for protocol error, look up original packet code
460 */
461 id_ctx = fr_radius_code_id_find(my->codes, code, data[1]);
462 if (!id_ctx) return false;
463
464 /*
465 * No reply yet, verify the response packet, and save it for later.
466 */
467 if (!id_ctx->response) {
469
470 if (fr_radius_verify(data, id_ctx->packet->data + 4,
471 my->cfg.verify.secret, my->cfg.verify.secret_len,
472 my->cfg.verify.require_message_authenticator,
473 my->cfg.verify.limit_proxy_state) < 0) {
474 return false;
475 }
476
477 retry = *retry_ctx_p = id_ctx->retry_ctx;
478
479 if (fr_time_gt(retry->retry.start, my->info.mrs_time)) my->info.mrs_time = retry->retry.start;
480 my->info.last_reply = fr_time(); /* @todo - cache this so read() doesn't call time? */
481
482 *retry_ctx_p = id_ctx->retry_ctx;
483 response->uctx = id_ctx->packet;
484 return true;
485 }
486
487 /*
488 * The reply has the correct ID / Code, but it's not the
489 * same as our previous reply: ignore it.
490 */
491 if (memcmp(buffer, id_ctx->response->data, RADIUS_HEADER_LENGTH) != 0) return false;
492
493 /*
494 * Tell the caller that it's a duplicate reply.
495 */
496 *retry_ctx_p = id_ctx->retry_ctx;
497 my->info.last_reply = fr_time(); /* @todo - cache this so read() doesn't call time? */
498 return false;
499}
500
502{
503 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio->uctx, fr_radius_client_fd_bio_t);
504 fr_radius_id_ctx_t *id_ctx = retry_ctx->uctx;
505 fr_packet_t *packet = id_ctx->packet;
506
507 fr_assert(id_ctx->packet == retry_ctx->packet_ctx);
508
509 fr_radius_code_id_push(my->codes, id_ctx->packet);
510
511 /*
512 * Free any pending rewrite CTX.
513 */
514 TALLOC_FREE(retry_ctx->rewrite_ctx);
515
516 /*
517 * We're no longer retrying this packet.
518 *
519 * However, we leave id_ctx->request_ctx and id_ctx->packet around, because the other code still
520 * needs it.
521 */
522 id_ctx->request_ctx = NULL;
523 id_ctx->retry_ctx = NULL;
524
525 /*
526 * Tell the application that this packet did not see a reply/
527 */
528 if (my->cfg.packet_cb_cfg.release && (reason == FR_BIO_RETRY_NO_REPLY)) my->cfg.packet_cb_cfg.release(&my->common, packet);
529
530 fr_assert(my->info.outstanding > 0);
531 my->info.outstanding--;
532
533 /*
534 * IO was blocked due to IDs. We now have a free ID, so we resume the normal write process.
535 */
536 if (my->all_ids_used) {
537 my->all_ids_used = false;
538
539 /*
540 * Tell the application to resume writing to the BIO.
541 */
542 if (my->common.cb.write_resume) my->common.cb.write_resume(&my->common);
543
544 } else if (!my->info.outstanding) {
545 my->info.last_idle = fr_time();
546 }
547}
548
549/** Cancel one packet.
550 *
551 * The packet can have a reply, or not. It doesn't matter.
552 *
553 * This also frees any IDs associated with the packet.
554 */
556{
557 fr_radius_id_ctx_t *id_ctx;
558 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
559
560 id_ctx = fr_radius_code_id_find(my->codes, packet->code, packet->id);
561
562 if (!id_ctx || !id_ctx->retry_ctx) return 0;
563 fr_assert(id_ctx->packet == packet);
564
565 if (!my->retry) goto done;
566
567 if (fr_bio_retry_entry_cancel(my->retry, id_ctx->retry_ctx) < 0) return -1;
568
569done:
570 id_ctx->retry_ctx = NULL;
571 id_ctx->packet = NULL;
572
573 return 0;
574}
575
576int fr_radius_client_fd_bio_read(fr_bio_packet_t *bio, void **pctx_p, fr_packet_t **packet_p,
577 UNUSED TALLOC_CTX *out_ctx, fr_pair_list_t *out)
578{
579 ssize_t slen;
580 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
581 fr_packet_t *reply, *original;
582
583 /*
584 * We don't need to set up response.socket for connected bios.
585 */
586 fr_packet_t response = {};
587
588 /*
589 * We read the response packet ctx into our local structure. If we have a real response, we will
590 * swap to using the request context, and not the response context.
591 */
592 slen = fr_bio_read(my->common.bio, &response, &my->buffer, sizeof(my->buffer));
593 if (!slen) return 0;
594
595 if (slen < 0) {
596 fr_assert(slen != fr_bio_error(IO_WOULD_BLOCK));
597 return slen;
598 }
599
600 original = response.uctx;
601
602 /*
603 * Allocate the new request data structure
604 */
605 reply = fr_packet_alloc(original, false);
606 if (!reply) return -1;
607
608 /*
609 * This is for connected sockets.
610 */
611 reply->socket = my->reply_socket;
612 reply->timestamp = fr_time();
613
614 reply->data = talloc_memdup(reply, my->buffer, slen);
615 if (!reply->data) {
616 talloc_free(reply);
617 return -1;
618 }
619 reply->data_len = slen;
620
621 reply->code = reply->data[0];
622 reply->id = reply->data[1];
623 memcpy(reply->vector, reply->data + 4, sizeof(reply->vector));
624
625 /*
626 * If this fails, we're out of memory.
627 */
628 if (fr_radius_decode_simple(original, out, reply->data, reply->data_len,
629 original->vector, (char const *) my->cfg.verify.secret) < 0) {
630 talloc_free(reply);
631 return -1;
632 }
633
634 *pctx_p = original->uctx;
635 *packet_p = reply;
636
637 return 1;
638}
639
641{
642 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
643
644 /*
645 * @todo - add API for ID allocation to track this.
646 */
647 if (!my->retry) return 0;
648
649 return fr_bio_retry_outstanding(my->retry);
650}
651
653{
654 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
655
656 return &my->info;
657}
658
659
661{
662 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(bio, fr_radius_client_fd_bio_t);
663
664 if (!code || (code >= FR_RADIUS_CODE_MAX)) {
665 fr_strerror_const("Invalid packet code");
666 return -1;
667 }
668
669 if ((id < 0) || (id > 256)) {
670 fr_strerror_const("Invalid ID");
671 return -1;
672 }
673
674 return fr_radius_code_id_force(my->codes, code, id);
675}
676
677/** We failed to connect in the given timeout, the connection is dead.
678 *
679 */
681{
682 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(uctx, fr_radius_client_fd_bio_t);
683
684 fr_assert(!my->retry || (my->info.retry_info->el->tl == tl));
685
686 if (my->common.cb.failed) my->common.cb.failed(&my->common);
687}
688
689
691{
692 fr_radius_client_fd_bio_t *my = talloc_get_type_abort(uctx, fr_radius_client_fd_bio_t);
693
694 fr_assert(my->common.cb.connected);
695 fr_assert(!my->retry || (my->info.retry_info->el == el));
696 fr_assert(my->info.fd_info->socket.fd == fd);
697
698 /*
699 * The socket is already connected, tell the application. This happens when the FD bio opens an
700 * unconnected socket. It calls our connected routine before the application has a chance to
701 * call our connect routine.
702 */
703 if (my->info.connected) return;
704
705 /*
706 * We don't pass the callbacks to fr_bio_fd_alloc(), so it can't call our connected routine.
707 * As a result, we have to check if the FD is open, and then call it ourselves.
708 */
709 if (my->info.fd_info->state == FR_BIO_FD_STATE_OPEN) {
711 return;
712 }
713
714 fr_assert(my->info.fd_info->type == FR_BIO_FD_CONNECTED);
715 fr_assert(my->info.fd_info->state == FR_BIO_FD_STATE_CONNECTING);
716
717 /*
718 * Try to connect it. Any magic handling is done in the callbacks.
719 */
720 (void) fr_bio_fd_connect(my->fd);
721}
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:184
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:161
void * uctx
user ctx, caller can manually set it.
Definition base.h:114
#define fr_bio_error(_x)
Definition base.h:200
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:839
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:891
size_t fr_bio_retry_outstanding(fr_bio_t *bio)
Definition retry.c:987
int fr_bio_retry_entry_cancel(fr_bio_t *bio, fr_bio_retry_entry_t *item)
Cancel one item.
Definition retry.c:807
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:425
fr_bio_retry_info_t const * fr_bio_retry_info(fr_bio_t *bio)
Definition retry.c:980
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:485
#define NDEBUG_UNUSED
Definition build.h:328
#define UNUSED
Definition build.h:317
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
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:968
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:1284
@ 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:81
char const * path
for Unix domain sockets
Definition fd.h:102
char const * filename
for files
Definition fd.h:108
#define fr_bio_fd_connect(_x)
Definition fd.h:153
int socket_type
SOCK_STREAM or SOCK_DGRAM.
Definition fd.h:83
Configuration for sockets.
Definition fd.h:80
void fr_bio_shutdown & my
Definition fd_errno.h:70
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:723
void fr_bio_packet_init(fr_bio_packet_t *my)
Definition packet.c:182
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:377
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:360
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:799
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:1213
ssize_t fr_radius_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_radius_encode_ctx_t *packet_ctx)
Definition base.c:974
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
Definition base.c:114
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:81
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:301
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:690
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:320
static const fr_radius_packet_code_t allowed_replies[FR_RADIUS_CODE_MAX]
Definition client.c:285
int fr_radius_client_fd_bio_cancel(fr_bio_packet_t *bio, fr_packet_t *packet)
Cancel one packet.
Definition client.c:555
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
static void fr_radius_client_bio_connect_timer(fr_timer_list_t *tl, fr_time_t now, void *uctx)
int fr_radius_client_bio_force_id(fr_bio_packet_t *bio, int code, int id)
Definition client.c:660
uint32_t initial
initial value
Definition client.c:36
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:576
size_t fr_radius_client_bio_outstanding(fr_bio_packet_t *bio)
Definition client.c:640
fr_radius_client_bio_info_t const * fr_radius_client_bio_info(fr_bio_packet_t *bio)
Definition client.c:652
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:176
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:64
#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
An event timer list.
Definition timer.c:50
#define fr_timer_in(...)
Definition timer.h:87
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:1291
static size_t char ** out
Definition value.h:1023