]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
more renames and moves
authorAlan T. DeKok <aland@freeradius.org>
Wed, 3 May 2017 15:20:34 +0000 (11:20 -0400)
committerAlan T. DeKok <aland@freeradius.org>
Wed, 3 May 2017 15:34:03 +0000 (11:34 -0400)
src/include/libradius.h
src/protocols/radius/base.c [moved from src/protocols/radius/radius.c with 99% similarity]
src/protocols/radius/decode.c [moved from src/protocols/radius/radius_decode.c with 99% similarity]
src/protocols/radius/encode.c [moved from src/protocols/radius/radius_encode.c with 99% similarity]
src/protocols/radius/list.c [new file with mode: 0644]
src/protocols/radius/packet.c
src/protocols/radius/radius_packet.c [deleted file]

index 59aa47617bfd6a4db59a23ff31200f16d9707fa5..db9f63458f35fbe0a7d8c95a36b2e2b2cab358b0 100644 (file)
@@ -205,7 +205,7 @@ do { \
 } while (0)
 
 /*
- *     radius.c
+ *     protocols/radius/base.c
  */
 #define AUTH_PASS_LEN (AUTH_VECTOR_LEN)
 #define MAX_PASS_LEN (128)
@@ -225,7 +225,7 @@ void                fr_radius_ascend_secret(uint8_t *digest, uint8_t const *vector,
 ssize_t                fr_radius_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, unsigned int *code);
 
 /*
- *     radius_packet.c
+ *     protocols/radius/packet.c
  */
 RADIUS_PACKET  *fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector);
 RADIUS_PACKET  *fr_radius_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *);
@@ -259,7 +259,7 @@ typedef struct fr_radius_ctx {
 } fr_radius_ctx_t;
 
 /*
- *     radius_encode.c
+ *     protocols/radius/encode.c
  */
 int            fr_radius_encode_password(char *encpw, size_t *len, char const *secret, uint8_t const *vector);
 
@@ -272,7 +272,7 @@ ssize_t             fr_radius_encode_value_hton(uint8_t *out, size_t outlen, VALUE_PAIR con
 ssize_t                fr_radius_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx);
 
 /*
- *     radius_decode.c
+ *     protocols/radius/decode.c
  */
 int            fr_radius_decode_tlv_ok(uint8_t const *data, size_t length, size_t dv_type, size_t dv_length);
 
similarity index 99%
rename from src/protocols/radius/radius.c
rename to src/protocols/radius/base.c
index 9a22d477011f5788a2d09de8548bef7633491e17..de551fe9ad451bd11c510257f47f18021f482a7b 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * $Id$
  *
- * @file radius.c
+ * @file base.c
  * @brief Functions to send/receive radius packets.
  *
  * @copyright 2000-2003,2006  The FreeRADIUS server project
similarity index 99%
rename from src/protocols/radius/radius_decode.c
rename to src/protocols/radius/decode.c
index b594e8d68d08c4bbeafc9989312ab4d3c8d89a8b..3340013b4309e7ea4ff766ad20d95706899f617b 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * $Id$
  *
- * @file radius.c
+ * @file decode.c
  * @brief Functions to decode RADIUS attributes
  *
  * @copyright 2000-2003,2006-2015  The FreeRADIUS server project
