Clean up loopback / inaddr_any checks
[freeradius.git] / src / main / listen.c
index b52f43c..e82d585 100644 (file)
@@ -50,6 +50,19 @@ RCSID("$Id$")
 #endif
 
 
+void print_packet(RADIUS_PACKET *packet)
+{
+       char src[256], dst[256];
+
+       ip_ntoh(&packet->src_ipaddr, src, sizeof(src));
+       ip_ntoh(&packet->dst_ipaddr, dst, sizeof(dst));
+
+       fprintf(stderr, "ID %d: %s %d -> %s %d\n", packet->id,
+               src, packet->src_port, dst, packet->dst_port);
+
+}
+
+
 /*
  *     We'll use this below.
  */
@@ -66,18 +79,6 @@ 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;
-#ifdef SO_BINDTODEVICE
-       const char              *interface;
-#endif
-       RADCLIENT_LIST  *clients;
-} listen_socket_t;
-
 static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type);
 
 /*
@@ -94,24 +95,21 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
        time_t now;
        RADCLIENT *client;
        RADCLIENT_LIST *clients;
+       listen_socket_t *sock;
 
        rad_assert(listener != NULL);
        rad_assert(ipaddr != NULL);
 
-       clients = ((listen_socket_t *)listener->data)->clients;
+       sock = listener->data;
+       clients = sock->clients;
 
        /*
         *      This HAS to have been initialized previously.
         */
        rad_assert(clients != NULL);
 
-       client = client_find(clients, ipaddr
-#ifdef WITH_TCP
-                            , IPPROTO_UDP
-#endif
-                            );
+       client = client_find(clients, ipaddr,sock->proto);
        if (!client) {
-               static time_t last_printed = 0;
                char name[256], buffer[128];
                                        
 #ifdef WITH_DYNAMIC_CLIENTS
@@ -119,11 +117,13 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
 #endif
 
                /*
-                *      DoS attack quenching, but only in debug mode.
+                *      DoS attack quenching, but only in daemon mode.
                 *      If they're running in debug mode, show them
                 *      every packet.
                 */
                if (debug_flag == 0) {
+                       static time_t last_printed = 0;
+
                        now = time(NULL);
                        if (last_printed == now) return NULL;
                        
@@ -132,10 +132,16 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
 
                listener->print(listener, name, sizeof(name));
 
-               radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d",
-                      name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
-                                      buffer, sizeof(buffer)),
-                      src_port);
+               radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d"
+#ifdef WITH_TCP
+                      " proto %s"
+#endif
+                      , name, inet_ntop(ipaddr->af, &ipaddr->ipaddr,
+                                        buffer, sizeof(buffer)), src_port
+#ifdef WITH_TCP
+                      , (sock->proto == IPPROTO_UDP) ? "udp" : "tcp"
+#endif
+                      );
                return NULL;
        }
 
