Coverage Report

Created: 2026-05-11 06:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/protocols/radius/list.c
Line
Count
Source
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: 23c34a82cf085a4570b3d63f01ab92fa520b588e $
19
 *
20
 * @file protocols/radius/list.c
21
 * @brief Functions to deal with outgoing lists / sets of packets.
22
 *
23
 * @copyright 2000-2017 The FreeRADIUS server project
24
 */
25
26
RCSID("$Id: 23c34a82cf085a4570b3d63f01ab92fa520b588e $")
27
28
#include "radius.h"
29
#include "tcp.h"
30
#include "list.h"
31
32
#include <fcntl.h>
33
#include <freeradius-devel/util/udp.h>
34
#include <freeradius-devel/util/syserror.h>
35
36
/*
37
 *  See if two packets are identical.
38
 *
39
 *  Note that we do NOT compare the authentication vectors.
40
 *  That's because if the authentication vector is different,
41
 *  it means that the NAS has given up on the earlier request.
42
 */
43
int8_t fr_packet_cmp(void const *a_v, void const *b_v)
44
0
{
45
0
  fr_packet_t const *a = a_v, *b = b_v;
46
0
  int8_t ret;
47
48
  /*
49
   *  256-way fanout for RADIUS IDs, then by FD.  And then
50
   *  since there are sometimes multiple clients for one FD,
51
   *  source port.  The comparison on dst_port is largely
52
   *  redundant with the comparison on FD, but it's not
53
   *  _completely_ redundant.
54
   */
55
0
  CMP_RETURN(a, b, id);
56
0
  CMP_RETURN(a, b, socket.fd);
57
0
  CMP_RETURN(a, b, socket.inet.src_port);
58
0
  CMP_RETURN(a, b, socket.inet.dst_port);
59
60
  /*
61
   *  Usually many client IPs, and few server IPs
62
   */
63
0
  ret = fr_ipaddr_cmp(&a->socket.inet.src_ipaddr, &b->socket.inet.src_ipaddr);
64
0
  if (ret != 0) return ret;
65
66
0
  return fr_ipaddr_cmp(&a->socket.inet.dst_ipaddr, &b->socket.inet.dst_ipaddr);
67
0
}
68
69
/*
70
 *  Create a fake "request" from a reply, for later lookup.
71
 */
72
void fr_request_from_reply(fr_packet_t *request,
73
         fr_packet_t const *reply)
74
0
{
75
0
  fr_socket_addr_swap(&request->socket, &reply->socket);
76
0
  request->id = reply->id;
77
0
}
78
79
/*
80
 *  We need to keep track of the socket & it's IP/port.
81
 */
82
typedef struct {
83
  fr_socket_t socket;
84
85
  int   src_any;
86
  int   dst_any;
87
  void    *ctx;
88
89
  uint32_t  num_outgoing;
90
91
  bool    dont_use;
92
93
  uint8_t   id[32];
94
95
  uint8_t   *buffer;
96
  size_t    bufsize;
97
  size_t    used;
98
} fr_packet_socket_t;
99
100
101
0
#define FNV_MAGIC_PRIME (0x01000193)
102
0
#define MAX_SOCKETS (256)
103
0
#define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
104
0
#define SOCK2OFFSET(_sockfd) ((_sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
105
106
/*
107
 *  Structure defining a list of packets (incoming or outgoing)
108
 *  that should be managed.
109
 */
110
struct fr_packet_list_s {
111
  fr_rb_tree_t  *tree;
112
113
  int   alloc_id;
114
  uint32_t  num_outgoing;
115
  int   last_recv;
116
  int   num_sockets;
117
118
  fr_packet_socket_t sockets[MAX_SOCKETS];
119
};
120
121
122
/*
123
 *  Ugh.  Doing this on every sent/received packet is not nice.
124
 */