similarity index 99%
rename from src/protocols/radius/radius_encode.c
rename to src/protocols/radius/encode.c
index 5cfa3d09844c559bfd85cc61ff4b4b6131ff5a98..507995fad37749f5ff181767662fca4b65a9562d 100644 (file)
@@ -17,7 +17,7 @@
 /**
  * $Id$
  *
- * @file radius.c
+ * @file encode.c
  * @brief Functions to encode RADIUS attributes
  *
  * @copyright 2000-2003,2006-2015  The FreeRADIUS server project
diff --git a/src/protocols/radius/list.c b/src/protocols/radius/list.c
new file mode 100644 (file)
index 0000000..8e837c8
--- /dev/null
@@ -0,0 +1,925 @@
+/*
+ * packet.c    Generic packet manipulation functions.
+ *
+ * Version:    $Id$
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2000-2006  The FreeRADIUS server project
+ */
+
+/**
+ * $Id$
+ *
+ * @file list.c
+ * @brief Functions to deal with outgoing lists / sets of packets.
+ *
+ * @copyright 2000-2017  The FreeRADIUS server project
+ */
+
+RCSID("$Id$")
+
+#include       <freeradius-devel/libradius.h>
+#include       <freeradius-devel/udp.h>
+
+#include <fcntl.h>
+
+/*
+ *     See if two packets are identical.
+ *
+ *     Note that we do NOT compare the authentication vectors.
+ *     That's because if the authentication vector is different,
+ *     it means that the NAS has given up on the earlier request.
+ */
+int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
+{
+       int rcode;
+
+       /*
+        *      256-way fanout.
+        */
+       if (a->id < b->id) return -1;
+       if (a->id > b->id) return +1;
+
+       if (a->sockfd < b->sockfd) return -1;
+       if (a->sockfd > b->sockfd) return +1;
+
+       /*
+        *      Source ports are pretty much random.
+        */
+       rcode = (int) a->src_port - (int) b->src_port;
+       if (rcode != 0) return rcode;
+
+       /*
+        *      Usually many client IPs, and few server IPs
+        */
+       rcode = fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
+       if (rcode != 0) return rcode;
+
+       /*
+        *      One socket can receive packets for multiple
+        *      destination IPs, so we check that before checking the
+        *      file descriptor.
+        */
+       rcode = fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
+       if (rcode != 0) return rcode;
+
+       /*
+        *      At this point, the order of comparing socket FDs
+        *      and/or destination ports doesn't matter.  One of those
+        *      fields will make the socket unique, and the other is
+        *      pretty much redundant.
+        */
+       rcode = (int) a->dst_port - (int) b->dst_port;
+       return rcode;
+}
+
+/** Determine if an address is the INADDR_ANY address for its address family
+ *
+ * @param ipaddr to check.
+ * @return
+ *     - 0 if it's not.
+ *     - 1 if it is.
+ *     - -1 on error.
+ */
+int fr_is_inaddr_any(fr_ipaddr_t *ipaddr)
+{
+
+       if (ipaddr->af == AF_INET) {
+               if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
+                       return 1;
+               }
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+       } else if (ipaddr->af == AF_INET6) {
+               if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
+                       return 1;
+               }
+#endif
+
+       } else {
+               fr_strerror_printf("Unknown address family");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/*
+ *     Create a fake "request" from a reply, for later lookup.
+ */
+void fr_request_from_reply(RADIUS_PACKET *request,
+                          RADIUS_PACKET const *reply)
+{
+       request->sockfd = reply->sockfd;
+       request->id = reply->id;
+#ifdef WITH_TCP
+       request->proto = reply->proto;
+#endif
+       request->src_port = reply->dst_port;
+       request->dst_port = reply->src_port;
+       request->src_ipaddr = reply->dst_ipaddr;
+       request->dst_ipaddr = reply->src_ipaddr;
+       request->if_index = reply->if_index;
+}
+
+/*
+ *     We need to keep track of the socket & it's IP/port.
+ */
+typedef struct fr_packet_socket_t {
+       int             sockfd;
+       void            *ctx;
+
+       uint32_t        num_outgoing;
+
+       int             src_any;
+       fr_ipaddr_t     src_ipaddr;
+       uint16_t        src_port;
+
+       int             dst_any;
+       fr_ipaddr_t     dst_ipaddr;
+       uint16_t        dst_port;
+
+       bool            dont_use;
+
+#ifdef WITH_TCP
+       int             proto;
+#endif
+
+       uint8_t         id[32];
+} fr_packet_socket_t;
+
+
+#define FNV_MAGIC_PRIME (0x01000193)
+#define MAX_SOCKETS (256)
+#define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
+#define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
+
+/*
+ *     Structure defining a list of packets (incoming or outgoing)
+ *     that should be managed.
+ */
+struct fr_packet_list_t {
+       rbtree_t        *tree;
+
+       int             alloc_id;
+       uint32_t        num_outgoing;
+       int             last_recv;
+       int             num_sockets;
+
+       fr_packet_socket_t sockets[MAX_SOCKETS];
+};
+
+
+/*
+ *     Ugh.  Doing this on every sent/received packet is not nice.
+ */
+static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl,
+                                         int sockfd)
+{
+       int i, start;
+
+       i = start = SOCK2OFFSET(sockfd);
+
+       do {                    /* make this hack slightly more efficient */
+               if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
+
+               i = (i + 1) & SOCKOFFSET_MASK;
+       } while (i != start);
+
+       return NULL;
+}
+
+bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
+{
+       fr_packet_socket_t *ps;
+
+       if (!pl) {
+               fr_strerror_printf("Invalid argument");
+               return false;
+       }
+
+       ps = fr_socket_find(pl, sockfd);
+       if (!ps) {
+               fr_strerror_printf("No such socket");
+               return false;
+       }
+
+       ps->dont_use = true;
+       return true;
+}
+
+bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
+{
+       fr_packet_socket_t *ps;
+
+       if (!pl) return false;
+
+       ps = fr_socket_find(pl, sockfd);
+       if (!ps) return false;
+
+       ps->dont_use = false;
+       return true;
+}
+
+
+bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd)
+{
+       fr_packet_socket_t *ps;
+
+       if (!pl) return false;
+
+       ps = fr_socket_find(pl, sockfd);
+       if (!ps) return false;
+
+       if (ps->num_outgoing != 0) return false;
+
+       ps->sockfd = -1;
+       pl->num_sockets--;
+
+       return true;
+}
+
+
+bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
+                             fr_ipaddr_t *dst_ipaddr, uint16_t dst_port,
+                             void *ctx)
+{
+       int i, start;
+       struct sockaddr_storage src;
+       socklen_t               sizeof_src;
+       fr_packet_socket_t      *ps;
+
+       if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
+               fr_strerror_printf("Invalid argument");
+               return false;
+       }
+
+       if (pl->num_sockets >= MAX_SOCKETS) {
+               fr_strerror_printf("Too many open sockets");
+               return false;
+       }
+
+#ifndef WITH_TCP
+       if (proto != IPPROTO_UDP) {
+               fr_strerror_printf("only UDP is supported");
+               return false;
+       }
+#endif
+
+       ps = NULL;
+       i = start = SOCK2OFFSET(sockfd);
+
+       do {
+               if (pl->sockets[i].sockfd == -1) {
+                       ps =  &pl->sockets[i];
+                       break;
+               }
+
+               i = (i + 1) & SOCKOFFSET_MASK;
+       } while (i != start);
+
+       if (!ps) {
+               fr_strerror_printf("All socket entries are full");
+               return false;
+       }
+
+       memset(ps, 0, sizeof(*ps));
+       ps->ctx = ctx;
+#ifdef WITH_TCP
+       ps->proto = proto;
+#endif
+
+       /*
+        *      Get address family, etc. first, so we know if we
+        *      need to do udpfromto.
+        *
+        *      FIXME: udpfromto also does this, but it's not
+        *      a critical problem.
+        */
+       sizeof_src = sizeof(src);
+       memset(&src, 0, sizeof_src);
+       if (getsockname(sockfd, (struct sockaddr *) &src,
+                       &sizeof_src) < 0) {
+               fr_strerror_printf("%s", fr_syserror(errno));
+               return false;
+       }
+
+       if (!fr_ipaddr_from_sockaddr(&src, sizeof_src, &ps->src_ipaddr,
+                               &ps->src_port)) {
+               fr_strerror_printf("Failed to get IP");
+               return false;
+       }
+
+       ps->dst_ipaddr = *dst_ipaddr;
+       ps->dst_port = dst_port;
+
+       ps->src_any = fr_is_inaddr_any(&ps->src_ipaddr);
+       if (ps->src_any < 0) return false;
+
+       ps->dst_any = fr_is_inaddr_any(&ps->dst_ipaddr);
+       if (ps->dst_any < 0) return false;
+
+       /*
+        *      As the last step before returning.
+        */
+       ps->sockfd = sockfd;
+       pl->num_sockets++;
+
+       return true;
+}
+
+static int packet_entry_cmp(void const *one, void const *two)
+{
+       RADIUS_PACKET const * const *a = one;
+       RADIUS_PACKET const * const *b = two;
+
+       return fr_packet_cmp(*a, *b);
+}
+
+void fr_packet_list_free(fr_packet_list_t *pl)
+{
+       if (!pl) return;
+
+       talloc_free(pl->tree);
+       talloc_free(pl);
+}
+
+
+/*
+ *     Caller is responsible for managing the packet entries.
+ */
+fr_packet_list_t *fr_packet_list_create(int alloc_id)
+{
+       int i;
+       fr_packet_list_t        *pl;
+
+       pl = talloc_zero(NULL, fr_packet_list_t);
+       if (!pl) return NULL;
+       pl->tree = rbtree_create(pl, packet_entry_cmp, NULL, 0);
+       if (!pl->tree) {
+               fr_packet_list_free(pl);
+               return NULL;
+       }
+
+       for (i = 0; i < MAX_SOCKETS; i++) {
+               pl->sockets[i].sockfd = -1;
+       }
+
+       pl->alloc_id = alloc_id;
+
+       return pl;
+}
+
+
+/*
+ *     If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
+ *     be called before inserting the packet into the list!
+ */
+bool fr_packet_list_insert(fr_packet_list_t *pl,
+                           RADIUS_PACKET **request_p)
+{
+       if (!pl || !request_p || !*request_p) return 0;
+
+       return rbtree_insert(pl->tree, request_p);
+}
+
+RADIUS_PACKET **fr_packet_list_find(fr_packet_list_t *pl,
+                                     RADIUS_PACKET *request)
+{
+       if (!pl || !request) return 0;
+
+       return rbtree_finddata(pl->tree, &request);
+}
+
+
+/*
+ *     This presumes that the reply has dst_ipaddr && dst_port set up
+ *     correctly (i.e. real IP, or "*").
+ */
+RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl, RADIUS_PACKET *reply)
+{
+       RADIUS_PACKET my_request, *request;
+       fr_packet_socket_t *ps;
+
+       if (!pl || !reply) return NULL;
+
+       ps = fr_socket_find(pl, reply->sockfd);
+       if (!ps) return NULL;
+
+       /*
+        *      Initialize request from reply, AND from the source
+        *      IP & port of this socket.  The client may have bound
+        *      the socket to 0, in which case it's some random port,
+        *      that is NOT in the original request->src_port.
+        */
+       my_request.sockfd = reply->sockfd;
+       my_request.id = reply->id;
+
+#ifdef WITH_TCP
+       /*
+        *      TCP sockets are always bound to the correct src/dst IP/port
+        */
+       if (ps->proto == IPPROTO_TCP) {
+               reply->dst_ipaddr = ps->src_ipaddr;
+               reply->dst_port = ps->src_port;
+               reply->src_ipaddr = ps->dst_ipaddr;
+               reply->src_port = ps->dst_port;
+
+               my_request.src_ipaddr = ps->src_ipaddr;
+               my_request.src_port = ps->src_port;
+               my_request.dst_ipaddr = ps->dst_ipaddr;
+               my_request.dst_port = ps->dst_port;
+
+       } else
+#endif
+       {
+               if (ps->src_any) {
+                       my_request.src_ipaddr = ps->src_ipaddr;
+               } else {
+                       my_request.src_ipaddr = reply->dst_ipaddr;
+               }
+               my_request.src_port = ps->src_port;
+
+               my_request.dst_ipaddr = reply->src_ipaddr;
+               my_request.dst_port = reply->src_port;
+       }
+
+#ifdef WITH_TCP
+       my_request.proto = reply->proto;
+#endif
+       request = &my_request;
+
+       return rbtree_finddata(pl->tree, &request);
+}
+
+
+bool fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request)
+{
+       rbnode_t *node;
+
+       if (!pl || !request) return false;
+
+       node = rbtree_find(pl->tree, &request);
+       if (!node) return false;
+
+       rbtree_delete(pl->tree, node);
+       return true;
+}
+
+uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl)
+{
+       if (!pl) return 0;
+
+       return rbtree_num_elements(pl->tree);
+}
+
+
+/*
+ *     1 == ID was allocated & assigned
+ *     0 == couldn't allocate ID.
+ *
+ *     Note that this ALSO assigns a socket to use, and updates
+ *     packet->request->src_ipaddr && packet->request->src_port
+ *
+ *     In multi-threaded systems, the calls to id_alloc && id_free
+ *     should be protected by a mutex.  This does NOT have to be
+ *     the same mutex as the one protecting the insert/find/yank
+ *     calls!
+ *
+ *     We assume that the packet has dst_ipaddr && dst_port
+ *     already initialized.  We will use those to find an
+ *     outgoing socket.  The request MAY also have src_ipaddr set.
+ *
+ *     We also assume that the sender doesn't care which protocol
+ *     should be used.
+ */
+bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
+                           RADIUS_PACKET **request_p, void **pctx)
+{
+       int i, j, k, fd, id, start_i, start_j, start_k;
+       int src_any = 0;
+       fr_packet_socket_t *ps= NULL;
+       RADIUS_PACKET *request = *request_p;
+
+       if ((request->dst_ipaddr.af == AF_UNSPEC) ||
+           (request->dst_port == 0)) {
+               fr_strerror_printf("No destination address/port specified");
+               return false;
+       }
+
+#ifndef WITH_TCP
+       if ((proto != 0) && (proto != IPPROTO_UDP)) {
+               fr_strerror_printf("Invalid destination protocol");
+               return false;
+       }
+#endif
+
+       /*
+        *      Special case: unspec == "don't care"
+        */
+       if (request->src_ipaddr.af == AF_UNSPEC) {
+               memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
+               request->src_ipaddr.af = request->dst_ipaddr.af;
+       }
+
+       src_any = fr_is_inaddr_any(&request->src_ipaddr);
+       if (src_any < 0) {
+               fr_strerror_printf("Can't check src_ipaddr");
+               return false;
+       }
+
+       /*
+        *      MUST specify a destination address.
+        */
+       if (fr_is_inaddr_any(&request->dst_ipaddr) != 0) {
+               fr_strerror_printf("Must specify a dst_ipaddr");
+               return false;
+       }
+
+       /*
+        *      FIXME: Go to an LRU system.  This prevents ID re-use
+        *      for as long as possible.  The main problem with that
+        *      approach is that it requires us to populate the
+        *      LRU/FIFO when we add a new socket, or a new destination,
+        *      which can be expensive.
+        *
+        *      The LRU can be avoided if the caller takes care to free
+        *      Id's only when all responses have been received, OR after
+        *      a timeout.
+        *
+        *      Right now, the random approach is almost OK... it's
+        *      brute-force over all of the available ID's, BUT using
+        *      random numbers for everything spreads the load a bit.
+        *
+        *      The old method had a hash lookup on allocation AND
+        *      on free.  The new method has brute-force on allocation,
+        *      and near-zero cost on free.
+        */
+
+       id = fd = -1;
+       start_i = fr_rand() & SOCKOFFSET_MASK;
+
+#define ID_i ((i + start_i) & SOCKOFFSET_MASK)
+       for (i = 0; i < MAX_SOCKETS; i++) {
+               if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
+
+               ps = &(pl->sockets[ID_i]);
+
+               /*
+                *      This socket is marked as "don't use for new
+                *      packets".  But we can still receive packets
+                *      that are outstanding.
+                */
+               if (ps->dont_use) continue;
+
+               /*
+                *      All IDs are allocated: ignore it.
+                */
+               if (ps->num_outgoing == 256) continue;
+
+#ifdef WITH_TCP
+               if (ps->proto != proto) continue;
+#endif
+
+               /*
+                *      Address families don't match, skip it.
+                */
+               if (ps->src_ipaddr.af != request->dst_ipaddr.af) continue;
+
+               /*
+                *      MUST match dst port, if we have one.
+                */
+               if ((ps->dst_port != 0) &&
+                   (ps->dst_port != request->dst_port)) continue;
+
+               /*
+                *      MUST match requested src port, if one has been given.
+                */
+               if ((request->src_port != 0) &&
+                   (ps->src_port != request->src_port)) continue;
+
+               /*
+                *      We don't care about the source IP, but this
+                *      socket is link local, and the requested
+                *      destination is not link local.  Ignore it.
+                */
+               if (src_any && (ps->src_ipaddr.af == AF_INET) &&
+                   (((ps->src_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) == 127) &&
+                   (((request->dst_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) != 127)) continue;
+
+               /*
+                *      We're sourcing from *, and they asked for a
+                *      specific source address: ignore it.
+                */
+               if (ps->src_any && !src_any) continue;
+
+               /*
+                *      We're sourcing from a specific IP, and they
+                *      asked for a source IP that isn't us: ignore
+                *      it.
+                */
+               if (!ps->src_any && !src_any &&
+                   (fr_ipaddr_cmp(&request->src_ipaddr,
+                                  &ps->src_ipaddr) != 0)) continue;
+
+               /*
+                *      UDP sockets are allowed to match
+                *      destination IPs exactly, OR a socket
+                *      with destination * is allowed to match
+                *      any requested destination.
+                *
+                *      TCP sockets must match the destination
+                *      exactly.  They *always* have dst_any=0,
+                *      so the first check always matches.
+                */
+               if (!ps->dst_any &&
+                   (fr_ipaddr_cmp(&request->dst_ipaddr,
+                                  &ps->dst_ipaddr) != 0)) continue;
+
+               /*
+                *      Otherwise, this socket is OK to use.
+                */
+
+               /*
+                *      Look for a free Id, starting from a random number.
+                */
+               start_j = fr_rand() & 0x1f;
+#define ID_j ((j + start_j) & 0x1f)
+               for (j = 0; j < 32; j++) {
+                       if (ps->id[ID_j] == 0xff) continue;
+
+
+                       start_k = fr_rand() & 0x07;
+#define ID_k ((k + start_k) & 0x07)
+                       for (k = 0; k < 8; k++) {
+                               if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
+
+                               ps->id[ID_j] |= (1 << ID_k);
+                               id = (ID_j * 8) + ID_k;
+                               fd = i;
+                               break;
+                       }
+                       if (fd >= 0) break;
+               }
+#undef ID_i
+#undef ID_j
+#undef ID_k
+               break;
+       }
+
+       /*
+        *      Ask the caller to allocate a new ID.
+        */
+       if (fd < 0) {
+               fr_strerror_printf("Failed finding socket, caller must allocate a new one");
+               return false;
+       }
+
+       /*
+        *      Set the ID, source IP, and source port.
+        */
+       request->id = id;
+
+       request->sockfd = ps->sockfd;
+       request->src_ipaddr = ps->src_ipaddr;
+       request->src_port = ps->src_port;
+
+       /*
+        *      If we managed to insert it, we're done.
+        */
+       if (fr_packet_list_insert(pl, request_p)) {
+               if (pctx) *pctx = ps->ctx;
+               ps->num_outgoing++;
+               pl->num_outgoing++;
+               return true;
+       }
+
+       /*
+        *      Mark the ID as free.  This is the one line from
+        *      id_free() that we care about here.
+        */
+       ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
+
+       request->id = -1;
+       request->sockfd = -1;
+       request->src_ipaddr.af = AF_UNSPEC;
+       request->src_port = 0;
+
+       return false;
+}
+
+/*
+ *     Should be called AFTER yanking it from the list, so that
+ *     any newly inserted entries don't collide with this one.
+ */
+bool fr_packet_list_id_free(fr_packet_list_t *pl,
+                           RADIUS_PACKET *request, bool yank)
+{
+       fr_packet_socket_t *ps;
+
+       if (!pl || !request) return false;
+
+       if (yank && !fr_packet_list_yank(pl, request)) return false;
+
+       ps = fr_socket_find(pl, request->sockfd);
+       if (!ps) return false;
+
+#if 0
+       if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
+               fr_exit(1);
+       }
+#endif
+
+       ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
+
+       ps->num_outgoing--;
+       pl->num_outgoing--;
+
+       request->id = -1;
+       request->src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */
+       request->src_port = 0;
+
+       return true;
+}
+
+/*
+ *     We always walk RBTREE_DELETE_ORDER, which is like RBTREE_IN_ORDER, except that
+ *     <0 means error, stop
+ *     0  means OK, continue
+ *     1  means delete current node and stop
+ *     2  means delete current node and continue
+ */
+int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx, rb_walker_t callback)
+{
+       if (!pl || !callback) return 0;
+
+       return rbtree_walk(pl->tree, RBTREE_DELETE_ORDER, callback, ctx);
+}
+
+int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
+{
+       int i, maxfd;
+
+       if (!pl || !set) return 0;
+
+       maxfd = -1;
+
+       for (i = 0; i < MAX_SOCKETS; i++) {
+               if (pl->sockets[i].sockfd == -1) continue;
+               FD_SET(pl->sockets[i].sockfd, set);
+               if (pl->sockets[i].sockfd > maxfd) {
+                       maxfd = pl->sockets[i].sockfd;
+               }
+       }
+
+       if (maxfd < 0) return -1;
+
+       return maxfd + 1;
+}
+
+/*
+ *     Round-robins the receivers, without priority.
+ *
+ *     FIXME: Add sockfd, if -1, do round-robin, else do sockfd
+ *             IF in fdset.
+ */
+RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
+{
+       int start;
+       RADIUS_PACKET *packet;
+
+       if (!pl || !set) return NULL;
+
+       start = pl->last_recv;
+       do {
+               start++;
+               start &= SOCKOFFSET_MASK;
+
+               if (pl->sockets[start].sockfd == -1) continue;
+
+               if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
+
+#ifdef WITH_TCP
+               if (pl->sockets[start].proto == IPPROTO_TCP) {
+                       packet = fr_tcp_recv(pl->sockets[start].sockfd, false);
+               } else
+#endif
+                       packet = fr_radius_packet_recv(NULL, pl->sockets[start].sockfd, UDP_FLAGS_NONE, false);
+               if (!packet) continue;
+
+               /*
+                *      Call fr_packet_list_find_byreply().  If it
+                *      doesn't find anything, discard the reply.
+                */
+
+               pl->last_recv = start;
+#ifdef WITH_TCP
+               packet->proto = pl->sockets[start].proto;
+#endif
+               return packet;
+       } while (start != pl->last_recv);
+
+       return NULL;
+}
+
+uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl)
+{
+       uint32_t num_elements;
+
+       if (!pl) return 0;
+
+       num_elements = rbtree_num_elements(pl->tree);
+       if (num_elements < pl->num_outgoing) return 0; /* panic! */
+
+       return num_elements - pl->num_outgoing;
+}
+
+uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl)
+{
+       if (!pl) return 0;
+
+       return pl->num_outgoing;
+}
+
+/*
+ *     Debug the packet if requested.
+ */
+void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received)
+{
+       char src_ipaddr[FR_IPADDR_STRLEN];
+       char dst_ipaddr[FR_IPADDR_STRLEN];
+#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
+       char if_name[IFNAMSIZ];
+#endif
+
+       if (!fp) return;
+       if (!packet) return;
+
+       /*
+        *      Client-specific debugging re-prints the input
+        *      packet into the client log.
+        *
+        *      This really belongs in a utility library
+        */
+       if (is_radius_code(packet->code)) {
+               fprintf(fp, "%s %s Id %i from %s%s%s:%i to %s%s%s:%i "
+#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
+                      "%s%s%s"
+#endif
+                      "length %zu\n",
+                       received ? "Received" : "Sent",
+                       fr_packet_codes[packet->code],
+                       packet->id,
+                       packet->src_ipaddr.af == AF_INET6 ? "[" : "",
+                       fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->src_ipaddr),
+                       packet->src_ipaddr.af == AF_INET6 ? "]" : "",
+                       packet->src_port,
+                       packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
+                       fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->dst_ipaddr),
+                       packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
+                       packet->dst_port,
+#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
+                       received ? "via " : "",
+                       received ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
+                       received ? " " : "",
+#endif
+                       packet->data_len);
+       } else {
+               fprintf(fp, "%s code %u Id %i from %s%s%s:%i to %s%s%s:%i "
+#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
+                      "%s%s%s"
+#endif
+                      "length %zu\n",
+                       received ? "Received" : "Sent",
+                       packet->code,
+                       packet->id,
+                       packet->src_ipaddr.af == AF_INET6 ? "[" : "",
+                       fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->src_ipaddr),
+                       packet->src_ipaddr.af == AF_INET6 ? "]" : "",
+                       packet->src_port,
+                       packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
+                       fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->dst_ipaddr),
+                       packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
+                       packet->dst_port,
+#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
+                       received ? "via " : "",
+                       received ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
+                       received ? " " : "",
+#endif
+                       packet->data_len);
+       }
+}
index 44d7d7a7da24b94e29c21120a6ca2efc9cfd02db..4b328ecf948faf7f9fd8c90ede405667c9e2f01a 100644 (file)
@@ -1,8 +1,4 @@
 /*
- * packet.c    Generic packet manipulation functions.
- *
- * Version:    $Id$
- *
  *   This library is free software; you can redistribute it and/or
  *   modify it under the terms of the GNU Lesser General Public
  *   License as published by the Free Software Foundation; either
  *   You should have received a copy of the GNU Lesser General Public
  *   License along with this library; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file packet.c
+ * @brief Functions to deal with RADIUS_PACKET data structures.
  *
- * Copyright 2000-2006  The FreeRADIUS server project
+ * @copyright 2000-2017  The FreeRADIUS server project
  */
 
 RCSID("$Id$")
 