@@ -181,11 +187,7 @@ RADCLIENT *client_listener_find(const rad_listen_t *listener,
                /*
                 *      Go find the enclosing network again.
                 */
-               client = client_find(clients, ipaddr
-#ifdef WITH_TCP
-                                    , IPPROTO_UDP
-#endif
-                                    );
+               client = client_find(clients, ipaddr, sock->proto);
 
                /*
                 *      WTF?
@@ -199,7 +201,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
@@ -291,7 +294,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 {
@@ -318,7 +321,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 {
@@ -345,7 +348,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 {
@@ -382,7 +385,241 @@ static int rad_status_server(REQUEST *request)
        return 0;
 }
 
+#ifdef WITH_TCP
+static int auth_tcp_recv(rad_listen_t *listener,
+                        RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+       int rcode;
+       RADIUS_PACKET   *packet;
+       RAD_REQUEST_FUNP fun = NULL;
+       listen_socket_t *sock = listener->data;
+       RADCLIENT       *client = sock->client;
+
+       /*
+        *      Allocate a packet for partial reads.
+        */
+       if (!sock->packet) {
+               sock->packet = rad_alloc(0);
+               if (!sock->packet) return 0;
+
+               sock->packet->sockfd = listener->fd;
+               sock->packet->src_ipaddr = sock->other_ipaddr;
+               sock->packet->src_port = sock->other_port;
+               sock->packet->dst_ipaddr = sock->my_ipaddr;
+               sock->packet->dst_port = sock->my_port;
+       }
+       
+       /*
+        *      Grab the packet currently being processed.
+        */
+       packet = sock->packet;
+
+       rcode = fr_tcp_read_packet(packet, 0);
+
+       /*
+        *      Still only a partial packet.  Put it back, and return,
+        *      so that we'll read more data when it's ready.
+        */
+       if (rcode == 0) {
+               return 0;
+       }
+
+       if (rcode == -1) {      /* error reading packet */
+               char buffer[256];
+
+               radlog(L_ERR, "Invalid packet from %s port %d: closing socket",
+                      ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
+                      packet->src_port);
+       }
+
+       if (rcode < 0) {        /* error or connection reset */
+               listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
+
+               /*
+                *      Decrement the number of connections.
+                */
+               if (sock->parent->num_connections > 0) {
+                       sock->parent->num_connections--;
+               }
+               if (sock->client->num_connections > 0) {
+                       sock->client->num_connections--;
+               }
+
+               /*
+                *      Tell the event handler that an FD has disappeared.
+                */
+               DEBUG("Client has closed connection");
+               event_new_fd(listener);
+
+               /*
+                *      Do NOT free the listener here.  It's in use by
+                *      a request, and will need to hang around until
+                *      all of the requests are done.
+                *
+                *      It is instead free'd in remove_from_request_hash()
+                */
+               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);
+               fun = rad_authenticate;
+               break;
+
+       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);
+                       DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
+                       rad_free(&sock->packet);
+                       return 0;
+               }
+               fun = rad_status_server;
+               break;
+
+       default:
+               RAD_STATS_INC(radius_auth_stats.total_unknown_types);
+               RAD_STATS_CLIENT_INC(listener, client, total_unknown_types);
+
+               DEBUG("Invalid packet code %d sent to authentication port 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);
+               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)
+{
+       int newfd, src_port;
+       rad_listen_t *this;
+       socklen_t salen;
+       struct sockaddr_storage src;
+       listen_socket_t *sock;
+       fr_ipaddr_t src_ipaddr;
+       RADCLIENT *client;
+       
+       salen = sizeof(src);
+
+       DEBUG2(" ... new connection request on TCP socket.");
+       
+       newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
+       if (newfd < 0) {
+               /*
+                *      Non-blocking sockets must handle this.
+                */
+               if (errno == EWOULDBLOCK) {
+                       return 0;
+               }
+
+               DEBUG2(" ... failed to accept connection.");
+               return -1;
+       }
+
+       if (!fr_sockaddr2ipaddr(&src, salen, &src_ipaddr, &src_port)) {
+               DEBUG2(" ... unknown address family.");
+               return 0;
+       }
+
+       /*
+        *      Enforce client IP address checks on accept, not on
+        *      every packet.
+        */
+       if ((client = client_listener_find(listener,
+                                          &src_ipaddr, src_port)) == NULL) {
+               close(newfd);
+               RAD_STATS_TYPE_INC(listener, total_invalid_requests);
+               return 0;
+       }
+
+       /*
+        *      Enforce max_connectionsx on client && listen section.
+        */
+       if ((client->max_connections != 0) &&
+           (client->max_connections == client->num_connections)) {
+               /*
+                *      FIXME: Print client IP/port, and server IP/port.
+                */
+               radlog(L_INFO, "Ignoring new connection due to client max_connections (%d)", client->max_connections);
+               close(newfd);
+               return 0;
+       }
+
+       sock = listener->data;
+       if ((sock->max_connections != 0) &&
+           (sock->max_connections == sock->num_connections)) {
+               /*
+                *      FIXME: Print client IP/port, and server IP/port.
+                */
+               radlog(L_INFO, "Ignoring new connection due to socket max_connections");
+               close(newfd);
+               return 0;
+       }
+       client->num_connections++;
+       sock->num_connections++;
+
+       /*
+        *      Add the new listener.
+        */
+       this = listen_alloc(listener->type);
+       if (!this) return -1;
+
+       /*
+        *      Copy everything, including the pointer to the socket
+        *      information.
+        */
+       sock = this->data;
+       memcpy(this->data, listener->data, sizeof(*sock));
+       memcpy(this, listener, sizeof(*this));
+       this->next = NULL;
+       this->data = sock;      /* fix it back */
+
+       sock->parent = listener->data;
+       sock->other_ipaddr = src_ipaddr;
+       sock->other_port = src_port;
+       sock->client = client;
+
+       this->fd = newfd;
+       this->status = RAD_LISTEN_STATUS_INIT;
+       this->recv = auth_tcp_recv;
+
+       /*
+        *      FIXME: set O_NONBLOCK on the accept'd fd.
+        *      See djb's portability rants for details.
+        */
+
+       /*
+        *      Tell the event loop that we have a new FD.
+        *      This can be called from a child thread...
+        */
+       event_new_fd(this);
+
+       return 0;
+}
+#endif
+
 
