The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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  */
25 RCSID("$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 
35 typedef 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 
42 static 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);
44 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);
46 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);
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:
98  talloc_free(my);
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 &&
169  if (fr_event_timer_in(my, cfg->el, &my->common.ev, cfg->connection_timeout, fr_radius_client_bio_connect_timer, my) < 0) {
170  talloc_free(my);
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);
195  fr_assert(packet->code < FR_RADIUS_CODE_MAX);
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 
300 
302 
305 
308 
310 };
311 
312 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)
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 
383 static 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  */
437  retry_ctx->rewrite = radius_client_rewrite_acct;
438 
439  data += 2;
440 
441  acct->initial = fr_nbo_to_uint32(data);
442  acct->start = fr_time_to_sec(retry_ctx->retry.start);
443  acct->offset = (size_t) (data - (uint8_t const *) buffer);
444 }
445 
446 
447 static 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) {
479  fr_bio_retry_entry_t *retry;
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 
580 done:
581  id_ctx->retry_ctx = NULL;
582  id_ctx->packet = NULL;
583 
584  return 0;
585 }
586 
587 int 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:574
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
Definition: base.h:112
fr_bio_packet_signal_t retry
Definition: packet.h:82
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
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
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:165
#define RCSID(id)
Definition: build.h:481
#define NDEBUG_UNUSED
Definition: build.h:324
#define UNUSED
Definition: build.h:313
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_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
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:1012
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:1323
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
Definition: fd.h:65
@ FR_BIO_FD_STATE_CONNECTING
Definition: fd.h:58
@ FR_BIO_FD_STATE_OPEN
error states must be before this
Definition: fd.h:57
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
Definition: fd.h:79
char const * path
for Unix domain sockets
Definition: fd.h:94
char const * filename
for files
Definition: fd.h:100
#define fr_bio_fd_connect(_x)
Definition: fd.h:134
int socket_type
SOCK_STREAM or SOCK_DGRAM.
Definition: fd.h:81
Configuration for sockets.
Definition: fd.h:78
fr_bio_shutdown & my
Definition: fd_errno.h:59
if(rcode > 0)
Definition: fd_read.h:9
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
Definition: merged_model.c:33
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
unsigned long int size_t
Definition: merged_model.c:25
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:165
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:777
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:1195
ssize_t fr_radius_encode(fr_dbuff_t *dbuff, fr_pair_list_t *vps, fr_radius_encode_ctx_t *packet_ctx)
Definition: base.c:952
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
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
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_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
static bool done
Definition: radclient.c:80
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
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
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 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
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
fr_radius_client_bio_info_t const * fr_radius_client_bio_info(fr_bio_packet_t *bio)
Definition: client.c:663
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
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_assert(0)
#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