125
static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl, int sockfd)
126
0
{
127
0
  int i, start;
128
129
0
  i = start = SOCK2OFFSET(sockfd);
130
131
0
  do {     /* make this hack slightly more efficient */
132
0
    if (pl->sockets[i].socket.fd == sockfd) return &pl->sockets[i];
133
134
0
    i = (i + 1) & SOCKOFFSET_MASK;
135
0
  } while (i != start);
136
137
0
  return NULL;
138
0
}
139
140
bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
141
0
{
142
0
  fr_packet_socket_t *ps;
143
144
0
  if (!pl) {
145
0
    fr_strerror_const("Invalid argument");
146
0
    return false;
147
0
  }
148
149
0
  ps = fr_socket_find(pl, sockfd);
150
0
  if (!ps) {
151
0
    fr_strerror_const("No such socket");
152
0
    return false;
153
0
  }
154
155
0
  ps->dont_use = true;
156
0
  return true;
157
0
}
158
159
bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
160
0
{
161
0
  fr_packet_socket_t *ps;
162
163
0
  if (!pl) return false;
164
165
0
  ps = fr_socket_find(pl, sockfd);
166
0
  if (!ps) return false;
167
168
0
  ps->dont_use = false;
169
0
  return true;
170
0
}
171
172
173
bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd)
174
0
{
175
0
  fr_packet_socket_t *ps;
176
177
0
  if (!pl) return false;
178
179
0
  ps = fr_socket_find(pl, sockfd);
180
0
  if (!ps) return false;
181
182
0
  if (ps->num_outgoing != 0) return false;
183
184
0
  TALLOC_FREE(ps->buffer);
185
186
0
  ps->socket.fd = -1;
187
0
  pl->num_sockets--;
188
189
0
  return true;
190
0
}
191
192
193
bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
194
            fr_ipaddr_t *dst_ipaddr, uint16_t dst_port,
195
            void *ctx)
196
0
{
197
0
  int i, start;
198
0
  struct sockaddr_storage src;
199
0
  socklen_t   sizeof_src;
200
0
  fr_packet_socket_t  *ps;
201
202
0
  if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
203
0
    fr_strerror_const("Invalid argument");
204
0
    return false;
205
0
  }
206
207
0
  if (pl->num_sockets >= MAX_SOCKETS) {
208
0
    fr_strerror_const("Too many open sockets");
209
0
    return false;
210
0
  }
211
212
0
  ps = NULL;
213
0
  i = start = SOCK2OFFSET(sockfd);
214
215
0
  do {
216
0
    if (pl->sockets[i].socket.fd == -1) {
217
0
      ps =  &pl->sockets[i];
218
0
      break;
219
0
    }
220
221
0
    i = (i + 1) & SOCKOFFSET_MASK;
222
0
  } while (i != start);
223
224
0
  if (!ps) {
225
0
    fr_strerror_const("All socket entries are full");
226
0
    return false;
227
0
  }
228
229
0
  memset(ps, 0, sizeof(*ps));
230
0
  ps->socket.fd = -1;
231
0
  ps->ctx = ctx;
232
0
  ps->socket.type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM;
233
234
  /*
235
   *  Get address family, etc. first, so we know if we
236
   *  need to do udpfromto.
237
   *
238
   *  FIXME: udpfromto also does this, but it's not
239
   *  a critical problem.
240
   */
241
0
  sizeof_src = sizeof(src);
242
0
  memset(&src, 0, sizeof_src);
243
0
  if (getsockname(sockfd, (struct sockaddr *) &src, &sizeof_src) < 0) {
244
0
    fr_strerror_printf("%s", fr_syserror(errno));
245
0
    return false;
246
0
  }
247
248
0
  if (fr_ipaddr_from_sockaddr(&ps->socket.inet.src_ipaddr, &ps->socket.inet.src_port, &src, sizeof_src) < 0) {
249
0
    fr_strerror_const("Failed to get IP");
250
0
    return false;
251
0
  }
252
253
0
  ps->socket.inet.dst_ipaddr = *dst_ipaddr;
254
0
  ps->socket.inet.dst_port = dst_port;
255
256
0
  ps->src_any = fr_ipaddr_is_inaddr_any(&ps->socket.inet.src_ipaddr);
257
0
  if (ps->src_any < 0) return false;
258
259
0
  ps->dst_any = fr_ipaddr_is_inaddr_any(&ps->socket.inet.dst_ipaddr);
260
0
  if (ps->dst_any < 0) return false;
261
262
0
  if (proto == IPPROTO_TCP) {
263
0
    ps->buffer = talloc_array(pl, uint8_t, RADIUS_MAX_PACKET_SIZE);
264
0
    if (ps->buffer) return false;
265
266
0
    ps->bufsize = RADIUS_MAX_PACKET_SIZE;
267
0
    ps->used = 0;
268
0
  }
269
270
  /*
271
   *  As the last step before returning.
272
   */
273
0
  ps->socket.fd = sockfd;
274
0
  pl->num_sockets++;
275
276
0
  return true;
277
0
}
278
279
void fr_packet_list_free(fr_packet_list_t *pl)
280
0
{
281
0
  if (!pl) return;
282
283
0
  talloc_free(pl->tree);
284
0
  talloc_free(pl);
285
0
}
286
287
288
/*
289
 *  Caller is responsible for managing the packet entries.
290
 */