+/*
+ *     This function is stupid and complicated.
+ */
 static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
 {
        size_t len;
@@ -440,25 +677,98 @@ static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
 
        ADDSTRING(name);
 
-#ifdef SO_BINDTODEVICE
        if (sock->interface) {
                ADDSTRING(" interface ");
                ADDSTRING(sock->interface);
        }
+
+#ifdef WITH_TCP
+       if (this->recv == auth_tcp_accept) {
+               ADDSTRING(" proto tcp");
+       }
+#endif
+
+#ifdef WITH_TCP
+       /*
+        *      TCP sockets get printed a little differently, to make
+        *      it clear what's going on.
+        */
+       if (sock->client) {
+               ADDSTRING(" from client (");
+               ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
+               FORWARD;
+
+               ADDSTRING(", ");
+               snprintf(buffer, bufsize, "%d", sock->other_port);
+               FORWARD;
+               ADDSTRING(") -> (");
+
+               if ((sock->my_ipaddr.af == AF_INET) &&
+                   (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+                       strlcpy(buffer, "*", bufsize);
+               } else {
+                       ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
+               }
+               FORWARD;
+               
+               ADDSTRING(", ");
+               snprintf(buffer, bufsize, "%d", sock->my_port);
+               FORWARD;
+
+               if (this->server) {
+                       ADDSTRING(", virtual-server=");
+                       ADDSTRING(this->server);
+               }
+
+               ADDSTRING(")");
+
+               return 1;
+       }
+
+       /*
+        *      Maybe it's a socket that we opened to a home server.
+        */
+       if ((sock->proto == IPPROTO_TCP) &&
+           (this->type == RAD_LISTEN_PROXY)) {
+               ADDSTRING(" (");
+               ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
+               FORWARD;
+
+               ADDSTRING(", ");
+               snprintf(buffer, bufsize, "%d", sock->my_port);
+               FORWARD;
+               ADDSTRING(") -> home_server (");
+
+               if ((sock->other_ipaddr.af == AF_INET) &&
+                   (sock->other_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+                       strlcpy(buffer, "*", bufsize);
+               } else {
+                       ip_ntoh(&sock->other_ipaddr, buffer, bufsize);
+               }
+               FORWARD;
+               
+               ADDSTRING(", ");
+               snprintf(buffer, bufsize, "%d", sock->other_port);
+               FORWARD;
+
+               ADDSTRING(")");
+
+               return 1;
+       }
 #endif
 
        ADDSTRING(" address ");
        
-       if ((sock->ipaddr.af == AF_INET) &&
-           (sock->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
+       if ((sock->my_ipaddr.af == AF_INET) &&
+           (sock->my_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_ANY))) {
                strlcpy(buffer, "*", bufsize);
        } else {
-               ip_ntoh(&sock->ipaddr, buffer, bufsize);
+               ip_ntoh(&sock->my_ipaddr, buffer, bufsize);
        }
        FORWARD;
 
        ADDSTRING(" port ");
-       snprintf(buffer, bufsize, "%d", sock->port);
+       snprintf(buffer, bufsize, "%d", sock->my_port);
        FORWARD;
 
        if (this->server) {
@@ -472,6 +782,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.
@@ -519,19 +831,73 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                        return -1;
        }
 
-       sock->ipaddr = ipaddr;
-       sock->port = listen_port;
+       sock->proto = IPPROTO_UDP;
+
+       if (cf_pair_find(cs, "proto")) {
+#ifndef WITH_TCP
+               cf_log_err(cf_sectiontoitem(cs),
+                          "System does not support the TCP protocol.  Delete this line from the configuration file.");
+               return -1;
+#else
+               char *proto = NULL;
+
+
+               rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR,
+                                     &proto, "udp");
+               if (rcode < 0) return -1;
+
+               if (strcmp(proto, "udp") == 0) {
+                       sock->proto = IPPROTO_UDP;
+
+               } else if (strcmp(proto, "tcp") == 0) {
+                       sock->proto = IPPROTO_TCP;
+
+                       rcode = cf_item_parse(cs, "max_connections", PW_TYPE_INTEGER,
+                                             &sock->max_connections, "64");
+                       if (rcode < 0) return -1;
+
+               } else {
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "Unknown proto name \"%s\"", proto);
+                       free(proto);
+                       return -1;
+               }
+               free(proto);
+
+               /*
+                *      TCP requires a destination IP for sockets.
+                *      UDP doesn't, so it's allowed.
+                */
+               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
+       }
+
+       sock->my_ipaddr = ipaddr;
+       sock->my_port = listen_port;
+
+       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 */
+       }
 
        /*
         *      If we can bind to interfaces, do so,
         *      else don't.
         */
        if (cf_pair_find(cs, "interface")) {
-#ifndef SO_BINDTODEVICE
-               cf_log_err(cf_sectiontoitem(cs),
-                          "System does not support binding to interfaces.  Delete this line from the configuration file.");
-               return -1;
-#else
                const char *value;
                CONF_PAIR *cp = cf_pair_find(cs, "interface");
 
@@ -543,8 +909,42 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                        return -1;
                }
                sock->interface = value;
+       }
+
+#ifdef WITH_DHCP
+       /*
+        *      If we can do broadcasts..
+        */
+       if (cf_pair_find(cs, "broadcast")) {
+#ifndef SO_BROADCAST
+               cf_log_err(cf_sectiontoitem(cs),
+                          "System does not support broadcast sockets.  Delete this line from the configuration file.");
+               return -1;
+#else
+               const char *value;
+               CONF_PAIR *cp = cf_pair_find(cs, "broadcast");
+
+               if (this->type != RAD_LISTEN_DHCP) {
+                       cf_log_err(cf_pairtoitem(cp),
+                                  "Broadcast can only be set for DHCP listeners.  Delete this line from the configuration file.");
+                       return -1;
+               }
+               
+               rad_assert(cp != NULL);
+               value = cf_pair_value(cp);
+               if (!value) {
+                       cf_log_err(cf_sectiontoitem(cs),
+                                  "No broadcast value given");
+                       return -1;
+               }
+
+               /*
+                *      Hack... whatever happened to cf_section_parse?
+                */
+               sock->broadcast = (strcmp(value, "yes") == 0);
 #endif
        }
+#endif
 
        /*
         *      And bind it to the port.
@@ -553,8 +953,8 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                char buffer[128];
                cf_log_err(cf_sectiontoitem(cs),
                           "Error binding to port for %s port %d",
-                          ip_ntoh(&sock->ipaddr, buffer, sizeof(buffer)),
-                          sock->port);
+                          ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)),
+                          sock->my_port);
                return -1;
        }
 
@@ -622,6 +1022,16 @@ static int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                return -1;
        }
 
+#ifdef WITH_TCP
+       if (sock->proto == IPPROTO_TCP) {
+               /*
+                *      Re-write the listener receive function to
+                *      allow us to accept the socket.
+                */
+               this->recv = auth_tcp_accept;
+       }
+#endif
+
        return 0;
 }
 
@@ -668,14 +1078,9 @@ static int acct_socket_send(rad_listen_t *listener, REQUEST *request)
  */
 static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
 {
-       listen_socket_t *sock = listener->data;
-
        rad_assert(request->proxy_listener == listener);
        rad_assert(listener->send == proxy_socket_send);
 
-       request->proxy->src_ipaddr = sock->ipaddr;
-       request->proxy->src_port = sock->port;
-
        return rad_send(request->proxy, request->packet,
                        request->home_server->secret);
 }
@@ -940,9 +1345,9 @@ static int rad_coa_reply(REQUEST *request)
        /*
         *      Inform the user about RFC requirements.
         */