-#include       <freeradius-devel/libradius.h>
-#include       <freeradius-devel/udp.h>
-
-#include <fcntl.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/udp.h>
 
-/*
- *     See if two packets are identical.
- *
- *     Note that we do NOT compare the authentication vectors.
- *     That's because if the authentication vector is different,
- *     it means that the NAS has given up on the earlier request.
- */
-int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b)
-{
-       int rcode;
+#ifdef WITH_UDPFROMTO
+#include <freeradius-devel/udpfromto.h>
+#endif
 
-       /*
-        *      256-way fanout.
-        */
-       if (a->id < b->id) return -1;
-       if (a->id > b->id) return +1;
+#include <fcntl.h>
+#include <ctype.h>
 
-       if (a->sockfd < b->sockfd) return -1;
-       if (a->sockfd > b->sockfd) return +1;
+typedef struct radius_packet_t {
+       uint8_t code;
+       uint8_t id;
+       uint8_t length[2];
+       uint8_t vector[AUTH_VECTOR_LEN];
+       uint8_t data[1];
+} radius_packet_t;
 
-       /*
-        *      Source ports are pretty much random.
-        */
-       rcode = (int) a->src_port - (int) b->src_port;
-       if (rcode != 0) return rcode;
 
-       /*
-        *      Usually many client IPs, and few server IPs
-        */
-       rcode = fr_ipaddr_cmp(&a->src_ipaddr, &b->src_ipaddr);
-       if (rcode != 0) return rcode;
-
-       /*
-        *      One socket can receive packets for multiple
-        *      destination IPs, so we check that before checking the
-        *      file descriptor.
-        */
-       rcode = fr_ipaddr_cmp(&a->dst_ipaddr, &b->dst_ipaddr);
-       if (rcode != 0) return rcode;
+/*
+ *     Some messages get printed out only in debugging mode.
+ */
+#define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf
 
-       /*
-        *      At this point, the order of comparing socket FDs
-        *      and/or destination ports doesn't matter.  One of those
-        *      fields will make the socket unique, and the other is
-        *      pretty much redundant.
-        */
-       rcode = (int) a->dst_port - (int) b->dst_port;
-       return rcode;
-}
 
-/** Determine if an address is the INADDR_ANY address for its address family
+/** Allocate a new RADIUS_PACKET
  *
- * @param ipaddr to check.
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ *     the packet is not associated with a REQUEST.
+ * @param new_vector if true a new request authenticator will be generated.
  * @return
- *     - 0 if it's not.
- *     - 1 if it is.
- *     - -1 on error.
+ *     - New RADIUS_PACKET.
+ *     - NULL on error.
  */
-int fr_is_inaddr_any(fr_ipaddr_t *ipaddr)
+RADIUS_PACKET *fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector)
 {
+       RADIUS_PACKET   *rp;
 
-       if (ipaddr->af == AF_INET) {
-               if (ipaddr->ipaddr.ip4addr.s_addr == INADDR_ANY) {
-                       return 1;
-               }
+       rp = talloc_zero(ctx, RADIUS_PACKET);
+       if (!rp) {
+               fr_strerror_printf("out of memory");
+               return NULL;
+       }
+       rp->id = -1;
+       rp->offset = -1;
 
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       } else if (ipaddr->af == AF_INET6) {
-               if (IN6_IS_ADDR_UNSPECIFIED(&(ipaddr->ipaddr.ip6addr))) {
-                       return 1;
-               }
-#endif
+       if (new_vector) {
+               int i;
+               uint32_t hash, base;
 
-       } else {
-               fr_strerror_printf("Unknown address family");
-               return -1;
+               /*
+                *      Don't expose the actual contents of the random
+                *      pool.
+                */
+               base = fr_rand();
+               for (i = 0; i < AUTH_VECTOR_LEN; i += sizeof(uint32_t)) {
+                       hash = fr_rand() ^ base;
+                       memcpy(rp->vector + i, &hash, sizeof(hash));
+               }
        }
+       fr_rand();              /* stir the pool again */
 
-       return 0;
+       return rp;
 }
 
-
-/*
- *     Create a fake "request" from a reply, for later lookup.
+/** Allocate a new RADIUS_PACKET response
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ *     the packet is not associated with a REQUEST.
+ * @param packet The request packet.
+ * @return
+ *     - New RADIUS_PACKET.
+ *     - NULL on error.
  */
-void fr_request_from_reply(RADIUS_PACKET *request,
-                          RADIUS_PACKET const *reply)
+RADIUS_PACKET *fr_radius_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
 {
-       request->sockfd = reply->sockfd;
-       request->id = reply->id;
-#ifdef WITH_TCP
-       request->proto = reply->proto;
-#endif
-       request->src_port = reply->dst_port;
-       request->dst_port = reply->src_port;
-       request->src_ipaddr = reply->dst_ipaddr;
-       request->dst_ipaddr = reply->src_ipaddr;
-       request->if_index = reply->if_index;
-}
+       RADIUS_PACKET *reply;
 
-/*
- *     We need to keep track of the socket & it's IP/port.
- */
-typedef struct fr_packet_socket_t {
-       int             sockfd;
-       void            *ctx;
-
-       uint32_t        num_outgoing;
+       if (!packet) return NULL;
 
-       int             src_any;
-       fr_ipaddr_t     src_ipaddr;
-       uint16_t        src_port;
+       reply = fr_radius_alloc(ctx, false);
+       if (!reply) return NULL;
 
-       int             dst_any;
-       fr_ipaddr_t     dst_ipaddr;
-       uint16_t        dst_port;
-
-       bool            dont_use;
+       /*
+        *      Initialize the fields from the request.
+        */
+       reply->sockfd = packet->sockfd;
+       reply->dst_ipaddr = packet->src_ipaddr;
+       reply->src_ipaddr = packet->dst_ipaddr;
+       reply->dst_port = packet->src_port;
+       reply->src_port = packet->dst_port;
+       reply->if_index = packet->if_index;
+       reply->id = packet->id;
+       reply->code = 0; /* UNKNOWN code */
+       memcpy(reply->vector, packet->vector,
+              sizeof(reply->vector));
+       reply->vps = NULL;
+       reply->data = NULL;
+       reply->data_len = 0;
 
 #ifdef WITH_TCP
-       int             proto;
+       reply->proto = packet->proto;
 #endif
-
-       uint8_t         id[32];
-} fr_packet_socket_t;
-
-
-#define FNV_MAGIC_PRIME (0x01000193)
-#define MAX_SOCKETS (256)
-#define SOCKOFFSET_MASK (MAX_SOCKETS - 1)
-#define SOCK2OFFSET(sockfd) ((sockfd * FNV_MAGIC_PRIME) & SOCKOFFSET_MASK)
-
-/*
- *     Structure defining a list of packets (incoming or outgoing)
- *     that should be managed.
- */
-struct fr_packet_list_t {
-       rbtree_t        *tree;
-
-       int             alloc_id;
-       uint32_t        num_outgoing;
-       int             last_recv;
-       int             num_sockets;
-
-       fr_packet_socket_t sockets[MAX_SOCKETS];
-};
+       return reply;
+}
 
 
-/*
- *     Ugh.  Doing this on every sent/received packet is not nice.
+/** Free a RADIUS_PACKET
+ *
  */
-static fr_packet_socket_t *fr_socket_find(fr_packet_list_t *pl,
-                                         int sockfd)
+void fr_radius_free(RADIUS_PACKET **radius_packet_ptr)
 {
-       int i, start;
+       RADIUS_PACKET *radius_packet;
 
-       i = start = SOCK2OFFSET(sockfd);
+       if (!radius_packet_ptr || !*radius_packet_ptr) return;
+       radius_packet = *radius_packet_ptr;
 
-       do {                    /* make this hack slightly more efficient */
-               if (pl->sockets[i].sockfd == sockfd) return &pl->sockets[i];
+       VERIFY_PACKET(radius_packet);
 
-               i = (i + 1) & SOCKOFFSET_MASK;
-       } while (i != start);
+       fr_pair_list_free(&radius_packet->vps);
 
-       return NULL;
+       talloc_free(radius_packet);
+       *radius_packet_ptr = NULL;
 }
 
-bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
+/** Duplicate a RADIUS_PACKET
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ *     the packet is not associated with a REQUEST.
+ * @param in The packet to copy
+ * @return
+ *     - New RADIUS_PACKET.
+ *     - NULL on error.
+ */
+RADIUS_PACKET *fr_radius_copy(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
 {
-       fr_packet_socket_t *ps;
+       RADIUS_PACKET *out;
 
-       if (!pl) {
-               fr_strerror_printf("Invalid argument");
-               return false;
-       }
+       out = fr_radius_alloc(ctx, false);
+       if (!out) return NULL;
 
-       ps = fr_socket_find(pl, sockfd);
-       if (!ps) {
-               fr_strerror_printf("No such socket");
-               return false;
-       }
-
-       ps->dont_use = true;
-       return true;
-}
+       /*
+        *      Bootstrap by copying everything.
+        */
+       memcpy(out, in, sizeof(*out));
 
-bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
-{
-       fr_packet_socket_t *ps;
+       /*
+        *      Then reset necessary fields
+        */
+       out->sockfd = -1;
 
-       if (!pl) return false;
+       out->data = NULL;
+       out->data_len = 0;
 
-       ps = fr_socket_find(pl, sockfd);
-       if (!ps) return false;
+       out->vps = fr_pair_list_copy(out, in->vps);
+       out->offset = 0;
 
-       ps->dont_use = false;
-       return true;
+       return out;
 }
 
 
-bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd)
+/** Encode a packet
+ *
+ */
+int fr_radius_packet_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                    char const *secret)
 {
-       fr_packet_socket_t *ps;
+       radius_packet_t         *hdr;
+       uint8_t                 *ptr;
+       uint16_t                total_length;
+       int                     len;
+       VALUE_PAIR const        *vp;
+       vp_cursor_t             cursor;
+       fr_radius_ctx_t encoder_ctx = { .packet = packet, .original = original, .secret = secret };
+
+       /*
+        *      A 4K packet, aligned on 64-bits.
+        */
+       uint64_t        data[MAX_PACKET_LEN / sizeof(uint64_t)];
+
+       /*
+        *      Double-check some things based on packet code.
+        */
+       switch (packet->code) {
+       case PW_CODE_ACCESS_ACCEPT:
+       case PW_CODE_ACCESS_REJECT:
+       case PW_CODE_ACCESS_CHALLENGE:
+               if (!original) {
+                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
+                       return -1;
+               }
+               break;
+
+               /*
+                *      These packet vectors start off as all zero.
+                */
+       case PW_CODE_ACCOUNTING_REQUEST:
+       case PW_CODE_DISCONNECT_REQUEST:
+       case PW_CODE_COA_REQUEST:
+               memset(packet->vector, 0, sizeof(packet->vector));
+               break;
 
-       if (!pl) return false;
+       default:
+               break;
+       }
 
-       ps = fr_socket_find(pl, sockfd);
-       if (!ps) return false;
+       /*
+        *      Use memory on the stack, until we know how
+        *      large the packet will be.
+        */
+       hdr = (radius_packet_t *) data;
 
-       if (ps->num_outgoing != 0) return false;
+       /*
+        *      Build standard header
+        */
+       hdr->code = packet->code;
+       hdr->id = packet->id;
 
-       ps->sockfd = -1;
-       pl->num_sockets--;
+       memcpy(hdr->vector, packet->vector, sizeof(hdr->vector));
 
-       return true;
-}
+       total_length = RADIUS_HDR_LEN;
 