291
fr_packet_list_t *fr_packet_list_create(int alloc_id)
292
0
{
293
0
  int i;
294
0
  fr_packet_list_t  *pl;
295
296
0
  pl = talloc_zero(NULL, fr_packet_list_t);
297
0
  if (!pl) return NULL;
298
0
  pl->tree = fr_rb_inline_alloc(pl, fr_packet_t, node, fr_packet_cmp, NULL); /* elements not talloc safe */
299
0
  if (!pl->tree) {
300
0
    fr_packet_list_free(pl);
301
0
    return NULL;
302
0
  }
303
304
0
  for (i = 0; i < MAX_SOCKETS; i++) {
305
0
    pl->sockets[i].socket.fd = -1;
306
0
  }
307
308
0
  pl->alloc_id = alloc_id;
309
310
0
  return pl;
311
0
}
312
313
314
/*
315
 *  If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
316
 *  be called before inserting the packet into the list!
317
 */
318
bool fr_packet_list_insert(fr_packet_list_t *pl,
319
          fr_packet_t *request)
320
0
{
321
0
  if (!pl || !request) return 0;
322
323
0
  return fr_rb_insert(pl->tree, request);
324
0
}
325
326
fr_packet_t *fr_packet_list_find(fr_packet_list_t *pl, fr_packet_t *request)
327
0
{
328
0
  if (!pl || !request) return 0;
329
330
0
  return fr_rb_find(pl->tree, request);
331
0
}
332
333
334
/*
335
 *  This presumes that the reply has dst_ipaddr && dst_port set up
336
 *  correctly (i.e. real IP, or "*").
337
 */
338
fr_packet_t *fr_packet_list_find_byreply(fr_packet_list_t *pl, fr_packet_t *reply)
339
0
{
340
0
  fr_packet_t my_request, *request;
341
0
  fr_packet_socket_t *ps;
342
343
0
  if (!pl || !reply) return NULL;
344
345
0
  ps = fr_socket_find(pl, reply->socket.fd);
346
0
  if (!ps) return NULL;
347
348
  /*
349
   *  TCP sockets are always bound to the correct src/dst IP/port
350
   */
351
0
  if (ps->socket.type == SOCK_STREAM) {
352
0
    fr_socket_addr_swap(&reply->socket, &ps->socket);
353
0
    my_request.socket = ps->socket;
354
0
  } else {
355
0
    my_request.socket = ps->socket;
356
357
0
    if (!ps->src_any) my_request.socket.inet.src_ipaddr = reply->socket.inet.dst_ipaddr;
358
0
    my_request.socket.inet.dst_ipaddr = reply->socket.inet.src_ipaddr;
359
0
    my_request.socket.inet.dst_port = reply->socket.inet.src_port;
360
0
  }
361
362
  /*
363
   *  Initialize request from reply, AND from the source
364
   *  IP & port of this socket.  The client may have bound
365
   *  the socket to 0, in which case it's some random port,
366
   *  that is NOT in the original request->socket.inet.src_port.
367
   */
368
0
  my_request.socket.fd = reply->socket.fd;
369
0
  my_request.id = reply->id;
370
0
  request = &my_request;
371
372
0
  return fr_rb_find(pl->tree, request);
373
0
}
374
375
376
bool fr_packet_list_yank(fr_packet_list_t *pl, fr_packet_t *request)
377
0
{
378
0
  if (!pl || !request) return false;
379
380
0
  return fr_rb_delete(pl->tree, request);
381
0
}
382
383
uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl)
384
0
{
385
0
  if (!pl) return 0;
386
387
0
  return fr_rb_num_elements(pl->tree);
388
0
}
389
390
391
/*
392
 *  1 == ID was allocated & assigned
393
 *  0 == couldn't allocate ID.
394
 *
395
 *  Note that this ALSO assigns a socket to use, and updates
396
 *  packet->request->socket.inet.src_ipaddr && packet->request->socket.inet.src_port
397
 *
398
 *  In multi-threaded systems, the calls to id_alloc && id_free
399
 *  should be protected by a mutex.  This does NOT have to be
400
 *  the same mutex as the one protecting the insert/find/yank
401
 *  calls!
402
 *
403
 *  We assume that the packet has dst_ipaddr && dst_port
404
 *  already initialized.  We will use those to find an
405
 *  outgoing socket.  The request MAY also have src_ipaddr set.
406
 *
407
 *  We also assume that the sender doesn't care which protocol
408
 *  should be used.
409
 */
