Remove "hash" from RADIUS_PACKET
[freeradius.git] / src / lib / packet.c
1 /*
2  * packet.c     Generic packet manipulation functions.
3  *
4  * Version:     $Id$
5  *
6  *   This library is free software; you can redistribute it and/or
7  *   modify it under the terms of the GNU Lesser General Public
8  *   License as published by the Free Software Foundation; either
9  *   version 2.1 of the License, or (at your option) any later version.
10  *
11  *   This library is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  *   Lesser General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Lesser General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000-2006  The FreeRADIUS server project
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/libradius.h>
27
28 #ifdef WITH_UDPFROMTO
29 #include        <freeradius-devel/udpfromto.h>
30 #endif
31
32 #include <fcntl.h>
33
34 /*
35  *      See if two packets are identical.
36  *
37  *      Note that we do NOT compare the authentication vectors.
38  *      That's because if the authentication vector is different,
39  *      it means that the NAS has given up on the earlier request.
40  */
41 int fr_packet_cmp(const RADIUS_PACKET *a, const RADIUS_PACKET *b)
42 {
43         int rcode;
44
45         rcode = a->id - b->id;
46         if (rcode != 0) return rcode;
47
48         rcode = (int) a->src_port - (int) b->src_port;
49         if (rcode != 0) return rcode;
50
51         rcode = (int) a->dst_port - (int) b->dst_port;
52         if (rcode != 0) return rcode;
53
54         rcode = a->sockfd - b->sockfd;
55         if (rcode != 0) return rcode;
56
57         rcode = fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
58         if (rcode != 0) return rcode;
59
60         return fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
61 }
62
63 int fr_inaddr_any(fr_ipaddr_t *ipaddr)
64 {
65
66         if (ipaddr->af == AF_INET) {
67                 if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
68                         return 1;
69                 }
70                 
71 #ifdef HAVE_STRUCT_SOCKADDR_IN6
72         } else if (ipaddr->af == AF_INET6) {
73                 if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
74                         return 1;
75                 }
76 #endif
77                 
78         } else {
79                 fr_strerror_printf("Unknown address family");
80                 return -1;
81         }
82
83         return 0;
84 }
85
86
87 /*
88  *      Create a fake "request" from a reply, for later lookup.
89  */
90 void fr_request_from_reply(RADIUS_PACKET *request,
91                              const RADIUS_PACKET *reply)
92 {
93         request->sockfd = reply->sockfd;
94         request->id = reply->id;
95         request->src_port = reply->dst_port;
96         request->dst_port = reply->src_port;
97         request->src_ipaddr = reply->dst_ipaddr;
98         request->dst_ipaddr = reply->src_ipaddr;
99 }
100
101
102 int fr_nonblock(UNUSED int fd)
103 {
104         int flags = 0;
105
106 #ifdef O_NONBLOCK
107
108         flags = fcntl(fd, F_GETFL, NULL);
109         if (flags >= 0) {
110                 flags |= O_NONBLOCK;
111                 return fcntl(fd, F_SETFL, flags);
112         }
113 #endif
114         return flags;
115 }
116
117 /*
118  *      Open a socket on the given IP and port.
119  */
120 int fr_socket(fr_ipaddr_t *ipaddr, int port)
121 {
122         int sockfd;
123         struct sockaddr_storage salocal;
124         socklen_t       salen;
125
126         if ((port < 0) || (port > 65535)) {
127                 fr_strerror_printf("Port %d is out of allowed bounds", port);
128                 return -1;
129         }
130
131         sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
132         if (sockfd < 0) {
133                 fr_strerror_printf("cannot open socket: %s", strerror(errno));
134                 return sockfd;
135         }
136
137 #ifdef WITH_UDPFROMTO
138         /*
139          *      Initialize udpfromto for all sockets.
140          */
141         if (udpfromto_init(sockfd) != 0) {
142                 close(sockfd);
143                 fr_strerror_printf("cannot initialize udpfromto: %s", strerror(errno));
144                 return -1;
145         }
146 #endif
147
148         if (fr_nonblock(sockfd) < 0) {
149                 close(sockfd);
150                 return -1;
151         }
152
153         if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
154                 return sockfd;
155         }
156
157 #ifdef HAVE_STRUCT_SOCKADDR_IN6
158         if (ipaddr->af == AF_INET6) {
159                 /*
160                  *      Listening on '::' does NOT get you IPv4 to
161                  *      IPv6 mapping.  You've got to listen on an IPv4
162                  *      address, too.  This makes the rest of the server
163                  *      design a little simpler.
164                  */
165 #ifdef IPV6_V6ONLY
166
167                 if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
168                         int on = 1;
169
170                         setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
171                                    (char *)&on, sizeof(on));
172                 }
173 #endif /* IPV6_V6ONLY */
174         }
175 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
176
177         if (ipaddr->af == AF_INET) {
178                 UNUSED int flag;
179                 
180 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
181                 /*
182                  *      Disable PMTU discovery.  On Linux, this
183                  *      also makes sure that the "don't fragment"
184                  *      flag is zero.
185                  */
186                 flag = IP_PMTUDISC_DONT;
187                 setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER,
188                            &flag, sizeof(flag));
189 #endif
190
191 #if defined(IP_DONTFRAG)
192                 /*
193                  *      Ensure that the "don't fragment" flag is zero.
194                  */
195                 flag = 0;
196                 setsockopt(sockfd, IPPROTO_IP, IP_DONTFRAG,
197                            &flag, sizeof(flag));
198 #endif
199         }
200
201         if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
202                 close(sockfd);
203                 fr_strerror_printf("cannot bind socket: %s", strerror(errno));
204                 return -1;
205         }
206
207         return sockfd;
208 }
209
210
211 /*
212  *      We need to keep track of the socket & it's IP/port.
213  */
214 typedef struct fr_packet_socket_t {
215         int             sockfd;
216         void            *ctx;
217
218         int             num_outgoing;
219
220         int             src_any;
221         fr_ipaddr_t     src_ipaddr;
222         int             src_port;
223
224         int             dst_any;
225         fr_ipaddr_t     dst_ipaddr;
226         int             dst_port;
227
228         int             dont_use;
229
230 #ifdef WITH_TCP
231         int             proto;
232 #endif
233
234         uint8_t         id[32];
235 } fr_packet_socket_t;
236
237
238 #define FNV_MAGIC_PRIME (0x01000193)
239 #define MAX_SOCKETS (256)
240 #define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
241 #define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
242
243 #define MAX_QUEUES (8)
244
245 /*
246  *      Structure defining a list of packets (incoming or outgoing)
247  *      that should be managed.
248  */
249 struct fr_packet_list_t {
250         rbtree_t        *tree;
251
252         int             alloc_id;
253         int             num_outgoing;
254         int             last_recv;
255         int             num_sockets;
256
257         fr_packet_socket_t sockets[MAX_SOCKETS];
258 };
259
260
261 /*
262  *      Ugh.  Doing this on every sent/received packet is not nice.
263  */
264 static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl,
265                                           int sockfd)
266 {
267         int i, start;
268
269         i = start = SOCK2OFFSET(sockfd);
270
271         do {                    /* make this hack slightly more efficient */
272                 if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
273
274                 i = (i + 1) & SOCKOFFSET_MASK;
275         } while (i != start);
276
277         return NULL;
278 }
279
280 int fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
281 {
282         fr_packet_socket_t *ps;
283
284         if (!pl) {
285                 fr_strerror_printf("Invalid argument");
286                 return 0;
287         }
288
289         ps = fr_socket_find(pl, sockfd);
290         if (!ps) {
291                 fr_strerror_printf("No such socket");
292                 return 0;
293         }
294
295         ps->dont_use = 1;
296         return 1;
297 }
298
299 int fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
300 {
301         fr_packet_socket_t *ps;
302
303         if (!pl) return 0;
304
305         ps = fr_socket_find(pl, sockfd);
306         if (!ps) return 0;
307
308         ps->dont_use = 0;
309         return 1;
310 }
311
312 int fr_packet_list_socket_remove(fr_packet_list_t *pl, int sockfd,
313                                  void **pctx)
314 {
315         fr_packet_socket_t *ps;
316
317         if (!pl) return 0;
318
319         ps = fr_socket_find(pl, sockfd);
320         if (!ps) return 0;
321
322         /*
323          *      FIXME: Allow the caller forcibly discard these?
324          */
325         if (ps->num_outgoing != 0) return 0;
326
327         ps->sockfd = -1;
328         pl->num_sockets--;
329         if (pctx) *pctx = ps->ctx;
330
331         return 1;
332 }
333
334 int fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
335                               fr_ipaddr_t *dst_ipaddr, int dst_port,
336                               void *ctx)
337 {
338         int i, start;
339         struct sockaddr_storage src;
340         socklen_t               sizeof_src;
341         fr_packet_socket_t      *ps;
342
343         if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
344                 fr_strerror_printf("Invalid argument");
345                 return 0;
346         }
347
348         if (pl->num_sockets >= MAX_SOCKETS) {
349                 fr_strerror_printf("Too many open sockets");
350                 return 0;
351         }
352
353 #ifndef WITH_TCP
354         if (proto != IPPROTO_UDP) {
355                 fr_strerror_printf("only UDP is supported");
356                 return 0;
357         }
358 #endif
359
360         ps = NULL;
361         i = start = SOCK2OFFSET(sockfd);
362
363         do {
364                 if (pl->sockets[i].sockfd == -1) {
365                         ps =  &pl->sockets[i];
366                         break;
367                 }
368
369                 i = (i + 1) & SOCKOFFSET_MASK;
370         } while (i != start);
371
372         if (!ps) {
373                 fr_strerror_printf("All socket entries are full");
374                 return 0;
375         }
376
377         memset(ps, 0, sizeof(*ps));
378         ps->ctx = ctx;
379 #ifdef WITH_TCP
380         ps->proto = proto;
381 #endif
382
383         /*
384          *      Get address family, etc. first, so we know if we
385          *      need to do udpfromto.
386          *
387          *      FIXME: udpfromto also does this, but it's not
388          *      a critical problem.
389          */
390         sizeof_src = sizeof(src);
391         memset(&src, 0, sizeof_src);
392         if (getsockname(sockfd, (struct sockaddr *) &src,
393                         &sizeof_src) < 0) {
394                 fr_strerror_printf("%s", strerror(errno));
395                 return 0;
396         }
397
398         if (!fr_sockaddr2ipaddr(&src, sizeof_src, &ps->src_ipaddr,
399                                 &ps->src_port)) {
400                 fr_strerror_printf("Failed to get IP");
401                 return 0;
402         }
403
404         ps->dst_ipaddr = *dst_ipaddr;
405         ps->dst_port = dst_port;
406
407         ps->src_any = fr_inaddr_any(&ps->src_ipaddr);
408         if (ps->src_any < 0) return 0;
409
410         ps->dst_any = fr_inaddr_any(&ps->dst_ipaddr);
411         if (ps->dst_any < 0) return 0;
412
413         /*
414          *      As the last step before returning.
415          */
416         ps->sockfd = sockfd;
417         pl->num_sockets++;
418
419         return 1;
420 }
421
422 static int packet_entry_cmp(const void *one, const void *two)
423 {
424         const RADIUS_PACKET * const *a = one;
425         const RADIUS_PACKET * const *b = two;
426
427         return fr_packet_cmp(*a, *b);
428 }
429
430 void fr_packet_list_free(fr_packet_list_t *pl)
431 {
432         if (!pl) return;
433
434         rbtree_free(pl->tree);
435         free(pl);
436 }
437
438
439 /*
440  *      Caller is responsible for managing the packet entries.
441  */
442 fr_packet_list_t *fr_packet_list_create(int alloc_id)
443 {
444         int i;
445         fr_packet_list_t        *pl;
446
447         pl = malloc(sizeof(*pl));
448         if (!pl) return NULL;
449         memset(pl, 0, sizeof(*pl));
450
451         pl->tree = rbtree_create(packet_entry_cmp, NULL, 0);
452         if (!pl->tree) {
453                 fr_packet_list_free(pl);
454                 return NULL;
455         }
456
457         for (i = 0; i < MAX_SOCKETS; i++) {
458                 pl->sockets[i].sockfd = -1;
459         }
460
461         pl->alloc_id = alloc_id;
462
463         return pl;
464 }
465
466
467 /*
468  *      If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
469  *      be called before inserting the packet into the list!
470  */
471 int fr_packet_list_insert(fr_packet_list_t *pl,
472                             RADIUS_PACKET **request_p)
473 {
474         if (!pl || !request_p || !*request_p) return 0;
475
476         return rbtree_insert(pl->tree, request_p);
477 }
478
479 RADIUS_PACKET **fr_packet_list_find(fr_packet_list_t *pl,
480                                       RADIUS_PACKET *request)
481 {
482         if (!pl || !request) return 0;
483
484         return rbtree_finddata(pl->tree, &request);
485 }
486
487
488 /*
489  *      This presumes that the reply has dst_ipaddr && dst_port set up
490  *      correctly (i.e. real IP, or "*").
491  */
492 RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl,
493                                               RADIUS_PACKET *reply)
494 {
495         RADIUS_PACKET my_request, *request;
496         fr_packet_socket_t *ps;
497
498         if (!pl || !reply) return NULL;
499
500         ps = fr_socket_find(pl, reply->sockfd);
501         if (!ps) return NULL;
502
503         /*
504          *      Initialize request from reply, AND from the source
505          *      IP & port of this socket.  The client may have bound
506          *      the socket to 0, in which case it's some random port,
507          *      that is NOT in the original request->src_port.
508          */
509         my_request.sockfd = reply->sockfd;
510         my_request.id = reply->id;
511
512         if (ps->src_any) {
513                 my_request.src_ipaddr = ps->src_ipaddr;
514         } else {
515                 my_request.src_ipaddr = reply->dst_ipaddr;
516         }
517         my_request.src_port = ps->src_port;
518
519         my_request.dst_ipaddr = reply->src_ipaddr;
520         my_request.dst_port = reply->src_port;
521
522         request = &my_request;
523
524         return rbtree_finddata(pl->tree, &request);
525 }
526
527
528 void fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request)
529 {
530         rbnode_t *node;
531
532         if (!pl || !request) return;
533
534         node = rbtree_find(pl->tree, &request);
535         if (!node) return;
536
537         rbtree_delete(pl->tree, node);
538 }
539
540 int fr_packet_list_num_elements(fr_packet_list_t *pl)
541 {
542         if (!pl) return 0;
543
544         return rbtree_num_elements(pl->tree);
545 }
546
547
548 /*
549  *      1 == ID was allocated & assigned
550  *      0 == error allocating memory
551  *      -1 == all ID's are used, caller should open a new socket.
552  *
553  *      Note that this ALSO assigns a socket to use, and updates
554  *      packet->request->src_ipaddr && packet->request->src_port
555  *
556  *      In multi-threaded systems, the calls to id_alloc && id_free
557  *      should be protected by a mutex.  This does NOT have to be
558  *      the same mutex as the one protecting the insert/find/yank
559  *      calls!
560  *
561  *      We assume that the packet has dst_ipaddr && dst_port
562  *      already initialized.  We will use those to find an
563  *      outgoing socket.  The request MAY also have src_ipaddr set.
564  *
565  *      We also assume that the sender doesn't care which protocol
566  *      should be used.
567  */
568 int fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
569                             RADIUS_PACKET *request, void **pctx)
570 {
571         int i, j, k, fd, id, start_i, start_j, start_k;
572         int src_any = 0;
573         fr_packet_socket_t *ps;
574
575         if ((request->dst_ipaddr.af == AF_UNSPEC) ||
576             (request->dst_port == 0)) {
577                 fr_strerror_printf("No destination address/port specified");
578                 return 0;
579         }
580
581 #ifndef WITH_TCP
582         if ((proto != 0) && (proto != IPPROTO_UDP)) {
583                 fr_strerror_printf("Invalid destination protocol");
584                 return 0;
585         }
586 #endif
587
588         /*
589          *      Special case: unspec == "don't care"
590          */
591         if (request->src_ipaddr.af == AF_UNSPEC) {
592                 memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
593                 request->src_ipaddr.af = request->dst_ipaddr.af;
594         }
595
596         src_any = fr_inaddr_any(&request->src_ipaddr);
597         if (src_any < 0) return 0;
598
599         /*
600          *      MUST specify a destination address.
601          */
602         if (fr_inaddr_any(&request->dst_ipaddr) != 0) return 0;
603
604         /*
605          *      FIXME: Go to an LRU system.  This prevents ID re-use
606          *      for as long as possible.  The main problem with that
607          *      approach is that it requires us to populate the
608          *      LRU/FIFO when we add a new socket, or a new destination,
609          *      which can be expensive.
610          *
611          *      The LRU can be avoided if the caller takes care to free
612          *      Id's only when all responses have been received, OR after
613          *      a timeout.
614          *
615          *      Right now, the random approach is almost OK... it's
616          *      brute-force over all of the available ID's, BUT using
617          *      random numbers for everything spreads the load a bit.
618          *
619          *      The old method had a hash lookup on allocation AND
620          *      on free.  The new method has brute-force on allocation,
621          *      and near-zero cost on free.
622          */
623
624         id = fd = -1;
625         start_i = fr_rand() & SOCKOFFSET_MASK;
626
627 #define ID_i ((i + start_i) & SOCKOFFSET_MASK)
628         for (i = 0; i < MAX_SOCKETS; i++) {
629                 if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
630
631                 ps = &(pl->sockets[ID_i]);
632
633                 /*
634                  *      This socket is marked as "don't use for new
635                  *      packets".  But we can still receive packets
636                  *      that are outstanding.
637                  */
638                 if (ps->dont_use) continue;
639
640                 /*
641                  *      All IDs are allocated: ignore it.
642                  */
643                 if (ps->num_outgoing == 256) continue;
644
645 #ifdef WITH_TCP
646                 if (ps->proto != proto) continue;
647 #endif
648
649                 /*
650                  *      Address families don't match, skip it.
651                  */
652                 if (ps->src_ipaddr.af != request->dst_ipaddr.af) continue;
653
654                 /*
655                  *      MUST match dst port, if we have one.
656                  */
657                 if ((ps->dst_port != 0) && 
658                     (ps->dst_port != request->dst_port)) continue;
659
660                 /*
661                  *      MUST match requested src port, if one has been given.
662                  */
663                 if ((request->src_port != 0) && 
664                     (ps->src_port != request->src_port)) continue;
665
666                 /*
667                  *      We're sourcing from *, and they asked for a
668                  *      specific source address: ignore it.
669                  */
670                 if (ps->src_any && !src_any) continue;
671
672                 /*
673                  *      We're sourcing from a specific IP, and they
674                  *      asked for a source IP that isn't us: ignore
675                  *      it.
676                  */
677                 if (!ps->src_any && !src_any &&
678                     (fr_ipaddr_cmp(&request->src_ipaddr,
679                                    &ps->src_ipaddr) != 0)) continue;
680
681                 /*
682                  *      UDP sockets are allowed to match
683                  *      destination IPs exactly, OR a socket
684                  *      with destination * is allowed to match
685                  *      any requested destination.
686                  *
687                  *      TCP sockets must match the destination
688                  *      exactly.  They *always* have dst_any=0,
689                  *      so the first check always matches.
690                  */
691                 if (!ps->dst_any &&
692                     (fr_ipaddr_cmp(&request->dst_ipaddr,
693                                    &ps->dst_ipaddr) != 0)) continue;
694                 
695                 /*
696                  *      Otherwise, this socket is OK to use.
697                  */
698
699                 /*
700                  *      Look for a free Id, starting from a random number.
701                  */
702                 start_j = fr_rand() & 0x1f;
703 #define ID_j ((j + start_j) & 0x1f)
704                 for (j = 0; j < 32; j++) {
705                         if (ps->id[ID_j] == 0xff) continue;
706
707
708                         start_k = fr_rand() & 0x07;
709 #define ID_k ((k + start_k) & 0x07)
710                         for (k = 0; k < 8; k++) {
711                                 if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
712
713                                 ps->id[ID_j] |= (1 << ID_k);
714                                 id = (ID_j * 8) + ID_k;
715                                 fd = i;
716                                 break;
717                         }
718                         if (fd >= 0) break;
719                 }
720 #undef ID_i
721 #undef ID_j
722 #undef ID_k
723                 if (fd >= 0) break;
724                 break;
725         }
726
727         /*
728          *      Ask the caller to allocate a new ID.
729          */
730         if (fd < 0) return 0;
731
732         ps->num_outgoing++;
733         pl->num_outgoing++;
734
735         /*
736          *      Set the ID, source IP, and source port.
737          */
738         request->id = id;
739
740         request->sockfd = ps->sockfd;
741         request->src_ipaddr = ps->src_ipaddr;
742         request->src_port = ps->src_port;
743
744         if (pctx) *pctx = ps->ctx;
745
746         return 1;
747 }
748
749 /*
750  *      Should be called AFTER yanking it from the list, so that
751  *      any newly inserted entries don't collide with this one.
752  */
753 int fr_packet_list_id_free(fr_packet_list_t *pl,
754                              RADIUS_PACKET *request)
755 {
756         fr_packet_socket_t *ps;
757
758         if (!pl || !request) return 0;
759
760         ps = fr_socket_find(pl, request->sockfd);
761         if (!ps) return 0;
762
763 #if 0
764         if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
765                 exit(1);
766         }
767 #endif
768
769         ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
770
771         ps->num_outgoing--;
772         pl->num_outgoing--;
773
774         return 1;
775 }
776
777 int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx,
778                           fr_hash_table_walk_t callback)
779 {
780         if (!pl || !callback) return 0;
781
782         return rbtree_walk(pl->tree, InOrder, callback, ctx);
783 }
784
785 int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
786 {
787         int i, maxfd;
788
789         if (!pl || !set) return 0;
790
791         maxfd = -1;
792
793         for (i = 0; i < MAX_SOCKETS; i++) {
794                 if (pl->sockets[i].sockfd == -1) continue;
795                 FD_SET(pl->sockets[i].sockfd, set);
796                 if (pl->sockets[i].sockfd > maxfd) {
797                         maxfd = pl->sockets[i].sockfd;
798                 }
799         }
800
801         if (maxfd < 0) return -1;
802
803         return maxfd + 1;
804 }
805
806 /*
807  *      Round-robins the receivers, without priority.
808  *
809  *      FIXME: Add sockfd, if -1, do round-robin, else do sockfd
810  *              IF in fdset.
811  */
812 RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
813 {
814         int start;
815         RADIUS_PACKET *packet;
816
817         if (!pl || !set) return NULL;
818
819         start = pl->last_recv;
820         do {
821                 start++;
822                 start &= SOCKOFFSET_MASK;
823
824                 if (pl->sockets[start].sockfd == -1) continue;
825
826                 if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
827
828 #ifdef WITH_TCP
829                 if (pl->sockets[start].proto == IPPROTO_TCP) {
830                         packet = fr_tcp_recv(pl->sockets[start].sockfd, 0);
831                 } else
832 #endif
833                 packet = rad_recv(pl->sockets[start].sockfd, 0);
834                 if (!packet) continue;
835
836                 /*
837                  *      Call fr_packet_list_find_byreply().  If it
838                  *      doesn't find anything, discard the reply.
839                  */
840
841                 pl->last_recv = start;
842                 return packet;
843         } while (start != pl->last_recv);
844
845         return NULL;
846 }
847
848 int fr_packet_list_num_incoming(fr_packet_list_t *pl)
849 {
850         int num_elements;
851
852         if (!pl) return 0;
853
854         num_elements = rbtree_num_elements(pl->tree);
855         if (num_elements < pl->num_outgoing) return 0; /* panic! */
856
857         return num_elements - pl->num_outgoing;
858 }
859
860 int fr_packet_list_num_outgoing(fr_packet_list_t *pl)
861 {
862         if (!pl) return 0;
863
864         return pl->num_outgoing;
865 }