-       s1 = pairfind(request->proxy->vps, PW_STATE);
+       s1 = pairfind(request->proxy->vps, PW_STATE, 0);
        if (s1) {
-               s2 = pairfind(request->proxy_reply->vps, PW_STATE);
+               s2 = pairfind(request->proxy_reply->vps, PW_STATE, 0);
 
                if (!s2) {
                        DEBUG("WARNING: Client was sent State in CoA, and did not respond with State.");
@@ -996,10 +1401,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;
@@ -1047,7 +1452,7 @@ 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);
 
        /*
@@ -1108,7 +1513,6 @@ static int coa_socket_recv(rad_listen_t *listener,
        int             code, src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
-       char            buffer[128];
        RADCLIENT       *client;
        fr_ipaddr_t     src_ipaddr;
 
@@ -1126,24 +1530,8 @@ static int coa_socket_recv(rad_listen_t *listener,
                                           &src_ipaddr, src_port)) == NULL) {
                rad_recv_discard(listener->fd);
                RAD_STATS_TYPE_INC(listener, total_invalid_requests);
-
-               if (debug_flag > 0) {
-                       char name[1024];
-
-                       listener->print(listener, name, sizeof(name));
-
-                       /*
-                        *      This is debugging rather than logging, so that
-                        *      DoS attacks don't affect us.
-                        */
-                       DEBUG("Ignoring request to %s from unknown client %s port %d",
-                             name,
-                             inet_ntop(src_ipaddr.af, &src_ipaddr.ipaddr,
-                                       buffer, sizeof(buffer)), src_port);
-               }
-
-               return 0;
-       }
+               return 0;
+       }
 
        /*
         *      Some sanity checks, based on the packet code.
@@ -1262,6 +1650,82 @@ static int proxy_socket_recv(rad_listen_t *listener,
 
        return 1;
 }
+
+#ifdef WITH_TCP
+/*
+ *     Recieve packets from a proxy socket.
+ */
+static int proxy_socket_tcp_recv(rad_listen_t *listener,
+                                RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+       REQUEST         *request;
+       RADIUS_PACKET   *packet;
+       RAD_REQUEST_FUNP fun = NULL;
+       listen_socket_t *sock = listener->data;
+       char            buffer[128];
+
+       packet = fr_tcp_recv(listener->fd, 0);
+       if (!packet) {
+               listener->status = RAD_LISTEN_STATUS_REMOVE_FD;
+               event_new_fd(listener);
+               return 0;
+       }
+
+       /*
+        *      FIXME: Client MIB updates?
+        */
+       switch(packet->code) {
+       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
+
+       default:
+               /*
+                *      FIXME: Update MIB for packet types?
+                */
+               radlog(L_ERR, "Invalid packet code %d sent to a proxy port "
+                      "from home server %s port %d - ID %d : IGNORED",
+                      packet->code,
+                      ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
+                      packet->src_port, packet->id);
+               rad_free(&packet);
+               return 0;
+       }
+
+       packet->src_ipaddr = sock->other_ipaddr;
+       packet->src_port = sock->other_port;
+       packet->dst_ipaddr = sock->my_ipaddr;
+       packet->dst_port = sock->my_port;
+
+       /*
+        *      FIXME: Have it return an indication of packets that
+        *      are OK to ignore (dups, too late), versus ones that
+        *      aren't OK to ignore (unknown response, spoofed, etc.)
+        *
+        *      Close the socket on bad packets...
+        */
+       request = received_proxy_response(packet);
+       if (!request) {
+               return 0;
+       }
+
+       rad_assert(fun != NULL);
+       sock->opened = sock->last_packet = request->timestamp;
+
+       *pfun = fun;
+       *prequest = request;
+
+       return 1;
+}
+#endif
 #endif
 
 
@@ -1368,7 +1832,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
@@ -1393,55 +1857,79 @@ static int listen_bind(rad_listen_t *this)
        struct sockaddr_storage salocal;
        socklen_t       salen;
        listen_socket_t *sock = this->data;
+#ifndef WITH_TCP
+#define proto_for_port "udp"
+#define sock_type SOCK_DGRAM
+#else
+       const char *proto_for_port = "udp";
+       int sock_type = SOCK_DGRAM;
+       
+       if (sock->proto == IPPROTO_TCP) {
+#ifdef WITH_VMPS
+               if (this->type == RAD_LISTEN_VQP) {
+                       radlog(L_ERR, "VQP does not support TCP transport");
+                       return -1;
+               }
+#endif
+
+               proto_for_port = "tcp";
+               sock_type = SOCK_STREAM;        
+       }
+#endif
 
        /*
         *      If the port is zero, then it means the appropriate
         *      thing from /etc/services.
         */
-       if (sock->port == 0) {
+       if (sock->my_port == 0) {
                struct servent  *svp;
 
                switch (this->type) {
                case RAD_LISTEN_AUTH:
-                       svp = getservbyname ("radius", "udp");
+                       svp = getservbyname ("radius", proto_for_port);
                        if (svp != NULL) {
-                               sock->port = ntohs(svp->s_port);
+                               sock->my_port = ntohs(svp->s_port);
                        } else {
-                               sock->port = PW_AUTH_UDP_PORT;
+                               sock->my_port = PW_AUTH_UDP_PORT;
                        }
                        break;
 
 #ifdef WITH_ACCOUNTING
                case RAD_LISTEN_ACCT:
-                       svp = getservbyname ("radacct", "udp");
+                       svp = getservbyname ("radacct", proto_for_port);
                        if (svp != NULL) {
-                               sock->port = ntohs(svp->s_port);
+                               sock->my_port = ntohs(svp->s_port);
                        } else {
-                               sock->port = PW_ACCT_UDP_PORT;
+                               sock->my_port = PW_ACCT_UDP_PORT;
                        }
                        break;
 #endif
 
 #ifdef WITH_PROXY
                case RAD_LISTEN_PROXY:
-                       sock->port = 0;
+                       /* leave it at zero */
                        break;
 #endif
 
 #ifdef WITH_VMPS
                case RAD_LISTEN_VQP:
-                       sock->port = 1589;
+                       sock->my_port = 1589;
                        break;
 #endif
 
 #ifdef WITH_COA
                case RAD_LISTEN_COA:
-                       sock->port = PW_COA_UDP_PORT;
+                       svp = getservbyname ("radius-dynauth", "udp");
+                       if (svp != NULL) {
+                               sock->my_port = ntohs(svp->s_port);
+                       } else {
+                               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;
                }
        }
@@ -1449,17 +1937,17 @@ static int listen_bind(rad_listen_t *this)
        /*
         *      Copy fr_socket() here, as we may need to bind to a device.
         */
-       this->fd = socket(sock->ipaddr.af, SOCK_DGRAM, 0);
+       this->fd = socket(sock->my_ipaddr.af, sock_type, 0);
        if (this->fd < 0) {
                radlog(L_ERR, "Failed opening socket: %s", strerror(errno));
                return -1;
        }
                
-#ifdef SO_BINDTODEVICE
        /*
         *      Bind to a device BEFORE touching IP addresses.
         */
        if (sock->interface) {
+#ifdef SO_BINDTODEVICE
                struct ifreq ifreq;
                strcpy(ifreq.ifr_name, sock->interface);
 
@@ -1473,9 +1961,60 @@ static int listen_bind(rad_listen_t *this)
                               sock->interface, strerror(errno));
                        return -1;
                } /* else it worked. */
+#else
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+#ifdef HAVE_NET_IF_H
+               /*
+                *      Odds are that any system supporting "bind to
+                *      device" also supports IPv6, so this next bit
+                *      isn't necessary.  But it's here for
+                *      completeness.
+                *
+                *      If we're doing IPv6, and the scope hasn't yet
+                *      been defined, set the scope to the scope of
+                *      the interface.
+                */
+               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));
+                                       return -1;
+                               }
+                       } /* else scope was defined: we're OK. */
+               } else
+#endif
+#endif
+                               /*
+                                *      IPv4: no link local addresses,
+                                *      and no bind to device.
+                                */
+               {
+                       close(this->fd);
+                       radlog(L_ERR, "Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
+                       return -1;
+               }
+#endif
+       }
+
+#ifdef WITH_TCP
+       if (sock->proto == IPPROTO_TCP) {
+               int on = 1;
+
+               if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+                       close(this->fd);
+                       radlog(L_ERR, "Failed to reuse address: %s", strerror(errno));
+                       return -1;
+               }
        }
 #endif
 