410
bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
411
          fr_packet_t *request, void **pctx)
412
0
{
413
0
  int i, j, k, fd, id, start_i, start_j, start_k;
414
0
  int src_any = 0;
415
0
  int type;
416
0
  fr_packet_socket_t *ps= NULL;
417
418
0
  if ((request->socket.inet.dst_ipaddr.af == AF_UNSPEC) ||
419
0
      (request->socket.inet.dst_port == 0)) {
420
0
    fr_strerror_const("No destination address/port specified");
421
0
    return false;
422
0
  }
423
424
  /*
425
   *  Special case: unspec == "don't care"
426
   */
427
0
  if (request->socket.inet.src_ipaddr.af == AF_UNSPEC) {
428
0
    memset(&request->socket.inet.src_ipaddr, 0, sizeof(request->socket.inet.src_ipaddr));
429
0
    request->socket.inet.src_ipaddr.af = request->socket.inet.dst_ipaddr.af;
430
0
  }
431
432
0
  src_any = fr_ipaddr_is_inaddr_any(&request->socket.inet.src_ipaddr);
433
0
  if (src_any < 0) {
434
0
    fr_strerror_const("Can't check src_ipaddr");
435
0
    return false;
436
0
  }
437
438
  /*
439
   *  MUST specify a destination address.
440
   */
441
0
  if (fr_ipaddr_is_inaddr_any(&request->socket.inet.dst_ipaddr) != 0) {
442
0
    fr_strerror_const("Must specify a dst_ipaddr");
443
0
    return false;
444
0
  }
445
446
  /*
447
   *  FIXME: Go to an LRU system.  This prevents ID reuse
448
   *  for as long as possible.  The main problem with that
449
   *  approach is that it requires us to populate the
450
   *  LRU/FIFO when we add a new socket, or a new destination,
451
   *  which can be expensive.
452
   *
453
   *  The LRU can be avoided if the caller takes care to free
454
   *  Id's only when all responses have been received, OR after
455
   *  a timeout.
456
   *
457
   *  Right now, the random approach is almost OK... it's
458
   *  brute-force over all of the available ID's, BUT using
459
   *  random numbers for everything spreads the load a bit.
460
   *
461
   *  The old method had a hash lookup on allocation AND
462
   *  on free.  The new method has brute-force on allocation,
463
   *  and near-zero cost on free.
464
   */
465
466
0
  id = fd = -1;
467
0
  if (request->id >= 0 && request->id < 256)
468
0
    id = request->id;
469
0
  start_i = fr_rand() & SOCKOFFSET_MASK;
470
471
0
  type = (proto == IPPROTO_TCP) ? SOCK_STREAM : SOCK_DGRAM;
472
473
0
#define ID_i ((i + start_i) & SOCKOFFSET_MASK)
474
0
  for (i = 0; i < MAX_SOCKETS; i++) {
475
0
    if (pl->sockets[ID_i].socket.fd == -1) continue; /* paranoia */
476
477
0
    ps = &(pl->sockets[ID_i]);
478
479
    /*
480
     *  This socket is marked as "don't use for new
481
     *  packets".  But we can still receive packets
482
     *  that are outstanding.
483
     */
484
0
    if (ps->dont_use) continue;
485
486
    /*
487
     *  All IDs are allocated: ignore it.
488
     */
489
0
    if (ps->num_outgoing == 256) continue;
490
491
0
    if (ps->socket.type != type) continue;
492
493
    /*
494
     *  Address families don't match, skip it.
495
     */
496
0
    if (ps->socket.inet.src_ipaddr.af != request->socket.inet.dst_ipaddr.af) continue;
497
498
    /*
499
     *  MUST match dst port, if we have one.
500
     */
501
0
    if ((ps->socket.inet.dst_port != 0) &&
502
0
        (ps->socket.inet.dst_port != request->socket.inet.dst_port)) continue;
503
504
    /*
505
     *  MUST match requested src port, if one has been given.
506
     */
507
0
    if ((request->socket.inet.src_port != 0) &&
508
0
        (ps->socket.inet.src_port != request->socket.inet.src_port)) continue;
509
510
    /*
511
     *  We don't care about the source IP, but this
512
     *  socket is link local, and the requested
513
     *  destination is not link local.  Ignore it.
514
     */
515
0
    if (src_any && (ps->socket.inet.src_ipaddr.af == AF_INET) &&
516
0
        (((ps->socket.inet.src_ipaddr.addr.v4.s_addr >> 24) & 0xff) == 127) &&
517
0
        (((request->socket.inet.dst_ipaddr.addr.v4.s_addr >> 24) & 0xff) != 127)) continue;
518
519
    /*
520
     *  We're sourcing from *, and they asked for a
521
     *  specific source address: ignore it.
522
     */
523
0
    if (ps->src_any && !src_any) continue;
524
525
    /*
526
     *  We're sourcing from a specific IP, and they
527
     *  asked for a source IP that isn't us: ignore
528
     *  it.
529
     */
530
0
    if (!ps->src_any && !src_any &&
531
0
        (fr_ipaddr_cmp(&request->socket.inet.src_ipaddr,
532
0
           &ps->socket.inet.src_ipaddr) != 0)) continue;
533
534
    /*
535
     *  UDP sockets are allowed to match
536
     *  destination IPs exactly, OR a socket
537
     *  with destination * is allowed to match
538
     *  any requested destination.
539
     *
540
     *  TCP sockets must match the destination
541
     *  exactly.  They *always* have dst_any=0,
542
     *  so the first check always matches.
543
     */
544
0
    if (!ps->dst_any &&
545
0
        (fr_ipaddr_cmp(&request->socket.inet.dst_ipaddr,
546
0
           &ps->socket.inet.dst_ipaddr) != 0)) continue;
547
548
    /*
549
     *  Otherwise, this socket is OK to use.
550
     */
551
552
    /*
553
     *  An explicit ID was requested
554
     */
555
556
0
    if (id != -1) {
557
0
      if  ((ps->id[(id >> 3) & 0x1f] & (1 << (id & 0x07))) != 0) continue;
558
559
0
      ps->id[(id >> 3) & 0x1f] |= (1 << (id & 0x07));
560
0
      fd = i;
561
0
      break;
562
0
    }
563
564
    /*
565
     *  Look for a free Id, starting from a random number.
566
     */
567
0
    start_j = fr_rand() & 0x1f;
568
0
#define ID_j ((j + start_j) & 0x1f)
569
0
    for (j = 0; j < 32; j++) {
570
0
      if (ps->id[ID_j] == 0xff) continue;
571
572
573
0
      start_k = fr_rand() & 0x07;
574
0
#define ID_k ((k + start_k) & 0x07)
575
0
      for (k = 0; k < 8; k++) {
576
0
        if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
577
578
0
        ps->id[ID_j] |= (1 << ID_k);
579
0
        id = (ID_j * 8) + ID_k;
580
0
        fd = i;
581
0
        break;
582
0
      }
583
0
      if (fd >= 0) break;
584
0
    }
585
0
#undef ID_i
586
0
#undef ID_j
587
0
#undef ID_k
588
0
    break;
589
0
  }
590
591
  /*
592
   *  Ask the caller to allocate a new ID.
593
   */
594
0
  if (fd < 0) {
595
0
    fr_strerror_const("Failed finding socket, caller must allocate a new one");
596
0
    return false;
597
0
  }
598
599
  /*
600
   *  Set the ID, source IP, and source port.
601
   */
602
0
  request->id = id;
603
604
0
  request->socket.fd = ps->socket.fd;
605
0
  request->socket.inet.src_ipaddr = ps->socket.inet.src_ipaddr;
606
0
  request->socket.inet.src_port = ps->socket.inet.src_port;
607
608
  /*
609
   *  If we managed to insert it, we're done.
610
   */
611
0
  if (fr_packet_list_insert(pl, request)) {
612
0
    if (pctx) *pctx = ps->ctx;
613
0
    ps->num_outgoing++;
614
0
    pl->num_outgoing++;
615
0
    return true;
616
0
  }
617
618
  /*
619
   *  Mark the ID as free.  This is the one line from
620
   *  id_free() that we care about here.
621
   */
622
0
  ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
623
624
0
  request->id = -1;
625
0
  request->socket.fd = -1;
626
0
  request->socket.inet.src_ipaddr.af = AF_UNSPEC;
627
0
  request->socket.inet.src_port = 0;
628
629
0
  return false;
630
0
}
631
632
/*
633
 *  Should be called AFTER yanking it from the list, so that
634
 *  any newly inserted entries don't collide with this one.
635
 */
