Coverage Report

Created: 2023-12-08 06:56

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