+       /*
+        *      Load up the configuration values for the user
+        */
+       ptr = hdr->data;
+       packet->offset = 0;
 
-bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
-                             fr_ipaddr_t *dst_ipaddr, uint16_t dst_port,
-                             void *ctx)
-{
-       int i, start;
-       struct sockaddr_storage src;
-       socklen_t               sizeof_src;
-       fr_packet_socket_t      *ps;
+       /*
+        *      Loop over the reply attributes for the packet.
+        */
+       fr_pair_cursor_init(&cursor, &packet->vps);
+       while ((vp = fr_pair_cursor_current(&cursor))) {
+               size_t          last_len, room;
+               char const      *last_name = NULL;
 
-       if (!pl || !dst_ipaddr || (dst_ipaddr->af == AF_UNSPEC)) {
-               fr_strerror_printf("Invalid argument");
-               return false;
-       }
+               VERIFY_VP(vp);
 
-       if (pl->num_sockets >= MAX_SOCKETS) {
-               fr_strerror_printf("Too many open sockets");
-               return false;
-       }
+               room = ((uint8_t *)data) + sizeof(data) - ptr;
 
-#ifndef WITH_TCP
-       if (proto != IPPROTO_UDP) {
-               fr_strerror_printf("only UDP is supported");
-               return false;
-       }
+               /*
+                *      Ignore non-wire attributes, but allow extended
+                *      attributes.
+                *
+                *      @fixme We should be able to get rid of this check
+                *      and just look at da->flags.internal
+                */
+               if (vp->da->flags.internal || ((vp->da->vendor == 0) && (vp->da->attr >= 256))) {
+#ifndef NDEBUG
+                       /*
+                        *      Permit the admin to send BADLY formatted
+                        *      attributes with a debug build.
+                        */
+                       if (vp->da->attr == PW_RAW_ATTRIBUTE) {
+                               if (vp->vp_length > room) {
+                                       len = room;
+                               } else {
+                                       len = vp->vp_length;
+                               }
+
+                               memcpy(ptr, vp->vp_octets, len);
+                               fr_pair_cursor_next(&cursor);
+                               goto next;
+                       }
 #endif
+                       fr_pair_cursor_next(&cursor);
+                       continue;
+               }
 
-       ps = NULL;
-       i = start = SOCK2OFFSET(sockfd);
-
-       do {
-               if (pl->sockets[i].sockfd == -1) {
-                       ps =  &pl->sockets[i];
-                       break;
+               /*
+                *      Set the Message-Authenticator to the correct
+                *      length and initial value.
+                */
+               if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) {
+                       /*
+                        *      Cache the offset to the
+                        *      Message-Authenticator
+                        */
+                       packet->offset = total_length;
+                       last_len = 16;
+               } else {
+                       last_len = vp->vp_length;
                }
+               last_name = vp->da->name;
 
-               i = (i + 1) & SOCKOFFSET_MASK;
-       } while (i != start);
+               if (room <= 2) break;
 
-       if (!ps) {
-               fr_strerror_printf("All socket entries are full");
-               return false;
-       }
+               len = fr_radius_encode_pair(ptr, room, &cursor, &encoder_ctx);
+               if (len < 0) return -1;
 
-       memset(ps, 0, sizeof(*ps));
-       ps->ctx = ctx;
-#ifdef WITH_TCP
-       ps->proto = proto;
+               /*
+                *      Failed to encode the attribute, likely because
+                *      the packet is full.
+                */
+               if (len == 0) {
+                       if (last_len != 0) {
+                               fr_strerror_printf("WARNING: Failed encoding attribute %s\n", last_name);
+                               break;
+                       } else {
+                               fr_strerror_printf("WARNING: Skipping zero-length attribute %s\n", last_name);
+                       }
+               }
+
+#ifndef NDEBUG
+       next:                   /* Used only for Raw-Attribute */
 #endif
+               ptr += len;
+               total_length += len;
+       } /* done looping over all attributes */
 
        /*
-        *      Get address family, etc. first, so we know if we
-        *      need to do udpfromto.
+        *      Fill in the rest of the fields, and copy the data over
+        *      from the local stack to the newly allocated memory.
         *
-        *      FIXME: udpfromto also does this, but it's not
-        *      a critical problem.
+        *      Yes, all this 'memcpy' is slow, but it means
+        *      that we only allocate the minimum amount of
+        *      memory for a request.
         */
-       sizeof_src = sizeof(src);
-       memset(&src, 0, sizeof_src);
-       if (getsockname(sockfd, (struct sockaddr *) &src,
-                       &sizeof_src) < 0) {
-               fr_strerror_printf("%s", fr_syserror(errno));
-               return false;
+       packet->data_len = total_length;
+       packet->data = talloc_array(packet, uint8_t, packet->data_len);
+       if (!packet->data) {
+               fr_strerror_printf("Out of memory");
+               return -1;
        }
 
-       if (!fr_ipaddr_from_sockaddr(&src, sizeof_src, &ps->src_ipaddr,
-                               &ps->src_port)) {
-               fr_strerror_printf("Failed to get IP");
-               return false;
-       }
+       memcpy(packet->data, hdr, packet->data_len);
+       hdr = (radius_packet_t *) packet->data;
 
-       ps->dst_ipaddr = *dst_ipaddr;
-       ps->dst_port = dst_port;
+       total_length = htons(total_length);
+       memcpy(hdr->length, &total_length, sizeof(total_length));
 
-       ps->src_any = fr_is_inaddr_any(&ps->src_ipaddr);
-       if (ps->src_any < 0) return false;
+       return 0;
+}
 
-       ps->dst_any = fr_is_inaddr_any(&ps->dst_ipaddr);
-       if (ps->dst_any < 0) return false;
 
+/** Calculate/check digest, and decode radius attributes
+ *
+ * @return
+ *     - 0 on success
+ *     - -1 on decoding error.
+ */
+int fr_radius_packet_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
+{
+       int                     packet_length;
+       uint32_t                num_attributes;
+       uint8_t                 *ptr;
+       radius_packet_t         *hdr;
+       VALUE_PAIR              *head = NULL;
+       vp_cursor_t             cursor, out;
+       fr_radius_ctx_t         decoder_ctx = {
+                                       .original = original,
+                                       .packet = packet,
+                                       .secret = secret
+                               };
        /*
-        *      As the last step before returning.
+        *      Extract attribute-value pairs
         */
-       ps->sockfd = sockfd;
-       pl->num_sockets++;
-
-       return true;
-}
+       hdr = (radius_packet_t *)packet->data;
+       ptr = hdr->data;
+       packet_length = packet->data_len - RADIUS_HDR_LEN;
+       num_attributes = 0;
 
-static int packet_entry_cmp(void const *one, void const *two)
-{
-       RADIUS_PACKET const * const *a = one;
-       RADIUS_PACKET const * const *b = two;
+       fr_pair_cursor_init(&cursor, &head);
 
-       return fr_packet_cmp(*a, *b);
-}
+       /*
+        *      Loop over the attributes, decoding them into VPs.
+        */
+       while (packet_length > 0) {
+               ssize_t my_len;
 
-void fr_packet_list_free(fr_packet_list_t *pl)
-{
-       if (!pl) return;
+               /*
+                *      This may return many VPs
+                */
+               my_len = fr_radius_decode_pair(packet, &cursor, fr_dict_root(fr_dict_internal),
+                                              ptr, packet_length, &decoder_ctx);
+               if (my_len < 0) {
+                       fr_pair_list_free(&head);
+                       return -1;
+               }
 
-       talloc_free(pl->tree);
-       talloc_free(pl);
-}
+               /*
+                *      This should really be an assertion.
+                */
+               if (my_len == 0) break;
 
+               /*
+                *      Count the ones which were just added
+                */
+               while (fr_pair_cursor_next(&cursor)) num_attributes++;
 
-/*
- *     Caller is responsible for managing the packet entries.
- */
-fr_packet_list_t *fr_packet_list_create(int alloc_id)
-{
-       int i;
-       fr_packet_list_t        *pl;
+               /*
+                *      VSA's may not have been counted properly in
+                *      fr_radius_packet_ok() above, as it is hard to count
+                *      then without using the dictionary.  We
+                *      therefore enforce the limits here, too.
+                */
+               if ((fr_max_attributes > 0) && (num_attributes > fr_max_attributes)) {
+                       char host_ipaddr[INET6_ADDRSTRLEN];
+
+                       fr_pair_list_free(&head);
+                       fr_strerror_printf("Possible DoS attack from host %s: Too many attributes in request "
+                                          "(received %d, max %d are allowed)",
+                                          inet_ntop(packet->src_ipaddr.af,
+                                                    &packet->src_ipaddr.ipaddr,
+                                                    host_ipaddr, sizeof(host_ipaddr)),
+                                          num_attributes, fr_max_attributes);
+                       return -1;
+               }
 
-       pl = talloc_zero(NULL, fr_packet_list_t);
-       if (!pl) return NULL;
-       pl->tree = rbtree_create(pl, packet_entry_cmp, NULL, 0);
-       if (!pl->tree) {
-               fr_packet_list_free(pl);
-               return NULL;
+               ptr += my_len;
+               packet_length -= my_len;
        }
 
-       for (i = 0; i < MAX_SOCKETS; i++) {
-               pl->sockets[i].sockfd = -1;
-       }
+       fr_pair_cursor_init(&out, &packet->vps);
+       fr_pair_cursor_last(&out);              /* Move insertion point to the end of the list */
+       fr_pair_cursor_merge(&out, head);
 
-       pl->alloc_id = alloc_id;
+       /*
+        *      Merge information from the outside world into our
+        *      random pool.
+        */
+       fr_rand_seed(packet->data, RADIUS_HDR_LEN);
 
-       return pl;
+       return 0;
 }
 
 
-/*
- *     If pl->alloc_id is set, then fr_packet_list_id_alloc() MUST
- *     be called before inserting the packet into the list!
+/** See if the data pointed to by PTR is a valid RADIUS packet.
+ *
+ * Packet is not 'const * const' because we may update data_len, if there's more data
+ * in the UDP packet than in the RADIUS packet.
+ *
+ * @param packet to check
+ * @param require_ma to require Message-Authenticator
+ * @param reason if not NULL, will have the failure reason written to where it points.
+ * @return
+ *     - True on success.
+ *     - False on failure.
  */
-bool fr_packet_list_insert(fr_packet_list_t *pl,
-                           RADIUS_PACKET **request_p)
+bool fr_radius_packet_ok(RADIUS_PACKET *packet, bool require_ma, decode_fail_t *reason)
 {
-       if (!pl || !request_p || !*request_p) return 0;
+       char host_ipaddr[INET6_ADDRSTRLEN];
+
+       if (!fr_radius_ok(packet->data, &packet->data_len, require_ma, reason)) {
+               FR_DEBUG_STRERROR_PRINTF("Bad packet received from host %s - %s",
+                                        inet_ntop(packet->src_ipaddr.af,
+                                                  &packet->src_ipaddr.ipaddr,
+                                                  host_ipaddr, sizeof(host_ipaddr)),
+                                        fr_strerror());
+               return false;
+       }
 
-       return rbtree_insert(pl->tree, request_p);
+       /*
+        *      Fill RADIUS header fields
+        */
+       packet->code = packet->data[0];
+       packet->id = packet->data[1];
+       memcpy(packet->vector, packet->data + 4, sizeof(packet->vector));
+       return true;
 }
 
-RADIUS_PACKET **fr_packet_list_find(fr_packet_list_t *pl,
-                                     RADIUS_PACKET *request)
+
+/** Verify the Request/Response Authenticator (and Message-Authenticator if present) of a packet
+ *
+ */
+int fr_radius_packet_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
 {
-       if (!pl || !request) return 0;
+       uint8_t const   *original_data;
+       char            buffer[INET6_ADDRSTRLEN];
 
-       return rbtree_finddata(pl->tree, &request);
+       if (!packet->data) return -1;
+
+       if (original) {
+               original_data = original->data;
+       } else {
+               original_data = NULL;
+       }
+
+       if (fr_radius_verify(packet->data, original_data,
+                            (uint8_t const *) secret, talloc_array_length(secret) - 1) < 0) {
+               fr_strerror_printf("Received packet from %s with %s",
+                                  inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
+                                            buffer, sizeof(buffer)),
+                                  fr_strerror());
+               return -1;
+       }
+
+       return 0;
 }
 
 
-/*
- *     This presumes that the reply has dst_ipaddr && dst_port set up
- *     correctly (i.e. real IP, or "*").
+/** Sign a previously encoded packet
+ *
  */
-RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl, RADIUS_PACKET *reply)
+int fr_radius_packet_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                         char const *secret)
 {
-       RADIUS_PACKET my_request, *request;
-       fr_packet_socket_t *ps;
-
-       if (!pl || !reply) return NULL;
-
-       ps = fr_socket_find(pl, reply->sockfd);
-       if (!ps) return NULL;
+       int rcode;
+       uint8_t const *original_data;
 
-       /*
-        *      Initialize request from reply, AND from the source
-        *      IP & port of this socket.  The client may have bound
-        *      the socket to 0, in which case it's some random port,
-        *      that is NOT in the original request->src_port.
-        */
-       my_request.sockfd = reply->sockfd;
-       my_request.id = reply->id;
+       if (original) {
+               original_data = original->data;
+       } else {
+               original_data = NULL;
+       }
 
-#ifdef WITH_TCP
        /*
-        *      TCP sockets are always bound to the correct src/dst IP/port
+        *      Copy the random vector to the packet.  Other packet
+        *      codes have the Request Authenticator be the packet
+        *      signature.
         */
-       if (ps->proto == IPPROTO_TCP) {
-               reply->dst_ipaddr = ps->src_ipaddr;
-               reply->dst_port = ps->src_port;
-               reply->src_ipaddr = ps->dst_ipaddr;
-               reply->src_port = ps->dst_port;
-
-               my_request.src_ipaddr = ps->src_ipaddr;
-               my_request.src_port = ps->src_port;
-               my_request.dst_ipaddr = ps->dst_ipaddr;
-               my_request.dst_port = ps->dst_port;
-
-       } else
-#endif
-       {
-               if (ps->src_any) {
-                       my_request.src_ipaddr = ps->src_ipaddr;
-               } else {
-                       my_request.src_ipaddr = reply->dst_ipaddr;
-               }
-               my_request.src_port = ps->src_port;
-
-               my_request.dst_ipaddr = reply->src_ipaddr;
-               my_request.dst_port = reply->src_port;
+       if ((packet->code == PW_CODE_ACCESS_REQUEST) ||
+           (packet->code == PW_CODE_STATUS_SERVER)) {
+               memcpy(packet->data + 4, packet->vector, sizeof(packet->vector));
        }
 
-#ifdef WITH_TCP
-       my_request.proto = reply->proto;
-#endif
-       request = &my_request;
+       rcode = fr_radius_sign(packet->data, original_data,
+                              (uint8_t const *) secret, talloc_array_length(secret) - 1);
+       if (rcode < 0) return rcode;
 
-       return rbtree_finddata(pl->tree, &request);
+       memcpy(packet->vector, packet->data + 4, AUTH_VECTOR_LEN);
+       return 0;
 }
 
 
-bool fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request)
+/** Wrapper for recvfrom, which handles recvfromto, IPv6, and all possible combinations
+ *
+ */
+static ssize_t rad_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags)
 {
-       rbnode_t *node;
+       ssize_t                 data_len;
 
-       if (!pl || !request) return false;
+       data_len = fr_radius_recv_header(sockfd, &packet->src_ipaddr, &packet->src_port, &packet->code);
+       if (data_len < 0) {
+               if ((errno == EAGAIN) || (errno == EINTR)) return 0;
+               return -1;
+       }
 
-       node = rbtree_find(pl->tree, &request);
-       if (!node) return false;
+       if (data_len == 0) return -1; /* invalid packet */
 
-       rbtree_delete(pl->tree, node);
-       return true;
-}
+       packet->data = talloc_array(packet, uint8_t, data_len);
+       if (!packet->data) return -1;
 
-uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl)
-{
-       if (!pl) return 0;
+       packet->data_len = data_len;
 
-       return rbtree_num_elements(pl->tree);
+       return udp_recv(sockfd, packet->data, packet->data_len, flags,
+                       &packet->src_ipaddr, &packet->src_port,
+                       &packet->dst_ipaddr, &packet->dst_port,
+                       &packet->if_index, &packet->timestamp);
 }
 
 
-/*
- *     1 == ID was allocated & assigned
- *     0 == couldn't allocate ID.
- *
- *     Note that this ALSO assigns a socket to use, and updates
- *     packet->request->src_ipaddr && packet->request->src_port
+/** Receive UDP client requests, and fill in the basics of a RADIUS_PACKET structure
  *
- *     In multi-threaded systems, the calls to id_alloc && id_free
- *     should be protected by a mutex.  This does NOT have to be
- *     the same mutex as the one protecting the insert/find/yank
- *     calls!
- *
- *     We assume that the packet has dst_ipaddr && dst_port
- *     already initialized.  We will use those to find an
- *     outgoing socket.  The request MAY also have src_ipaddr set.
- *
- *     We also assume that the sender doesn't care which protocol
- *     should be used.
  */
-bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
-                           RADIUS_PACKET **request_p, void **pctx)
+RADIUS_PACKET *fr_radius_packet_recv(TALLOC_CTX *ctx, int fd, int flags, bool require_ma)
 {
-       int i, j, k, fd, id, start_i, start_j, start_k;
-       int src_any = 0;
-       fr_packet_socket_t *ps= NULL;
-       RADIUS_PACKET *request = *request_p;
-
-       if ((request->dst_ipaddr.af == AF_UNSPEC) ||
-           (request->dst_port == 0)) {
-               fr_strerror_printf("No destination address/port specified");
-               return false;
+       ssize_t data_len;
+       RADIUS_PACKET           *packet;
+
+       /*
+        *      Allocate the new request data structure
+        */
+       packet = fr_radius_alloc(ctx, false);
+       if (!packet) {
+               fr_strerror_printf("out of memory");
+               return NULL;
        }
 
-#ifndef WITH_TCP
-       if ((proto != 0) && (proto != IPPROTO_UDP)) {
-               fr_strerror_printf("Invalid destination protocol");
-               return false;
+       data_len = rad_recvfrom(fd, packet, flags);
+       if (data_len < 0) {
+               FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
+               fr_radius_free(&packet);
+               return NULL;
        }
-#endif
 
+#ifdef WITH_VERIFY_PTR
        /*
-        *      Special case: unspec == "don't care"
+        *      Double-check that the fields we want are filled in.
         */
-       if (request->src_ipaddr.af == AF_UNSPEC) {
-               memset(&request->src_ipaddr, 0, sizeof(request->src_ipaddr));
-               request->src_ipaddr.af = request->dst_ipaddr.af;
+       if ((packet->src_ipaddr.af == AF_UNSPEC) ||
+           (packet->src_port == 0) ||
+           (packet->dst_ipaddr.af == AF_UNSPEC) ||
+           (packet->dst_port == 0)) {
+               FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
+               fr_radius_free(&packet);
+               return NULL;
        }
+#endif
 
-       src_any = fr_is_inaddr_any(&request->src_ipaddr);
-       if (src_any < 0) {
-               fr_strerror_printf("Can't check src_ipaddr");
-               return false;
-       }
+       packet->data_len = data_len; /* unsigned vs signed */
 
        /*
-        *      MUST specify a destination address.
+        *      If the packet is too big, then rad_recvfrom did NOT
+        *      allocate memory.  Instead, it just discarded the
+        *      packet.
         */
-       if (fr_is_inaddr_any(&request->dst_ipaddr) != 0) {
-               fr_strerror_printf("Must specify a dst_ipaddr");
-               return false;
+       if (packet->data_len > MAX_PACKET_LEN) {
+               FR_DEBUG_STRERROR_PRINTF("Discarding packet: Larger than RFC limitation of 4096 bytes");
+               fr_radius_free(&packet);
+               return NULL;
        }
 
        /*
-        *      FIXME: Go to an LRU system.  This prevents ID re-use
-        *      for as long as possible.  The main problem with that
-        *      approach is that it requires us to populate the
-        *      LRU/FIFO when we add a new socket, or a new destination,
-        *      which can be expensive.
-        *
-        *      The LRU can be avoided if the caller takes care to free
-        *      Id's only when all responses have been received, OR after
-        *      a timeout.
-        *
-        *      Right now, the random approach is almost OK... it's
-        *      brute-force over all of the available ID's, BUT using
-        *      random numbers for everything spreads the load a bit.
-        *
-        *      The old method had a hash lookup on allocation AND
-        *      on free.  The new method has brute-force on allocation,
-        *      and near-zero cost on free.
+        *      Read no data.  Continue.
+        *      This check is AFTER the MAX_PACKET_LEN check above, because
+        *      if the packet is larger than MAX_PACKET_LEN, we also have
+        *      packet->data == NULL
         */
-
-       id = fd = -1;
-       start_i = fr_rand() & SOCKOFFSET_MASK;
-
-#define ID_i ((i + start_i) & SOCKOFFSET_MASK)
-       for (i = 0; i < MAX_SOCKETS; i++) {
-               if (pl->sockets[ID_i].sockfd == -1) continue; /* paranoia */
-
-               ps = &(pl->sockets[ID_i]);
-
-               /*
-                *      This socket is marked as "don't use for new
-                *      packets".  But we can still receive packets
-                *      that are outstanding.
-                */
-               if (ps->dont_use) continue;
-
-               /*
-                *      All IDs are allocated: ignore it.
-                */
-               if (ps->num_outgoing == 256) continue;
-
-#ifdef WITH_TCP
-               if (ps->proto != proto) continue;
-#endif
-
-               /*
-                *      Address families don't match, skip it.
-                */
-               if (ps->src_ipaddr.af != request->dst_ipaddr.af) continue;
-
-               /*
-                *      MUST match dst port, if we have one.
-                */
-               if ((ps->dst_port != 0) &&
-                   (ps->dst_port != request->dst_port)) continue;
-
-               /*
-                *      MUST match requested src port, if one has been given.
-                */
-               if ((request->src_port != 0) &&
-                   (ps->src_port != request->src_port)) continue;
-
-               /*
-                *      We don't care about the source IP, but this
-                *      socket is link local, and the requested
-                *      destination is not link local.  Ignore it.
-                */
-               if (src_any && (ps->src_ipaddr.af == AF_INET) &&
-                   (((ps->src_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) == 127) &&
-                   (((request->dst_ipaddr.ipaddr.ip4addr.s_addr >> 24) & 0xff) != 127)) continue;
-
-               /*
-                *      We're sourcing from *, and they asked for a
-                *      specific source address: ignore it.
-                */
-               if (ps->src_any && !src_any) continue;
-
-               /*
-                *      We're sourcing from a specific IP, and they
-                *      asked for a source IP that isn't us: ignore
-                *      it.
-                */
-               if (!ps->src_any && !src_any &&
-                   (fr_ipaddr_cmp(&request->src_ipaddr,
-                                  &ps->src_ipaddr) != 0)) continue;
-
-               /*
-                *      UDP sockets are allowed to match
-                *      destination IPs exactly, OR a socket
-                *      with destination * is allowed to match
-                *      any requested destination.
-                *
-                *      TCP sockets must match the destination
-                *      exactly.  They *always* have dst_any=0,
-                *      so the first check always matches.
-                */
-               if (!ps->dst_any &&
-                   (fr_ipaddr_cmp(&request->dst_ipaddr,
-                                  &ps->dst_ipaddr) != 0)) continue;
-
-               /*
-                *      Otherwise, this socket is OK to use.
-                */
-
-               /*
-                *      Look for a free Id, starting from a random number.
-                */
-               start_j = fr_rand() & 0x1f;
-#define ID_j ((j + start_j) & 0x1f)
-               for (j = 0; j < 32; j++) {
-                       if (ps->id[ID_j] == 0xff) continue;
-
-
-                       start_k = fr_rand() & 0x07;
-#define ID_k ((k + start_k) & 0x07)
-                       for (k = 0; k < 8; k++) {
-                               if ((ps->id[ID_j] & (1 << ID_k)) != 0) continue;
-
-                               ps->id[ID_j] |= (1 << ID_k);
-                               id = (ID_j * 8) + ID_k;
-                               fd = i;
-                               break;
-                       }
-                       if (fd >= 0) break;
-               }
-#undef ID_i
-#undef ID_j
-#undef ID_k
-               break;
+       if ((packet->data_len == 0) || !packet->data) {
+               FR_DEBUG_STRERROR_PRINTF("Empty packet: Socket is not ready");
+               fr_radius_free(&packet);
+               return NULL;
        }
 
        /*
-        *      Ask the caller to allocate a new ID.
+        *      See if it's a well-formed RADIUS packet.
         */
-       if (fd < 0) {
-               fr_strerror_printf("Failed finding socket, caller must allocate a new one");
-               return false;
+       if (!fr_radius_packet_ok(packet, require_ma, NULL)) {
+               fr_radius_free(&packet);
+               return NULL;
        }
 
        /*
-        *      Set the ID, source IP, and source port.
+        *      Remember which socket we read the packet from.
         */
-       request->id = id;
-
-       request->sockfd = ps->sockfd;
-       request->src_ipaddr = ps->src_ipaddr;
-       request->src_port = ps->src_port;
+       packet->sockfd = fd;
 
        /*
-        *      If we managed to insert it, we're done.
+        *      FIXME: Do even more filtering by only permitting
+        *      certain IP's.  The problem is that we don't know
+        *      how to do this properly for all possible clients...
         */
-       if (fr_packet_list_insert(pl, request_p)) {
-               if (pctx) *pctx = ps->ctx;
-               ps->num_outgoing++;
-               pl->num_outgoing++;
-               return true;
-       }
 
        /*
-        *      Mark the ID as free.  This is the one line from
-        *      id_free() that we care about here.
+        *      Explicitely set the VP list to empty.
         */
-       ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
+       packet->vps = NULL;
 
-       request->id = -1;
-       request->sockfd = -1;
-       request->src_ipaddr.af = AF_UNSPEC;
-       request->src_port = 0;
+#ifndef NDEBUG
+       if ((fr_debug_lvl > 3) && fr_log_fp) fr_radius_print_hex(packet);
+#endif
 
-       return false;
+       return packet;
 }
 
-/*
- *     Should be called AFTER yanking it from the list, so that
- *     any newly inserted entries don't collide with this one.
+/** Reply to the request
+ *
+ * Also attach reply attribute value pairs and any user message provided.
  */
-bool fr_packet_list_id_free(fr_packet_list_t *pl,
-                           RADIUS_PACKET *request, bool yank)
+int fr_radius_packet_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                         char const *secret)
 {
-       fr_packet_socket_t *ps;
-
-       if (!pl || !request) return false;
+       /*
+        *      Maybe it's a fake packet.  Don't send it.
+        */
+       if (packet->sockfd < 0) {
+               return 0;
+       }
 
-       if (yank && !fr_packet_list_yank(pl, request)) return false;
+       /*
+        *  First time through, allocate room for the packet
+        */
+       if (!packet->data) {
+               /*
+                *      Encode the packet.
+                */
+               if (fr_radius_packet_encode(packet, original, secret) < 0) {
+                       return -1;
+               }
 
-       ps = fr_socket_find(pl, request->sockfd);
-       if (!ps) return false;
+               /*
+                *      Re-sign it, including updating the
+                *      Message-Authenticator.
+                */
+               if (fr_radius_packet_sign(packet, original, secret) < 0) {
+                       return -1;
+               }
 
-#if 0
-       if (!ps->id[(request->id >> 3) & 0x1f] & (1 << (request->id & 0x07))) {
-               fr_exit(1);
+               /*
+                *      If packet->data points to data, then we print out
+                *      the VP list again only for debugging.
+                */
        }
-#endif
-
-       ps->id[(request->id >> 3) & 0x1f] &= ~(1 << (request->id & 0x07));
 
-       ps->num_outgoing--;
-       pl->num_outgoing--;
+#ifndef NDEBUG
+       if ((fr_debug_lvl > 3) && fr_log_fp) fr_radius_print_hex(packet);
+#endif
 
-       request->id = -1;
-       request->src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */
-       request->src_port = 0;
+#ifdef WITH_TCP
+       /*
+        *      If the socket is TCP, call write().  Calling sendto()
+        *      is allowed on some platforms, but it's not nice.  Even
+        *      worse, if UDPFROMTO is defined, we *can't* use it on
+        *      TCP sockets.  So... just call write().
+        */
+       if (packet->proto == IPPROTO_TCP) {
+               ssize_t rcode;
 
-       return true;
-}
+               rcode = write(packet->sockfd, packet->data, packet->data_len);
+               if (rcode >= 0) return rcode;
 
-/*
- *     We always walk RBTREE_DELETE_ORDER, which is like RBTREE_IN_ORDER, except that
- *     <0 means error, stop
- *     0  means OK, continue
- *     1  means delete current node and stop
- *     2  means delete current node and continue
- */
-int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx, rb_walker_t callback)
-{
-       if (!pl || !callback) return 0;
+               fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
+               return -1;
+       }
+#endif
 
-       return rbtree_walk(pl->tree, RBTREE_DELETE_ORDER, callback, ctx);
+       /*
+        *      And send it on it's way.
+        */
+       return udp_send(packet->sockfd, packet->data, packet->data_len, 0,
+                       &packet->src_ipaddr, packet->src_port, packet->if_index,
+                       &packet->dst_ipaddr, packet->dst_port);
 }
 
-int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
+static void print_hex_data(uint8_t const *ptr, int attrlen, int depth)
 {
-       int i, maxfd;
-
-       if (!pl || !set) return 0;
-
-       maxfd = -1;
+       int i;
+       static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
 
-       for (i = 0; i < MAX_SOCKETS; i++) {
-               if (pl->sockets[i].sockfd == -1) continue;
-               FD_SET(pl->sockets[i].sockfd, set);
-               if (pl->sockets[i].sockfd > maxfd) {
-                       maxfd = pl->sockets[i].sockfd;
-               }
+       for (i = 0; i < attrlen; i++) {
+               if ((i > 0) && ((i & 0x0f) == 0x00))
+                       fprintf(fr_log_fp, "%.*s", depth, tabs);
+               fprintf(fr_log_fp, "%02x ", ptr[i]);
+               if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
        }
-
-       if (maxfd < 0) return -1;
-
-       return maxfd + 1;
+       if ((i & 0x0f) != 0) fprintf(fr_log_fp, "\n");
 }
 
-/*
- *     Round-robins the receivers, without priority.
- *
- *     FIXME: Add sockfd, if -1, do round-robin, else do sockfd
- *             IF in fdset.
- */
-RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
-{
-       int start;
-       RADIUS_PACKET *packet;
-
-       if (!pl || !set) return NULL;
-
-       start = pl->last_recv;
-       do {
-               start++;
-               start &= SOCKOFFSET_MASK;
 
-               if (pl->sockets[start].sockfd == -1) continue;
+void fr_radius_print_hex(RADIUS_PACKET const *packet)
+{
+       int i;
 
-               if (!FD_ISSET(pl->sockets[start].sockfd, set)) continue;
+       if (!packet->data || !fr_log_fp) return;
 
+       fprintf(fr_log_fp, "  Socket:\t%d\n", packet->sockfd);
 #ifdef WITH_TCP
-               if (pl->sockets[start].proto == IPPROTO_TCP) {
-                       packet = fr_tcp_recv(pl->sockets[start].sockfd, false);
-               } else
+       fprintf(fr_log_fp, "  Proto:\t%d\n", packet->proto);
 #endif
-                       packet = fr_radius_packet_recv(NULL, pl->sockets[start].sockfd, UDP_FLAGS_NONE, false);
-               if (!packet) continue;
 
-               /*
-                *      Call fr_packet_list_find_byreply().  If it
-                *      doesn't find anything, discard the reply.
-                */
+       if (packet->src_ipaddr.af == AF_INET) {
+               char buffer[INET6_ADDRSTRLEN];
 
-               pl->last_recv = start;
-#ifdef WITH_TCP
-               packet->proto = pl->sockets[start].proto;
-#endif
-               return packet;
-       } while (start != pl->last_recv);
+               fprintf(fr_log_fp, "  Src IP:\t%s\n",
+                       inet_ntop(packet->src_ipaddr.af,
+                                 &packet->src_ipaddr.ipaddr,
+                                 buffer, sizeof(buffer)));
+               fprintf(fr_log_fp, "    port:\t%u\n", packet->src_port);
 
-       return NULL;
-}
+               fprintf(fr_log_fp, "  Dst IP:\t%s\n",
+                       inet_ntop(packet->dst_ipaddr.af,
+                                 &packet->dst_ipaddr.ipaddr,
+                                 buffer, sizeof(buffer)));
+               fprintf(fr_log_fp, "    port:\t%u\n", packet->dst_port);
+       }
 
-uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl)
-{
-       uint32_t num_elements;
+       if (packet->data[0] < FR_MAX_PACKET_CODE) {
+               fprintf(fr_log_fp, "  Code:\t\t(%d) %s\n", packet->data[0], fr_packet_codes[packet->data[0]]);
+       } else {
+               fprintf(fr_log_fp, "  Code:\t\t%u\n", packet->data[0]);
+       }
+       fprintf(fr_log_fp, "  Id:\t\t%u\n", packet->data[1]);
+       fprintf(fr_log_fp, "  Length:\t%u\n", ((packet->data[2] << 8) |
+                                  (packet->data[3])));
+       fprintf(fr_log_fp, "  Vector:\t");
+       for (i = 4; i < 20; i++) {
+               fprintf(fr_log_fp, "%02x", packet->data[i]);
+       }
+       fprintf(fr_log_fp, "\n");
 
-       if (!pl) return 0;
+       if (packet->data_len > 20) {
+               int total;
+               uint8_t const *ptr;
+               fprintf(fr_log_fp, "  Data:");
 
-       num_elements = rbtree_num_elements(pl->tree);
-       if (num_elements < pl->num_outgoing) return 0; /* panic! */
+               total = packet->data_len - 20;
+               ptr = packet->data + 20;
 
-       return num_elements - pl->num_outgoing;
-}
+               while (total > 0) {
+                       int attrlen;
+                       unsigned int vendor = 0;
 
-uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl)
-{
-       if (!pl) return 0;
+                       fprintf(fr_log_fp, "\t\t");
+                       if (total < 2) { /* too short */
+                               fprintf(fr_log_fp, "%02x\n", *ptr);
+                               break;
+                       }
 
-       return pl->num_outgoing;
-}
+                       if (ptr[1] > total) { /* too long */
+                               for (i = 0; i < total; i++) {
+                                       fprintf(fr_log_fp, "%02x ", ptr[i]);
+                               }
+                               break;
+                       }
 
-/*
- *     Debug the packet if requested.
- */
-void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received)
-{
-       char src_ipaddr[FR_IPADDR_STRLEN];
-       char dst_ipaddr[FR_IPADDR_STRLEN];
-#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
-       char if_name[IFNAMSIZ];
-#endif
+                       fprintf(fr_log_fp, "%02x  %02x  ", ptr[0], ptr[1]);
+                       attrlen = ptr[1] - 2;
+
+                       if ((ptr[0] == PW_VENDOR_SPECIFIC) &&
+                           (attrlen > 4)) {
+                               vendor = (ptr[3] << 16) | (ptr[4] << 8) | ptr[5];
+                               fprintf(fr_log_fp, "%02x%02x%02x%02x (%u)  ",
+                                      ptr[2], ptr[3], ptr[4], ptr[5], vendor);
+                               attrlen -= 4;
+                               ptr += 6;
+                               total -= 6;
+
+                       } else {
+                               ptr += 2;
+                               total -= 2;
+                       }
 
-       if (!fp) return;
-       if (!packet) return;
+                       print_hex_data(ptr, attrlen, 3);
 
-       /*
-        *      Client-specific debugging re-prints the input
-        *      packet into the client log.
-        *
-        *      This really belongs in a utility library
-        */
-       if (is_radius_code(packet->code)) {
-               fprintf(fp, "%s %s Id %i from %s%s%s:%i to %s%s%s:%i "
-#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
-                      "%s%s%s"
-#endif
-                      "length %zu\n",
-                       received ? "Received" : "Sent",
-                       fr_packet_codes[packet->code],
-                       packet->id,
-                       packet->src_ipaddr.af == AF_INET6 ? "[" : "",
-                       fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->src_ipaddr),
-                       packet->src_ipaddr.af == AF_INET6 ? "]" : "",
-                       packet->src_port,
-                       packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
-                       fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->dst_ipaddr),
-                       packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
-                       packet->dst_port,
-#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
-                       received ? "via " : "",
-                       received ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
-                       received ? " " : "",
-#endif
-                       packet->data_len);
-       } else {
-               fprintf(fp, "%s code %u Id %i from %s%s%s:%i to %s%s%s:%i "
-#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
-                      "%s%s%s"
-#endif
-                      "length %zu\n",
-                       received ? "Received" : "Sent",
-                       packet->code,
-                       packet->id,
-                       packet->src_ipaddr.af == AF_INET6 ? "[" : "",
-                       fr_inet_ntop(src_ipaddr, sizeof(src_ipaddr), &packet->src_ipaddr),
-                       packet->src_ipaddr.af == AF_INET6 ? "]" : "",
-                       packet->src_port,
-                       packet->dst_ipaddr.af == AF_INET6 ? "[" : "",
-                       fr_inet_ntop(dst_ipaddr, sizeof(dst_ipaddr), &packet->dst_ipaddr),
-                       packet->dst_ipaddr.af == AF_INET6 ? "]" : "",
-                       packet->dst_port,
-#if defined(WITH_UDPFROMTO) && defined(WITH_IFINDEX_NAME_RESOLUTION)
-                       received ? "via " : "",
-                       received ? fr_ifname_from_ifindex(if_name, packet->if_index) : "",
-                       received ? " " : "",
-#endif
-                       packet->data_len);
+                       ptr += attrlen;
+                       total -= attrlen;
+               }
        }
+       fflush(stdout);
 }
diff --git a/src/protocols/radius/radius_packet.c b/src/protocols/radius/radius_packet.c
deleted file mode 100644 (file)
index 0e1278a..0000000
+++ /dev/null
@@ -1,848 +0,0 @@
-/*
- *   This library is free software; you can redistribute it and/or
- *   modify it under the terms of the GNU Lesser General Public
- *   License as published by the Free Software Foundation; either
- *   version 2.1 of the License, or (at your option) any later version.
- *
- *   This library is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- *   Lesser General Public License for more details.
- *
- *   You should have received a copy of the GNU Lesser General Public
- *   License along with this library; if not, write to the Free Software
- *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- */
-
-/**
- * $Id$
- *
- * @file radius_packet.c
- * @brief Functions to deal with RADIUS_PACKET data structures.
- *
- * @copyright 2000-2017  The FreeRADIUS server project
- */
-
-RCSID("$Id$")
-
-#include <freeradius-devel/libradius.h>
-#include <freeradius-devel/udp.h>
-
-#ifdef WITH_UDPFROMTO
-#include <freeradius-devel/udpfromto.h>
-#endif
-
-#include <fcntl.h>
-#include <ctype.h>
-
-typedef struct radius_packet_t {
-       uint8_t code;
-       uint8_t id;
-       uint8_t length[2];
-       uint8_t vector[AUTH_VECTOR_LEN];
-       uint8_t data[1];
-} radius_packet_t;
-
-
-/*
- *     Some messages get printed out only in debugging mode.
- */
-#define FR_DEBUG_STRERROR_PRINTF if (fr_debug_lvl) fr_strerror_printf
-
-
-/** Allocate a new RADIUS_PACKET
- *
- * @param ctx the context in which the packet is allocated. May be NULL if
- *     the packet is not associated with a REQUEST.
- * @param new_vector if true a new request authenticator will be generated.
- * @return
- *     - New RADIUS_PACKET.
- *     - NULL on error.
- */
-RADIUS_PACKET *fr_radius_alloc(TALLOC_CTX *ctx, bool new_vector)
-{
-       RADIUS_PACKET   *rp;
-
-       rp = talloc_zero(ctx, RADIUS_PACKET);
-       if (!rp) {
-               fr_strerror_printf("out of memory");
-               return NULL;
-       }
-       rp->id = -1;
-       rp->offset = -1;
-
-       if (new_vector) {
-               int i;
-               uint32_t hash, base;
-
-               /*
-                *      Don't expose the actual contents of the random
-                *      pool.
-                */
-               base = fr_rand();
-               for (i = 0; i < AUTH_VECTOR_LEN; i += sizeof(uint32_t)) {
-                       hash = fr_rand() ^ base;
-                       memcpy(rp->vector + i, &hash, sizeof(hash));
-               }
-       }
-       fr_rand();              /* stir the pool again */
-
-       return rp;
-}
-
-/** Allocate a new RADIUS_PACKET response
- *
- * @param ctx the context in which the packet is allocated. May be NULL if
- *     the packet is not associated with a REQUEST.
- * @param packet The request packet.
- * @return
- *     - New RADIUS_PACKET.
- *     - NULL on error.
- */
-RADIUS_PACKET *fr_radius_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
-{
-       RADIUS_PACKET *reply;
-
-       if (!packet) return NULL;
-
-       reply = fr_radius_alloc(ctx, false);
-       if (!reply) return NULL;
-
-       /*
-        *      Initialize the fields from the request.
-        */
-       reply->sockfd = packet->sockfd;
-       reply->dst_ipaddr = packet->src_ipaddr;
-       reply->src_ipaddr = packet->dst_ipaddr;
-       reply->dst_port = packet->src_port;
-       reply->src_port = packet->dst_port;
-       reply->if_index = packet->if_index;
-       reply->id = packet->id;
-       reply->code = 0; /* UNKNOWN code */
-       memcpy(reply->vector, packet->vector,
-              sizeof(reply->vector));
-       reply->vps = NULL;
-       reply->data = NULL;
-       reply->data_len = 0;
-
-#ifdef WITH_TCP
-       reply->proto = packet->proto;
-#endif
-       return reply;
-}
-
-
-/** Free a RADIUS_PACKET
- *
- */
-void fr_radius_free(RADIUS_PACKET **radius_packet_ptr)
-{
-       RADIUS_PACKET *radius_packet;
-
-       if (!radius_packet_ptr || !*radius_packet_ptr) return;
-       radius_packet = *radius_packet_ptr;
-
-       VERIFY_PACKET(radius_packet);
-
-       fr_pair_list_free(&radius_packet->vps);
-
-       talloc_free(radius_packet);
-       *radius_packet_ptr = NULL;
-}
-
-/** Duplicate a RADIUS_PACKET
- *
- * @param ctx the context in which the packet is allocated. May be NULL if
- *     the packet is not associated with a REQUEST.
- * @param in The packet to copy
- * @return
- *     - New RADIUS_PACKET.
- *     - NULL on error.
- */
-RADIUS_PACKET *fr_radius_copy(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
-{
-       RADIUS_PACKET *out;
-
-       out = fr_radius_alloc(ctx, false);
-       if (!out) return NULL;
-
-       /*
-        *      Bootstrap by copying everything.
-        */
-       memcpy(out, in, sizeof(*out));
-
-       /*
-        *      Then reset necessary fields
-        */
-       out->sockfd = -1;
-
-       out->data = NULL;
-       out->data_len = 0;
-
-       out->vps = fr_pair_list_copy(out, in->vps);
-       out->offset = 0;
-
-       return out;
-}
-
-
-/** Encode a packet
- *
- */
-int fr_radius_packet_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
-                    char const *secret)
-{
-       radius_packet_t         *hdr;
-       uint8_t                 *ptr;
-       uint16_t                total_length;
-       int                     len;
-       VALUE_PAIR const        *vp;
-       vp_cursor_t             cursor;
-       fr_radius_ctx_t encoder_ctx = { .packet = packet, .original = original, .secret = secret };
-
-       /*
-        *      A 4K packet, aligned on 64-bits.
-        */
-       uint64_t        data[MAX_PACKET_LEN / sizeof(uint64_t)];
-
-       /*
-        *      Double-check some things based on packet code.
-        */
-       switch (packet->code) {
-       case PW_CODE_ACCESS_ACCEPT:
-       case PW_CODE_ACCESS_REJECT:
-       case PW_CODE_ACCESS_CHALLENGE:
-               if (!original) {
-                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
-                       return -1;
-               }
-               break;
-
-               /*
-                *      These packet vectors start off as all zero.
-                */
-       case PW_CODE_ACCOUNTING_REQUEST:
-       case PW_CODE_DISCONNECT_REQUEST:
-       case PW_CODE_COA_REQUEST:
-               memset(packet->vector, 0, sizeof(packet->vector));
-               break;
-
-       default:
-               break;
-       }
-
-       /*
-        *      Use memory on the stack, until we know how
-        *      large the packet will be.
-        */
-       hdr = (radius_packet_t *) data;
-
-       /*
-        *      Build standard header
-        */
-       hdr->code = packet->code;
-       hdr->id = packet->id;
-
-       memcpy(hdr->vector, packet->vector, sizeof(hdr->vector));
-
-       total_length = RADIUS_HDR_LEN;
-
-       /*
-        *      Load up the configuration values for the user
-        */
-       ptr = hdr->data;
-       packet->offset = 0;
-
-       /*
-        *      Loop over the reply attributes for the packet.
-        */
-       fr_pair_cursor_init(&cursor, &packet->vps);
-       while ((vp = fr_pair_cursor_current(&cursor))) {
-               size_t          last_len, room;
-               char const      *last_name = NULL;
-
-               VERIFY_VP(vp);
-
-               room = ((uint8_t *)data) + sizeof(data) - ptr;
-
-               /*
-                *      Ignore non-wire attributes, but allow extended
-                *      attributes.
-                *
-                *      @fixme We should be able to get rid of this check
-                *      and just look at da->flags.internal
-                */
-               if (vp->da->flags.internal || ((vp->da->vendor == 0) && (vp->da->attr >= 256))) {
-#ifndef NDEBUG
-                       /*
-                        *      Permit the admin to send BADLY formatted
-                        *      attributes with a debug build.
-                        */
-                       if (vp->da->attr == PW_RAW_ATTRIBUTE) {
-                               if (vp->vp_length > room) {
-                                       len = room;
-                               } else {
-                                       len = vp->vp_length;
-                               }
-
-                               memcpy(ptr, vp->vp_octets, len);
-                               fr_pair_cursor_next(&cursor);
-                               goto next;
-                       }
-#endif
-                       fr_pair_cursor_next(&cursor);
-                       continue;
-               }
-
-               /*
-                *      Set the Message-Authenticator to the correct
-                *      length and initial value.
-                */
-               if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) {
-                       /*
-                        *      Cache the offset to the
-                        *      Message-Authenticator
-                        */
-                       packet->offset = total_length;
-                       last_len = 16;
-               } else {
-                       last_len = vp->vp_length;
-               }
-               last_name = vp->da->name;
-
-               if (room <= 2) break;
-
-               len = fr_radius_encode_pair(ptr, room, &cursor, &encoder_ctx);
-               if (len < 0) return -1;
-
-               /*
-                *      Failed to encode the attribute, likely because
-                *      the packet is full.
-                */
-               if (len == 0) {
-                       if (last_len != 0) {
-                               fr_strerror_printf("WARNING: Failed encoding attribute %s\n", last_name);
-                               break;
-                       } else {
-                               fr_strerror_printf("WARNING: Skipping zero-length attribute %s\n", last_name);
-                       }
-               }
-
-#ifndef NDEBUG
-       next:                   /* Used only for Raw-Attribute */
-#endif
-               ptr += len;
-               total_length += len;
-       } /* done looping over all attributes */
-
-       /*
-        *      Fill in the rest of the fields, and copy the data over
-        *      from the local stack to the newly allocated memory.
-        *
-        *      Yes, all this 'memcpy' is slow, but it means
-        *      that we only allocate the minimum amount of
-        *      memory for a request.
-        */
-       packet->data_len = total_length;
-       packet->data = talloc_array(packet, uint8_t, packet->data_len);
-       if (!packet->data) {
-               fr_strerror_printf("Out of memory");
-               return -1;
-       }
-
-       memcpy(packet->data, hdr, packet->data_len);
-       hdr = (radius_packet_t *) packet->data;
-
-       total_length = htons(total_length);
-       memcpy(hdr->length, &total_length, sizeof(total_length));
-
-       return 0;
-}
-
-
-/** Calculate/check digest, and decode radius attributes
- *
- * @return
- *     - 0 on success
- *     - -1 on decoding error.
- */
-int fr_radius_packet_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
-{
-       int                     packet_length;
-       uint32_t                num_attributes;
-       uint8_t                 *ptr;
-       radius_packet_t         *hdr;
-       VALUE_PAIR              *head = NULL;
-       vp_cursor_t             cursor, out;
-       fr_radius_ctx_t         decoder_ctx = {
-                                       .original = original,
-                                       .packet = packet,
-                                       .secret = secret
-                               };
-       /*
-        *      Extract attribute-value pairs
-        */
-       hdr = (radius_packet_t *)packet->data;
-       ptr = hdr->data;
-       packet_length = packet->data_len - RADIUS_HDR_LEN;
-       num_attributes = 0;
-
-       fr_pair_cursor_init(&cursor, &head);
-
-       /*
-        *      Loop over the attributes, decoding them into VPs.
-        */
-       while (packet_length > 0) {
-               ssize_t my_len;
-
-               /*
-                *      This may return many VPs
-                */
-               my_len = fr_radius_decode_pair(packet, &cursor, fr_dict_root(fr_dict_internal),
-                                              ptr, packet_length, &decoder_ctx);
-               if (my_len < 0) {
-                       fr_pair_list_free(&head);
-                       return -1;
-               }
-
-               /*
-                *      This should really be an assertion.
-                */
-               if (my_len == 0) break;
-
-               /*
-                *      Count the ones which were just added
-                */
-               while (fr_pair_cursor_next(&cursor)) num_attributes++;
-
-               /*
-                *      VSA's may not have been counted properly in
-                *      fr_radius_packet_ok() above, as it is hard to count
-                *      then without using the dictionary.  We
-                *      therefore enforce the limits here, too.
-                */
-               if ((fr_max_attributes > 0) && (num_attributes > fr_max_attributes)) {
-                       char host_ipaddr[INET6_ADDRSTRLEN];
-
-                       fr_pair_list_free(&head);
-                       fr_strerror_printf("Possible DoS attack from host %s: Too many attributes in request "
-                                          "(received %d, max %d are allowed)",
-                                          inet_ntop(packet->src_ipaddr.af,
-                                                    &packet->src_ipaddr.ipaddr,
-                                                    host_ipaddr, sizeof(host_ipaddr)),
-                                          num_attributes, fr_max_attributes);
-                       return -1;
-               }
-
-               ptr += my_len;
-               packet_length -= my_len;
-       }
-
-       fr_pair_cursor_init(&out, &packet->vps);
-       fr_pair_cursor_last(&out);              /* Move insertion point to the end of the list */
-       fr_pair_cursor_merge(&out, head);
-
-       /*
-        *      Merge information from the outside world into our
-        *      random pool.
-        */
-       fr_rand_seed(packet->data, RADIUS_HDR_LEN);
-
-       return 0;
-}
-
-
-/** See if the data pointed to by PTR is a valid RADIUS packet.
- *
- * Packet is not 'const * const' because we may update data_len, if there's more data
- * in the UDP packet than in the RADIUS packet.
- *
- * @param packet to check
- * @param require_ma to require Message-Authenticator
- * @param reason if not NULL, will have the failure reason written to where it points.
- * @return
- *     - True on success.
- *     - False on failure.
- */
-bool fr_radius_packet_ok(RADIUS_PACKET *packet, bool require_ma, decode_fail_t *reason)
-{
-       char host_ipaddr[INET6_ADDRSTRLEN];
-
-       if (!fr_radius_ok(packet->data, &packet->data_len, require_ma, reason)) {
-               FR_DEBUG_STRERROR_PRINTF("Bad packet received from host %s - %s",
-                                        inet_ntop(packet->src_ipaddr.af,
-                                                  &packet->src_ipaddr.ipaddr,
-                                                  host_ipaddr, sizeof(host_ipaddr)),
-                                        fr_strerror());
-               return false;
-       }
-
-       /*
-        *      Fill RADIUS header fields
-        */
-       packet->code = packet->data[0];
-       packet->id = packet->data[1];
-       memcpy(packet->vector, packet->data + 4, sizeof(packet->vector));
-       return true;
-}
-
-
-/** Verify the Request/Response Authenticator (and Message-Authenticator if present) of a packet
- *
- */
-int fr_radius_packet_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original, char const *secret)
-{
-       uint8_t const   *original_data;
-       char            buffer[INET6_ADDRSTRLEN];
-
-       if (!packet->data) return -1;
-
-       if (original) {
-               original_data = original->data;
-       } else {
-               original_data = NULL;
-       }
-
-       if (fr_radius_verify(packet->data, original_data,
-                            (uint8_t const *) secret, talloc_array_length(secret) - 1) < 0) {
-               fr_strerror_printf("Received packet from %s with %s",
-                                  inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
-                                            buffer, sizeof(buffer)),
-                                  fr_strerror());
-               return -1;
-       }
-
-       return 0;
-}
-
-
-/** Sign a previously encoded packet
- *
- */
-int fr_radius_packet_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
-                         char const *secret)
-{
-       int rcode;
-       uint8_t const *original_data;
-
-       if (original) {
-               original_data = original->data;
-       } else {
-               original_data = NULL;
-       }
-
-       /*
-        *      Copy the random vector to the packet.  Other packet
-        *      codes have the Request Authenticator be the packet
-        *      signature.
-        */
-       if ((packet->code == PW_CODE_ACCESS_REQUEST) ||
-           (packet->code == PW_CODE_STATUS_SERVER)) {
-               memcpy(packet->data + 4, packet->vector, sizeof(packet->vector));
-       }
-
-       rcode = fr_radius_sign(packet->data, original_data,
-                              (uint8_t const *) secret, talloc_array_length(secret) - 1);
-       if (rcode < 0) return rcode;
-
-       memcpy(packet->vector, packet->data + 4, AUTH_VECTOR_LEN);
-       return 0;
-}
-
-
-/** Wrapper for recvfrom, which handles recvfromto, IPv6, and all possible combinations
- *
- */
-static ssize_t rad_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags)
-{
-       ssize_t                 data_len;
-
-       data_len = fr_radius_recv_header(sockfd, &packet->src_ipaddr, &packet->src_port, &packet->code);
-       if (data_len < 0) {
-               if ((errno == EAGAIN) || (errno == EINTR)) return 0;
-               return -1;
-       }
-
-       if (data_len == 0) return -1; /* invalid packet */
-
-       packet->data = talloc_array(packet, uint8_t, data_len);
-       if (!packet->data) return -1;
-
-       packet->data_len = data_len;
-
-       return udp_recv(sockfd, packet->data, packet->data_len, flags,
-                       &packet->src_ipaddr, &packet->src_port,
-                       &packet->dst_ipaddr, &packet->dst_port,
-                       &packet->if_index, &packet->timestamp);
-}
-
-
-/** Receive UDP client requests, and fill in the basics of a RADIUS_PACKET structure
- *
- */
-RADIUS_PACKET *fr_radius_packet_recv(TALLOC_CTX *ctx, int fd, int flags, bool require_ma)
-{
-       ssize_t data_len;
-       RADIUS_PACKET           *packet;
-
-       /*
-        *      Allocate the new request data structure
-        */
-       packet = fr_radius_alloc(ctx, false);
-       if (!packet) {
-               fr_strerror_printf("out of memory");
-               return NULL;
-       }
-
-       data_len = rad_recvfrom(fd, packet, flags);
-       if (data_len < 0) {
-               FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
-               fr_radius_free(&packet);
-               return NULL;
-       }
-
-#ifdef WITH_VERIFY_PTR
-       /*
-        *      Double-check that the fields we want are filled in.
-        */
-       if ((packet->src_ipaddr.af == AF_UNSPEC) ||
-           (packet->src_port == 0) ||
-           (packet->dst_ipaddr.af == AF_UNSPEC) ||
-           (packet->dst_port == 0)) {
-               FR_DEBUG_STRERROR_PRINTF("Error receiving packet: %s", fr_syserror(errno));
-               fr_radius_free(&packet);
-               return NULL;
-       }
-#endif
-
-       packet->data_len = data_len; /* unsigned vs signed */
-
-       /*
-        *      If the packet is too big, then rad_recvfrom did NOT
-        *      allocate memory.  Instead, it just discarded the
-        *      packet.
-        */
-       if (packet->data_len > MAX_PACKET_LEN) {
-               FR_DEBUG_STRERROR_PRINTF("Discarding packet: Larger than RFC limitation of 4096 bytes");
-               fr_radius_free(&packet);
-               return NULL;
-       }
-
-       /*
-        *      Read no data.  Continue.
-        *      This check is AFTER the MAX_PACKET_LEN check above, because
-        *      if the packet is larger than MAX_PACKET_LEN, we also have
-        *      packet->data == NULL
-        */
-       if ((packet->data_len == 0) || !packet->data) {
-               FR_DEBUG_STRERROR_PRINTF("Empty packet: Socket is not ready");
-               fr_radius_free(&packet);
-               return NULL;
-       }
-
-       /*
-        *      See if it's a well-formed RADIUS packet.
-        */
-       if (!fr_radius_packet_ok(packet, require_ma, NULL)) {
-               fr_radius_free(&packet);
-               return NULL;
-       }
-
-       /*
-        *      Remember which socket we read the packet from.
-        */
-       packet->sockfd = fd;
-
-       /*
-        *      FIXME: Do even more filtering by only permitting
-        *      certain IP's.  The problem is that we don't know
-        *      how to do this properly for all possible clients...
-        */
-
-       /*
-        *      Explicitely set the VP list to empty.
-        */
-       packet->vps = NULL;
-
-#ifndef NDEBUG
-       if ((fr_debug_lvl > 3) && fr_log_fp) fr_radius_print_hex(packet);
-#endif
-
-       return packet;
-}
-
-/** Reply to the request
- *
- * Also attach reply attribute value pairs and any user message provided.
- */
-int fr_radius_packet_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
-                         char const *secret)
-{
-       /*
-        *      Maybe it's a fake packet.  Don't send it.
-        */
-       if (packet->sockfd < 0) {
-               return 0;
-       }
-
-       /*
-        *  First time through, allocate room for the packet
-        */
-       if (!packet->data) {
-               /*
-                *      Encode the packet.
-                */
-               if (fr_radius_packet_encode(packet, original, secret) < 0) {
-                       return -1;
-               }
-
-               /*
-                *      Re-sign it, including updating the
-                *      Message-Authenticator.
-                */
-               if (fr_radius_packet_sign(packet, original, secret) < 0) {
-                       return -1;
-               }
-
-               /*
-                *      If packet->data points to data, then we print out
-                *      the VP list again only for debugging.
-                */
-       }
-
-#ifndef NDEBUG
-       if ((fr_debug_lvl > 3) && fr_log_fp) fr_radius_print_hex(packet);
-#endif
-
-#ifdef WITH_TCP
-       /*
-        *      If the socket is TCP, call write().  Calling sendto()
-        *      is allowed on some platforms, but it's not nice.  Even
-        *      worse, if UDPFROMTO is defined, we *can't* use it on
-        *      TCP sockets.  So... just call write().
-        */
-       if (packet->proto == IPPROTO_TCP) {
-               ssize_t rcode;
-
-               rcode = write(packet->sockfd, packet->data, packet->data_len);
-               if (rcode >= 0) return rcode;
-
-               fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
-               return -1;
-       }
-#endif
-
-       /*
-        *      And send it on it's way.
-        */
-       return udp_send(packet->sockfd, packet->data, packet->data_len, 0,
-                       &packet->src_ipaddr, packet->src_port, packet->if_index,
-                       &packet->dst_ipaddr, packet->dst_port);
-}
-
-static void print_hex_data(uint8_t const *ptr, int attrlen, int depth)
-{
-       int i;
-       static char const tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
-
-       for (i = 0; i < attrlen; i++) {
-               if ((i > 0) && ((i & 0x0f) == 0x00))
-                       fprintf(fr_log_fp, "%.*s", depth, tabs);
-               fprintf(fr_log_fp, "%02x ", ptr[i]);
-               if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n");
-       }
-       if ((i & 0x0f) != 0) fprintf(fr_log_fp, "\n");
-}
-
-
-void fr_radius_print_hex(RADIUS_PACKET const *packet)
-{
-       int i;
-
-       if (!packet->data || !fr_log_fp) return;
-
-       fprintf(fr_log_fp, "  Socket:\t%d\n", packet->sockfd);
-#ifdef WITH_TCP
-       fprintf(fr_log_fp, "  Proto:\t%d\n", packet->proto);
-#endif
-
-       if (packet->src_ipaddr.af == AF_INET) {
-               char buffer[INET6_ADDRSTRLEN];
-
-               fprintf(fr_log_fp, "  Src IP:\t%s\n",
-                       inet_ntop(packet->src_ipaddr.af,
-                                 &packet->src_ipaddr.ipaddr,
-                                 buffer, sizeof(buffer)));
-               fprintf(fr_log_fp, "    port:\t%u\n", packet->src_port);
-
-               fprintf(fr_log_fp, "  Dst IP:\t%s\n",
-                       inet_ntop(packet->dst_ipaddr.af,
-                                 &packet->dst_ipaddr.ipaddr,
-                                 buffer, sizeof(buffer)));
-               fprintf(fr_log_fp, "    port:\t%u\n", packet->dst_port);
-       }
-
-       if (packet->data[0] < FR_MAX_PACKET_CODE) {
-               fprintf(fr_log_fp, "  Code:\t\t(%d) %s\n", packet->data[0], fr_packet_codes[packet->data[0]]);
-       } else {
-               fprintf(fr_log_fp, "  Code:\t\t%u\n", packet->data[0]);
-       }
-       fprintf(fr_log_fp, "  Id:\t\t%u\n", packet->data[1]);
-       fprintf(fr_log_fp, "  Length:\t%u\n", ((packet->data[2] << 8) |
-                                  (packet->data[3])));
-       fprintf(fr_log_fp, "  Vector:\t");
-       for (i = 4; i < 20; i++) {
-               fprintf(fr_log_fp, "%02x", packet->data[i]);
-       }
-       fprintf(fr_log_fp, "\n");
-
-       if (packet->data_len > 20) {
-               int total;
-               uint8_t const *ptr;
-               fprintf(fr_log_fp, "  Data:");
-
-               total = packet->data_len - 20;
-               ptr = packet->data + 20;
-
-               while (total > 0) {
-                       int attrlen;
-                       unsigned int vendor = 0;
-
-                       fprintf(fr_log_fp, "\t\t");
-                       if (total < 2) { /* too short */
-                               fprintf(fr_log_fp, "%02x\n", *ptr);
-                               break;
-                       }
-
-                       if (ptr[1] > total) { /* too long */
-                               for (i = 0; i < total; i++) {
-                                       fprintf(fr_log_fp, "%02x ", ptr[i]);
-                               }
-                               break;
-                       }
-
-                       fprintf(fr_log_fp, "%02x  %02x  ", ptr[0], ptr[1]);
-                       attrlen = ptr[1] - 2;
-
-                       if ((ptr[0] == PW_VENDOR_SPECIFIC) &&
-                           (attrlen > 4)) {
-                               vendor = (ptr[3] << 16) | (ptr[4] << 8) | ptr[5];
-                               fprintf(fr_log_fp, "%02x%02x%02x%02x (%u)  ",
-                                      ptr[2], ptr[3], ptr[4], ptr[5], vendor);
-                               attrlen -= 4;
-                               ptr += 6;
-                               total -= 6;
-
-                       } else {
-                               ptr += 2;
-                               total -= 2;
-                       }
-
-                       print_hex_data(ptr, attrlen, 3);
-
-                       ptr += attrlen;
-                       total -= attrlen;
-               }
-       }
-       fflush(stdout);
-}