636
bool fr_packet_list_id_free(fr_packet_list_t *pl,
637
          fr_packet_t *request, bool yank)
638
0
{
639
0
  fr_packet_socket_t *ps;
640
641
0
  if (!pl || !request) return false;
642
643
0
  if (yank && !fr_packet_list_yank(pl, request)) return false;
644
645
0
  ps = fr_socket_find(pl, request->socket.fd);
646
0
  if (!ps) return false;
647
648
0
  ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
649
650
0
  ps->num_outgoing--;
651
0
  pl->num_outgoing--;
652
653
0
  request->id = -1;
654
0
  request->socket.inet.src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */
655
0
  request->socket.inet.src_port = 0;
656
657
0
  return true;
658
0
}
659
660
int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
661
0
{
662
0
  int i, maxfd;
663
664
0
  if (!pl || !set) return 0;
665
666
0
  maxfd = -1;
667
668
0
  for (i = 0; i < MAX_SOCKETS; i++) {
669
0
    if (pl->sockets[i].socket.fd == -1) continue;
670
0
    FD_SET(pl->sockets[i].socket.fd, set);
671
0
    if (pl->sockets[i].socket.fd > maxfd) {
672
0
      maxfd = pl->sockets[i].socket.fd;
673
0
    }
674
0
  }
675
676
0
  if (maxfd < 0) return -1;
677
678
0
  return maxfd + 1;
679
0
}
680
681
/*
682
 *  Round-robins the receivers, without priority.
683
 *
684
 *  FIXME: Add socket.fd, if -1, do round-robin, else do socket.fd
685
 *    IF in fdset.
686
 */
