Moved socket code from radiusd.c & mainconfig.c to new file listen.c
authoraland <aland>
Tue, 3 May 2005 22:29:53 +0000 (22:29 +0000)
committeraland <aland>
Tue, 3 May 2005 22:29:53 +0000 (22:29 +0000)
We now have per-socket callback functions to turn a socket which
has data ready into a REQUEST* and RAD_REQUEST_FUNP, which means
that we can have per-socket callbacks.

i.e. the code is MUCH cleaner, data driven, and it should now
be pretty easy to have per-socket clients.

src/main/Makefile.in
src/main/listen.c [new file with mode: 0644]
src/main/mainconfig.c
src/main/radiusd.c

index 6d120a9..8d009eb 100644 (file)
@@ -8,7 +8,7 @@ SERVER_SRCS     = radiusd.c files.c util.c acct.c nas.c log.c valuepair.c \
                  version.c proxy.c exec.c auth.c conffile.c \
                  modules.c modcall.c session.c xlat.c threads.c smux.c \
                  radius_snmp.c client.c request_list.c mainconfig.c \
-                 request_process.c
+                 listen.c request_process.c
 
 SERVER_OBJS    += $(SERVER_SRCS:.c=.o)
 
diff --git a/src/main/listen.c b/src/main/listen.c
new file mode 100644 (file)
index 0000000..ffd27ab
--- /dev/null
@@ -0,0 +1,1182 @@
+/*
+ * socket.c    Handle socket stuff
+ *
+ * Version:    $Id$
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Copyright 2005  The FreeRADIUS server project
+ * Copyright 2005  Alan DeKok <aland@ox.org>
+ */
+
+#include "autoconf.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+
+#include "radiusd.h"
+#include "rad_assert.h"
+#include "conffile.h"
+#include "token.h"
+
+#include <sys/resource.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#ifdef WITH_UDPFROMTO
+#include "udpfromto.h"
+#endif
+
+#include "radius_snmp.h"
+#include "request_list.h"
+
+static time_t start_time = 0;
+static int last_proxy_port = 0;
+
+/*
+ *     FIXME: Delete this crap!
+ */
+extern time_t time_now;
+extern int auth_port;
+
+static uint32_t server_ip = 0; /* INADDR_ANY */
+
+/*
+ *     Process and reply to a server-status request.
+ *     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)
+{
+       char            reply_msg[64];
+       time_t          t;
+       VALUE_PAIR      *vp;
+
+       /*
+        *      Reply with an ACK. We might want to add some more
+        *      interesting reply attributes, such as server uptime.
+        */
+       t = request->timestamp - start_time;
+       sprintf(reply_msg, "FreeRADIUS up %d day%s, %02d:%02d",
+               (int)(t / 86400), (t / 86400) == 1 ? "" : "s",
+               (int)((t / 3600) % 24), (int)(t / 60) % 60);
+       request->reply->code = PW_AUTHENTICATION_ACK;
+
+       vp = pairmake("Reply-Message", reply_msg, T_OP_SET);
+       pairadd(&request->reply->vps, vp); /* don't need to check if !vp */
+
+       return 0;
+}
+
+/*
+ *     Check for dups, etc.  Common to Access-Request &&
+ *     Accounting-Request packets.
+ */
+static int common_checks(rad_listen_t *listener,
+                        RADIUS_PACKET *packet, REQUEST **prequest,
+                        const uint8_t *secret)
+{
+       REQUEST *curreq;
+       static int request_num_counter = 0;
+       char buffer[128];
+
+       /*
+        *      If there is no existing request of id, code, etc.,
+        *      then we can return, and let it be processed.
+        */
+       if ((curreq = rl_find(packet)) == NULL) {
+               /*
+                *      Count the total number of requests, to see if
+                *      there are too many.  If so, return with an
+                *      error.
+                */
+               if (mainconfig.max_requests) {
+                       int request_count = rl_num_requests();
+
+                       /*
+                        *      This is a new request.  Let's see if
+                        *      it makes us go over our configured
+                        *      bounds.
+                        */
+                       if (request_count > mainconfig.max_requests) {
+                               radlog(L_ERR, "Dropping request (%d is too many): "
+                                      "from client %s port %d - ID: %d", request_count,
+                                      client_name(&packet->src_ipaddr),
+                                      packet->src_port, packet->id);
+                               radlog(L_INFO, "WARNING: Please check the radiusd.conf file.\n"
+                                      "\tThe value for 'max_requests' is probably set too low.\n");
+                               return 0;
+                       } /* else there were a small number of requests */
+               } /* else there was no configured limit for requests */
+
+               /*
+                *      FIXME: Add checks for system load.  If the
+                *      system is busy, start dropping requests...
+                *
+                *      We can probably keep some statistics
+                *      ourselves...  if there are more requests
+                *      coming in than we can handle, start dropping
+                *      some.
+                */
+
+       /*
+        *      The current request isn't finished, which
+        *      means that the NAS sent us a new packet, while
+        *      we are still processing the old request.
+        */
+       } else if (!curreq->finished) {
+               /*
+                *      If the authentication vectors are identical,
+                *      then the NAS is re-transmitting it, trying to
+                *      kick us into responding to the request.
+                */
+               if (memcmp(curreq->packet->vector, packet->vector,
+                          sizeof(packet->vector)) == 0) {
+                       RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
+
+                       /*
+                        *      It's not finished because the request
+                        *      was proxied, but there was no reply
+                        *      from the home server.
+                        */
+                       if (curreq->proxy && !curreq->proxy_reply) {
+                               /*
+                                *      We're taking care of sending
+                                *      duplicate proxied packets, so
+                                *      we ignore any duplicate
+                                *      requests from the NAS.
+                                *
+                                *      FIXME: Make it ALWAYS synchronous!
+                                */
+                               if (!mainconfig.proxy_synchronous) {
+                                       RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
+
+                                       DEBUG2("Ignoring duplicate packet from client "
+                                              "%s port %d - ID: %d, due to outstanding proxied request %d.",
+                                              client_name(&packet->src_ipaddr),
+                                              packet->src_port, packet->id,
+                                              curreq->number);
+                                       return 0;
+
+                                       /*
+                                        *      We ARE proxying the request,
+                                        *      and we have NOT received a
+                                        *      proxy reply yet, and we ARE
+                                        *      doing synchronous proxying.
+                                        *
+                                        *      In that case, go kick
+                                        *      the home RADIUS server
+                                        *      again.
+                                        */
+                               } else {
+                                       DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
+                                              inet_ntop(curreq->proxy->dst_ipaddr.af,
+                                                        &curreq->proxy->dst_ipaddr.ipaddr,
+                                                        buffer, sizeof(buffer)),                                              curreq->proxy->dst_port,
+
+                                              curreq->proxy->id);
+                               }
+                               curreq->proxy_next_try = time_now + mainconfig.proxy_retry_delay;
+                               rad_send(curreq->proxy, curreq->packet,
+                                        curreq->proxysecret);
+                               return 0;
+                       } /* else the packet was not proxied */
+
+                       /*
+                        *      Someone's still working on it, so we
+                        *      ignore the duplicate request.
+                        */
+                       radlog(L_ERR, "Discarding duplicate request from "
+                              "client %s port %d - ID: %d due to unfinished request %d",
+                              client_name(&packet->src_ipaddr),
+                              packet->src_port, packet->id,
+                              curreq->number);
+                       return 0;
+               } /* else the authentication vectors were different */
+
+               /*
+                *      The authentication vectors are different, so
+                *      the NAS has given up on us, as we've taken too
+                *      long to process the request.  This is a
+                *      SERIOUS problem!
+                */
+               RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
+
+               radlog(L_ERR, "Dropping conflicting packet from "
+                      "client %s port %d - ID: %d due to unfinished request %d",
+                      client_name(&packet->src_ipaddr),
+                      packet->src_port, packet->id,
+                      curreq->number);
+               return 0;
+               
+               /*
+                *      The old request is finished.  We now check the
+                *      authentication vectors.  If the client has sent us a
+                *      request with identical code && ID, but different
+                *      vector, then they MUST have gotten our response, so we
+                *      can delete the original request, and process the new
+                *      one.
+                *
+                *      If the vectors are the same, then it's a duplicate
+                *      request, and we can send a duplicate reply.
+                */
+       } else if (memcmp(curreq->packet->vector, packet->vector,
+                         sizeof(packet->vector)) == 0) {
+               RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
+
+               /*
+                *      If the packet has been delayed, then silently
+                *      send a response, and clear the delayed flag.
+                *
+                *      Note that this means if the NAS kicks us while
+                *      we're delaying a reject, then the reject may
+                *      be sent sooner than otherwise.
+                *
+                *      This COULD be construed as a bug.  Maybe what
+                *      we want to do is to ignore the duplicate
+                *      packet, and send the reject later.
+                */
+               if (curreq->options & RAD_REQUEST_OPTION_DELAYED_REJECT) {
+                       curreq->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
+                       rad_send(curreq->reply, curreq->packet, curreq->secret);
+                       return 0;
+               }
+
+               /*
+                *      Maybe we've saved a reply packet.  If so,
+                *      re-send it.  Otherwise, just complain.
+                */
+               if (curreq->reply->code != 0) {
+                       DEBUG2("Sending duplicate reply "
+                              "to client %s port %d - ID: %d",
+                              client_name(&packet->src_ipaddr),
+                              packet->src_port, packet->id);
+                       rad_send(curreq->reply, curreq->packet, curreq->secret);
+                       return 0;
+               }
+
+               /*
+                *      Else we never sent a reply to the NAS,
+                *      as we decided somehow we didn't like the request.
+                *
+                *      This shouldn't happen, in general...
+                */
+               DEBUG2("Discarding duplicate request from client %s port %d - ID: %d",
+                      client_name(&packet->src_ipaddr),
+                      packet->src_port, packet->id);
+               return 0;
+       } /* else the vectors were different, so we discard the old request. */
+
+       /*
+        *      'packet' has the same source IP, source port, code,
+        *      and Id as 'curreq', but a different authentication
+        *      vector.  We can therefore delete 'curreq', as we were
+        *      only keeping it around to send out duplicate replies,
+        *      if the first reply got lost in the network.
+        */
+       if (curreq) rl_delete(curreq);
+
+       /*
+        *      A unique per-request counter.
+        */
+       
+       curreq = request_alloc(); /* never fails */
+       curreq->packet = packet;
+       curreq->number = request_num_counter++;
+       strNcpy(curreq->secret, secret,
+               sizeof(curreq->secret));
+       
+       /*
+        *      Remember the request in the list.
+        */
+       rl_add(curreq);
+       
+       /*
+        *      ADD IN "server identifier" from "listen"
+        *      directive!
+        */
+       
+       /*
+        *      The request passes many of our sanity checks.
+        *      From here on in, if anything goes wrong, we
+        *      send a reject message, instead of dropping the
+        *      packet.
+        *
+        *      Build the reply template from the request
+        *      template.
+                */
+       rad_assert(curreq->reply == NULL);
+       if ((curreq->reply = rad_alloc(0)) == NULL) {
+               radlog(L_ERR, "No memory");
+               exit(1);
+       }
+
+       curreq->reply->sockfd = curreq->packet->sockfd;
+       curreq->reply->dst_ipaddr = curreq->packet->src_ipaddr;
+       curreq->reply->src_ipaddr = curreq->packet->dst_ipaddr;
+       curreq->reply->dst_port = curreq->packet->src_port;
+       curreq->reply->src_port = curreq->packet->dst_port;
+       curreq->reply->id = curreq->packet->id;
+       curreq->reply->code = 0; /* UNKNOWN code */
+       memcpy(curreq->reply->vector, curreq->packet->vector,
+              sizeof(curreq->reply->vector));
+       curreq->reply->vps = NULL;
+       curreq->reply->data = NULL;
+       curreq->reply->data_len = 0;
+
+       *prequest = curreq;
+       return 1;
+}
+
+
+/*
+ *     Check if an incoming request is "ok"
+ *
+ *     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)
+{
+       RADIUS_PACKET   *packet;
+       RAD_REQUEST_FUNP fun = NULL;
+       char            buffer[128];
+       RADCLIENT       *cl;
+
+       packet = rad_recv(listener->fd);
+       if (!packet) {
+               radlog(L_ERR, "%s", librad_errstr);
+               return 0;
+       }
+
+       RAD_SNMP_TYPE_INC(listener, total_requests); /* FIXME: auth specific */
+
+       if ((cl = client_find(&packet->src_ipaddr)) == NULL) {
+               RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
+               
+               radlog(L_ERR, "Ignoring request from unknown client %s port %d",
+                      inet_ntop(packet->src_ipaddr.af,
+                                &packet->src_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      packet->src_port);
+               rad_free(&packet);
+               return 0;
+       }
+
+       /*
+        *      Some sanity checks, based on the packet code.
+        */
+       switch(packet->code) {
+       case PW_AUTHENTICATION_REQUEST:
+               fun = rad_authenticate;
+               break;
+               
+       case PW_STATUS_SERVER:
+               if (!mainconfig.status_server) {
+                       DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
+                       rad_free(&packet);
+                       return 0;
+               }
+               fun = rad_status_server;
+               break;
+
+       default:
+               RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
+               
+               radlog(L_ERR, "Invalid packet code %d sent to authentication port from client %s port %d "
+                      "- ID %d : IGNORED", packet->code,
+                      client_name(&packet->src_ipaddr),
+                      packet->src_port, packet->id);
+               rad_free(&packet);
+               return 0;
+               break;
+       } /* switch over packet types */
+       
+       if (!common_checks(listener, packet, prequest, cl->secret)) {
+               rad_free(&packet);
+               return 0;
+       }
+
+       *pfun = fun;
+       return 1;
+}
+
+
+/*
+ *     Receive packets from an accounting socket
+ */
+static int acct_socket_recv(rad_listen_t *listener,
+       RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+       RADIUS_PACKET   *packet;
+       RAD_REQUEST_FUNP fun = NULL;
+       char            buffer[128];
+       RADCLIENT       *cl;
+       
+       packet = rad_recv(listener->fd);
+       if (!packet) {
+               radlog(L_ERR, "%s", librad_errstr);
+               return 0;
+       }
+       
+       RAD_SNMP_TYPE_INC(listener, total_requests); /* FIXME: acct-specific */
+
+       if ((cl = client_find(&packet->src_ipaddr)) == NULL) {
+               RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
+               
+               radlog(L_ERR, "Ignoring request from unknown client %s port %d",
+                      inet_ntop(packet->src_ipaddr.af,
+                                &packet->src_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      packet->src_port);
+               rad_free(&packet);
+               return 0;
+       }
+
+       switch(packet->code) {
+       case PW_ACCOUNTING_REQUEST:
+               fun = rad_accounting;
+               break;
+               
+       default:
+               /*
+                *      FIXME: Update MIB for packet types?
+                */
+               radlog(L_ERR, "Invalid packet code %d sent to a accounting port "
+                      "from client %s port %d - ID %d : IGNORED",
+                      packet->code,
+                      client_name(&packet->src_ipaddr),
+                      packet->src_port, packet->id);
+               rad_free(&packet);
+               return 0;
+       }
+
+       /*
+        *      FIXME: Accounting duplicates should be handled
+        *      differently than authentication duplicates.
+        */
+       if (!common_checks(listener, packet, prequest, cl->secret)) {
+               rad_free(&packet);
+               return 0;
+       }
+
+       *pfun = fun;
+       return 1;
+}
+
+
+/*
+ *     Recieve packets from a proxy socket.
+ */
+static int proxy_socket_recv(rad_listen_t *listener,
+                             RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
+{
+       REALM           *cl;
+       REQUEST         *oldreq;
+       RADIUS_PACKET   *packet;
+       RAD_REQUEST_FUNP fun = NULL;
+       char            buffer[128];
+       
+       packet = rad_recv(listener->fd);
+       if (!packet) {
+               radlog(L_ERR, "%s", librad_errstr);
+               return 0;
+       }
+
+       /*
+        *      Unsupported stuff
+        */
+       if (packet->src_ipaddr.af != AF_INET) {
+               rad_assert("PROXY IPV6 NOT SUPPORTED" == NULL);
+       }
+       
+       /*
+        *      FIXME: Add support for home servers!
+        */
+       if ((cl = realm_findbyaddr(packet->src_ipaddr.ipaddr.ip4addr.s_addr,
+                                  packet->src_port)) == NULL) {
+               radlog(L_ERR, "Ignoring request from unknown home server %s port %d",
+                      inet_ntop(packet->src_ipaddr.af,
+                                &packet->src_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                              packet->src_port);
+               rad_free(&packet);
+               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;
+               
+       case PW_ACCOUNTING_RESPONSE:
+               fun = rad_accounting;
+               break;
+               
+       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;
+       }
+
+       /*
+        *      Find the original request in the request list
+        */
+       oldreq = rl_find_proxy(packet);
+
+       /*
+        *      If we haven't found the original request which was
+        *      sent, to get this reply.  Complain, and discard this
+        *      request, as there's no way for us to send it to a NAS.
+        */
+       if (!oldreq) {
+               radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s port %d - ID %d",
+                      inet_ntop(packet->src_ipaddr.af,
+                                &packet->src_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      packet->src_port, packet->id);
+               rad_free(&packet);
+               return 0;
+       }
+
+       /*
+        *      The proxy reply has arrived too late, as the original
+        *      (old) request has timed out, been rejected, and marked
+        *      as finished.  The client has already received a
+        *      response, so there is nothing that can be done. Delete
+        *      the tardy reply from the home server, and return nothing.
+        */
+       if ((oldreq->reply->code != 0) ||
+           (oldreq->finished)) {
+               radlog(L_ERR, "Reply from home server %s port %d  - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
+                      inet_ntop(packet->src_ipaddr.af,
+                                &packet->src_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      packet->src_port, packet->id,
+                      oldreq->number);
+               rad_free(&packet);
+               return 0;
+       }
+
+       /*
+        *      If there is already a reply, maybe this one is a
+        *      duplicate?
+        */
+       if (oldreq->proxy_reply) {
+               if (memcmp(oldreq->proxy_reply->vector,
+                          packet->vector,
+                          sizeof(oldreq->proxy_reply->vector)) == 0) {
+                       radlog(L_ERR, "Discarding duplicate reply from home server %s port %d  - ID: %d for request %d",
+                              inet_ntop(packet->src_ipaddr.af,
+                                        &packet->src_ipaddr.ipaddr,
+                                        buffer, sizeof(buffer)),
+                              packet->src_port, packet->id,
+                              oldreq->number);
+               } else {
+                       /*
+                        *      ? The home server gave us a new proxy
+                        *      reply, which doesn't match the old
+                        *      one.  Delete it.
+                        */
+                       DEBUG2("Ignoring conflicting proxy reply");
+               }
+
+               /*
+                *      We've already received a reply, so
+                *      we discard this one, as we don't want
+                *      to do duplicate work.
+                */
+               rad_free(&packet);
+               return 0;
+       } /* else there wasn't a proxy reply yet, so we can process it */
+
+       /*
+        *       Refresh the old request, and update it with the proxy
+        *       reply.
+        *
+        *      ? Can we delete the proxy request here?  * Is there
+        *      any more need for it?
+        *
+        *      FIXME: we probably shouldn't be updating the time
+        *      stamp here.
+        */
+       oldreq->timestamp = time_now;
+       oldreq->proxy_reply = packet;
+
+       /*
+        *      FIXME: we should really verify the digest here,
+        *      before marking this packet as a valid response.
+        *
+        *      This is a security problem, I think...
+        */
+
+       /*
+        *      Now that we've verified the packet IS actually from
+        *      that home server, and not forged, we can go mark the
+        *      entries for this home server as active.
+        *
+        *      If we had done this check in the 'find realm by IP address'
+        *      function, then an attacker could force us to use a home
+        *      server which was inactive, by forging reply packets
+        *      which didn't match any request.  We would think that
+        *      the reply meant the home server was active, would
+        *      re-activate the realms, and THEN bounce the packet
+        *      as garbage.
+        */
+       for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
+               if (oldreq->proxy_reply->src_ipaddr.af != cl->ipaddr.af) continue;
+               if (cl->ipaddr.af != AF_INET) continue; /* FIXME */
+
+               if (oldreq->proxy_reply->src_ipaddr.ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) {
+                       if (oldreq->proxy_reply->src_port == cl->auth_port) {
+                               cl->active = TRUE;
+                               cl->last_reply = oldreq->timestamp;
+                       } else if (oldreq->proxy_reply->src_port == cl->acct_port) {
+                               cl->acct_active = TRUE;
+                               cl->last_reply = oldreq->timestamp;
+                       }
+               }
+       }
+
+       rad_assert(fun != NULL);
+       *pfun = fun;
+       *prequest = oldreq;
+
+       return 1;
+}
+
+/*
+ *     Free a linked list of listeners;
+ */
+void listen_free(rad_listen_t **head)
+{
+       rad_listen_t *list;
+
+       if (!head || !*head) return;
+
+       list = *head;
+       while (list) {
+               rad_listen_t *next = list->next;
+               
+               /*
+                *      The code below may have eaten the FD.
+                */
+               if (list->fd >= 0) close(list->fd);
+               free(list);
+               
+               list = next;
+       }
+
+       *head = NULL;
+}
+
+
+/*
+ *     Binds a listener to a socket.
+ */
+static int listen_bind(rad_listen_t *this)
+{
+       struct sockaddr salocal;
+       socklen_t       salen;
+       rad_listen_t    **last;
+
+       switch (this->type) {
+       case RAD_LISTEN_AUTH:
+               this->recv = auth_socket_recv;
+               break;
+
+       case RAD_LISTEN_ACCT:
+               this->recv = acct_socket_recv;
+               break;
+
+       case RAD_LISTEN_PROXY:
+               this->recv = proxy_socket_recv;
+               break;
+
+       default:
+               rad_assert(0 == 1);
+               return -1;
+       }
+
+       /*
+        *      If the port is zero, then it means the appropriate
+        *      thing from /etc/services.
+        */
+       if (this->port == 0) {
+               struct servent  *svp;
+
+               switch (this->type) {
+               case RAD_LISTEN_AUTH:
+                       svp = getservbyname ("radius", "udp");
+                       if (svp != NULL) {
+                               this->port = ntohs(svp->s_port);
+                       } else {
+                               this->port = PW_AUTH_UDP_PORT;
+                       }
+                       break;
+
+               case RAD_LISTEN_ACCT:
+                       svp = getservbyname ("radacct", "udp");
+                       if (svp != NULL) {
+                               this->port = ntohs(svp->s_port);
+                       } else {
+                               this->port = PW_ACCT_UDP_PORT;
+                       }
+                       break;
+
+               default:
+                       radlog(L_ERR|L_CONS, "ERROR: Non-fatal internal sanity check failed in bind.");
+                       return -1;
+               }
+       }
+
+       /*
+        *      Find it in the old list, AFTER updating the port.  If
+        *      it's there, use that, rather than creating a new
+        *      socket.  This allows HUP's to re-use the old sockets,
+        *      which means that packets waiting in the socket queue
+        *      don't get lost.  */
+       for (last = &mainconfig.listen;
+            *last != NULL;
+            last = &((*last)->next)) {
+               if ((this->type == (*last)->type) &&
+                   (this->port == (*last)->port) &&
+                   (this->ipaddr.af == (*last)->ipaddr.af) &&
+                   (memcmp(&this->ipaddr.ipaddr,
+                           &(*last)->ipaddr.ipaddr,
+                           ((this->ipaddr.af == AF_INET) ?
+                            sizeof(this->ipaddr.ipaddr.ip4addr) :
+                            sizeof(this->ipaddr.ipaddr.ip6addr))))) {
+                       this->fd = (*last)->fd;
+                       (*last)->fd = -1;
+                       return 0;
+               }
+       }
+
+       /*
+        *      Create the socket.
+        */
+       this->fd = socket(this->ipaddr.af, SOCK_DGRAM, 0);
+       if (this->fd < 0) {
+               radlog(L_ERR|L_CONS, "ERROR: Failed to open socket: %s",
+                      strerror(errno));
+               return -1;
+       }
+       
+
+#ifdef WITH_UDPFROMTO
+       /*
+        *      Initialize udpfromto for all sockets.
+        */
+       if (udpfromto_init(this->fd) != 0) {
+               radlog(L_ERR|L_CONS, "ERROR: udpfromto init failed.");
+       }
+#endif
+
+       if (this->ipaddr.af == AF_INET) {
+               struct sockaddr_in *sa;
+
+               sa = (struct sockaddr_in *) &salocal;
+               memset(sa, 0, sizeof(salocal));
+               sa->sin_family = AF_INET;
+               sa->sin_addr = this->ipaddr.ipaddr.ip4addr;
+               sa->sin_port = htons(this->port);
+               salen = sizeof(*sa);
+
+#ifdef HAVE_STRUCT_SOCKADDR_IN6
+       } else if (this->ipaddr.af == AF_INET6) {
+               struct sockaddr_in6 *sa;
+
+               sa = (struct sockaddr_in6 *) &salocal;
+               memset(sa, 0, sizeof(salocal));
+               sa->sin6_family = AF_INET6;
+               sa->sin6_addr = this->ipaddr.ipaddr.ip6addr;
+               sa->sin6_port = htons(this->port);
+               salen = sizeof(*sa);
+#endif
+       } else {
+               radlog(L_ERR|L_CONS, "ERROR: Unsupported protocol family %d",
+                      this->ipaddr.af);
+               close(this->fd);
+               this->fd = -1;
+               return -1;
+       }
+
+       if (bind(this->fd, &salocal, salen) < 0) {
+               char buffer[128];
+
+               radlog(L_ERR|L_CONS, "ERROR: Bind to %s port %d failed: %s",
+                      inet_ntop(this->ipaddr.af, &this->ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      this->port, strerror(errno));
+                                
+               close(this->fd);
+               this->fd = -1;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/*
+ *     Externally visible function for creating a new proxy LISTENER.
+ *
+ *     For now, don't take ipaddr or port.
+ */
+int proxy_new_listener(void)
+{
+       int port;
+       rad_listen_t *this;
+
+       this = rad_malloc(sizeof(*this));
+
+       memset(this, 0, sizeof(*this));
+
+       this->ipaddr.af = AF_INET;
+       this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
+       this->type = RAD_LISTEN_PROXY;
+
+       /*
+        *      Proxying was not previously defined: die.
+        */
+       if (last_proxy_port == 0) return -1;
+
+       /*
+        *      Keep going until we find an unused port.
+        */
+       for (port = last_proxy_port + 1; port < 64000; port++) {
+               this->port = port;
+               if (listen_bind(this) == 0) {
+                       rad_listen_t **last;
+
+                       last_proxy_port = port;
+
+                       /*
+                        *      Add the new listener to the list of
+                        *      listeners.
+                        */
+                       for (last = &mainconfig.listen;
+                            *last != NULL;
+                            last = &((*last)->next)) {
+                               /* do nothing */
+                       }
+
+                       *last = this;
+                       return this->fd;
+               }
+       }
+
+       return -1;
+}
+
+
+/*
+ *     Hack the OLD way of listening on a socket.
+ */
+static int old_listen_init(rad_listen_t **head)
+{
+       CONF_PAIR       *cp;
+       rad_listen_t    *this, **last;
+
+       /*
+        *      No IP from the command-line, look for bind_address.
+        */
+       if (mainconfig.myip.af == AF_UNSPEC) {
+               cp = cf_pair_find(mainconfig.config, "bind_address");
+               if (!cp) return 0;
+       }
+
+       last = head;
+
+       this = rad_malloc(sizeof(*this));
+       memset(this, 0, sizeof(*this));
+
+       /*
+        *      Create the authentication socket.
+        */
+       this->ipaddr.af = AF_INET;
+               this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
+       this->type = RAD_LISTEN_AUTH;
+       this->port = auth_port;
+
+       if (listen_bind(this) < 0) {
+               radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the authentication port %d", this->port);
+               free(this);
+               return -1;
+       }
+       auth_port = this->port; /* may have been updated in listen_bind */
+       *last = this;
+       last = &(this->next);
+
+       /*
+        *  Open Accounting Socket.
+        *
+        *  If we haven't already gotten acct_port from /etc/services,
+        *  then make it auth_port + 1.
+        */
+       this = rad_malloc(sizeof(*this));
+       memset(this, 0, sizeof(*this));
+
+       /*
+        *      Create the accounting socket.
+        *
+        *      The accounting port is always the authentication port + 1
+        */
+       this->ipaddr.af = AF_INET;
+               this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
+       this->type = RAD_LISTEN_ACCT;
+       this->port = auth_port + 1;
+
+       if (listen_bind(this) < 0) {
+               radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the accounting port %d", this->port);
+               free(this);
+               return -1;
+       }
+       *last = this;
+
+       return 0;
+}
+
+/*
+ *     FIXME: Get rid of this...
+ */
+static const CONF_PARSER bind_config[] = {
+       { "bind_address", PW_TYPE_IPADDR, 0, &server_ip, "*" },
+       { NULL, -1, 0, NULL, NULL }
+};
+
+
+static const LRAD_NAME_NUMBER listen_compare[] = {
+       { "auth",       RAD_LISTEN_AUTH },
+       { "acct",       RAD_LISTEN_ACCT },
+       { NULL, 0 },
+};
+
+
+/*
+ *     Generate a list of listeners.  Takes an input list of
+ *     listeners, too, so we don't close sockets with waiting packets.
+ */
+int listen_init(const char *filename, rad_listen_t **head)
+{
+       CONF_SECTION    *cs;
+       rad_listen_t    **last;
+       char            buffer[128];
+       rad_listen_t    *this;
+
+       if (start_time != 0) start_time = time(NULL);
+
+       /*
+        *      Read bind_address into server_ip, but only if an IP
+        *      isn't specified on the command-line
+        */
+       if (mainconfig.myip.af == AF_UNSPEC) {
+               cf_section_parse(mainconfig.config, NULL, bind_config);
+       } else if (mainconfig.myip.af == AF_INET) {
+               server_ip = mainconfig.myip.ipaddr.ip4addr.s_addr; /* over-ride server_ip */
+       } else if (mainconfig.myip.af == AF_INET6) {
+               rad_assert(0 == 1); /* IPv6 unsupported */
+       } else rad_assert(0 == 1);
+
+       if (old_listen_init(head) < 0) {
+               exit(1);
+       }
+
+       /*
+        *      Add to the end of the list.
+        */
+       for (last = head; *last != NULL; last = &((*last)->next)) {
+               /* do nothing */
+       }
+
+       /*
+        *      Walk through the "listen" directives, but ONLY if there
+        *      was no address specified on the command-line.
+        */
+       if (mainconfig.myip.af != AF_UNSPEC) goto do_proxy;
+
+       for (cs = cf_subsection_find_next(mainconfig.config,
+                                         NULL, "listen");
+            cs != NULL;
+            cs = cf_subsection_find_next(mainconfig.config,
+                                         cs, "listen")) {
+               int             rcode, type;
+               char            *listen_type;
+               int             listen_port;
+               int             lineno = cf_section_lineno(cs);
+               lrad_ipaddr_t   ipaddr;
+
+               listen_port = 0;
+               listen_type = NULL;
+               
+               /*
+                *      Try IPv4 first
+                */
+               ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
+               rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
+                                     &ipaddr.ipaddr.ip4addr, NULL);
+               if (rcode < 0) return -1;
+
+               if (rcode == 0) { /* successfully parsed IPv4 */
+                       ipaddr.af = AF_INET;
+
+               } else {        /* maybe IPv6? */
+                       rcode = cf_item_parse(cs, "ipv6addr", PW_TYPE_IPV6ADDR,
+                                             &ipaddr.ipaddr.ip6addr, NULL);
+                       if (rcode < 0) return -1;
+
+                       if (rcode == 1) {
+                               radlog(L_ERR, "%s[%d]: No address specified in listen section",
+                                      filename, lineno);
+                               return -1;
+                       }
+                       ipaddr.af = AF_INET6;
+               }
+
+
+               rcode = cf_item_parse(cs, "port", PW_TYPE_INTEGER,
+                                     &listen_port, "0");
+               if (rcode < 0) return -1;
+
+               rcode = cf_item_parse(cs, "type", PW_TYPE_STRING_PTR,
+                                     &listen_type, "");
+               if (rcode < 0) return -1;
+               if (rcode == 1) {
+                       free(listen_type);
+                       radlog(L_ERR, "%s[%d]: No type specified in listen section",
+                              filename, lineno);
+                       return -1;
+               }
+
+               type = lrad_str2int(listen_compare, listen_type,
+                                   RAD_LISTEN_NONE);
+               if (type == RAD_LISTEN_NONE) {
+                       radlog(L_CONS|L_ERR, "%s[%d]: Invalid type in listen section.",
+                              filename, lineno);
+                       return -1;
+               }
+
+               this = rad_malloc(sizeof(*this));
+               this->ipaddr = ipaddr;
+               this->type = type;
+               this->port = listen_port;
+
+               /*
+                *      And bind it to the port.
+                */
+               if (listen_bind(this) < 0) {
+                       radlog(L_CONS|L_ERR, "%s[%d]: Error binding to port for %s port %d",
+                              filename, cf_section_lineno(cs),
+                              ip_ntoh(&this->ipaddr, buffer, sizeof(buffer)),
+                              this->port);
+                       free(this);
+                       return -1;
+               }
+
+               *last = this;
+               last = &(this->next);           
+       }
+
+       /*
+        *      If we're proxying requests, open the proxy FD.
+        *      Otherwise, don't do anything.
+        */
+ do_proxy:
+       if (mainconfig.proxy_requests == TRUE) {
+               int             port = -1;
+               rad_listen_t    *auth;
+
+               /*
+                *      Find the first authentication port,
+                *      and use it
+                */
+               for (auth = *head; auth != NULL; auth = auth->next) {
+                       if (auth->type == RAD_LISTEN_AUTH) {
+                               port = auth->port + 2;
+                               break;
+                       }
+               }
+
+               /*
+                *      Not found, pick an accounting port.
+                */
+               if (port < 0) for (auth = *head; auth != NULL; auth = auth->next) {
+                       if (auth->type == RAD_LISTEN_ACCT) {
+                               port = auth->port + 1;
+                               break;
+                       }
+               }
+
+               /*
+                *      Still no port.  Don't do anything.
+                */
+               if (port < 0) {
+                       return 0;
+               }
+
+               this = rad_malloc(sizeof(*this));
+               memset(this, 0, sizeof(*this));
+               
+               /*
+                *      Create the proxy socket.
+                */
+               this->ipaddr.af = AF_INET;
+               this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
+               this->type = RAD_LISTEN_PROXY;
+
+               /*
+                *      Try to find a proxy port (value doesn't matter)
+                */
+               for (this->port = port;
+                    this->port < 64000;
+                    this->port++) {
+                       if (listen_bind(this) == 0) {
+                               last_proxy_port = this->port;
+                               *last = this;
+                               return 0;
+                       }
+               }
+
+               radlog(L_ERR|L_CONS, "Failed to open socket for proxying");
+               free(this);
+               return -1;
+       }
+
+       return 0;
+}
+
index f4f3c26..0b0c6bd 100644 (file)
 #      include <syslog.h>
 #endif
 
-#ifdef WITH_UDPFROMTO
-#include "udpfromto.h"
-#endif
-
-
 struct main_config_t mainconfig;
 
 /*
@@ -72,8 +67,7 @@ static gid_t server_gid;
  */
 static const char *localstatedir = NULL;
 static const char *prefix = NULL;
-static int auth_port = 0;
-static uint32_t server_ip = 0; /* INADDR_ANY */
+int auth_port = 0;
 static char *syslog_facility = NULL;
 static const LRAD_NAME_NUMBER str2fac[] = {
 #ifdef LOG_KERN
@@ -233,15 +227,6 @@ static const CONF_PARSER server_config[] = {
 };
 
 
-/*
- *     Allowed to over-ride command-line
- */
-static const CONF_PARSER bind_config[] = {
-       { "bind_address", PW_TYPE_IPADDR, 0, &server_ip, "*" },
-       { NULL, -1, 0, NULL, NULL }
-};
-
-
 #define MAX_ARGV (256)
 /*
  *     Xlat for %{config:section.subsection.attribute}
@@ -946,473 +931,6 @@ static RADCLIENT *generate_clients(const char *filename, CONF_SECTION *section)
 }
 
 
-static const LRAD_NAME_NUMBER listen_compare[] = {
-       { "auth",       RAD_LISTEN_AUTH },
-       { "acct",       RAD_LISTEN_ACCT },
-       { NULL, 0 },
-};
-
-
-/*
- *     Free a linked list of listeners;
- */
-static void listen_free(rad_listen_t *list)
-{
-       while (list) {
-               rad_listen_t *next = list->next;
-               
-               /*
-                *      The code below may have eaten the FD.
-                */
-               if (list->fd >= 0) close(list->fd);
-               free(list);
-               
-               list = next;
-       }
-}
-
-/*
- *     Binds a listener to a socket.
- */
-static int listen_bind(rad_listen_t *this)
-{
-       struct sockaddr salocal;
-       socklen_t       salen;
-       rad_listen_t    **last;
-
-       /*
-        *      If the port is zero, then it means the appropriate
-        *      thing from /etc/services.
-        */
-       if (this->port == 0) {
-               struct servent  *svp;
-
-               switch (this->type) {
-               case RAD_LISTEN_AUTH:
-                       svp = getservbyname ("radius", "udp");
-                       if (svp != NULL) {
-                               this->port = ntohs(svp->s_port);
-                       } else {
-                               this->port = PW_AUTH_UDP_PORT;
-                       }
-                       break;
-
-               case RAD_LISTEN_ACCT:
-                       svp = getservbyname ("radacct", "udp");
-                       if (svp != NULL) {
-                               this->port = ntohs(svp->s_port);
-                       } else {
-                               this->port = PW_ACCT_UDP_PORT;
-                       }
-                       break;
-
-               default:
-                       radlog(L_ERR|L_CONS, "ERROR: Non-fatal internal sanity check failed in bind.");
-                       return -1;
-               }
-       }
-
-       /*
-        *      Find it in the old list, AFTER updating the port.  If
-        *      it's there, use that, rather than creating a new
-        *      socket.  This allows HUP's to re-use the old sockets,
-        *      which means that packets waiting in the socket queue
-        *      don't get lost.  */
-       for (last = &mainconfig.listen;
-            *last != NULL;
-            last = &((*last)->next)) {
-               if ((this->type == (*last)->type) &&
-                   (this->port == (*last)->port) &&
-                   (this->ipaddr.af == (*last)->ipaddr.af) &&
-                   (memcmp(&this->ipaddr.ipaddr,
-                           &(*last)->ipaddr.ipaddr,
-                           ((this->ipaddr.af == AF_INET) ?
-                            sizeof(this->ipaddr.ipaddr.ip4addr) :
-                            sizeof(this->ipaddr.ipaddr.ip6addr))))) {
-                       this->fd = (*last)->fd;
-                       (*last)->fd = -1;
-                       return 0;
-               }
-       }
-
-       /*
-        *      Create the socket.
-        */
-       this->fd = socket(this->ipaddr.af, SOCK_DGRAM, 0);
-       if (this->fd < 0) {
-               radlog(L_ERR|L_CONS, "ERROR: Failed to open socket: %s",
-                      strerror(errno));
-               return -1;
-       }
-       
-
-#ifdef WITH_UDPFROMTO
-       /*
-        *      Initialize udpfromto for all sockets.
-        */
-       if (udpfromto_init(this->fd) != 0) {
-               radlog(L_ERR|L_CONS, "ERROR: udpfromto init failed.");
-       }
-#endif
-
-       if (this->ipaddr.af == AF_INET) {
-               struct sockaddr_in *sa;
-
-               sa = (struct sockaddr_in *) &salocal;
-               memset(sa, 0, sizeof(salocal));
-               sa->sin_family = AF_INET;
-               sa->sin_addr = this->ipaddr.ipaddr.ip4addr;
-               sa->sin_port = htons(this->port);
-               salen = sizeof(*sa);
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       } else if (this->ipaddr.af == AF_INET6) {
-               struct sockaddr_in6 *sa;
-
-               sa = (struct sockaddr_in6 *) &salocal;
-               memset(sa, 0, sizeof(salocal));
-               sa->sin6_family = AF_INET6;
-               sa->sin6_addr = this->ipaddr.ipaddr.ip6addr;
-               sa->sin6_port = htons(this->port);
-               salen = sizeof(*sa);
-#endif
-       } else {
-               radlog(L_ERR|L_CONS, "ERROR: Unsupported protocol family %d",
-                      this->ipaddr.af);
-               close(this->fd);
-               this->fd = -1;
-               return -1;
-       }
-
-       if (bind(this->fd, &salocal, salen) < 0) {
-               char buffer[128];
-
-               radlog(L_ERR|L_CONS, "ERROR: Bind to %s port %d failed: %s",
-                      inet_ntop(this->ipaddr.af, &this->ipaddr.ipaddr,
-                                buffer, sizeof(buffer)),
-                      this->port, strerror(errno));
-                                
-               close(this->fd);
-               this->fd = -1;
-               return -1;
-       }
-
-       return 0;
-}
-
-
-static int last_proxy_port = 0;
-
-/*
- *     Externally visible function for creating a new proxy LISTENER.
- *
- *     For now, don't take ipaddr or port.
- */
-int proxy_new_listener(void)
-{
-       int port;
-       rad_listen_t *this;
-
-       this = rad_malloc(sizeof(*this));
-
-       memset(this, 0, sizeof(*this));
-
-       this->ipaddr.af = AF_INET;
-       this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
-       this->type = RAD_LISTEN_PROXY;
-
-       /*
-        *      Proxying was not previously defined: die.
-        */
-       if (last_proxy_port == 0) return -1;
-
-       /*
-        *      Keep going until we find an unused port.
-        */
-       for (port = last_proxy_port + 1; port < 64000; port++) {
-               this->port = port;
-               if (listen_bind(this) == 0) {
-                       rad_listen_t **last;
-
-                       last_proxy_port = port;
-
-                       /*
-                        *      Add the new listener to the list of
-                        *      listeners.
-                        */
-                       for (last = &mainconfig.listen;
-                            *last != NULL;
-                            last = &((*last)->next)) {
-                               /* do nothing */
-                       }
-
-                       *last = this;
-                       return this->fd;
-               }
-       }
-
-       return -1;
-}
-
-
-/*
- *     Code for handling listening on multiple ports.
- */
-static const char *listen_type = NULL;
-static int listen_port;
-
-static const CONF_PARSER listen_config[] = {
-       { "port", PW_TYPE_INTEGER,
-         0, &listen_port, "0" },
-
-       { "type", PW_TYPE_STRING_PTR,
-         0, &listen_type, "" },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
-};
-
-
-/*
- *     Generate a list of listeners.  Takes an input list of
- *     listeners, too, so we don't close sockets with waiting packets.
- */
-static int listen_init(const char *filename, rad_listen_t **head)
-{
-       CONF_SECTION    *cs;
-       rad_listen_t    **last;
-       char            buffer[128];
-       rad_listen_t    *this;
-
-       /*
-        *      Add to the end of the list.
-        */
-       for (last = head; *last != NULL; last = &((*last)->next)) {
-               /* do nothing */
-       }
-
-       /*
-        *      Walk through the "listen" directives, but ONLY if there
-        *      was no address specified on the command-line.
-        */
-       if (mainconfig.myip.af != AF_UNSPEC) goto do_proxy;
-
-       for (cs = cf_subsection_find_next(mainconfig.config,
-                                         NULL, "listen");
-            cs != NULL;
-            cs = cf_subsection_find_next(mainconfig.config,
-                                         cs, "listen")) {
-               int rcode, type;
-               int lineno = cf_section_lineno(cs);
-               lrad_ipaddr_t ipaddr;
-
-               listen_port = 0;
-               listen_type = NULL;
-               
-               /*
-                *      Try IPv4 first
-                */
-               ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-               rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
-                                     &ipaddr.ipaddr.ip4addr, NULL);
-               if (rcode < 0) return -1;
-
-               if (rcode == 0) { /* successfully parsed IPv4 */
-                       ipaddr.af = AF_INET;
-
-               } else {        /* maybe IPv6? */
-                       rcode = cf_item_parse(cs, "ipv6addr", PW_TYPE_IPV6ADDR,
-                                             &ipaddr.ipaddr.ip6addr, NULL);
-                       if (rcode < 0) return -1;
-
-                       if (rcode == 1) {
-                               radlog(L_ERR, "%s[%d]: No address specified in listen section",
-                                      filename, lineno);
-                               return -1;
-                       }
-                       ipaddr.af = AF_INET6;
-               }
-
-               /*
-                *      Parse the configuration section
-                */
-               if (cf_section_parse(cs, NULL, listen_config) < 0) {
-                       radlog(L_CONS|L_ERR, "%s[%d]: Error parsing listen section.",
-                              filename, lineno);
-                       return -1;
-               }
-
-               type = RAD_LISTEN_NONE;
-               if (listen_type) {
-                       type = lrad_str2int(listen_compare,
-                                           listen_type, 0);
-                       free(listen_type);
-                       listen_type = NULL;
-               }
-
-               if (type == RAD_LISTEN_NONE) {
-                       radlog(L_CONS|L_ERR, "%s[%d]: Invalid type in listen section.",
-                              filename, lineno);
-                       return -1;
-               }
-
-               this = rad_malloc(sizeof(*this));
-               this->ipaddr = ipaddr;
-               this->type = type;
-               this->port = listen_port;
-
-               /*
-                *      And bind it to the port.
-                */
-               if (listen_bind(this) < 0) {
-                       radlog(L_CONS|L_ERR, "%s[%d]: Error binding to port for %s port %d",
-                              filename, cf_section_lineno(cs),
-                              ip_ntoh(&this->ipaddr, buffer, sizeof(buffer)),
-                              this->port);
-                       free(this);
-                       return -1;
-               }
-
-               *last = this;
-               last = &(this->next);           
-       }
-
-       /*
-        *      If we're proxying requests, open the proxy FD.
-        *      Otherwise, don't do anything.
-        */
- do_proxy:
-       if (mainconfig.proxy_requests == TRUE) {
-               int             port = -1;
-               rad_listen_t    *auth;
-
-               /*
-                *      Find the first authentication port,
-                *      and use it
-                */
-               for (auth = *head; auth != NULL; auth = auth->next) {
-                       if (auth->type == RAD_LISTEN_AUTH) {
-                               port = auth->port + 2;
-                               break;
-                       }
-               }
-
-               /*
-                *      Not found, pick an accounting port.
-                */
-               if (port < 0) for (auth = *head; auth != NULL; auth = auth->next) {
-                       if (auth->type == RAD_LISTEN_ACCT) {
-                               port = auth->port + 1;
-                               break;
-                       }
-               }
-
-               /*
-                *      Still no port.  Don't do anything.
-                */
-               if (port < 0) {
-                       return 0;
-               }
-
-               this = rad_malloc(sizeof(*this));
-               memset(this, 0, sizeof(*this));
-               
-               /*
-                *      Create the proxy socket.
-                */
-               this->ipaddr.af = AF_INET;
-               this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
-               this->type = RAD_LISTEN_PROXY;
-
-               /*
-                *      Try to find a proxy port (value doesn't matter)
-                */
-               for (this->port = port;
-                    this->port < 64000;
-                    this->port++) {
-                       if (listen_bind(this) == 0) {
-                               last_proxy_port = this->port;
-                               *last = this;
-                               return 0;
-                       }
-               }
-
-               radlog(L_ERR|L_CONS, "Failed to open socket for proxying");
-               free(this);
-               return -1;
-       }
-
-       return 0;
-}
-
-
-/*
- *     Hack the OLD way of listening on a socket.
- */
-static int old_listen_init(rad_listen_t **head)
-{
-       CONF_PAIR       *cp;
-       rad_listen_t    *this, **last;
-
-       /*
-        *      No IP from the command-line, look for bind_address.
-        */
-       if (mainconfig.myip.af == AF_UNSPEC) {
-               cp = cf_pair_find(mainconfig.config, "bind_address");
-               if (!cp) return 0;
-       }
-
-       last = head;
-
-       this = rad_malloc(sizeof(*this));
-       memset(this, 0, sizeof(*this));
-
-       /*
-        *      Create the authentication socket.
-        */
-       this->ipaddr.af = AF_INET;
-               this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
-       this->type = RAD_LISTEN_AUTH;
-       this->port = auth_port;
-
-       if (listen_bind(this) < 0) {
-               radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the authentication port %d", this->port);
-               free(this);
-               return -1;
-       }
-       auth_port = this->port; /* may have been updated in listen_bind */
-       *last = this;
-       last = &(this->next);
-
-       /*
-        *  Open Accounting Socket.
-        *
-        *  If we haven't already gotten acct_port from /etc/services,
-        *  then make it auth_port + 1.
-        */
-       this = rad_malloc(sizeof(*this));
-       memset(this, 0, sizeof(*this));
-
-       /*
-        *      Create the accounting socket.
-        *
-        *      The accounting port is always the authentication port + 1
-        */
-       this->ipaddr.af = AF_INET;
-               this->ipaddr.ipaddr.ip4addr.s_addr = server_ip;
-       this->type = RAD_LISTEN_ACCT;
-       this->port = auth_port + 1;
-
-       if (listen_bind(this) < 0) {
-               radlog(L_CONS|L_ERR, "There appears to be another RADIUS server running on the accounting port %d", this->port);
-               free(this);
-               return -1;
-       }
-       *last = this;
-
-       return 0;
-}
-
-
 #ifndef RADIUS_CONFIG
 #define RADIUS_CONFIG "radiusd.conf"
 #endif
@@ -1435,18 +953,6 @@ CONF_SECTION *read_radius_conf_file(void)
        cf_section_parse(cs, NULL, server_config);
 
        /*
-        *      Read bind_address into server_ip, but only if an IP
-        *      isn't specified on the command-line
-        */
-       if (mainconfig.myip.af == AF_UNSPEC) {
-               cf_section_parse(cs, NULL, bind_config);
-       } else if (mainconfig.myip.af == AF_INET) {
-               server_ip = mainconfig.myip.ipaddr.ip4addr.s_addr; /* over-ride server_ip */
-       } else if (mainconfig.myip.af == AF_INET6) {
-               rad_assert(0 == 1); /* IPv6 unsupported */
-       } else rad_assert(0 == 1);
-
-       /*
         *      If the port is specified on the command-line,
         *      it over-rides the configuration file.
         */
@@ -1685,9 +1191,6 @@ int read_mainconfig(int reload)
         *      Initialize the old "bind_address" and "port", first.
         */
        listener = NULL;
-       if (old_listen_init(&listener) < 0) {
-               exit(1);
-       }
 
        /*
         *      Read the list of listeners.
@@ -1720,6 +1223,7 @@ int free_mainconfig(void)
        cf_section_free(&mainconfig.config);
        realm_free(mainconfig.realms);
        clients_free(mainconfig.clients);
+       listen_free(&mainconfig.listen);
 
        return 0;
 }
index 6f70c44..5fb788f 100644 (file)
@@ -87,13 +87,12 @@ int log_auth_detail = FALSE;
 int need_reload = FALSE;
 const char *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
 
-static time_t time_now;
+time_t time_now;
 static pid_t radius_pid;
 
 /*
  *  Configuration items.
  */
-static time_t start_time = 0;
 static int do_exit = 0;
 
 /*
@@ -104,575 +103,12 @@ static void usage(int);
 static void sig_fatal (int);
 static void sig_hup (int);
 
-static int rad_status_server(REQUEST *request);
-
-
-/*
- *     Check if an incoming request is "ok"
- *
- *     It takes packets, not requests.  It sees if the packet looks
- *     OK.  If so, it does a number of sanity checks on it.
-  */
-static RAD_REQUEST_FUNP packet_ok(RADIUS_PACKET *packet,
-                                 rad_listen_t *listener)
-{
-       REQUEST         *curreq;
-       RAD_REQUEST_FUNP fun = NULL;
-
-       /*
-        *      Some sanity checks, based on the packet code.
-        */
-       switch(packet->code) {
-               case PW_AUTHENTICATION_REQUEST:
-                       /*
-                        *      Check for requests sent to the wrong
-                        *      port, and ignore them, if so.
-                        */
-                       if (listener->type != RAD_LISTEN_AUTH) {
-                               RAD_SNMP_INC(rad_snmp.auth.total_packets_dropped);
-                               radlog(L_ERR, "Authentication-Request sent to a non-authentication port from "
-                                      "client %s port %d - ID %d : IGNORED",
-                                      client_name(&packet->src_ipaddr),
-                                      packet->src_port, packet->id);
-                               return NULL;
-                       }
-                       fun = rad_authenticate;
-                       break;
-
-               case PW_ACCOUNTING_REQUEST:
-                       /*
-                        *      Check for requests sent to the wrong
-                        *      port, and ignore them, if so.
-                        */
-                       if (listener->type != RAD_LISTEN_ACCT) {
-                               RAD_SNMP_INC(rad_snmp.acct.total_packets_dropped);
-                               radlog(L_ERR, "Accounting-Request packet sent to a non-accounting port from "
-                                      "client %s port %d - ID %d : IGNORED",
-                                      client_name(&packet->src_ipaddr),
-                                      packet->src_port, packet->id);
-                               return NULL;
-                       }
-                       fun = rad_accounting;
-                       break;
-
-               case PW_AUTHENTICATION_ACK:
-               case PW_ACCESS_CHALLENGE:
-               case PW_AUTHENTICATION_REJECT:
-                       /*
-                        *      Replies NOT sent to the proxy port get
-                        *      an error message logged, and the
-                        *      packet is dropped.
-                        */
-                       if (listener->type != RAD_LISTEN_PROXY) {
-                               RAD_SNMP_INC(rad_snmp.auth.total_packets_dropped);
-                               radlog(L_ERR, "Authentication reply packet code %d sent to a non-proxy reply port from "
-                                      "client %s port %d - ID %d : IGNORED",
-                                      packet->code,
-                                      client_name(&packet->src_ipaddr),
-                                      packet->src_port, packet->id);
-                               return NULL;
-                       }
-                       fun = rad_authenticate;
-                       break;
-
-               case PW_ACCOUNTING_RESPONSE:
-                       /*
-                        *      Replies NOT sent to the proxy port get
-                        *      an error message logged, and the
-                        *      packet is dropped.
-                        */
-                       if (listener->type != RAD_LISTEN_PROXY) {
-                               RAD_SNMP_INC(rad_snmp.acct.total_packets_dropped);
-                               radlog(L_ERR, "Accounting reply packet code %d sent to a non-proxy reply port from "
-                                      "client %s port %d - ID %d : IGNORED",
-                                      packet->code,
-                                      client_name(&packet->src_ipaddr),
-                                      packet->src_port, packet->id);
-                               return 0;
-                       }
-                       fun = rad_accounting;
-                       break;
-
-               case PW_STATUS_SERVER:
-                       if (!mainconfig.status_server) {
-                               DEBUG("WARNING: Ignoring Status-Server request due to security configuration");
-                               return NULL;
-                       }
-                       fun = rad_status_server;
-                       break;
-
-               case PW_PASSWORD_REQUEST:
-                       RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
-                       /*
-                        *  We don't support this anymore.
-                        */
-                       radlog(L_ERR, "Deprecated password change request from client %s port %d - ID %d : IGNORED",
-                                       client_name(&packet->src_ipaddr),
-                              packet->src_port, packet->id);
-                       return NULL;
-                       break;
-
-               default:
-                       RAD_SNMP_INC(rad_snmp.auth.total_unknown_types);
-
-                       radlog(L_ERR, "Unknown packet code %d from client %s port %d "
-                              "- ID %d : IGNORED", packet->code,
-                              client_name(&packet->src_ipaddr),
-                              packet->src_port, packet->id);
-                       return NULL;
-                       break;
-
-       } /* switch over packet types */
-
-       /*
-        *      Don't handle proxy replies here.  They need to
-        *      return the *old* request, so we can re-process it.
-        */
-       if (listener->type == RAD_LISTEN_PROXY) {
-               return fun;
-       }
-
-       /*
-        *      If there is no existing request of id, code, etc.,
-        *      then we can return, and let it be processed.
-        */
-       if ((curreq = rl_find(packet)) == NULL) {
-               /*
-                *      Count the total number of requests, to see if
-                *      there are too many.  If so, return with an
-                *      error.
-                */
-               if (mainconfig.max_requests) {
-                       int request_count = rl_num_requests();
-
-                       /*
-                        *      This is a new request.  Let's see if
-                        *      it makes us go over our configured
-                        *      bounds.
-                        */
-                       if (request_count > mainconfig.max_requests) {
-                               radlog(L_ERR, "Dropping request (%d is too many): "
-                                      "from client %s port %d - ID: %d", request_count,
-                                      client_name(&packet->src_ipaddr),
-                                      packet->src_port, packet->id);
-                               radlog(L_INFO, "WARNING: Please check the radiusd.conf file.\n"
-                                      "\tThe value for 'max_requests' is probably set too low.\n");
-                               return NULL;
-                       } /* else there were a small number of requests */
-               } /* else there was no configured limit for requests */
-
-               /*
-                *      FIXME: Add checks for system load.  If the
-                *      system is busy, start dropping requests...
-                *
-                *      We can probably keep some statistics
-                *      ourselves...  if there are more requests
-                *      coming in than we can handle, start dropping
-                *      some.
-                */
-
-               return fun;
-       }
-
-       /*
-        *      "fake" requests MUST NEVER be in the request list.
-        *
-        *      They're used internally in the server.  Any reply
-        *      is a reply to the local server, and any proxied packet
-        *      gets sent outside of the tunnel.
-        */
-       rad_assert((curreq->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0);
-
-       /*
-        *      The current request isn't finished, which
-        *      means that the NAS sent us a new packet, while
-        *      we are still processing the old request.
-        */
-       if (!curreq->finished) {
-               /*
-                *      If the authentication vectors are identical,
-                *      then the NAS is re-transmitting it, trying to
-                *      kick us into responding to the request.
-                */
-               if (memcmp(curreq->packet->vector, packet->vector,
-                          sizeof(packet->vector)) == 0) {
-                       RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
-                       /*
-                        *      It's not finished because the request
-                        *      was proxied, but there was no reply
-                        *      from the home server.
-                        */
-                       if (curreq->proxy && !curreq->proxy_reply) {
-                               /*
-                                *      We're taking care of sending
-                                *      duplicate proxied packets, so
-                                *      we ignore any duplicate
-                                *      requests from the NAS.
-                                *
-                                *      FIXME: Make it ALWAYS synchronous!
-                                */
-                               if (!mainconfig.proxy_synchronous) {
-                                       RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
-                                       DEBUG2("Ignoring duplicate packet from client "
-                                              "%s port %d - ID: %d, due to outstanding proxied request %d.",
-                                              client_name(&packet->src_ipaddr),
-                                              packet->src_port, packet->id,
-                                              curreq->number);
-                                       return NULL;
-
-                                       /*
-                                        *      We ARE proxying the request,
-                                        *      and we have NOT received a
-                                        *      proxy reply yet, and we ARE
-                                        *      doing synchronous proxying.
-                                        *
-                                        *      In that case, go kick
-                                        *      the home RADIUS server
-                                        *      again.
-                                        */
-                               } else {
-                                       char buffer[128];
-
-                                       DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
-                                              inet_ntop(curreq->proxy->dst_ipaddr.af,
-                                                        &curreq->proxy->dst_ipaddr.ipaddr,
-                                                        buffer, sizeof(buffer)),                                              curreq->proxy->dst_port,
-
-                                              curreq->proxy->id);
-                               }
-                               curreq->proxy_next_try = time_now + mainconfig.proxy_retry_delay;
-                               rad_send(curreq->proxy, curreq->packet,
-                                        curreq->proxysecret);
-                               return NULL;
-                       } /* else the packet was not proxied */
-
-                       /*
-                        *      Someone's still working on it, so we
-                        *      ignore the duplicate request.
-                        */
-                       radlog(L_ERR, "Discarding duplicate request from "
-                              "client %s port %d - ID: %d due to unfinished request %d",
-                              client_name(&packet->src_ipaddr),
-                              packet->src_port, packet->id,
-                              curreq->number);
-                       return NULL;
-               } /* else the authentication vectors were different */
-
-               /*
-                *      The authentication vectors are different, so
-                *      the NAS has given up on us, as we've taken too
-                *      long to process the request.  This is a
-                *      SERIOUS problem!
-                */
-               RAD_SNMP_TYPE_INC(listener, total_packets_dropped);
-
-               radlog(L_ERR, "Dropping conflicting packet from "
-                      "client %s port %d - ID: %d due to unfinished request %d",
-                      client_name(&packet->src_ipaddr),
-                      packet->src_port, packet->id,
-                      curreq->number);
-               return NULL;
-       }
-
-       /*
-        *      The old request is finished.  We now check the
-        *      authentication vectors.  If the client has sent us a
-        *      request with identical code && ID, but different
-        *      vector, then they MUST have gotten our response, so we
-        *      can delete the original request, and process the new
-        *      one.
-        *
-        *      If the vectors are the same, then it's a duplicate
-        *      request, and we can send a duplicate reply.
-        */
-       if (memcmp(curreq->packet->vector, packet->vector,
-                  sizeof(packet->vector)) == 0) {
-               RAD_SNMP_INC(rad_snmp.auth.total_dup_requests);
-
-               /*
-                *      If the packet has been delayed, then silently
-                *      send a response, and clear the delayed flag.
-                *
-                *      Note that this means if the NAS kicks us while
-                *      we're delaying a reject, then the reject may
-                *      be sent sooner than otherwise.
-                *
-                *      This COULD be construed as a bug.  Maybe what
-                *      we want to do is to ignore the duplicate
-                *      packet, and send the reject later.
-                */
-               if (curreq->options & RAD_REQUEST_OPTION_DELAYED_REJECT) {
-                       curreq->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
-                       rad_send(curreq->reply, curreq->packet, curreq->secret);
-                       return NULL;
-               }
-
-               /*
-                *      Maybe we've saved a reply packet.  If so,
-                *      re-send it.  Otherwise, just complain.
-                */
-               if (curreq->reply->code != 0) {
-                       DEBUG2("Sending duplicate reply "
-                              "to client %s port %d - ID: %d",
-                              client_name(&packet->src_ipaddr),
-                              packet->src_port, packet->id);
-                       rad_send(curreq->reply, curreq->packet, curreq->secret);
-                       return NULL;
-               }
-
-               /*
-                *      Else we never sent a reply to the NAS,
-                *      as we decided somehow we didn't like the request.
-                *
-                *      This shouldn't happen, in general...
-                */
-               DEBUG2("Discarding duplicate request from client %s port %d - ID: %d",
-                      client_name(&packet->src_ipaddr),
-                      packet->src_port, packet->id);
-               return NULL;
-       } /* else the vectors were different, so we discard the old request. */
-
-       /*
-        *      'packet' has the same source IP, source port, code,
-        *      and Id as 'curreq', but a different authentication
-        *      vector.  We can therefore delete 'curreq', as we were
-        *      only keeping it around to send out duplicate replies,
-        *      if the first reply got lost in the network.
-        */
-       rl_delete(curreq);
-
-       /*
-        *      The request is OK.  We can process it...
-        *
-        *      Don't bother checking the maximum nubmer of requests
-        *      here.  we've just deleted one, so we KNOW we're under
-        *      the limit if we add one more.
-        */
-       return fun;
-}
-
-
-/*
- *  Do a proxy check of the REQUEST list when using the new proxy code.
- */
-static REQUEST *proxy_ok(RADIUS_PACKET *packet)
-{
-       REALM *cl;
-       REQUEST *oldreq;
-       char buffer[128];
-
-       /*
-        *      Find the original request in the request list
-        */
-       oldreq = rl_find_proxy(packet);
-
-       /*
-        *      If we haven't found the original request which was
-        *      sent, to get this reply.  Complain, and discard this
-        *      request, as there's no way for us to send it to a NAS.
-        */
-       if (!oldreq) {
-               radlog(L_PROXY, "No outstanding request was found for proxy reply from home server %s port %d - ID %d",
-                      inet_ntop(packet->src_ipaddr.af,
-                                &packet->src_ipaddr.ipaddr,
-                                buffer, sizeof(buffer)),
-                      packet->src_port, packet->id);
-               return NULL;
-       }
-
-       /*
-        *      The proxy reply has arrived too late, as the original
-        *      (old) request has timed out, been rejected, and marked
-        *      as finished.  The client has already received a
-        *      response, so there is nothing that can be done. Delete
-        *      the tardy reply from the home server, and return NULL.
-        */
-       if ((oldreq->reply->code != 0) ||
-           (oldreq->finished)) {
-               radlog(L_ERR, "Reply from home server %s port %d  - ID: %d arrived too late for request %d. Try increasing 'retry_delay' or 'max_request_time'",
-                      inet_ntop(packet->src_ipaddr.af,
-                                &packet->src_ipaddr.ipaddr,
-                                buffer, sizeof(buffer)),
-                      packet->src_port, packet->id,
-                      oldreq->number);
-               return NULL;
-       }
-
-       /*
-        *      If there is already a reply, maybe this one is a
-        *      duplicate?
-        */
-       if (oldreq->proxy_reply) {
-               if (memcmp(oldreq->proxy_reply->vector,
-                          packet->vector,
-                          sizeof(oldreq->proxy_reply->vector)) == 0) {
-                       radlog(L_ERR, "Discarding duplicate reply from home server %s port %d  - ID: %d for request %d",
-                              inet_ntop(packet->src_ipaddr.af,
-                                        &packet->src_ipaddr.ipaddr,
-                                        buffer, sizeof(buffer)),
-                              packet->src_port, packet->id,
-                              oldreq->number);
-               } else {
-                       /*
-                        *      ? The home server gave us a new *
-                        *      proxy reply, which doesn't match * the
-                        *      old one.  Delete it
-                        !  */
-                       DEBUG2("Ignoring conflicting proxy reply");
-               }
-
-               /*
-                *      We've already received a reply, so
-                *      we discard this one, as we don't want
-                *      to do duplicate work.
-                */
-               return NULL;
-       } /* else there wasn't a proxy reply yet, so we can process it */
-
-       /*
-        *       Refresh the old request, and update it with the proxy
-        *       reply.
-        *
-        *      ? Can we delete the proxy request here?  * Is there
-        *      any more need for it?
-        *
-        *      FIXME: we probably shouldn't be updating the time
-        *      stamp here.
-        */
-       oldreq->timestamp = time_now;
-       oldreq->proxy_reply = packet;
-
-       /*
-        *      Now that we've verified the packet IS actually
-        *      from that realm, and not forged, we can go mark the
-        *      realms for this home server as active.
-        *
-        *      If we had done this check in the 'find realm by IP address'
-        *      function, then an attacker could force us to use a home
-        *      server which was inactive, by forging reply packets
-        *      which didn't match any request.  We would think that
-        *      the reply meant the home server was active, would
-        *      re-activate the realms, and THEN bounce the packet
-        *      as garbage.
-        */
-       for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
-               if (oldreq->proxy_reply->src_ipaddr.af != cl->ipaddr.af) continue;
-               if (cl->ipaddr.af != AF_INET) continue; /* FIXME */
-
-               if (oldreq->proxy_reply->src_ipaddr.ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) {
-                       if (oldreq->proxy_reply->src_port == cl->auth_port) {
-                               cl->active = TRUE;
-                               cl->last_reply = oldreq->timestamp;
-                       } else if (oldreq->proxy_reply->src_port == cl->acct_port) {
-                               cl->acct_active = TRUE;
-                               cl->last_reply = oldreq->timestamp;
-                       }
-               }
-       }
-
-       return oldreq;
-}
-
-/*
- *     Do more checks, this time on the REQUEST data structure.
- *
- *     The main purpose of this code is to handle proxied requests.
- */
-static REQUEST *request_ok(RADIUS_PACKET *packet, uint8_t *secret,
-                          rad_listen_t *listener)
-{
-       REQUEST         *request = NULL;
-
-       /*
-        *      If the request has come in on the proxy FD, then
-        *      it's a proxy reply, so pass it through the code which
-        *      tries to find the original request, which we should
-        *      process, rather than processing the reply as a "new"
-        *      request.
-        */
-       if (listener->type == RAD_LISTEN_PROXY) {
-               /*
-                *      Find the old request, based on the current
-                *      packet.
-                */
-               request = proxy_ok(packet);
-               if (!request) {
-                       return NULL;
-               }
-               rad_assert(request->magic == REQUEST_MAGIC);
-
-               /*
-                *      We must have passed through the code below
-                *      for the original request, which adds the
-                *      reply packet to it.
-                */
-               rad_assert(request->reply != NULL);
-
-       } else {                /* remember the new request */
-               /*
-                *      A unique per-request counter.
-                */
-               static int request_num_counter = 0;
-
-               request = request_alloc(); /* never fails */
-               request->packet = packet;
-               request->number = request_num_counter++;
-               strNcpy(request->secret, (char *)secret,
-                       sizeof(request->secret));
-
-               /*
-                *      Remember the request.
-                */
-               rl_add(request);
-
-               /*
-                *      ADD IN "server identifier" from "listen"
-                *      directive!
-                */
-
-               /*
-                *      The request passes many of our sanity checks.
-                *      From here on in, if anything goes wrong, we
-                *      send a reject message, instead of dropping the
-                *      packet.
-                *
-                *      Build the reply template from the request
-                *      template.
-                */
-               rad_assert(request->reply == NULL);
-               if ((request->reply = rad_alloc(0)) == NULL) {
-                       radlog(L_ERR, "No memory");
-                       exit(1);
-               }
-               request->reply->sockfd = request->packet->sockfd;
-               request->reply->dst_ipaddr = request->packet->src_ipaddr;
-               request->reply->src_ipaddr = request->packet->dst_ipaddr;
-               request->reply->dst_port = request->packet->src_port;
-               request->reply->src_port = request->packet->dst_port;
-               request->reply->id = request->packet->id;
-               request->reply->code = 0; /* UNKNOWN code */
-               memcpy(request->reply->vector, request->packet->vector,
-                      sizeof(request->reply->vector));
-               request->reply->vps = NULL;
-               request->reply->data = NULL;
-               request->reply->data_len = 0;
-       }
-
-       return request;
-}
-
-
 /*
  *     The main guy.
  */
 int main(int argc, char *argv[])
 {
        REQUEST *request;
-       RADIUS_PACKET *packet;
-       u_char *secret;
        unsigned char buffer[4096];
        fd_set readfds;
        int argval;
@@ -1029,7 +465,6 @@ int main(int argc, char *argv[])
        }
 
        radlog(L_INFO, "Ready to process requests.");
-       start_time = time(NULL);
 
        /*
         *  Receive user requests
@@ -1216,89 +651,16 @@ int main(int argc, char *argv[])
                        if (sig_hup_block != FALSE) {
                                continue;
                        }
-                       packet = rad_recv(listener->fd);
-                       if (packet == NULL) {
-                               radlog(L_ERR, "%s", librad_errstr);
-                               continue;
-                       }
-
-                       RAD_SNMP_TYPE_INC(listener, total_requests);
-
-                       /*
-                        *      FIXME: Move this next check into
-                        *      the packet_ok() function, and add
-                        *      a 'secret' to the RAIDUS_PACKET
-                        *      data structure.  This involves changing
-                        *      a bunch of code, but it's probably the
-                        *      best thing to do.
-                        */
-
-                       /*
-                        *  Check if we know this client for
-                        *  authentication and accounting.  Check if we know
-                        *  this proxy for proxying.
-                        */
-                       if (listener->type != RAD_LISTEN_PROXY) {
-                               RADCLIENT *cl;
-                               if ((cl = client_find(&packet->src_ipaddr)) == NULL) {
-                                       RAD_SNMP_TYPE_INC(listener, total_invalid_requests);
-
-                                       radlog(L_ERR, "Ignoring request from unknown client %s port %d",
-                                              inet_ntop(packet->src_ipaddr.af,
-                                                        &packet->src_ipaddr.ipaddr,
-                                                        buffer, sizeof(buffer)),
-                                       packet->src_port);
-                                       rad_free(&packet);
-                                       continue;
-                               }
-                               secret = cl->secret;
-                       } else {    /* It came in on the proxy port */
-                               REALM *rl;
-
-                               if (packet->src_ipaddr.af != AF_INET) {
-                                       rad_assert("PROXY IPV6 NOT SUPPORTED" == NULL);
-                               }
-
-                               if ((rl = realm_findbyaddr(packet->src_ipaddr.ipaddr.ip4addr.s_addr,
-                                                          packet->src_port)) == NULL) {
-                                       radlog(L_ERR, "Ignoring request from unknown home server %s port %d",
-                                              inet_ntop(packet->src_ipaddr.af,
-                                                        &packet->src_ipaddr.ipaddr,
-                                                        buffer, sizeof(buffer)),
-                                              packet->src_port);
-                                       rad_free(&packet);
-                                       continue;
-                               }
-
-                               /*
-                                *      The secret isn't needed here,
-                                *      as it's already in the old request
-                                */
-                               secret = NULL;
-                       }
 
                        /*
-                        *      Do some simple checks before we process
-                        *      the request.
+                        *      Do per-socket receive processing of the
+                        *      packet.
                         */
-                       if ((fun = packet_ok(packet, listener)) == NULL) {
-                               rad_free(&packet);
+                       if (!listener->recv(listener, &fun, &request)) {
                                continue;
                        }
                        
                        /*
-                        *      Allocate a new request for packets from
-                        *      our clients, OR find the old request,
-                        *      for packets which are replies from a home
-                        *      server.
-                        */
-                       request = request_ok(packet, secret, listener);
-                       if (!request) {
-                               rad_free(&packet);
-                               continue;
-                       }
-
-                       /*
                         *      Drop the request into the thread pool,
                         *      and let the thread pool take care of
                         *      doing something with it.
@@ -1435,31 +797,3 @@ static void sig_hup(int sig)
        }
 #endif
 }
-
-
-/*
- *     Process and reply to a server-status request.
- *     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)
-{
-       char            reply_msg[64];
-       time_t          t;
-       VALUE_PAIR      *vp;
-
-       /*
-        *      Reply with an ACK. We might want to add some more
-        *      interesting reply attributes, such as server uptime.
-        */
-       t = request->timestamp - start_time;
-       sprintf(reply_msg, "FreeRADIUS up %d day%s, %02d:%02d",
-               (int)(t / 86400), (t / 86400) == 1 ? "" : "s",
-               (int)((t / 3600) % 24), (int)(t / 60) % 60);
-       request->reply->code = PW_AUTHENTICATION_ACK;
-
-       vp = pairmake("Reply-Message", reply_msg, T_OP_SET);
-       pairadd(&request->reply->vps, vp); /* don't need to check if !vp */
-
-       return 0;
-}