cat request_process.c | tr -d \\r > foo;mv foo request_process.c
authoraland <aland>
Wed, 16 Feb 2005 01:31:19 +0000 (01:31 +0000)
committeraland <aland>
Wed, 16 Feb 2005 01:31:19 +0000 (01:31 +0000)
src/main/request_process.c

index e470f97..4b2695f 100755 (executable)
-/*\r
- * proxy.c     Proxy stuff.\r
- *\r
- * Version:    $Id$\r
- *\r
- *   This program is free software; you can redistribute it and/or modify\r
- *   it under the terms of the GNU General Public License as published by\r
- *   the Free Software Foundation; either version 2 of the License, or\r
- *   (at your option) any later version.\r
- *\r
- *   This program is distributed in the hope that it will be useful,\r
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- *   GNU General Public License for more details.\r
- *\r
- *   You should have received a copy of the GNU General Public License\r
- *   along with this program; if not, write to the Free Software\r
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- *\r
- * Copyright 2000  The FreeRADIUS server project\r
- * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>\r
- * Copyright 2000  Chris Parker <cparker@starnetusa.com>\r
- */\r
-\r
-static const char rcsid[] = "$Id$";\r
-\r
-#include "autoconf.h"\r
-#include "libradius.h"\r
-\r
-#include <sys/socket.h>\r
-\r
-#ifdef HAVE_NETINET_IN_H\r
-#      include <netinet/in.h>\r
-#endif\r
-\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include <ctype.h>\r
-#include <string.h>\r
-\r
-#include "radiusd.h"\r
-#include "rad_assert.h"\r
-#include "modules.h"\r
-\r
-\r
-/*\r
- *     Reprocess the request in possibly a child thread, only through\r
- *     a subsection of the post-proxy section of radiusd.conf.\r
- */\r
-static int process_post_proxy_fail(REQUEST *request)\r
-{\r
-       VALUE_PAIR *vps;\r
-\r
-       /*\r
-        *\r
-        */\r
-\r
-\r
-       /*\r
-        *      Hmm... this code is copied from below, which isn't good,\r
-        *      and is similar to the code in rad_respond.\r
-        */\r
-       switch (request->packet->code) {\r
-               /*\r
-                *  Accounting requests, etc. get dropped on the floor.\r
-                */\r
-       default:\r
-       case PW_ACCOUNTING_REQUEST:\r
-       case PW_STATUS_SERVER:\r
-               break;\r
-               \r
-               /*\r
-                *  Authentication requests get their Proxy-State\r
-                *  attributes copied over, and an otherwise blank\r
-                *  reject message sent.\r
-                */\r
-       case PW_AUTHENTICATION_REQUEST:\r
-               request->reply->code = PW_AUTHENTICATION_REJECT;\r
-               \r
-               /*\r
-                *  Perform RFC limitations on outgoing replies.\r
-                */\r
-               rfc_clean(request->reply);\r
-               \r
-               /*\r
-                *  Need to copy Proxy-State from request->packet->vps\r
-                */\r
-               vps = paircopy2(request->packet->vps, PW_PROXY_STATE);\r
-               if (vps != NULL)\r
-                       pairadd(&(request->reply->vps), vps);\r
-               break;\r
-       }\r
-       \r
-       /*\r
-        *      If a reply exists, send it.\r
-        *\r
-        *      But DON'T send a RADIUS packet for a fake request.\r
-        */\r
-       if ((request->reply->code != 0) &&\r
-           ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {\r
-               /*\r
-                *      If we're not delaying authentication rejects,\r
-                *      then send the response immediately.  Otherwise,\r
-                *      mark the request as delayed, and do NOT send a\r
-                *      response.\r
-                */\r
-               if (mainconfig.reject_delay == 0) {\r
-                       rad_send(request->reply, request->packet,\r
-                                    request->secret);\r
-               } else {\r
-                       request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;\r
-               }\r
-       }\r
-\r
-       return 0;               /* ignored for now */\r
-}\r
-\r
-\r
-/*\r
- *     If we're told to proxess the request through the post-proxy "fail"\r
- *     subsection, do so.\r
- *\r
- *     Called only from request_reject in util.c\r
- */\r
-static void proxy_failed_home_server(REQUEST *request, request_fail_t reason)\r
-{\r
-       DICT_VALUE      *val;\r
-\r
-       val = dict_valbyname(PW_POST_PROXY_TYPE, mainconfig.proxy_fail_type);\r
-       if (!val) {\r
-               DEBUG("ERROR: No such post-proxy type of \"%s\", cancelling post-proxy-failure call.", mainconfig.proxy_fail_type);\r
-               return;\r
-       }\r
-\r
-       request->options |= RAD_REQUEST_OPTION_REPROCESS;\r
-\r
-       thread_pool_addrequest(request, process_post_proxy_fail);\r
-}\r
-\r
-/*\r
- *  Perform any RFC specified cleaning of outgoing replies\r
- */\r
-void rfc_clean(RADIUS_PACKET *packet)\r
-{\r
-       VALUE_PAIR *vps = NULL;\r
-\r
-       switch (packet->code) {\r
-               /*\r
-                *      In the default case, we just move all of the\r
-                *      attributes over.\r
-                */\r
-       default:\r
-               vps = packet->vps;\r
-               packet->vps = NULL;\r
-               break;\r
-\r
-               /*\r
-                *      Accounting responses can only contain\r
-                *      Proxy-State and VSA's.  Note that we do NOT\r
-                *      move the Proxy-State attributes over, as the\r
-                *      Proxy-State attributes in this packet are NOT\r
-                *      the right ones to use.  The reply function\r
-                *      takes care of copying those attributes from\r
-                *      the original request, which ARE the right ones\r
-                *      to use.\r
-                */\r
-       case PW_ACCOUNTING_RESPONSE:\r
-               pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);\r
-               break;\r
-\r
-               /*\r
-                *      Authentication REJECT's can have only\r
-                *      EAP-Message, Message-Authenticator\r
-                *      Reply-Message and Proxy-State.\r
-                *\r
-                *      We delete everything other than these.\r
-                *      Proxy-State is added below, just before the\r
-                *      reply is sent.\r
-                */\r
-       case PW_AUTHENTICATION_REJECT:\r
-               pairmove2(&vps, &(packet->vps), PW_EAP_MESSAGE);\r
-               pairmove2(&vps, &(packet->vps), PW_MESSAGE_AUTHENTICATOR);\r
-               pairmove2(&vps, &(packet->vps), PW_REPLY_MESSAGE);\r
-               pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);\r
-               break;\r
-       }\r
-\r
-       /*\r
-        *      Move the newly cleaned attributes over.\r
-        */\r
-       pairfree(&packet->vps);\r
-       packet->vps = vps;\r
-\r
-       /*\r
-        *      FIXME: Perform other, more generic sanity checks.\r
-        */\r
-}\r
-\r
-\r
-/*\r
- *     For debugging\r
- */\r
-static LRAD_NAME_NUMBER request_fail_reason[] = {\r
-       { "no threads available to handle the request",\r
-         REQUEST_FAIL_NO_THREADS },\r
-\r
-       { "malformed RADIUS packet",\r
-         REQUEST_FAIL_DECODE},\r
-\r
-       { "pre-proxying failed",\r
-         REQUEST_FAIL_PROXY},\r
-\r
-       { "sending of the proxy packet failed",\r
-         REQUEST_FAIL_PROXY_SEND},\r
-\r
-       { "failure to be told how to respond",\r
-         REQUEST_FAIL_NO_RESPONSE},\r
-\r
-       { "no response from the home server",\r
-         REQUEST_FAIL_HOME_SERVER},\r
-       \r
-       { "no response from the home server after multiple tries",\r
-         REQUEST_FAIL_HOME_SERVER2},\r
-       \r
-       { "no response from the home server for a long period of time",\r
-         REQUEST_FAIL_HOME_SERVER3},\r
-\r
-       { "we were told to reject the request",\r
-         REQUEST_FAIL_NORMAL_REJECT},\r
-\r
-       { NULL, REQUEST_FAIL_UNKNOWN }\r
-};\r
-\r
-\r
-/*\r
- *  Reject a request, by sending a trivial reply packet.\r
- */\r
- void request_reject(REQUEST *request, request_fail_t reason)\r
-{\r
-       VALUE_PAIR *vps;\r
-\r
-       /*\r
-        *      Already rejected.  Don't do anything.\r
-        */\r
-       if (request->options & RAD_REQUEST_OPTION_REJECTED) {\r
-               return;\r
-       }\r
-\r
-       DEBUG2("Server rejecting request %d due to %s.",\r
-              request->number, lrad_int2str(request_fail_reason,\r
-                                            reason, "unknown"));\r
-\r
-       /*\r
-        *      Remember that it was rejected.\r
-        */\r
-       request->options |= RAD_REQUEST_OPTION_REJECTED;\r
-\r
-       switch (reason) {\r
-       case REQUEST_FAIL_NO_THREADS:\r
-               DEBUG("WARNING: We recommend that you fix any TIMEOUT errors, or increase the value for \"max_servers\".");\r
-               break;\r
-\r
-       case REQUEST_FAIL_DECODE:\r
-               DEBUG("WARNING: Someone may be attacking your RADIUS server.");\r
-               break;\r
-\r
-       case REQUEST_FAIL_NO_RESPONSE:\r
-               DEBUG("WARNING: You did not configure the server to accept, or reject the user.  Double-check Auth-Type.");\r
-               break;\r
-\r
-               /*\r
-                *      If the home server goes down for some reason,\r
-                *      we want to be able to know when.  We do this\r
-                *      by calling a sub-section of the post_proxy section,\r
-                *      and processing any modules we find there.\r
-                *\r
-                *      Note that this subsection CAN edit the response\r
-                *      to the NAS.\r
-                */\r
-       case REQUEST_FAIL_HOME_SERVER: /* Hmm... we may want only one */\r
-       case REQUEST_FAIL_HOME_SERVER2:\r
-       case REQUEST_FAIL_HOME_SERVER3:\r
-               if (!mainconfig.proxy_fail_type) {\r
-                       request->finished = TRUE;\r
-                       break;\r
-               }\r
-\r
-               /*\r
-                *      This function takes care of doing everything\r
-                *      below...\r
-                */\r
-               proxy_failed_home_server(request, reason);\r
-               return;         \r
-               break;\r
-\r
-       case REQUEST_FAIL_SERVER_TIMEOUT:\r
-               radlog(L_ERR, "TIMEOUT for request %d in module %s, component %s",\r
-                      request->number,\r
-                      request->module ? request->module : "<server core>",\r
-                      request->component ? request->component : "<server core>");\r
-\r
-               /*\r
-                *      Tell the server to stop processing the request\r
-                */\r
-               request->options |= RAD_REQUEST_OPTION_STOP_NOW;\r
-               break;\r
-\r
-       default:                /* no additional messages, or things to do */\r
-               break;\r
-       }\r
-\r
-       switch (request->packet->code) {\r
-               /*\r
-                *  Accounting requests, etc. get dropped on the floor.\r
-                */\r
-               default:\r
-               case PW_ACCOUNTING_REQUEST:\r
-               case PW_STATUS_SERVER:\r
-                       break;\r
-\r
-               /*\r
-                *  Authentication requests get their Proxy-State\r
-                *  attributes copied over, and an otherwise blank\r
-                *  reject message sent.\r
-                */\r
-               case PW_AUTHENTICATION_REQUEST:\r
-                       request->reply->code = PW_AUTHENTICATION_REJECT;\r
-\r
-                       /*\r
-                        *  Perform RFC limitations on outgoing replies.\r
-                        */\r
-                       rfc_clean(request->reply);\r
-\r
-                       /*\r
-                        *  Need to copy Proxy-State from request->packet->vps\r
-                        */\r
-                       vps = paircopy2(request->packet->vps, PW_PROXY_STATE);\r
-                       if (vps != NULL)\r
-                               pairadd(&(request->reply->vps), vps);\r
-                       break;\r
-       }\r
-\r
-       /*\r
-        *      If a reply exists, send it.\r
-        *\r
-        *      But DON'T send a RADIUS packet for a fake request.\r
-        */\r
-       if ((request->reply->code != 0) &&\r
-           ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {\r
-               /*\r
-                *      If we're not delaying authentication rejects,\r
-                *      then send the response immediately.  Otherwise,\r
-                *      mark the request as delayed, and do NOT send a\r
-                *      response.\r
-                */\r
-               if (mainconfig.reject_delay == 0) {\r
-                       rad_send(request->reply, request->packet,\r
-                                    request->secret);\r
-               } else {\r
-                       request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;\r
-               }\r
-       }\r
-}\r
-\r
-\r
-/*\r
- * FIXME:  The next two functions should all\r
- * be in a module.  But not until we have\r
- * more control over module execution.\r
- * -jcarneal\r
- */\r
-\r
-/*\r
- *  Lowercase the string value of a pair.\r
- */\r
-static int rad_lowerpair(REQUEST *request UNUSED, VALUE_PAIR *vp) {\r
-       if (vp == NULL) {\r
-               return -1;\r
-       }\r
-\r
-       rad_lowercase((char *)vp->strvalue);\r
-       DEBUG2("rad_lowerpair:  %s now '%s'", vp->name, vp->strvalue);\r
-       return 0;\r
-}\r
-\r
-/*\r
- *  Remove spaces in a pair.\r
- */\r
-static int rad_rmspace_pair(REQUEST *request UNUSED, VALUE_PAIR *vp) {\r
-       if (vp == NULL) {\r
-               return -1;\r
-       }\r
-\r
-       rad_rmspace((char *)vp->strvalue);\r
-       vp->length = strlen((char *)vp->strvalue);\r
-       DEBUG2("rad_rmspace_pair:  %s now '%s'", vp->name, vp->strvalue);\r
-\r
-       return 0;\r
-}\r
-\r
-\r
-/*\r
- *  Respond to a request packet.\r
- *\r
- *  Maybe we reply, maybe we don't.\r
- *  Maybe we proxy the request to another server, or else maybe\r
- *  we replicate it to another server.\r
- */\r
-int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)\r
-{\r
-       RADIUS_PACKET *packet, *original;\r
-       const char *secret;\r
-       int finished = FALSE;\r
-       int reprocess = 0;\r
-       int decoderesult = 0;\r
-\r
-       rad_assert(request->magic == REQUEST_MAGIC);\r
-\r
-       /*\r
-        *      Don't decode the packet if it's an internal "fake"\r
-        *      request.  Instead, just skip ahead to processing it.\r
-        */\r
-       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {\r
-               goto skip_decode;\r
-       }\r
-\r
-\r
-       /*\r
-        *      Re-process the request.\r
-        */\r
-       if ((request->options & RAD_REQUEST_OPTION_REPROCESS) != 0) {\r
-               goto skip_decode;\r
-       }\r
-\r
-       /*\r
-        *  Put the decoded packet into it's proper place.\r
-        */\r
-       if (request->proxy_reply != NULL) {\r
-               packet = request->proxy_reply;\r
-               secret = request->proxysecret;\r
-               original = request->proxy;\r
-       } else {\r
-               packet = request->packet;\r
-               secret = request->secret;\r
-               original = NULL;\r
-       }\r
-\r
-       /*\r
-        *  Decode the packet, verifying it's signature,\r
-        *  and parsing the attributes into structures.\r
-        *\r
-        *  Note that we do this CPU-intensive work in\r
-        *  a child thread, not the master.  This helps to\r
-        *  spread the load a little bit.\r
-        *\r
-        *  Internal requests (ones that never go on the\r
-        *  wire) have ->data==NULL (data is the wire\r
-        *  format) and don't need to be "decoded"\r
-        */\r
-       if (packet->data) {\r
-               decoderesult = rad_decode(packet, original, secret);\r
-               switch (decoderesult) {\r
-                       case -1:\r
-                               radlog(L_ERR, "%s", librad_errstr);\r
-                               request_reject(request, REQUEST_FAIL_DECODE);\r
-                               goto finished_request;\r
-                               break;\r
-                       case -2:\r
-                               radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);\r
-                               /* Since accounting packets get this set in\r
-                                * request_reject but no response is sent...\r
-                                */\r
-                               request->options |= RAD_REQUEST_OPTION_REJECTED;\r
-                               goto finished_request;\r
-               }\r
-       }\r
-\r
-       /*\r
-        *  For proxy replies, remove non-allowed\r
-        *  attributes from the list of VP's.\r
-        */\r
-       if (request->proxy) {\r
-               int rcode;\r
-               rcode = proxy_receive(request);\r
-               switch (rcode) {\r
-                default:  /* Don't Do Anything */\r
-                       break;\r
-                case RLM_MODULE_FAIL:\r
-                       /* on error just continue with next request */\r
-                       goto next_request;\r
-                case RLM_MODULE_HANDLED:\r
-                       /* if this was a replicated request, mark it as\r
-                        * finished first, because it was postponed\r
-                        */\r
-                       goto finished_request;\r
-               }\r
-\r
-       } else {\r
-               /*\r
-                *      This is the initial incoming request which\r
-                *      we're processing.\r
-                *\r
-                *      Some requests do NOT get cached, as they\r
-                *      CANNOT possibly have duplicates.  Set the\r
-                *      magic option here.\r
-                *\r
-                *      Status-Server messages are easy to generate,\r
-                *      so we toss them as soon as we see a reply.\r
-                *\r
-                *      Accounting-Request packets WITHOUT an\r
-                *      Acct-Delay-Time attribute are NEVER\r
-                *      duplicated, as RFC 2866 Section 4.1 says that\r
-                *      the Acct-Delay-Time MUST be updated when the\r
-                *      packet is re-sent, which means the packet\r
-                *      changes, so it MUST have a new identifier and\r
-                *      Request Authenticator.  */\r
-               if ((request->packet->code == PW_STATUS_SERVER) ||\r
-                   ((request->packet->code == PW_ACCOUNTING_REQUEST) &&\r
-                    (pairfind(request->packet->vps, PW_ACCT_DELAY_TIME) == NULL))) {\r
-                       request->options |= RAD_REQUEST_OPTION_DONT_CACHE;\r
-               }\r
-       }\r
-\r
- skip_decode:\r
-       /*\r
-        *      We should have a User-Name attribute now.\r
-        */\r
-       if (request->username == NULL) {\r
-               request->username = pairfind(request->packet->vps,\r
-                               PW_USER_NAME);\r
-       }\r
-\r
-       /*\r
-        *  FIXME:  All this lowercase/nospace junk will be moved\r
-        *  into a module after module failover is fully in place\r
-        *\r
-        *  See if we have to lower user/pass before processing\r
-        */\r
-       if(strcmp(mainconfig.do_lower_user, "before") == 0)\r
-               rad_lowerpair(request, request->username);\r
-       if(strcmp(mainconfig.do_lower_pass, "before") == 0)\r
-               rad_lowerpair(request,\r
-                             pairfind(request->packet->vps, PW_PASSWORD));\r
-\r
-       if(strcmp(mainconfig.do_nospace_user, "before") == 0)\r
-               rad_rmspace_pair(request, request->username);\r
-       if(strcmp(mainconfig.do_nospace_pass, "before") == 0)\r
-               rad_rmspace_pair(request,\r
-                                pairfind(request->packet->vps, PW_PASSWORD));\r
-\r
-       (*fun)(request);\r
-\r
-       /*\r
-        *      If the request took too long to process, don't do\r
-        *      anything else.\r
-        */\r
-       if (request->options & RAD_REQUEST_OPTION_REJECTED) {\r
-               finished = TRUE;\r
-               goto postpone_request;\r
-       }\r
-\r
-       /*\r
-        *      Reprocess if we rejected last time\r
-        */\r
-       if ((fun == rad_authenticate) &&\r
-           (request->reply->code == PW_AUTHENTICATION_REJECT)) {\r
-         /* See if we have to lower user/pass after processing */\r
-         if (strcmp(mainconfig.do_lower_user, "after") == 0) {\r
-                 rad_lowerpair(request, request->username);\r
-                 reprocess = 1;\r
-         }\r
-         if (strcmp(mainconfig.do_lower_pass, "after") == 0) {\r
-               rad_lowerpair(request,\r
-                             pairfind(request->packet->vps, PW_PASSWORD));\r
-               reprocess = 1;\r
-         }\r
-         if (strcmp(mainconfig.do_nospace_user, "after") == 0) {\r
-                 rad_rmspace_pair(request, request->username);\r
-                 reprocess = 1;\r
-         }\r
-         if (strcmp(mainconfig.do_nospace_pass, "after") == 0) {\r
-                 rad_rmspace_pair(request,\r
-                                  pairfind(request->packet->vps, PW_PASSWORD));\r
-                 reprocess = 1;\r
-         }\r
-\r
-         /*\r
-          *    If we're re-processing the request, re-set it.\r
-          */\r
-         if (reprocess) {\r
-                 pairfree(&request->config_items);\r
-                 pairfree(&request->reply->vps);\r
-                 request->reply->code = 0;\r
-                 (*fun)(request);\r
-                 \r
-                 /*\r
-                  *    If the request took too long to process, don't do\r
-                  *    anything else.\r
-                  */\r
-                 if (request->options & RAD_REQUEST_OPTION_REJECTED) {\r
-                         finished = TRUE;\r
-                         goto postpone_request;\r
-                 }\r
-         }\r
-       }\r
-\r
-       /*\r
-        *      Status-Server requests NEVER get proxied.\r
-        */\r
-       if (mainconfig.proxy_requests) {\r
-               if ((request->packet->code != PW_STATUS_SERVER) &&\r
-                   ((request->options & RAD_REQUEST_OPTION_PROXIED) == 0)) {\r
-                       int rcode;\r
-\r
-                       /*\r
-                        *      Try to proxy this request.\r
-                        */\r
-                       rcode = proxy_send(request);\r
-\r
-                       switch (rcode) {\r
-                       default:\r
-                               break;\r
-\r
-                       /*\r
-                        *  There was an error trying to proxy the request.\r
-                        *  Drop it on the floor.\r
-                        */\r
-                       case RLM_MODULE_FAIL:\r
-                               DEBUG2("Error trying to proxy request %d: Rejecting it", request->number);\r
-                               request_reject(request, REQUEST_FAIL_PROXY);\r
-                               goto finished_request;\r
-                               break;\r
-\r
-                       /*\r
-                        *  The pre-proxy module has decided to reject\r
-                        *  the request.  Do so.\r
-                        */\r
-                       case RLM_MODULE_REJECT:\r
-                               DEBUG2("Request %d rejected in proxy_send.", request->number);\r
-                               request_reject(request, REQUEST_FAIL_PROXY_SEND);\r
-                               goto finished_request;\r
-                               break;\r
-\r
-                       /*\r
-                        *  If the proxy code has handled the request,\r
-                        *  then postpone more processing, until we get\r
-                        *  the reply packet from the home server.\r
-                        */\r
-                       case RLM_MODULE_HANDLED:\r
-                               goto postpone_request;\r
-                               break;\r
-                       }\r
-\r
-                       /*\r
-                        *  Else rcode==RLM_MODULE_NOOP\r
-                        *  and the proxy code didn't do anything, so\r
-                        *  we continue handling the request here.\r
-                        */\r
-               }\r
-       } else if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&\r
-                  (request->reply->code == 0)) {\r
-               /*\r
-                *  We're not configured to reply to the packet,\r
-                *  and we're not proxying, so the DEFAULT behaviour\r
-                *  is to REJECT the user.\r
-                */\r
-               DEBUG2("There was no response configured: rejecting request %d", request->number);\r
-               request_reject(request, REQUEST_FAIL_NO_RESPONSE);\r
-               goto finished_request;\r
-       }\r
-\r
-       /*\r
-        *  If we have a reply to send, copy the Proxy-State\r
-        *  attributes from the request to the tail of the reply,\r
-        *  and send the packet.\r
-        */\r
-       rad_assert(request->magic == REQUEST_MAGIC);\r
-       if (request->reply->code != 0) {\r
-               VALUE_PAIR *vp = NULL;\r
-\r
-               /*\r
-                *      Perform RFC limitations on outgoing replies.\r
-                */\r
-               rfc_clean(request->reply);\r
-\r
-               /*\r
-                *      Need to copy Proxy-State from request->packet->vps\r
-                */\r
-               vp = paircopy2(request->packet->vps, PW_PROXY_STATE);\r
-               if (vp) pairadd(&(request->reply->vps), vp);\r
-\r
-               /*\r
-                *  If the request isn't an authentication reject, OR\r
-                *  it's a reject, but the reject_delay is zero, then\r
-                *  send it immediately.\r
-                *\r
-                *  Otherwise, delay the authentication reject to shut\r
-                *  up DoS attacks.\r
-                */\r
-               if ((request->reply->code != PW_AUTHENTICATION_REJECT) ||\r
-                   (mainconfig.reject_delay == 0)) {\r
-                       /*\r
-                        *      Send the response. IF it's a real request.\r
-                        */\r
-                       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {\r
-                               rad_send(request->reply, request->packet,\r
-                                        request->secret);\r
-                       }\r
-                       /*\r
-                        *      Otherwise, it's a tunneled request.\r
-                        *      Don't do anything.\r
-                        */\r
-               } else {\r
-                       DEBUG2("Delaying request %d for %d seconds",\r
-                              request->number, mainconfig.reject_delay);\r
-                       request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;\r
-               }\r
-       }\r
-\r
-       /*\r
-        *  We're done processing the request, set the\r
-        *  request to be finished, clean up as necessary,\r
-        *  and forget about the request.\r
-        */\r
-\r
-finished_request:\r
-\r
-       /*\r
-        *      Don't decode the packet if it's an internal "fake"\r
-        *      request.  Instead, just skip ahead to processing it.\r
-        */\r
-       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {\r
-               goto skip_free;\r
-       }\r
-\r
-       /*\r
-        *  We're done handling the request.  Free up the linked\r
-        *  lists of value pairs.  This might take a long time,\r
-        *  so it's more efficient to do it in a child thread,\r
-        *  instead of in the main handler when it eventually\r
-        *  gets around to deleting the request.\r
-        *\r
-        *  Also, no one should be using these items after the\r
-        *  request is finished, and the reply is sent.  Cleaning\r
-        *  them up here ensures that they're not being used again.\r
-        *\r
-        *  Hmm... cleaning them up in the child thread also seems\r
-        *  to make the server run more efficiently!\r
-        *\r
-        *  If we've delayed the REJECT, then do NOT clean up the request,\r
-        *  as we haven't created the REJECT message yet.\r
-        */\r
-       if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) {\r
-               if (request->packet) {\r
-                       pairfree(&request->packet->vps);\r
-                       request->username = NULL;\r
-                       request->password = NULL;\r
-               }\r
-\r
-               /*\r
-                *  If we've sent a reply to the NAS, then this request is\r
-                *  pretty much finished, and we have no more need for any\r
-                *  of the value-pair's in it, including the proxy stuff.\r
-                */\r
-               if (request->reply->code != 0) {\r
-                       pairfree(&request->reply->vps);\r
-               }\r
-       }\r
-\r
-       pairfree(&request->config_items);\r
-       if (request->proxy) {\r
-               pairfree(&request->proxy->vps);\r
-       }\r
-       if (request->proxy_reply) {\r
-               pairfree(&request->proxy_reply->vps);\r
-       }\r
-\r
- skip_free:\r
-       DEBUG2("Finished request %d", request->number);\r
-       finished = TRUE;\r
-\r
-       /*\r
-        *  Go to the next request, without marking\r
-        *  the current one as finished.\r
-        *\r
-        *  Hmm... this may not be the brightest thing to do.\r
-        */\r
-next_request:\r
-       DEBUG2("Going to the next request");\r
-\r
-postpone_request:\r
-#ifdef HAVE_PTHREAD_H\r
-       /*\r
-        *  We are finished with the child thread.  The thread is detached,\r
-        *  so that when it exits, there's nothing more for the server\r
-        *  to do.\r
-        *\r
-        *  If we're running with thread pools, then this frees up the\r
-        *  thread in the pool for another request.\r
-        */\r
-       request->child_pid = NO_SUCH_CHILD_PID;\r
-#endif\r
-       request->finished = finished; /* do as the LAST thing before exiting */\r
-       return 0;\r
-}\r
+/*
+ * proxy.c     Proxy 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 2000  The FreeRADIUS server project
+ * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
+ * Copyright 2000  Chris Parker <cparker@starnetusa.com>
+ */
+
+static const char rcsid[] = "$Id$";
+
+#include "autoconf.h"
+#include "libradius.h"
+
+#include <sys/socket.h>
+
+#ifdef HAVE_NETINET_IN_H
+#      include <netinet/in.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#include "radiusd.h"
+#include "rad_assert.h"
+#include "modules.h"
+
+
+/*
+ *     Reprocess the request in possibly a child thread, only through
+ *     a subsection of the post-proxy section of radiusd.conf.
+ */
+static int process_post_proxy_fail(REQUEST *request)
+{
+       VALUE_PAIR *vps;
+
+       /*
+        *
+        */
+
+
+       /*
+        *      Hmm... this code is copied from below, which isn't good,
+        *      and is similar to the code in rad_respond.
+        */
+       switch (request->packet->code) {
+               /*
+                *  Accounting requests, etc. get dropped on the floor.
+                */
+       default:
+       case PW_ACCOUNTING_REQUEST:
+       case PW_STATUS_SERVER:
+               break;
+               
+               /*
+                *  Authentication requests get their Proxy-State
+                *  attributes copied over, and an otherwise blank
+                *  reject message sent.
+                */
+       case PW_AUTHENTICATION_REQUEST:
+               request->reply->code = PW_AUTHENTICATION_REJECT;
+               
+               /*
+                *  Perform RFC limitations on outgoing replies.
+                */
+               rfc_clean(request->reply);
+               
+               /*
+                *  Need to copy Proxy-State from request->packet->vps
+                */
+               vps = paircopy2(request->packet->vps, PW_PROXY_STATE);
+               if (vps != NULL)
+                       pairadd(&(request->reply->vps), vps);
+               break;
+       }
+       
+       /*
+        *      If a reply exists, send it.
+        *
+        *      But DON'T send a RADIUS packet for a fake request.
+        */
+       if ((request->reply->code != 0) &&
+           ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {
+               /*
+                *      If we're not delaying authentication rejects,
+                *      then send the response immediately.  Otherwise,
+                *      mark the request as delayed, and do NOT send a
+                *      response.
+                */
+               if (mainconfig.reject_delay == 0) {
+                       rad_send(request->reply, request->packet,
+                                    request->secret);
+               } else {
+                       request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
+               }
+       }
+
+       return 0;               /* ignored for now */
+}
+
+
+/*
+ *     If we're told to proxess the request through the post-proxy "fail"
+ *     subsection, do so.
+ *
+ *     Called only from request_reject in util.c
+ */
+static void proxy_failed_home_server(REQUEST *request, request_fail_t reason)
+{
+       DICT_VALUE      *val;
+
+       val = dict_valbyname(PW_POST_PROXY_TYPE, mainconfig.proxy_fail_type);
+       if (!val) {
+               DEBUG("ERROR: No such post-proxy type of \"%s\", cancelling post-proxy-failure call.", mainconfig.proxy_fail_type);
+               return;
+       }
+
+       request->options |= RAD_REQUEST_OPTION_REPROCESS;
+
+       thread_pool_addrequest(request, process_post_proxy_fail);
+}
+
+/*
+ *  Perform any RFC specified cleaning of outgoing replies
+ */
+void rfc_clean(RADIUS_PACKET *packet)
+{
+       VALUE_PAIR *vps = NULL;
+
+       switch (packet->code) {
+               /*
+                *      In the default case, we just move all of the
+                *      attributes over.
+                */
+       default:
+               vps = packet->vps;
+               packet->vps = NULL;
+               break;
+
+               /*
+                *      Accounting responses can only contain
+                *      Proxy-State and VSA's.  Note that we do NOT
+                *      move the Proxy-State attributes over, as the
+                *      Proxy-State attributes in this packet are NOT
+                *      the right ones to use.  The reply function
+                *      takes care of copying those attributes from
+                *      the original request, which ARE the right ones
+                *      to use.
+                */
+       case PW_ACCOUNTING_RESPONSE:
+               pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);
+               break;
+
+               /*
+                *      Authentication REJECT's can have only
+                *      EAP-Message, Message-Authenticator
+                *      Reply-Message and Proxy-State.
+                *
+                *      We delete everything other than these.
+                *      Proxy-State is added below, just before the
+                *      reply is sent.
+                */
+       case PW_AUTHENTICATION_REJECT:
+               pairmove2(&vps, &(packet->vps), PW_EAP_MESSAGE);
+               pairmove2(&vps, &(packet->vps), PW_MESSAGE_AUTHENTICATOR);
+               pairmove2(&vps, &(packet->vps), PW_REPLY_MESSAGE);
+               pairmove2(&vps, &(packet->vps), PW_VENDOR_SPECIFIC);
+               break;
+       }
+
+       /*
+        *      Move the newly cleaned attributes over.
+        */
+       pairfree(&packet->vps);
+       packet->vps = vps;
+
+       /*
+        *      FIXME: Perform other, more generic sanity checks.
+        */
+}
+
+
+/*
+ *     For debugging
+ */
+static LRAD_NAME_NUMBER request_fail_reason[] = {
+       { "no threads available to handle the request",
+         REQUEST_FAIL_NO_THREADS },
+
+       { "malformed RADIUS packet",
+         REQUEST_FAIL_DECODE},
+
+       { "pre-proxying failed",
+         REQUEST_FAIL_PROXY},
+
+       { "sending of the proxy packet failed",
+         REQUEST_FAIL_PROXY_SEND},
+
+       { "failure to be told how to respond",
+         REQUEST_FAIL_NO_RESPONSE},
+
+       { "no response from the home server",
+         REQUEST_FAIL_HOME_SERVER},
+       
+       { "no response from the home server after multiple tries",
+         REQUEST_FAIL_HOME_SERVER2},
+       
+       { "no response from the home server for a long period of time",
+         REQUEST_FAIL_HOME_SERVER3},
+
+       { "we were told to reject the request",
+         REQUEST_FAIL_NORMAL_REJECT},
+
+       { NULL, REQUEST_FAIL_UNKNOWN }
+};
+
+
+/*
+ *  Reject a request, by sending a trivial reply packet.
+ */
+ void request_reject(REQUEST *request, request_fail_t reason)
+{
+       VALUE_PAIR *vps;
+
+       /*
+        *      Already rejected.  Don't do anything.
+        */
+       if (request->options & RAD_REQUEST_OPTION_REJECTED) {
+               return;
+       }
+
+       DEBUG2("Server rejecting request %d due to %s.",
+              request->number, lrad_int2str(request_fail_reason,
+                                            reason, "unknown"));
+
+       /*
+        *      Remember that it was rejected.
+        */
+       request->options |= RAD_REQUEST_OPTION_REJECTED;
+
+       switch (reason) {
+       case REQUEST_FAIL_NO_THREADS:
+               DEBUG("WARNING: We recommend that you fix any TIMEOUT errors, or increase the value for \"max_servers\".");
+               break;
+
+       case REQUEST_FAIL_DECODE:
+               DEBUG("WARNING: Someone may be attacking your RADIUS server.");
+               break;
+
+       case REQUEST_FAIL_NO_RESPONSE:
+               DEBUG("WARNING: You did not configure the server to accept, or reject the user.  Double-check Auth-Type.");
+               break;
+
+               /*
+                *      If the home server goes down for some reason,
+                *      we want to be able to know when.  We do this
+                *      by calling a sub-section of the post_proxy section,
+                *      and processing any modules we find there.
+                *
+                *      Note that this subsection CAN edit the response
+                *      to the NAS.
+                */
+       case REQUEST_FAIL_HOME_SERVER: /* Hmm... we may want only one */
+       case REQUEST_FAIL_HOME_SERVER2:
+       case REQUEST_FAIL_HOME_SERVER3:
+               if (!mainconfig.proxy_fail_type) {
+                       request->finished = TRUE;
+                       break;
+               }
+
+               /*
+                *      This function takes care of doing everything
+                *      below...
+                */
+               proxy_failed_home_server(request, reason);
+               return;         
+               break;
+
+       case REQUEST_FAIL_SERVER_TIMEOUT:
+               radlog(L_ERR, "TIMEOUT for request %d in module %s, component %s",
+                      request->number,
+                      request->module ? request->module : "<server core>",
+                      request->component ? request->component : "<server core>");
+
+               /*
+                *      Tell the server to stop processing the request
+                */
+               request->options |= RAD_REQUEST_OPTION_STOP_NOW;
+               break;
+
+       default:                /* no additional messages, or things to do */
+               break;
+       }
+
+       switch (request->packet->code) {
+               /*
+                *  Accounting requests, etc. get dropped on the floor.
+                */
+               default:
+               case PW_ACCOUNTING_REQUEST:
+               case PW_STATUS_SERVER:
+                       break;
+
+               /*
+                *  Authentication requests get their Proxy-State
+                *  attributes copied over, and an otherwise blank
+                *  reject message sent.
+                */
+               case PW_AUTHENTICATION_REQUEST:
+                       request->reply->code = PW_AUTHENTICATION_REJECT;
+
+                       /*
+                        *  Perform RFC limitations on outgoing replies.
+                        */
+                       rfc_clean(request->reply);
+
+                       /*
+                        *  Need to copy Proxy-State from request->packet->vps
+                        */
+                       vps = paircopy2(request->packet->vps, PW_PROXY_STATE);
+                       if (vps != NULL)
+                               pairadd(&(request->reply->vps), vps);
+                       break;
+       }
+
+       /*
+        *      If a reply exists, send it.
+        *
+        *      But DON'T send a RADIUS packet for a fake request.
+        */
+       if ((request->reply->code != 0) &&
+           ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {
+               /*
+                *      If we're not delaying authentication rejects,
+                *      then send the response immediately.  Otherwise,
+                *      mark the request as delayed, and do NOT send a
+                *      response.
+                */
+               if (mainconfig.reject_delay == 0) {
+                       rad_send(request->reply, request->packet,
+                                    request->secret);
+               } else {
+                       request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
+               }
+       }
+}
+
+
+/*
+ * FIXME:  The next two functions should all
+ * be in a module.  But not until we have
+ * more control over module execution.
+ * -jcarneal
+ */
+
+/*
+ *  Lowercase the string value of a pair.
+ */
+static int rad_lowerpair(REQUEST *request UNUSED, VALUE_PAIR *vp) {
+       if (vp == NULL) {
+               return -1;
+       }
+
+       rad_lowercase((char *)vp->strvalue);
+       DEBUG2("rad_lowerpair:  %s now '%s'", vp->name, vp->strvalue);
+       return 0;
+}
+
+/*
+ *  Remove spaces in a pair.
+ */
+static int rad_rmspace_pair(REQUEST *request UNUSED, VALUE_PAIR *vp) {
+       if (vp == NULL) {
+               return -1;
+       }
+
+       rad_rmspace((char *)vp->strvalue);
+       vp->length = strlen((char *)vp->strvalue);
+       DEBUG2("rad_rmspace_pair:  %s now '%s'", vp->name, vp->strvalue);
+
+       return 0;
+}
+
+
+/*
+ *  Respond to a request packet.
+ *
+ *  Maybe we reply, maybe we don't.
+ *  Maybe we proxy the request to another server, or else maybe
+ *  we replicate it to another server.
+ */
+int rad_respond(REQUEST *request, RAD_REQUEST_FUNP fun)
+{
+       RADIUS_PACKET *packet, *original;
+       const char *secret;
+       int finished = FALSE;
+       int reprocess = 0;
+       int decoderesult = 0;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+
+       /*
+        *      Don't decode the packet if it's an internal "fake"
+        *      request.  Instead, just skip ahead to processing it.
+        */
+       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
+               goto skip_decode;
+       }
+
+
+       /*
+        *      Re-process the request.
+        */
+       if ((request->options & RAD_REQUEST_OPTION_REPROCESS) != 0) {
+               goto skip_decode;
+       }
+
+       /*
+        *  Put the decoded packet into it's proper place.
+        */
+       if (request->proxy_reply != NULL) {
+               packet = request->proxy_reply;
+               secret = request->proxysecret;
+               original = request->proxy;
+       } else {
+               packet = request->packet;
+               secret = request->secret;
+               original = NULL;
+       }
+
+       /*
+        *  Decode the packet, verifying it's signature,
+        *  and parsing the attributes into structures.
+        *
+        *  Note that we do this CPU-intensive work in
+        *  a child thread, not the master.  This helps to
+        *  spread the load a little bit.
+        *
+        *  Internal requests (ones that never go on the
+        *  wire) have ->data==NULL (data is the wire
+        *  format) and don't need to be "decoded"
+        */
+       if (packet->data) {
+               decoderesult = rad_decode(packet, original, secret);
+               switch (decoderesult) {
+                       case -1:
+                               radlog(L_ERR, "%s", librad_errstr);
+                               request_reject(request, REQUEST_FAIL_DECODE);
+                               goto finished_request;
+                               break;
+                       case -2:
+                               radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);
+                               /* Since accounting packets get this set in
+                                * request_reject but no response is sent...
+                                */
+                               request->options |= RAD_REQUEST_OPTION_REJECTED;
+                               goto finished_request;
+               }
+       }
+
+       /*
+        *  For proxy replies, remove non-allowed
+        *  attributes from the list of VP's.
+        */
+       if (request->proxy) {
+               int rcode;
+               rcode = proxy_receive(request);
+               switch (rcode) {
+                default:  /* Don't Do Anything */
+                       break;
+                case RLM_MODULE_FAIL:
+                       /* on error just continue with next request */
+                       goto next_request;
+                case RLM_MODULE_HANDLED:
+                       /* if this was a replicated request, mark it as
+                        * finished first, because it was postponed
+                        */
+                       goto finished_request;
+               }
+
+       } else {
+               /*
+                *      This is the initial incoming request which
+                *      we're processing.
+                *
+                *      Some requests do NOT get cached, as they
+                *      CANNOT possibly have duplicates.  Set the
+                *      magic option here.
+                *
+                *      Status-Server messages are easy to generate,
+                *      so we toss them as soon as we see a reply.
+                *
+                *      Accounting-Request packets WITHOUT an
+                *      Acct-Delay-Time attribute are NEVER
+                *      duplicated, as RFC 2866 Section 4.1 says that
+                *      the Acct-Delay-Time MUST be updated when the
+                *      packet is re-sent, which means the packet
+                *      changes, so it MUST have a new identifier and
+                *      Request Authenticator.  */
+               if ((request->packet->code == PW_STATUS_SERVER) ||
+                   ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+                    (pairfind(request->packet->vps, PW_ACCT_DELAY_TIME) == NULL))) {
+                       request->options |= RAD_REQUEST_OPTION_DONT_CACHE;
+               }
+       }
+
+ skip_decode:
+       /*
+        *      We should have a User-Name attribute now.
+        */
+       if (request->username == NULL) {
+               request->username = pairfind(request->packet->vps,
+                               PW_USER_NAME);
+       }
+
+       /*
+        *  FIXME:  All this lowercase/nospace junk will be moved
+        *  into a module after module failover is fully in place
+        *
+        *  See if we have to lower user/pass before processing
+        */
+       if(strcmp(mainconfig.do_lower_user, "before") == 0)
+               rad_lowerpair(request, request->username);
+       if(strcmp(mainconfig.do_lower_pass, "before") == 0)
+               rad_lowerpair(request,
+                             pairfind(request->packet->vps, PW_PASSWORD));
+
+       if(strcmp(mainconfig.do_nospace_user, "before") == 0)
+               rad_rmspace_pair(request, request->username);
+       if(strcmp(mainconfig.do_nospace_pass, "before") == 0)
+               rad_rmspace_pair(request,
+                                pairfind(request->packet->vps, PW_PASSWORD));
+
+       (*fun)(request);
+
+       /*
+        *      If the request took too long to process, don't do
+        *      anything else.
+        */
+       if (request->options & RAD_REQUEST_OPTION_REJECTED) {
+               finished = TRUE;
+               goto postpone_request;
+       }
+
+       /*
+        *      Reprocess if we rejected last time
+        */
+       if ((fun == rad_authenticate) &&
+           (request->reply->code == PW_AUTHENTICATION_REJECT)) {
+         /* See if we have to lower user/pass after processing */
+         if (strcmp(mainconfig.do_lower_user, "after") == 0) {
+                 rad_lowerpair(request, request->username);
+                 reprocess = 1;
+         }
+         if (strcmp(mainconfig.do_lower_pass, "after") == 0) {
+               rad_lowerpair(request,
+                             pairfind(request->packet->vps, PW_PASSWORD));
+               reprocess = 1;
+         }
+         if (strcmp(mainconfig.do_nospace_user, "after") == 0) {
+                 rad_rmspace_pair(request, request->username);
+                 reprocess = 1;
+         }
+         if (strcmp(mainconfig.do_nospace_pass, "after") == 0) {
+                 rad_rmspace_pair(request,
+                                  pairfind(request->packet->vps, PW_PASSWORD));
+                 reprocess = 1;
+         }
+
+         /*
+          *    If we're re-processing the request, re-set it.
+          */
+         if (reprocess) {
+                 pairfree(&request->config_items);
+                 pairfree(&request->reply->vps);
+                 request->reply->code = 0;
+                 (*fun)(request);
+                 
+                 /*
+                  *    If the request took too long to process, don't do
+                  *    anything else.
+                  */
+                 if (request->options & RAD_REQUEST_OPTION_REJECTED) {
+                         finished = TRUE;
+                         goto postpone_request;
+                 }
+         }
+       }
+
+       /*
+        *      Status-Server requests NEVER get proxied.
+        */
+       if (mainconfig.proxy_requests) {
+               if ((request->packet->code != PW_STATUS_SERVER) &&
+                   ((request->options & RAD_REQUEST_OPTION_PROXIED) == 0)) {
+                       int rcode;
+
+                       /*
+                        *      Try to proxy this request.
+                        */
+                       rcode = proxy_send(request);
+
+                       switch (rcode) {
+                       default:
+                               break;
+
+                       /*
+                        *  There was an error trying to proxy the request.
+                        *  Drop it on the floor.
+                        */
+                       case RLM_MODULE_FAIL:
+                               DEBUG2("Error trying to proxy request %d: Rejecting it", request->number);
+                               request_reject(request, REQUEST_FAIL_PROXY);
+                               goto finished_request;
+                               break;
+
+                       /*
+                        *  The pre-proxy module has decided to reject
+                        *  the request.  Do so.
+                        */
+                       case RLM_MODULE_REJECT:
+                               DEBUG2("Request %d rejected in proxy_send.", request->number);
+                               request_reject(request, REQUEST_FAIL_PROXY_SEND);
+                               goto finished_request;
+                               break;
+
+                       /*
+                        *  If the proxy code has handled the request,
+                        *  then postpone more processing, until we get
+                        *  the reply packet from the home server.
+                        */
+                       case RLM_MODULE_HANDLED:
+                               goto postpone_request;
+                               break;
+                       }
+
+                       /*
+                        *  Else rcode==RLM_MODULE_NOOP
+                        *  and the proxy code didn't do anything, so
+                        *  we continue handling the request here.
+                        */
+               }
+       } else if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
+                  (request->reply->code == 0)) {
+               /*
+                *  We're not configured to reply to the packet,
+                *  and we're not proxying, so the DEFAULT behaviour
+                *  is to REJECT the user.
+                */
+               DEBUG2("There was no response configured: rejecting request %d", request->number);
+               request_reject(request, REQUEST_FAIL_NO_RESPONSE);
+               goto finished_request;
+       }
+
+       /*
+        *  If we have a reply to send, copy the Proxy-State
+        *  attributes from the request to the tail of the reply,
+        *  and send the packet.
+        */
+       rad_assert(request->magic == REQUEST_MAGIC);
+       if (request->reply->code != 0) {
+               VALUE_PAIR *vp = NULL;
+
+               /*
+                *      Perform RFC limitations on outgoing replies.
+                */
+               rfc_clean(request->reply);
+
+               /*
+                *      Need to copy Proxy-State from request->packet->vps
+                */
+               vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
+               if (vp) pairadd(&(request->reply->vps), vp);
+
+               /*
+                *  If the request isn't an authentication reject, OR
+                *  it's a reject, but the reject_delay is zero, then
+                *  send it immediately.
+                *
+                *  Otherwise, delay the authentication reject to shut
+                *  up DoS attacks.
+                */
+               if ((request->reply->code != PW_AUTHENTICATION_REJECT) ||
+                   (mainconfig.reject_delay == 0)) {
+                       /*
+                        *      Send the response. IF it's a real request.
+                        */
+                       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
+                               rad_send(request->reply, request->packet,
+                                        request->secret);
+                       }
+                       /*
+                        *      Otherwise, it's a tunneled request.
+                        *      Don't do anything.
+                        */
+               } else {
+                       DEBUG2("Delaying request %d for %d seconds",
+                              request->number, mainconfig.reject_delay);
+                       request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
+               }
+       }
+
+       /*
+        *  We're done processing the request, set the
+        *  request to be finished, clean up as necessary,
+        *  and forget about the request.
+        */
+
+finished_request:
+
+       /*
+        *      Don't decode the packet if it's an internal "fake"
+        *      request.  Instead, just skip ahead to processing it.
+        */
+       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
+               goto skip_free;
+       }
+
+       /*
+        *  We're done handling the request.  Free up the linked
+        *  lists of value pairs.  This might take a long time,
+        *  so it's more efficient to do it in a child thread,
+        *  instead of in the main handler when it eventually
+        *  gets around to deleting the request.
+        *
+        *  Also, no one should be using these items after the
+        *  request is finished, and the reply is sent.  Cleaning
+        *  them up here ensures that they're not being used again.
+        *
+        *  Hmm... cleaning them up in the child thread also seems
+        *  to make the server run more efficiently!
+        *
+        *  If we've delayed the REJECT, then do NOT clean up the request,
+        *  as we haven't created the REJECT message yet.
+        */
+       if ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) {
+               if (request->packet) {
+                       pairfree(&request->packet->vps);
+                       request->username = NULL;
+                       request->password = NULL;
+               }
+
+               /*
+                *  If we've sent a reply to the NAS, then this request is
+                *  pretty much finished, and we have no more need for any
+                *  of the value-pair's in it, including the proxy stuff.
+                */
+               if (request->reply->code != 0) {
+                       pairfree(&request->reply->vps);
+               }
+       }
+
+       pairfree(&request->config_items);
+       if (request->proxy) {
+               pairfree(&request->proxy->vps);
+       }
+       if (request->proxy_reply) {
+               pairfree(&request->proxy_reply->vps);
+       }
+
+ skip_free:
+       DEBUG2("Finished request %d", request->number);
+       finished = TRUE;
+
+       /*
+        *  Go to the next request, without marking
+        *  the current one as finished.
+        *
+        *  Hmm... this may not be the brightest thing to do.
+        */
+next_request:
+       DEBUG2("Going to the next request");
+
+postpone_request:
+#ifdef HAVE_PTHREAD_H
+       /*
+        *  We are finished with the child thread.  The thread is detached,
+        *  so that when it exits, there's nothing more for the server
+        *  to do.
+        *
+        *  If we're running with thread pools, then this frees up the
+        *  thread in the pool for another request.
+        */
+       request->child_pid = NO_SUCH_CHILD_PID;
+#endif
+       request->finished = finished; /* do as the LAST thing before exiting */
+       return 0;
+}