687
int fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set, TALLOC_CTX *ctx, fr_packet_t **packet_p, uint32_t max_attributes, bool require_message_authenticator)
688
0
{
689
0
  int start;
690
0
  fr_packet_t *packet;
691
692
0
  if (!pl || !set) return 0;
693
694
0
  start = pl->last_recv;
695
0
  do {
696
0
    fr_packet_socket_t *ps;
697
698
0
    start++;
699
0
    start &= SOCKOFFSET_MASK;
700
701
0
    if (pl->sockets[start].socket.fd == -1) continue;
702
703
0
    if (!FD_ISSET(pl->sockets[start].socket.fd, set)) continue;
704
705
0
    if (pl->sockets[start].socket.type == SOCK_STREAM) {
706
0
      int rcode;
707
0
      ps = &pl->sockets[start];
708
709
0
      rcode = fr_tcp_read_packet(ps->socket.fd, ps->buffer, ps->bufsize,
710
0
               &ps->used, max_attributes, require_message_authenticator);
711
0
      if (rcode <= 0) return rcode;
712
713
0
      fr_assert(ps->used >= RADIUS_HEADER_LENGTH);
714
715
0
      packet = fr_packet_alloc(ctx, false);
716
0
      if (!packet) return -1;
717
718
0
      packet->data = talloc_memdup(packet, ps->buffer, rcode);
719
0
      if (!packet->data) {
720
0
        talloc_free(packet);
721
0
        return -1;
722
0
      }
723
724
0
      packet->data_len = rcode;
725
0
      packet->socket = ps->socket;
726
727
0
      if (ps->used == (size_t) rcode) {
728
0
        ps->used = 0;
729
0
      } else {
730
0
        fr_assert(ps->used > (size_t) rcode);
731
732
0
        memmove(ps->buffer, ps->buffer + rcode, ps->used - rcode);
733
0
        ps->used -= rcode;
734
0
      }
735
736
0
    } else {
737
0
      packet = fr_packet_recv(ctx, pl->sockets[start].socket.fd, UDP_FLAGS_NONE,
738
0
                   max_attributes, require_message_authenticator);
739
0
    }
740
0
    if (!packet) continue;
741
742
    /*
743
     *  Call fr_packet_list_find_byreply().  If it
744
     *  doesn't find anything, discard the reply.
745
     */
746
747
0
    pl->last_recv = start;
748
0
    packet->socket.type = pl->sockets[start].socket.type;
749
0
    *packet_p = packet;
750
0
    return 0;
751
0
  } while (start != pl->last_recv);
752
753
0
  return 0;
754
0
}
755
756
uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl)
757
0
{
758
0
  uint32_t num_elements;
759
760
0
  if (!pl) return 0;
761
762
0
  num_elements = fr_rb_num_elements(pl->tree);
763
0
  if (num_elements < pl->num_outgoing) return 0; /* panic! */
764
765
0
  return num_elements - pl->num_outgoing;
766
0
}
767
768
uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl)
769
0
{
770
0
  if (!pl) return 0;
771
772
0
  return pl->num_outgoing;
773
0
}