Portability fixes for Mingw33
[freeradius.git] / src / main / listen.c
index 4e982ef..dd87af6 100644 (file)
@@ -29,6 +29,7 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 #include <freeradius-devel/vqp.h>
 #include <freeradius-devel/dhcp.h>
+#include <freeradius-devel/process.h>
 
 #include <freeradius-devel/vmps.h>
 #include <freeradius-devel/detail.h>
@@ -50,7 +51,7 @@ RCSID("$Id$")
 #endif
 
 
-void print_packet(RADIUS_PACKET *packet)
+static void print_packet(RADIUS_PACKET *packet)
 {
        char src[256], dst[256];
 
@@ -79,22 +80,41 @@ typedef struct rad_listen_master_t {
        rad_listen_decode_t     decode;
 } rad_listen_master_t;
 
-typedef struct listen_socket_t {
-       /*
-        *      For normal sockets.
-        */
-       fr_ipaddr_t     ipaddr;
-       int             port;
-       const char              *interface;
-       RADCLIENT_LIST  *clients;
-} listen_socket_t;
-
 static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
 
 /*
+ *     Xlat for %{listen:foo}
+ */
+static size_t xlat_listen(UNUSED void *instance, REQUEST *request,
+                      char *fmt, char *out,
+                      size_t outlen,
+                      UNUSED RADIUS_ESCAPE_STRING func)
+{
+       const char *value = NULL;
+       CONF_PAIR *cp;
+
+       if (!fmt || !out || (outlen < 1)) return 0;
+
+       if (!request || !request->listener) {
+               *out = '\0';
+               return 0;
+       }
+
+       cp = cf_pair_find(request->listener->cs, fmt);
+       if (!cp || !(value = cf_pair_value(cp))) {
+               *out = '\0';
+               return 0;
+       }
+       
+       strlcpy(out, value, outlen);
+
+       return strlen(out);
+}
+
+/*
  *     Find a per-socket client.
  */
-RADCLIENT *client_listener_find(const rad_listen_t *listener,
+RADCLIENT *client_listener_find(rad_listen_t *listener,
                                const fr_ipaddr_t *ipaddr, int src_port)
 {
 #ifdef WITH_DYNAMIC_CLIENTS
@@ -211,7 +231,8 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
                 *      can be defined.
                 */
                rad_assert(client->dynamic == 0);
-       } else {
+
+       } else if (!client->dynamic && client->rate_limit) {
                /*
                 *      The IP is unknown, so we've found an enclosing
                 *      network.  Enable DoS protection.  We only
@@ -238,7 +259,7 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
                request_free(&request);
                goto unknown;
        }
-       request->packet->timestamp = request->timestamp;
+       gettimeofday(&request->packet->timestamp, NULL);
        request->number = 0;
        request->priority = listener->type;
        request->server = client->client_server;
@@ -277,6 +298,10 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
                 */
                if (!client_validate(clients, client, created)) goto unknown;
        }
+
+       request->server = client->server;
+       exec_trigger(request, NULL, "server.client.add");
+
        request_free(&request);
 
        if (!created) goto unknown;
@@ -293,7 +318,7 @@ static int listen_bind(rad_listen_t *this);
  *     Like rad_authenticate and rad_accounting this should
  *     live in it's own file but it's so small we don't bother.
  */
-static int rad_status_server(REQUEST *request)
+int rad_status_server(REQUEST *request)
 {
        int rcode = RLM_MODULE_OK;
        DICT_VALUE *dval;
@@ -303,7 +328,7 @@ static int rad_status_server(REQUEST *request)
        case RAD_LISTEN_NONE:
 #endif
        case RAD_LISTEN_AUTH:
-               dval = dict_valbyname(PW_AUTZ_TYPE, "Status-Server");
+               dval = dict_valbyname(PW_AUTZ_TYPE, 0, "Status-Server");
                if (dval) {
                        rcode = module_authorize(dval->value, request);
                } else {
@@ -330,7 +355,7 @@ static int rad_status_server(REQUEST *request)
 
 #ifdef WITH_ACCOUNTING
        case RAD_LISTEN_ACCT:
-               dval = dict_valbyname(PW_ACCT_TYPE, "Status-Server");
+               dval = dict_valbyname(PW_ACCT_TYPE, 0, "Status-Server");
                if (dval) {
                        rcode = module_accounting(dval->value, request);
                } else {
@@ -357,7 +382,7 @@ static int rad_status_server(REQUEST *request)
                 *      the WG.  We like it, so it goes in here.
                 */
        case RAD_LISTEN_COA:
-               dval = dict_valbyname(PW_RECV_COA_TYPE, "Status-Server");
+               dval = dict_valbyname(PW_RECV_COA_TYPE, 0, "Status-Server");
                if (dval) {
                        rcode = module_recv_coa(dval->value, request);
                } else {
@@ -395,8 +420,7 @@ static int rad_status_server(REQUEST *request)
 }
 
 #ifdef WITH_TCP
-static int auth_tcp_recv(rad_listen_t *listener,
-                        RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int dual_tcp_recv(rad_listen_t *listener)
 {
        int rcode;
        RADIUS_PACKET   *packet;
@@ -470,21 +494,27 @@ static int auth_tcp_recv(rad_listen_t *listener,
                return 0;
        }
 
-       RAD_STATS_TYPE_INC(listener, total_requests);
-
        /*
         *      Some sanity checks, based on the packet code.
         */
        switch(packet->code) {
        case PW_AUTHENTICATION_REQUEST:
-               RAD_STATS_CLIENT_INC(listener, client, total_requests);
+               if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
+               FR_STATS_INC(auth, total_requests);
                fun = rad_authenticate;
                break;
 
+#ifdef WITH_ACCOUNTING
+       case PW_ACCOUNTING_REQUEST:
+               if (listener->type != RAD_LISTEN_ACCT) goto bad_packet;
+               FR_STATS_INC(acct, total_requests);
+               fun = rad_accounting;
+               break;
+#endif
+
        case PW_STATUS_SERVER:
                if (!mainconfig.status_server) {
-                       RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-                       RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+                       FR_STATS_INC(auth, total_unknown_types);
                        DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
                        rad_free(&sock->packet);
                        return 0;
@@ -493,30 +523,26 @@ static int auth_tcp_recv(rad_listen_t *listener,
                break;
 
        default:
-               RAD_STATS_INC(radius_auth_stats.total_unknown_types);
-               RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+       bad_packet:
+               FR_STATS_INC(auth, total_unknown_types);
 
-               DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
+               DEBUG("Invalid packet code %d sent from client %s port %d : IGNORED",
                      packet->code, client->shortname, packet->src_port);
                rad_free(&sock->packet);
                return 0;
        } /* switch over packet types */
 
-       if (!received_request(listener, packet, prequest, sock->client)) {
-               RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-               RAD_STATS_CLIENT_INC(listener, sock->client, total_packets_dropped);
+       if (!request_receive(listener, packet, client, fun)) {
+               FR_STATS_INC(auth, total_packets_dropped);
                rad_free(&sock->packet);
                return 0;
        }
 
-       *pfun = fun;
        sock->packet = NULL;    /* we have no need for more partial reads */
        return 1;
 }
 
-static int auth_tcp_accept(rad_listen_t *listener,
-                          UNUSED RAD_REQUEST_FUNP *pfun,
-                          UNUSED REQUEST **prequest)
+static int dual_tcp_accept(rad_listen_t *listener)
 {
        int newfd, src_port;
        rad_listen_t *this;
@@ -524,7 +550,7 @@ static int auth_tcp_accept(rad_listen_t *listener,
        struct sockaddr_storage src;
        listen_socket_t *sock;
        fr_ipaddr_t src_ipaddr;
-       RADCLIENT *client;
+       RADCLIENT *client = NULL;
        
        salen = sizeof(src);
 
@@ -535,9 +561,11 @@ static int auth_tcp_accept(rad_listen_t *listener,
                /*
                 *      Non-blocking sockets must handle this.
                 */
+#ifdef EWOULDBLOCK
                if (errno == EWOULDBLOCK) {
                        return 0;
                }
+#endif
 
                DEBUG2(" ... failed to accept connection.");
                return -1;
@@ -555,7 +583,7 @@ static int auth_tcp_accept(rad_listen_t *listener,
        if ((client = client_listener_find(listener,
                                           &src_ipaddr, src_port)) == NULL) {
                close(newfd);
-               RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+               FR_STATS_INC(auth, total_invalid_requests);
                return 0;
        }
 
@@ -605,10 +633,18 @@ static int auth_tcp_accept(rad_listen_t *listener,
        sock->other_ipaddr = src_ipaddr;
        sock->other_port = src_port;
        sock->client = client;
+       sock->opened = sock->last_packet = time(NULL);
 
        this->fd = newfd;
        this->status = RAD_LISTEN_STATUS_INIT;
-       this->recv = auth_tcp_recv;
+       this->recv = dual_tcp_recv;
+
+#ifdef WITH_TLS
+       if (this->tls) {
+               this->recv = dual_tls_recv;
+               this->send = dual_tls_send;
+       }
+#endif
 
        /*
         *      FIXME: set O_NONBLOCK on the accept'd fd.
@@ -629,7 +665,7 @@ static int auth_tcp_accept(rad_listen_t *listener,
 /*
  *     This function is stupid and complicated.
  */
-static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
+static int socket_print(const rad_listen_t *this, char *buffer, size_t bufsize)
 {
        size_t len;
        listen_socket_t *sock = this->data;
@@ -692,7 +728,7 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
        }
 
 #ifdef WITH_TCP
-       if (this->recv == auth_tcp_accept) {
+       if (this->recv == dual_tcp_accept) {
                ADDSTRING(" proto tcp");
        }
 #endif
@@ -734,6 +770,7 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
                return 1;
        }
 
+#ifdef WITH_PROXY
        /*
         *      Maybe it's a socket that we opened to a home server.
         */
@@ -764,7 +801,8 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
 
                return 1;
        }
-#endif
+#endif /* WITH_PROXY */
+#endif /* WITH_TCP */
 
        ADDSTRING(" address ");
        
@@ -780,6 +818,13 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
        snprintf(buffer, bufsize, "%d", sock->my_port);
        FORWARD;
 
+#ifdef WITH_TLS
+       if (this->tls) {
+               ADDSTRING(" (TLS)");
+               FORWARD;
+       }
+#endif
+
        if (this->server) {
                ADDSTRING(" as server ");
                ADDSTRING(this->server);
@@ -791,6 +836,8 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
        return 1;
 }
 
+extern int check_config;       /* radiusd.c */
+
 
 /*
  *     Parse an authentication or accounting socket.
@@ -804,9 +851,12 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        char            *section_name = NULL;
        CONF_SECTION    *client_cs, *parentcs;
 
+       this->cs = cs;
+
        /*
         *      Try IPv4 first
         */
+       memset(&ipaddr, 0, sizeof(ipaddr));
        ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
        rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
                              &ipaddr.ipaddr.ip4addr, NULL);
@@ -847,7 +897,9 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                return -1;
 #else
                char *proto = NULL;
-
+#ifdef WITH_TLS
+               CONF_SECTION *tls;
+#endif
 
                rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR,
                                      &proto, "udp");
@@ -875,18 +927,87 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                 *      TCP requires a destination IP for sockets.
                 *      UDP doesn't, so it's allowed.
                 */
+#ifdef WITH_PROXY
                if ((this->type == RAD_LISTEN_PROXY) &&
                    (sock->proto != IPPROTO_UDP)) {
                        cf_log_err(cf_sectiontoitem(cs),
                                   "Proxy listeners can only listen on proto = udp");
                        return -1;
                }
+#endif /* WITH_PROXY */
+
+#ifdef WITH_TLS
+               tls = cf_section_sub_find(cs, "tls");
+
+               /*
+                *      Don't allow TLS configurations for UDP sockets.
+                */
+               if (sock->proto != IPPROTO_TCP) {
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "TLS transport is not available for UDP sockets.");
+                       return -1;
+               }
+
+               if (tls) {
+                       /*
+                        *      FIXME: Make this better.
+                        */
+                       if (listen_port == 0) listen_port = 2083;
+
+                       this->tls = tls_server_conf_parse(tls);
+                       if (!this->tls) {
+                               return -1;
+                       }
+
+#ifdef HAVE_PTRHEAD_H
+                       if (pthread_mutex_init(&sock->mutex, NULL) < 0) {
+                               rad_assert(0 == 1);
+                               listen_free(&this);
+                               return 0;
+                       }
 #endif
+
+               }               
+#else  /* WITH_TLS */
+               /*
+                *      Built without TLS.  Disallow it.
+                */
+               if (cf_section_sub_find(cs, "tls")) {
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "TLS transport is not available in this executable.");
+                       return -1;
+               }
+#endif /* WITH_TLS */
+
+#endif /* WITH_TCP */
+
+               /*
+                *      No "proto" field.  Disallow TLS.
+                */
+       } else if (cf_section_sub_find(cs, "tls")) {
+               cf_log_err(cf_sectiontoitem(cs),
+                          "TLS transport is not available in this \"listen\" section.");
+               return -1;
        }
 
        sock->my_ipaddr = ipaddr;
        sock->my_port = listen_port;
 
+#ifdef WITH_PROXY
+       if (check_config) {
+               if (home_server_find(&sock->my_ipaddr, sock->my_port, sock->proto)) {
+                               char buffer[128];
+                               
+                               DEBUG("ERROR: We have been asked to listen on %s port %d, which is also listed as a home server.  This can create a proxy loop.",
+                                     ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)),
+                                     sock->my_port);
+                               return -1;
+               }
+
+               return 0;       /* don't do anything */
+       }
+#endif
+
        /*
         *      If we can bind to interfaces, do so,
         *      else don't.
@@ -1022,7 +1143,7 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                 *      Re-write the listener receive function to
                 *      allow us to accept the socket.
                 */
-               this->recv = auth_tcp_accept;
+               this->recv = dual_tcp_accept;
        }
 #endif
 
@@ -1037,8 +1158,14 @@ static int auth_socket_send(rad_listen_t *listener, REQUEST *request)
        rad_assert(request->listener == listener);
        rad_assert(listener->send == auth_socket_send);
 
-       return rad_send(request->reply, request->packet,
-                       request->client->secret);
+       if (rad_send(request->reply, request->packet,
+                    request->client->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
+                              fr_strerror());
+               return -1;
+       }
+
+       return 0;
 }
 
 
@@ -1059,8 +1186,14 @@ static int acct_socket_send(rad_listen_t *listener, REQUEST *request)
         */
        if (request->reply->code == 0) return 0;
 
-       return rad_send(request->reply, request->packet,
-                       request->client->secret);
+       if (rad_send(request->reply, request->packet,
+                    request->client->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed sending reply: %s",
+                              fr_strerror());
+               return -1;
+       }
+
+       return 0;
 }
 #endif
 
@@ -1075,8 +1208,14 @@ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
        rad_assert(request->proxy_listener == listener);
        rad_assert(listener->send == proxy_socket_send);
 
-       return rad_send(request->proxy, request->packet,
-                       request->home_server->secret);
+       if (rad_send(request->proxy, NULL,
+                    request->home_server->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed sending proxied request: %s",
+                              fr_strerror());
+               return -1;
+       }
+
+       return 0;
 }
 #endif
 
@@ -1087,32 +1226,33 @@ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
  *     It takes packets, not requests.  It sees if the packet looks
  *     OK.  If so, it does a number of sanity checks on it.
   */
-static int stats_socket_recv(rad_listen_t *listener,
-                           RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int stats_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
        int             code, src_port;
        RADIUS_PACKET   *packet;
-       RADCLIENT       *client;
+       RADCLIENT       *client = NULL;
        fr_ipaddr_t     src_ipaddr;
 
        rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
        if (rcode < 0) return 0;
 
-       RAD_STATS_TYPE_INC(listener, total_requests);
+       FR_STATS_INC(auth, total_requests);
 
        if (rcode < 20) {       /* AUTH_HDR_LEN */
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(auth, total_malformed_requests);
                return 0;
        }
 
        if ((client = client_listener_find(listener,
                                           &src_ipaddr, src_port)) == NULL) {
                rad_recv_discard(listener->fd);
-               RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+               FR_STATS_INC(auth, total_invalid_requests);
                return 0;
        }
 
+       FR_STATS_TYPE_INC(client->auth.total_requests);
+
        /*
         *      We only understand Status-Server on this socket.
         */
@@ -1120,8 +1260,7 @@ static int stats_socket_recv(rad_listen_t *listener,
                DEBUG("Ignoring packet code %d sent to Status-Server port",
                      code);
                rad_recv_discard(listener->fd);
-               RAD_STATS_TYPE_INC(listener, total_unknown_types);
-               RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+               FR_STATS_INC(auth, total_unknown_types);
                return 0;
        }
 
@@ -1131,19 +1270,17 @@ static int stats_socket_recv(rad_listen_t *listener,
         */
        packet = rad_recv(listener->fd, 1); /* require message authenticator */
        if (!packet) {
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(auth, total_malformed_requests);
                DEBUG("%s", fr_strerror());
                return 0;
        }
 
-       if (!received_request(listener, packet, prequest, client)) {
-               RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-               RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+       if (!request_receive(listener, packet, client, rad_status_server)) {
+               FR_STATS_INC(auth, total_packets_dropped);
                rad_free(&packet);
                return 0;
        }
 
-       *pfun = rad_status_server;
        return 1;
 }
 #endif
@@ -1155,47 +1292,46 @@ static int stats_socket_recv(rad_listen_t *listener,
  *     It takes packets, not requests.  It sees if the packet looks
  *     OK.  If so, it does a number of sanity checks on it.
   */
-static int auth_socket_recv(rad_listen_t *listener,
-                           RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int auth_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
        int             code, src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
-       RADCLIENT       *client;
+       RADCLIENT       *client = NULL;
        fr_ipaddr_t     src_ipaddr;
 
        rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
        if (rcode < 0) return 0;
 
-       RAD_STATS_TYPE_INC(listener, total_requests);
+       FR_STATS_INC(auth, total_requests);
 
        if (rcode < 20) {       /* AUTH_HDR_LEN */
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(auth, total_malformed_requests);
                return 0;
        }
 
        if ((client = client_listener_find(listener,
                                           &src_ipaddr, src_port)) == NULL) {
                rad_recv_discard(listener->fd);
-               RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+               FR_STATS_INC(auth, total_invalid_requests);
                return 0;
        }
 
+       FR_STATS_TYPE_INC(client->auth.total_requests);
+
        /*
         *      Some sanity checks, based on the packet code.
         */
        switch(code) {
        case PW_AUTHENTICATION_REQUEST:
-               RAD_STATS_CLIENT_INC(listener, client, total_requests);
                fun = rad_authenticate;
                break;
 
        case PW_STATUS_SERVER:
                if (!mainconfig.status_server) {
                        rad_recv_discard(listener->fd);
-                       RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-                       RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+                       FR_STATS_INC(auth, total_unknown_types);
                        DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
                        return 0;
                }
@@ -1204,8 +1340,7 @@ static int auth_socket_recv(rad_listen_t *listener,
 
        default:
                rad_recv_discard(listener->fd);
-               RAD_STATS_INC(radius_auth_stats.total_unknown_types);
-               RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+               FR_STATS_INC(auth,total_unknown_types);
 
                DEBUG("Invalid packet code %d sent to authentication port from client %s port %d : IGNORED",
                      code, client->shortname, src_port);
@@ -1219,19 +1354,17 @@ static int auth_socket_recv(rad_listen_t *listener,
         */
        packet = rad_recv(listener->fd, client->message_authenticator);
        if (!packet) {
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(auth, total_malformed_requests);
                DEBUG("%s", fr_strerror());
                return 0;
        }
 
-       if (!received_request(listener, packet, prequest, client)) {
-               RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-               RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+       if (!request_receive(listener, packet, client, fun)) {
+               FR_STATS_INC(auth, total_packets_dropped);
                rad_free(&packet);
                return 0;
        }
 
-       *pfun = fun;
        return 1;
 }
 
@@ -1240,47 +1373,46 @@ static int auth_socket_recv(rad_listen_t *listener,
 /*
  *     Receive packets from an accounting socket
  */
-static int acct_socket_recv(rad_listen_t *listener,
-                           RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int acct_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
        int             code, src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
-       RADCLIENT       *client;
+       RADCLIENT       *client = NULL;
        fr_ipaddr_t     src_ipaddr;
 
        rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
        if (rcode < 0) return 0;
 
-       RAD_STATS_TYPE_INC(listener, total_requests);
+       FR_STATS_INC(acct, total_requests);
 
        if (rcode < 20) {       /* AUTH_HDR_LEN */
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(acct, total_malformed_requests);
                return 0;
        }
 
        if ((client = client_listener_find(listener,
                                           &src_ipaddr, src_port)) == NULL) {
                rad_recv_discard(listener->fd);
-               RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+               FR_STATS_INC(acct, total_invalid_requests);
                return 0;
        }
 
+       FR_STATS_TYPE_INC(client->acct.total_requests);
+
        /*
         *      Some sanity checks, based on the packet code.
         */
        switch(code) {
        case PW_ACCOUNTING_REQUEST:
-               RAD_STATS_CLIENT_INC(listener, client, total_requests);
                fun = rad_accounting;
                break;
 
        case PW_STATUS_SERVER:
                if (!mainconfig.status_server) {
                        rad_recv_discard(listener->fd);
-                       RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-                       RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+                       FR_STATS_INC(acct, total_unknown_types);
 
                        DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
                        return 0;
@@ -1290,8 +1422,7 @@ static int acct_socket_recv(rad_listen_t *listener,
 
        default:
                rad_recv_discard(listener->fd);
-               RAD_STATS_TYPE_INC(listener, total_unknown_types);
-               RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+               FR_STATS_INC(acct, total_unknown_types);
 
                DEBUG("Invalid packet code %d sent to a accounting port from client %s port %d : IGNORED",
                      code, client->shortname, src_port);
@@ -1304,7 +1435,7 @@ static int acct_socket_recv(rad_listen_t *listener,
         */
        packet = rad_recv(listener->fd, 0);
        if (!packet) {
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(acct, total_malformed_requests);
                radlog(L_ERR, "%s", fr_strerror());
                return 0;
        }
@@ -1312,48 +1443,37 @@ static int acct_socket_recv(rad_listen_t *listener,
        /*
         *      There can be no duplicate accounting packets.
         */
-       if (!received_request(listener, packet, prequest, client)) {
-               RAD_STATS_TYPE_INC(listener, total_packets_dropped);
-               RAD_STATS_CLIENT_INC(listener, client, total_packets_dropped);
+       if (!request_receive(listener, packet, client, fun)) {
+               FR_STATS_INC(acct, total_packets_dropped);
                rad_free(&packet);
                return 0;
        }
 
-       *pfun = fun;
        return 1;
 }
 #endif
 
 
 #ifdef WITH_COA
-/*
- *     For now, all CoA requests are *only* originated, and not
- *     proxied.  So all of the necessary work is done in the
- *     post-proxy section, which is automatically handled by event.c.
- *     As a result, we don't have to do anything here.
- */
-static int rad_coa_reply(REQUEST *request)
+static int do_proxy(REQUEST *request)
 {
-       VALUE_PAIR *s1, *s2;
-
-       /*
-        *      Inform the user about RFC requirements.
-        */
-       s1 = pairfind(request->proxy->vps, PW_STATE);
-       if (s1) {
-               s2 = pairfind(request->proxy_reply->vps, PW_STATE);
+       VALUE_PAIR *vp;
 
-               if (!s2) {
-                       DEBUG("WARNING: Client was sent State in CoA, and did not respond with State.");
+       if (request->in_proxy_hash ||
+           (request->proxy_reply && (request->proxy_reply->code != 0))) {
+               return 0;
+       }
 
-               } else if ((s1->length != s2->length) ||
-                          (memcmp(s1->vp_octets, s2->vp_octets,
-                                  s1->length) != 0)) {
-                       DEBUG("WARNING: Client was sent State in CoA, and did not respond with the same State.");
-               }
+       vp = pairfind(request->config_items, PW_HOME_SERVER_POOL, 0);
+       if (!vp) return 0;
+       
+       if (!home_pool_byname(vp->vp_strvalue, HOME_TYPE_COA)) {
+               RDEBUG2("ERROR: Cannot proxy to unknown pool %s",
+                       vp->vp_strvalue);
+               return 0;
        }
 
-       return RLM_MODULE_OK;
+       return 1;
 }
 
 /*
@@ -1395,10 +1515,10 @@ static int rad_coa_recv(REQUEST *request)
                 *      with Service-Type = Authorize-Only, it MUST
                 *      have a State attribute in it.
                 */
-               vp = pairfind(request->packet->vps, PW_SERVICE_TYPE);
+               vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0);
                if (request->packet->code == PW_COA_REQUEST) {
                        if (vp && (vp->vp_integer == 17)) {
-                               vp = pairfind(request->packet->vps, PW_STATE);
+                               vp = pairfind(request->packet->vps, PW_STATE, 0);
                                if (!vp || (vp->length == 0)) {
                                        RDEBUG("ERROR: CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
                                        request->reply->code = PW_COA_NAK;
@@ -1431,10 +1551,11 @@ static int rad_coa_recv(REQUEST *request)
                case RLM_MODULE_NOTFOUND:
                case RLM_MODULE_OK:
                case RLM_MODULE_UPDATED:
+                       if (do_proxy(request)) return RLM_MODULE_OK;
                        request->reply->code = ack;
                        break;
                }
-       } else {
+       } else if (request->proxy_reply) {
                /*
                 *      Start the reply code with the proxy reply
                 *      code.
@@ -1446,20 +1567,21 @@ static int rad_coa_recv(REQUEST *request)
         *      Copy State from the request to the reply.
         *      See RFC 5176 Section 3.3.
         */
-       vp = paircopy2(request->packet->vps, PW_STATE);
+       vp = paircopy2(request->packet->vps, PW_STATE, 0);
        if (vp) pairadd(&request->reply->vps, vp);
 
        /*
         *      We may want to over-ride the reply.
         */
-       rcode = module_send_coa(0, request);
-       switch (rcode) {
-               /*
-                *      We need to send CoA-NAK back if Service-Type
-                *      is Authorize-Only.  Rely on the user's policy
-                *      to do that.  We're not a real NAS, so this
-                *      restriction doesn't (ahem) apply to us.
-                */
+       if (request->reply->code) {
+               rcode = module_send_coa(0, request);
+               switch (rcode) {
+                       /*
+                        *      We need to send CoA-NAK back if Service-Type
+                        *      is Authorize-Only.  Rely on the user's policy
+                        *      to do that.  We're not a real NAS, so this
+                        *      restriction doesn't (ahem) apply to us.
+                        */
                case RLM_MODULE_FAIL:
                case RLM_MODULE_INVALID:
                case RLM_MODULE_REJECT:
@@ -1487,7 +1609,7 @@ static int rad_coa_recv(REQUEST *request)
                                request->reply->code = ack;
                        }
                        break;
-
+               }
        }
 
        return RLM_MODULE_OK;
@@ -1500,30 +1622,29 @@ static int rad_coa_recv(REQUEST *request)
  *     It takes packets, not requests.  It sees if the packet looks
  *     OK.  If so, it does a number of sanity checks on it.
   */
-static int coa_socket_recv(rad_listen_t *listener,
-                           RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int coa_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
        int             code, src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
-       RADCLIENT       *client;
+       RADCLIENT       *client = NULL;
        fr_ipaddr_t     src_ipaddr;
 
        rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
        if (rcode < 0) return 0;
 
-       RAD_STATS_TYPE_INC(listener, total_requests);
-
        if (rcode < 20) {       /* AUTH_HDR_LEN */
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(coa, total_requests);
+               FR_STATS_INC(coa, total_malformed_requests);
                return 0;
        }
 
        if ((client = client_listener_find(listener,
                                           &src_ipaddr, src_port)) == NULL) {
                rad_recv_discard(listener->fd);
-               RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+               FR_STATS_INC(coa, total_requests);
+               FR_STATS_INC(coa, total_invalid_requests);
                return 0;
        }
 
@@ -1532,16 +1653,21 @@ static int coa_socket_recv(rad_listen_t *listener,
         */
        switch(code) {
        case PW_COA_REQUEST:
+               FR_STATS_INC(coa, total_requests);
+               fun = rad_coa_recv;
+               break;
+
        case PW_DISCONNECT_REQUEST:
+               FR_STATS_INC(dsc, total_requests);
                fun = rad_coa_recv;
                break;
 
        default:
                rad_recv_discard(listener->fd);
+               FR_STATS_INC(coa, total_unknown_types);
                DEBUG("Invalid packet code %d sent to coa port from client %s port %d : IGNORED",
                      code, client->shortname, src_port);
                return 0;
-               break;
        } /* switch over packet types */
 
        /*
@@ -1550,17 +1676,17 @@ static int coa_socket_recv(rad_listen_t *listener,
         */
        packet = rad_recv(listener->fd, client->message_authenticator);
        if (!packet) {
-               RAD_STATS_TYPE_INC(listener, total_malformed_requests);
+               FR_STATS_INC(coa, total_malformed_requests);
                DEBUG("%s", fr_strerror());
                return 0;
        }
 
-       if (!received_request(listener, packet, prequest, client)) {
+       if (!request_receive(listener, packet, client, fun)) {
+               FR_STATS_INC(coa, total_packets_dropped);
                rad_free(&packet);
                return 0;
        }
 
-       *pfun = fun;
        return 1;
 }
 #endif
@@ -1569,12 +1695,9 @@ static int coa_socket_recv(rad_listen_t *listener,
 /*
  *     Recieve packets from a proxy socket.
  */
-static int proxy_socket_recv(rad_listen_t *listener,
-                             RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int proxy_socket_recv(rad_listen_t *listener)
 {
-       REQUEST         *request;
        RADIUS_PACKET   *packet;
-       RAD_REQUEST_FUNP fun = NULL;
        char            buffer[128];
 
        packet = rad_recv(listener->fd, 0);
@@ -1590,12 +1713,10 @@ static int proxy_socket_recv(rad_listen_t *listener,
        case PW_AUTHENTICATION_ACK:
        case PW_ACCESS_CHALLENGE:
        case PW_AUTHENTICATION_REJECT:
-               fun = rad_authenticate;
                break;
 
 #ifdef WITH_ACCOUNTING
        case PW_ACCOUNTING_RESPONSE:
-               fun = rad_accounting;
                break;
 #endif
 
@@ -1604,7 +1725,6 @@ static int proxy_socket_recv(rad_listen_t *listener,
        case PW_DISCONNECT_NAK:
        case PW_COA_ACK:
        case PW_COA_NAK:
-               fun = rad_coa_reply;
                break;
 #endif
 
@@ -1621,27 +1741,11 @@ static int proxy_socket_recv(rad_listen_t *listener,
                return 0;
        }
 
-       request = received_proxy_response(packet);
-       if (!request) {
+       if (!request_proxy_reply(packet)) {
                rad_free(&packet);
                return 0;
        }
 
-#ifdef WITH_COA
-       /*
-        *      Distinguish proxied CoA requests from ones we
-        *      originate.
-        */
-       if ((fun == rad_coa_reply) &&
-           (request->packet->code == request->proxy->code)) {
-               fun = rad_coa_recv;
-       }
-#endif
-
-       rad_assert(fun != NULL);
-       *pfun = fun;
-       *prequest = request;
-
        return 1;
 }
 
@@ -1649,12 +1753,9 @@ static int proxy_socket_recv(rad_listen_t *listener,
 /*
  *     Recieve packets from a proxy socket.
  */
-static int proxy_socket_tcp_recv(rad_listen_t *listener,
-                                RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+static int proxy_socket_tcp_recv(rad_listen_t *listener)
 {
-       REQUEST         *request;
        RADIUS_PACKET   *packet;
-       RAD_REQUEST_FUNP fun = NULL;
        listen_socket_t *sock = listener->data;
        char            buffer[128];
 
@@ -1672,12 +1773,10 @@ static int proxy_socket_tcp_recv(rad_listen_t *listener,
        case PW_AUTHENTICATION_ACK:
        case PW_ACCESS_CHALLENGE:
        case PW_AUTHENTICATION_REJECT:
-               fun = rad_authenticate;
                break;
 
 #ifdef WITH_ACCOUNTING
        case PW_ACCOUNTING_RESPONSE:
-               fun = rad_accounting;
                break;
 #endif
 
@@ -1706,16 +1805,12 @@ static int proxy_socket_tcp_recv(rad_listen_t *listener,
         *
         *      Close the socket on bad packets...
         */
-       request = received_proxy_response(packet);
-       if (!request) {
+       if (!request_proxy_reply(packet)) {
+               rad_free(&packet);
                return 0;
        }
 
-       rad_assert(fun != NULL);
-       sock->opened = sock->last_packet = request->timestamp;
-
-       *pfun = fun;
-       *prequest = request;
+       sock->opened = sock->last_packet = time(NULL);
 
        return 1;
 }
@@ -1727,10 +1822,19 @@ static int client_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
 {
        if (!request->reply->code) return 0;
 
-       rad_encode(request->reply, request->packet,
-                  request->client->secret);
-       rad_sign(request->reply, request->packet,
-                request->client->secret);
+       if (rad_encode(request->reply, request->packet,
+                      request->client->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed encoding packet: %s",
+                              fr_strerror());
+               return -1;
+       }
+
+       if (rad_sign(request->reply, request->packet,
+                    request->client->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed signing packet: %s",
+                              fr_strerror());
+               return -1;
+       }
 
        return 0;
 }
@@ -1750,8 +1854,17 @@ static int client_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
 #ifdef WITH_PROXY
 static int proxy_socket_encode(UNUSED rad_listen_t *listener, REQUEST *request)
 {
-       rad_encode(request->proxy, NULL, request->home_server->secret);
-       rad_sign(request->proxy, NULL, request->home_server->secret);
+       if (rad_encode(request->proxy, NULL, request->home_server->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed encoding proxied packet: %s",
+                              fr_strerror());
+               return -1;
+       }
+
+       if (rad_sign(request->proxy, NULL, request->home_server->secret) < 0) {
+               radlog_request(L_ERR, 0, request, "Failed signing proxied packet: %s",
+                              fr_strerror());
+               return -1;
+       }
 
        return 0;
 }
@@ -1826,7 +1939,7 @@ static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
 
 #ifdef WITH_COMMAND_SOCKET
        /* TCP command socket */
-       { command_socket_parse, NULL,
+       { command_socket_parse, command_socket_free,
          command_domain_accept, command_domain_send,
          command_socket_print, command_socket_encode, command_socket_decode },
 #endif
@@ -1915,25 +2028,37 @@ static int listen_bind(rad_listen_t *this)
                case RAD_LISTEN_COA:
                        svp = getservbyname ("radius-dynauth", "udp");
                        if (svp != NULL) {
-                               sock->port = ntohs(svp->s_port);
+                               sock->my_port = ntohs(svp->s_port);
                        } else {
-                               sock->port = PW_COA_UDP_PORT;
+                               sock->my_port = PW_COA_UDP_PORT;
                        }
                        break;
 #endif
 
                default:
-                       radlog(L_ERR, "ERROR: Non-fatal internal sanity check failed in bind.");
+                       DEBUG("WARNING: Internal sanity check failed in binding to socket.  Ignoring problem.");
                        return -1;
                }
        }
 
        /*
+        *      Don't open sockets if we're checking the config.
+        */
+       if (check_config) {
+               this->fd = -1;
+               return 0;
+       }
+
+       /*
         *      Copy fr_socket() here, as we may need to bind to a device.
         */
        this->fd = socket(sock->my_ipaddr.af, sock_type, 0);
        if (this->fd < 0) {
-               radlog(L_ERR, "Failed opening socket: %s", strerror(errno));
+               char buffer[256];
+
+               this->print(this, buffer, sizeof(buffer));
+
+               radlog(L_ERR, "Failed opening %s: %s", buffer, strerror(errno));
                return -1;
        }
                
@@ -1943,7 +2068,9 @@ static int listen_bind(rad_listen_t *this)
        if (sock->interface) {
 #ifdef SO_BINDTODEVICE
                struct ifreq ifreq;
-               strcpy(ifreq.ifr_name, sock->interface);
+
+               memset(&ifreq, 0, sizeof(ifreq));
+               strlcpy(ifreq.ifr_name, sock->interface, sizeof(ifreq.ifr_name));
 
                fr_suid_up();
                rcode = setsockopt(this->fd, SOL_SOCKET, SO_BINDTODEVICE,
@@ -1968,10 +2095,10 @@ static int listen_bind(rad_listen_t *this)
                 *      been defined, set the scope to the scope of
                 *      the interface.
                 */
-               if (sock->ipaddr.af == AF_INET6) {
-                       if (sock->ipaddr.scope == 0) {
-                               sock->ipaddr.scope = if_nametoindex(sock->interface);
-                               if (sock->ipaddr.scope == 0) {
+               if (sock->my_ipaddr.af == AF_INET6) {
+                       if (sock->my_ipaddr.scope == 0) {
+                               sock->my_ipaddr.scope = if_nametoindex(sock->interface);
+                               if (sock->my_ipaddr.scope == 0) {
                                        close(this->fd);
                                        radlog(L_ERR, "Failed finding interface %s: %s",
                                               sock->interface, strerror(errno));
@@ -2014,6 +2141,8 @@ static int listen_bind(rad_listen_t *this)
         *      Initialize udpfromto for all sockets.
         */
        if (udpfromto_init(this->fd) != 0) {
+               radlog(L_ERR, "Failed initializing udpfromto: %s",
+                      strerror(errno));
                close(this->fd);
                return -1;
        }
@@ -2246,6 +2375,9 @@ int proxy_new_listener(home_server *home, int src_port)
 {
        rad_listen_t *this;
        listen_socket_t *sock;
+#ifndef NDEBUG
+       char buffer[256];
+#endif
 
        if (!home) return 0;
 
@@ -2267,8 +2399,13 @@ int proxy_new_listener(home_server *home, int src_port)
        sock->my_port = src_port;
        sock->proto = home->proto;
 
+       if (debug_flag >= 2) {
+               this->print(this, buffer, sizeof(buffer));
+               DEBUG("Opening new %s", buffer);
+       }
+
 #ifdef WITH_TCP
-       sock->last_packet = time(NULL);
+       sock->opened = sock->last_packet = time(NULL);
 
        if (home->proto == IPPROTO_TCP) {
                this->recv = proxy_socket_tcp_recv;
@@ -2282,12 +2419,27 @@ int proxy_new_listener(home_server *home, int src_port)
                 */
                this->fd = fr_tcp_client_socket(&home->src_ipaddr,
                                                &home->ipaddr, home->port);
+#ifdef WITH_TLS
+               if (home->tls) {
+                       DEBUG("Trying SSL to port %d\n", home->port);
+                       sock->ssn = tls_new_client_session(home->tls, this->fd);
+                       if (!sock->ssn) {
+                               listen_free(&this);
+                               return 0;
+                       }
+
+                       this->recv = proxy_tls_recv;
+                       this->send = proxy_tls_send;
+               }
+#endif
        } else
 #endif
                this->fd = fr_socket(&home->src_ipaddr, src_port);
 
        if (this->fd < 0) {
-               DEBUG("Failed opening client socket: %s", fr_strerror());
+               this->print(this, buffer,sizeof(buffer));
+               DEBUG("Failed opening client socket ::%s:: : %s",
+                     buffer, fr_strerror());
                listen_free(&this);
                return 0;
        }
@@ -2435,11 +2587,33 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
        return this;
 }
 
+#ifdef WITH_PROXY
+static int is_loopback(const fr_ipaddr_t *ipaddr)
+{
+       /*
+        *      We shouldn't proxy on loopback.
+        */
+       if ((ipaddr->af == AF_INET) &&
+           (ipaddr->ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) {
+               return 1;
+       }
+       
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+       if ((ipaddr->af == AF_INET6) &&
+           (IN6_IS_ADDR_LINKLOCAL(&ipaddr->ipaddr.ip6addr))) {
+               return 1;
+       }
+#endif
+
+       return 0;
+}
+#endif
+
 /*
  *     Generate a list of listeners.  Takes an input list of
  *     listeners, too, so we don't close sockets with waiting packets.
  */
-int listen_init(CONF_SECTION *config, rad_listen_t **head)
+int listen_init(CONF_SECTION *config, rad_listen_t **head, int spawn_flag)
 {
        int             override = FALSE;
        int             rcode;
@@ -2451,6 +2625,9 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
 #ifdef WITH_PROXY
        int             defined_proxy = 0;
 #endif
+#ifndef WITH_TLS
+       spawn_flag = spawn_flag; /* -Wunused */
+#endif
 
        /*
         *      We shouldn't be called with a pre-existing list.
@@ -2661,6 +2838,15 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
 
 add_sockets:
        /*
+        *      No sockets to receive packets, this is an error.
+        *      proxying is pointless.
+        */
+       if (!*head) {
+               radlog(L_ERR, "The server is not configured to listen on any ports.  Cannot start.");
+               return -1;
+       }
+
+       /*
         *      Print out which sockets we're listening on, and
         *      add them to the event list.
         */
@@ -2671,7 +2857,15 @@ add_sockets:
                }
 
 #endif
-               event_new_fd(this);
+
+#ifdef WITH_TLS
+               if (!spawn_flag && this->tls) {
+                       cf_log_err(cf_sectiontoitem(this->cs), "Threading must be enabled for TLS sockets to function properly.");
+                       cf_log_err(cf_sectiontoitem(this->cs), "You probably need to do 'radiusd -fxx -l stdout' for debugging");
+                       return -1;
+               }
+#endif
+               if (!check_config) event_new_fd(this);
        }
 
        /*
@@ -2680,6 +2874,7 @@ add_sockets:
         */
 #ifdef WITH_PROXY
        if ((mainconfig.proxy_requests == TRUE) &&
+           !check_config &&
            (*head != NULL) && !defined_proxy) {
                listen_socket_t *sock = NULL;
                int             port = 0;
@@ -2700,6 +2895,9 @@ add_sockets:
                for (this = *head; this != NULL; this = this->next) {
                        if (this->type == RAD_LISTEN_AUTH) {
                                sock = this->data;
+
+                               if (is_loopback(&sock->my_ipaddr)) continue;
+
                                if (home.src_ipaddr.af == AF_UNSPEC) {
                                        home.src_ipaddr = sock->my_ipaddr;
                                }
@@ -2709,6 +2907,9 @@ add_sockets:
 #ifdef WITH_ACCT
                        if (this->type == RAD_LISTEN_ACCT) {
                                sock = this->data;
+
+                               if (is_loopback(&sock->my_ipaddr)) continue;
+
                                if (home.src_ipaddr.af == AF_UNSPEC) {
                                        home.src_ipaddr = sock->my_ipaddr;
                                }
@@ -2741,6 +2942,7 @@ add_sockets:
         */
        if (!*head) return -1;
 
+       xlat_register("listen", xlat_listen, NULL);
 
        return 0;
 }
@@ -2767,19 +2969,35 @@ void listen_free(rad_listen_t **head)
                        master_listen[this->type].free(this);
                }
 
+#ifdef WITH_TLS
+               if (this->tls) tls_server_conf_free(this->tls);         
+#endif
+
 #ifdef WITH_TCP
-               if ((this->type == RAD_LISTEN_AUTH) ||
+               if ((this->type == RAD_LISTEN_AUTH)
 #ifdef WITH_ACCT
-                   (this->type == RAD_LISTEN_ACCT) ||
+                   || (this->type == RAD_LISTEN_ACCT)
 #endif
 #ifdef WITH_PROXY
-                   (this->type == RAD_LISTEN_PROXY)
+                   || (this->type == RAD_LISTEN_PROXY)
 #endif
                        ) {
                        listen_socket_t *sock = this->data;
-                       rad_free(&sock->packet);
-               }
+
+#ifdef WITH_TLS
+                       if (sock->request) {
+                               pthread_mutex_destroy(&(sock->mutex));
+                               request_free(&sock->request);
+                               sock->packet = NULL;
+
+                               if (sock->ssn) session_free(sock->ssn);
+                               request_free(&sock->request);
+                       } else
 #endif
+                               rad_free(&sock->packet);
+
+               }
+#endif /* WITH_TCP */
 
                free(this->data);
                free(this);
@@ -2799,8 +3017,11 @@ RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
        for (this = mainconfig.listen; this != NULL; this = this->next) {
                listen_socket_t *sock;
 
-               if ((this->type != RAD_LISTEN_AUTH) &&
-                   (this->type != RAD_LISTEN_ACCT)) continue;
+               if ((this->type != RAD_LISTEN_AUTH)
+#ifdef WITH_ACCOUNTING
+                   && (this->type != RAD_LISTEN_ACCT)
+#endif
+                   ) continue;
                
                sock = this->data;
 
@@ -2814,40 +3035,35 @@ RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
 }
 #endif
 
-rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port)
+rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port, int proto)
 {
        rad_listen_t *this;
 
        for (this = mainconfig.listen; this != NULL; this = this->next) {
                listen_socket_t *sock;
 
-               /*
-                *      FIXME: For TCP, ignore the *secondary*
-                *      listeners associated with the main socket.
-                */
-               if ((this->type != RAD_LISTEN_AUTH) &&
-                   (this->type != RAD_LISTEN_ACCT)) continue;
-               
                sock = this->data;
 
-               if ((sock->my_port == port) &&
-                   (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
-                       return this;
-               }
+               if (sock->my_port != port) continue;
+               if (sock->proto != proto) continue;
+               if (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) != 0) continue;
 
-               if ((sock->my_port == port) &&
-                   ((sock->my_ipaddr.af == AF_INET) &&
-                    (sock->my_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
-                       return this;
-               }
+               return this;
+       }
 
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-               if ((sock->my_port == port) &&
-                   (sock->my_ipaddr.af == AF_INET6) &&
-                   (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr))) {
-                       return this;
-               }
-#endif
+       /*
+        *      Failed to find a specific one.  Find INADDR_ANY
+        */
+       for (this = mainconfig.listen; this != NULL; this = this->next) {
+               listen_socket_t *sock;
+
+               sock = this->data;
+
+               if (sock->my_port != port) continue;
+               if (sock->proto != proto) continue;
+               if (!fr_inaddr_any(&sock->my_ipaddr)) continue;
+
+               return this;
        }
 
        return NULL;