+#if defined(WITH_TCP) && defined(WITH_UDPFROMTO)
+       else                    /* UDP sockets get UDPfromto */
+#endif
+
 #ifdef WITH_UDPFROMTO
        /*
         *      Initialize udpfromto for all sockets.
@@ -1485,17 +2024,17 @@ static int listen_bind(rad_listen_t *this)
                return -1;
        }
 #endif
-       
+
        /*
         *      Set up sockaddr stuff.
         */
-       if (!fr_ipaddr2sockaddr(&sock->ipaddr, sock->port, &salocal, &salen)) {
+       if (!fr_ipaddr2sockaddr(&sock->my_ipaddr, sock->my_port, &salocal, &salen)) {
                close(this->fd);
                return -1;
        }
                
 #ifdef HAVE_STRUCT_SOCKADDR_IN6
-       if (sock->ipaddr.af == AF_INET6) {
+       if (sock->my_ipaddr.af == AF_INET6) {
                /*
                 *      Listening on '::' does NOT get you IPv4 to
                 *      IPv6 mapping.  You've got to listen on an IPv4
@@ -1504,7 +2043,7 @@ static int listen_bind(rad_listen_t *this)
                 */
 #ifdef IPV6_V6ONLY
                
-               if (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr)) {
+               if (IN6_IS_ADDR_UNSPECIFIED(&sock->my_ipaddr.ipaddr.ip6addr)) {
                        int on = 1;
                        
                        setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
@@ -1514,8 +2053,7 @@ static int listen_bind(rad_listen_t *this)
        }
 #endif /* HAVE_STRUCT_SOCKADDR_IN6 */
 
-
-       if (sock->ipaddr.af == AF_INET) {
+       if (sock->my_ipaddr.af == AF_INET) {
                UNUSED int flag;
                
 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
@@ -1539,65 +2077,100 @@ static int listen_bind(rad_listen_t *this)
 #endif
        }
 
-       /*
-        *      May be binding to priviledged ports.
-        */
-       fr_suid_up();
-       rcode = bind(this->fd, (struct sockaddr *) &salocal, salen);
-       fr_suid_down();
-       if (rcode < 0) {
-               char buffer[256];
-               close(this->fd);
+#ifdef WITH_DHCP
+#ifdef SO_BROADCAST
+       if (sock->broadcast) {
+               int on = 1;
                
-               this->print(this, buffer, sizeof(buffer));
-               radlog(L_ERR, "Failed binding to %s: %s\n",
-                      buffer, strerror(errno));
-               return -1;
+               if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
+                       radlog(L_ERR, "Can't set broadcast option: %s\n",
+                              strerror(errno));
+                       return -1;
+               }
        }
-       
+#endif
+#endif
+
        /*
-        *      FreeBSD jail issues.  We bind to 0.0.0.0, but the
-        *      kernel instead binds us to a 1.2.3.4.  If this
-        *      happens, notice, and remember our real IP.
+        *      May be binding to priviledged ports.
         */
-       {
-               struct sockaddr_storage src;
-               socklen_t               sizeof_src = sizeof(src);
+       if (sock->my_port != 0) {
+#ifdef SO_REUSEADDR
+               int on = 1;
 
-               memset(&src, 0, sizeof_src);
-               if (getsockname(this->fd, (struct sockaddr *) &src,
-                               &sizeof_src) < 0) {
-                       radlog(L_ERR, "Failed getting socket name: %s",
+               if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+                       radlog(L_ERR, "Can't set re-use address option: %s\n",
                               strerror(errno));
                        return -1;
                }
+#endif
 
-               if (!fr_sockaddr2ipaddr(&src, sizeof_src,
-                                       &sock->ipaddr, &sock->port)) {
-                       radlog(L_ERR, "Socket has unsupported address family");
+               fr_suid_up();
+               rcode = bind(this->fd, (struct sockaddr *) &salocal, salen);
+               fr_suid_down();
+               if (rcode < 0) {
+                       char buffer[256];
+                       close(this->fd);
+                       
+                       this->print(this, buffer, sizeof(buffer));
+                       radlog(L_ERR, "Failed binding to %s: %s\n",
+                              buffer, strerror(errno));
                        return -1;
                }
+       
+               /*
+                *      FreeBSD jail issues.  We bind to 0.0.0.0, but the
+                *      kernel instead binds us to a 1.2.3.4.  If this
+                *      happens, notice, and remember our real IP.
+                */
+               {
+                       struct sockaddr_storage src;
+                       socklen_t               sizeof_src = sizeof(src);
+                       
+                       memset(&src, 0, sizeof_src);
+                       if (getsockname(this->fd, (struct sockaddr *) &src,
+                                       &sizeof_src) < 0) {
+                               radlog(L_ERR, "Failed getting socket name: %s",
+                                      strerror(errno));
+                               return -1;
+                       }
+                       
+                       if (!fr_sockaddr2ipaddr(&src, sizeof_src,
+                                               &sock->my_ipaddr, &sock->my_port)) {
+                               radlog(L_ERR, "Socket has unsupported address family");
+                               return -1;
+                       }
+               }
        }
 
-#ifdef O_NONBLOCK
-       {
-               int flags;
-               
-               if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0)  {
-                       radlog(L_ERR, "Failure getting socket flags: %s)\n",
-                              strerror(errno));
-                       return -1;
-               }
-               
-               flags |= O_NONBLOCK;
-               if( fcntl(this->fd, F_SETFL, flags) < 0) {
-                       radlog(L_ERR, "Failure setting socket flags: %s)\n",
-                              strerror(errno));
+#ifdef WITH_TCP
+       if (sock->proto == IPPROTO_TCP) {
+               if (listen(this->fd, 8) < 0) {
+                       close(this->fd);
+                       radlog(L_ERR, "Failed in listen(): %s", strerror(errno));
                        return -1;
                }
-       }
+       } else
 #endif
 
+         if (fr_nonblock(this->fd) < 0) {
+                 close(this->fd);
+                 radlog(L_ERR, "Failed setting non-blocking on socket: %s",
+                        strerror(errno));
+                 return -1;
+         }
+
+       /*
+        *      Mostly for proxy sockets.
+        */
+       sock->other_ipaddr.af = sock->my_ipaddr.af;
+
+/*
+ *     Don't screw up other people.
+ */
+#undef proto_for_port
+#undef sock_type
+
        return 0;
 }
 
@@ -1668,7 +2241,6 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
        return this;
 }
 
-
 #ifdef WITH_PROXY
 /*
  *     Externally visible function for creating a new proxy LISTENER.
@@ -1676,95 +2248,93 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
  *     Not thread-safe, but all calls to it are protected by the
  *     proxy mutex in event.c
  */
-rad_listen_t *proxy_new_listener(fr_ipaddr_t *ipaddr, int exists)
+int proxy_new_listener(home_server *home, int src_port)
 {
-       int last_proxy_port, port;
-       rad_listen_t *this, *tmp, **last;
-       listen_socket_t *sock, *old;
+       rad_listen_t *this;
+       listen_socket_t *sock;
 
-       /*
-        *      Find an existing proxy socket to copy.
-        */
-       last_proxy_port = 0;
-       old = NULL;
-       last = &mainconfig.listen;
-       for (tmp = mainconfig.listen; tmp != NULL; tmp = tmp->next) {
-               /*
-                *      Not proxy, ignore it.
-                */
-               if (tmp->type != RAD_LISTEN_PROXY) continue;
+       if (!home) return 0;
 
-               sock = tmp->data;
+       if ((home->max_connections > 0) &&
+           (home->num_connections >= home->max_connections)) {
+               DEBUG("WARNING: Home server has too many open connections (%d)",
+                     home->max_connections);
+               return 0;
+       }
 
-               /*
-                *      If we were asked to copy a specific one, do
-                *      so.  If we're just finding one that already
-                *      exists, return a pointer to it.  Otherwise,
-                *      create ANOTHER one with the same IP address.
-                */
-               if ((ipaddr->af != AF_UNSPEC) &&
-                   (fr_ipaddr_cmp(&sock->ipaddr, ipaddr) != 0)) {
-                       if (exists) return tmp;
-                       continue;
-               }
-               
-               if (sock->port > last_proxy_port) {
-                       last_proxy_port = sock->port + 1;
-               }
-               if (!old) old = sock;
+       this = listen_alloc(RAD_LISTEN_PROXY);
 
-               last = &(tmp->next);
-       }
+       sock = this->data;
+       sock->other_ipaddr = home->ipaddr;
+       sock->other_port = home->port;
+       sock->home = home;
+
+       sock->my_ipaddr = home->src_ipaddr;
+       sock->my_port = src_port;
+       sock->proto = home->proto;
+
+#ifdef WITH_TCP
+       sock->last_packet = time(NULL);
+
+       if (home->proto == IPPROTO_TCP) {
+               this->recv = proxy_socket_tcp_recv;
 
-       if (!old) {
                /*
-                *      The socket MUST already exist if we're binding
-                *      to an address while proxying.
+                *      FIXME: connect() is blocking!
+                *      We do this with the proxy mutex locked, which may
+                *      cause large delays!
                 *
-                *      If we're initializing the server, it's OK for the
-                *      socket to NOT exist.
+                *      http://www.developerweb.net/forum/showthread.php?p=13486
                 */
-               if (!exists) return NULL;
-
-               this = listen_alloc(RAD_LISTEN_PROXY);
-
-               sock = this->data;
-               sock->ipaddr = *ipaddr;
+               this->fd = fr_tcp_client_socket(&home->src_ipaddr,
+                                               &home->ipaddr, home->port);
+       } else
+#endif
+               this->fd = fr_socket(&home->src_ipaddr, src_port);
 
-       } else {
-               this = listen_alloc(RAD_LISTEN_PROXY);
-               
-               sock = this->data;
-               sock->ipaddr = old->ipaddr;
+       if (this->fd < 0) {
+               DEBUG("Failed opening client socket: %s", fr_strerror());
+               listen_free(&this);
+               return 0;
        }
 
        /*
-        *      Keep going until we find an unused port.
+        *      Figure out which port we were bound to.
         */
-       for (port = last_proxy_port; port < 64000; port++) {
-               int rcode;
-
-               sock->port = port;
-
-               rcode = listen_bind(this);
-               if (rcode < 0) {
+       if (sock->my_port == 0) {
+               struct sockaddr_storage src;
+               socklen_t               sizeof_src = sizeof(src);
+               
+               memset(&src, 0, sizeof_src);
+               if (getsockname(this->fd, (struct sockaddr *) &src,
+                               &sizeof_src) < 0) {
+                       radlog(L_ERR, "Failed getting socket name: %s",
+                              strerror(errno));
                        listen_free(&this);
-                       return NULL;
+                       return 0;
                }
                
-               /*
-                *      Add the new listener to the list of
-                *      listeners.
-                */
-               *last = this;
-               return this;
+               if (!fr_sockaddr2ipaddr(&src, sizeof_src,
+                                       &sock->my_ipaddr, &sock->my_port)) {
+                       radlog(L_ERR, "Socket has unsupported address family");
+                       listen_free(&this);
+                       return 0;
+               }
        }
 
-       listen_free(&this);
-       return NULL;
+       /*
+        *      Tell the event loop that we have a new FD
+        */
+       if (!event_new_fd(this)) {
+               listen_free(&this);
+               return 0;
+       }
+       
+       return 1;
 }
 #endif
 
+
 static const FR_NAME_NUMBER listen_compare[] = {
 #ifdef WITH_STATS
        { "status",     RAD_LISTEN_NONE },
@@ -1824,7 +2394,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
                return NULL;
        }
        free(listen_type);
-       
+
        /*
         *      Allow listen sections in the default config to
         *      refer to a server.
@@ -1839,6 +2409,18 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
                if (rcode < 0) return NULL;
        }
 
+#ifdef WITH_PROXY
+       /*
+        *      We were passed a virtual server, so the caller is
+        *      defining a proxy listener inside of a virtual server.
+        *      This isn't allowed right now.
+        */
+       else if (type == RAD_LISTEN_PROXY) {
+               radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section");
+               return NULL;
+       }
+#endif
+
        /*
         *      Set up cross-type data.
         */
@@ -1859,6 +2441,26 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, const char *server)
        return this;
 }
 
+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;
+}
+
 /*
  *     Generate a list of listeners.  Takes an input list of
  *     listeners, too, so we don't close sockets with waiting packets.
@@ -1916,7 +2518,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                listen_socket_t *sock;
                server_ipaddr.af = AF_INET;
 
-               radlog(L_INFO, "WARNING: The directive 'bind_adress' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
+               radlog(L_INFO, "WARNING: The directive 'bind_address' is deprecated, and will be removed in future versions of FreeRADIUS. Please edit the configuration files to use the directive 'listen'.");
 
        bind_it:
 #ifdef WITH_VMPS
@@ -1929,8 +2531,8 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
 
                sock = this->data;
 
-               sock->ipaddr = server_ipaddr;
-               sock->port = auth_port;
+               sock->my_ipaddr = server_ipaddr;
+               sock->my_port = auth_port;
 
                sock->clients = clients_parse_section(config);
                if (!sock->clients) {
@@ -1942,11 +2544,11 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
 
                if (listen_bind(this) < 0) {
                        listen_free(head);
-                       radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->port);
+                       radlog(L_ERR, "There appears to be another RADIUS server running on the authentication port %d", sock->my_port);
                        listen_free(&this);
                        return -1;
                }
-               auth_port = sock->port; /* may have been updated in listen_bind */
+               auth_port = sock->my_port;      /* may have been updated in listen_bind */
                if (override) {
                        cs = cf_section_sub_find_name2(config, "server",
                                                       mainconfig.name);
@@ -1960,7 +2562,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                /*
                 *      No acct for vmpsd
                 */
-               if (strcmp(progname, "vmpsd") == 0) goto do_proxy;
+               if (strcmp(progname, "vmpsd") == 0) goto add_sockets;
 #endif
 
 #ifdef WITH_ACCOUNTING
@@ -1979,8 +2581,8 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                 *      The accounting port is always the
                 *      authentication port + 1
                 */
-               sock->ipaddr = server_ipaddr;
-               sock->port = auth_port + 1;
+               sock->my_ipaddr = server_ipaddr;
+               sock->my_port = auth_port + 1;
 
                sock->clients = clients_parse_section(config);
                if (!sock->clients) {
@@ -1992,7 +2594,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                if (listen_bind(this) < 0) {
                        listen_free(&this);
                        listen_free(head);
-                       radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->port);
+                       radlog(L_ERR, "There appears to be another RADIUS server running on the accounting port %d", sock->my_port);
                        return -1;
                }
 
@@ -2021,7 +2623,7 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
 
                cs = cf_section_sub_find_name2(config, "server",
                                               mainconfig.name);
-               if (!cs) goto do_proxy;
+               if (!cs) goto add_sockets;
 
                /*
                 *      Should really abstract this code...
@@ -2035,15 +2637,11 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                                return -1;
                        }
 
-#ifdef WITH_PROXY
-                       if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1;
-#endif
-                       
                        *last = this;
                        last = &(this->next);
                } /* loop over "listen" directives in server <foo> */
 
-               goto do_proxy;
+               goto add_sockets;
        }
 
        /*
@@ -2058,10 +2656,6 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                        return -1;
                }
 
-#ifdef WITH_PROXY
-               if (this->type == RAD_LISTEN_PROXY) defined_proxy = 1;
-#endif
-
                *last = this;
                last = &(this->next);
        }
@@ -2086,36 +2680,53 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                                return -1;
                        }
                        
-#ifdef WITH_PROXY
-                       if (this->type == RAD_LISTEN_PROXY) {
-                               radlog(L_ERR, "Error: listen type \"proxy\" Cannot appear in a virtual server section");
-                               listen_free(head);
-                               return -1;
-                       }
-#endif
-
                        *last = this;
                        last = &(this->next);
                } /* loop over "listen" directives in virtual servers */
        } /* loop over virtual servers */
 
+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.
+        */
+       for (this = *head; this != NULL; this = this->next) {
+#ifdef WITH_PROXY
+               if (this->type == RAD_LISTEN_PROXY) {
+                       defined_proxy = 1;
+               }
+
+#endif
+               event_new_fd(this);
+       }
+
        /*
         *      If we're proxying requests, open the proxy FD.
         *      Otherwise, don't do anything.
         */
- do_proxy:
 #ifdef WITH_PROXY
-       if (mainconfig.proxy_requests == TRUE) {
-               int             port = -1;
+       if ((mainconfig.proxy_requests == TRUE) &&
+           (*head != NULL) && !defined_proxy) {
                listen_socket_t *sock = NULL;
+               int             port = 0;
+               home_server     home;
+
+               memset(&home, 0, sizeof(home));
 
                /*
-                *      No sockets to receive packets, therefore
-                *      proxying is pointless.
+                *      
                 */
-               if (!*head) return -1;
-
-               if (defined_proxy) goto check_home_servers;
+               home.proto = IPPROTO_UDP;
+               home.src_ipaddr = server_ipaddr;
 
                /*
                 *      Find the first authentication port,
@@ -2124,71 +2735,54 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head)
                for (this = *head; this != NULL; this = this->next) {
                        if (this->type == RAD_LISTEN_AUTH) {
                                sock = this->data;
-                               if (server_ipaddr.af == AF_UNSPEC) {
-                                       server_ipaddr = sock->ipaddr;
+
+                               if (is_loopback(&sock->my_ipaddr)) continue;
+
+                               if (home.src_ipaddr.af == AF_UNSPEC) {
+                                       home.src_ipaddr = sock->my_ipaddr;
                                }
-                               port = sock->port + 2; /* skip acct port */
+                               port = sock->my_port + 2;
                                break;
                        }
-#ifdef WITH_VMPS
-                       if (this->type == RAD_LISTEN_VQP) {
+#ifdef WITH_ACCT
+                       if (this->type == RAD_LISTEN_ACCT) {
                                sock = this->data;
-                               if (server_ipaddr.af == AF_UNSPEC) {
-                                       server_ipaddr = sock->ipaddr;
+
+                               if (is_loopback(&sock->my_ipaddr)) continue;
+
+                               if (home.src_ipaddr.af == AF_UNSPEC) {
+                                       home.src_ipaddr = sock->my_ipaddr;
                                }
-                               port = sock->port + 1;
+                               port = sock->my_port + 1;
                                break;
                        }
 #endif
                }
 
-               if (port < 0) port = 1024 + (fr_rand() & 0x1ff);
-
                /*
                 *      Address is still unspecified, use IPv4.
                 */
-               if (server_ipaddr.af == AF_UNSPEC) {
-                       server_ipaddr.af = AF_INET;
-                       server_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
+               if (home.src_ipaddr.af == AF_UNSPEC) {
+                       home.src_ipaddr.af = AF_INET;
+                       /* everything else is already set to zero */
                }
 
-               this = listen_alloc(RAD_LISTEN_PROXY);
-               sock = this->data;
+               home.ipaddr.af = home.src_ipaddr.af;
+               /* everything else is already set to zero */
 
-               /*
-                *      Create the first proxy socket.
-                */
-               sock->ipaddr = server_ipaddr;
-
-               /*
-                *      Try to find a proxy port (value doesn't matter)
-                */
-               for (sock->port = port;
-                    sock->port < 64000;
-                    sock->port++) {
-                       if (listen_bind(this) == 0) {
-                               *last = this;
-                               last = &(this->next); /* just in case */
-                               break;
-                       }
-               }
-
-               if (sock->port >= 64000) {
+               if (!proxy_new_listener(&home, port)) {
                        listen_free(head);
-                       listen_free(&this);
-                       radlog(L_ERR, "Failed to open socket for proxying");
                        return -1;
                }
-               
-               /*
-                *      Create *additional* proxy listeners, based
-                *      on their src_ipaddr.
-                */
-       check_home_servers:
-               if (home_server_create_listeners(*head) != 0) return -1;
        }
 #endif
 
+       /*
+        *      Haven't defined any sockets.  Die.
+        */
+       if (!*head) return -1;
+
+
        return 0;
 }
 
@@ -2213,6 +2807,21 @@ void listen_free(rad_listen_t **head)
                if (master_listen[this->type].free) {
                        master_listen[this->type].free(this);
                }
+
+#ifdef WITH_TCP
+               if ((this->type == RAD_LISTEN_AUTH) ||
+#ifdef WITH_ACCT
+                   (this->type == RAD_LISTEN_ACCT) ||
+#endif
+#ifdef WITH_PROXY
+                   (this->type == RAD_LISTEN_PROXY)
+#endif
+                       ) {
+                       listen_socket_t *sock = this->data;
+                       rad_free(&sock->packet);
+               }
+#endif
+
                free(this->data);
                free(this);
 
@@ -2236,8 +2845,8 @@ RADCLIENT_LIST *listener_find_client_list(const fr_ipaddr_t *ipaddr,
                
                sock = this->data;
 
-               if ((sock->port == port) &&
-                   (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+               if ((sock->my_port == port) &&
+                   (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
                        return sock->clients;
                }
        }
@@ -2262,24 +2871,15 @@ rad_listen_t *listener_find_byipaddr(const fr_ipaddr_t *ipaddr, int port)
                
                sock = this->data;
 
-               if ((sock->port == port) &&
-                   (fr_ipaddr_cmp(ipaddr, &sock->ipaddr) == 0)) {
+               if ((sock->my_port == port) &&
+                   (fr_ipaddr_cmp(ipaddr, &sock->my_ipaddr) == 0)) {
                        return this;
                }
 
-               if ((sock->port == port) &&
-                   ((sock->ipaddr.af == AF_INET) &&
-                    (sock->ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY))) {
+               if ((sock->my_port == port) &&
+                   fr_inaddr_any(&sock->my_ipaddr)) {
                        return this;
                }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-               if ((sock->port == port) &&
-                   (sock->ipaddr.af == AF_INET6) &&
-                   (IN6_IS_ADDR_UNSPECIFIED(&sock->ipaddr.ipaddr.ip6addr))) {
-                       return this;
-               }
-#endif
        }
 
        return NULL;