Massive change to the server core to remove horrid code in
authoraland <aland>
Tue, 3 Apr 2007 13:25:58 +0000 (13:25 +0000)
committeraland <aland>
Tue, 3 Apr 2007 13:25:58 +0000 (13:25 +0000)
request*.c, and move to an event-based system.  Much better,
and somewhat tested

28 files changed:
raddb/proxy.conf
src/include/event.h [new file with mode: 0644]
src/include/radiusd.h
src/include/realms.h [new file with mode: 0644]
src/include/request_list.h [deleted file]
src/lib/Makefile
src/lib/event.c [new file with mode: 0644]
src/main/Makefile.in
src/main/acct.c
src/main/auth.c
src/main/event.c [new file with mode: 0644]
src/main/files.c
src/main/listen.c
src/main/mainconfig.c
src/main/modcall.c
src/main/modules.c
src/main/proxy.c [deleted file]
src/main/radiusd.c
src/main/realms.c [new file with mode: 0644]
src/main/request_list.c [deleted file]
src/main/request_process.c [deleted file]
src/main/threads.c
src/main/util.c
src/modules/rlm_detail/rlm_detail.c
src/modules/rlm_eap/eap.c
src/modules/rlm_eap/rlm_eap.c
src/modules/rlm_pap/rlm_pap.c
src/modules/rlm_realm/rlm_realm.c

index 433178d..f42aea0 100644 (file)
 #  to which it sends proxy requests.
 #
 proxy server {
-#
-#  The time (in seconds) to wait for a response from the proxy, before
-#  re-sending the proxied request.
-#
-#  If this time is set too high, then the NAS may re-send the request,
-#  or it may give up entirely, and reject the user.
-#
-#  If it is set too low, then the RADIUS server which receives the proxy
-#  request will get kicked unnecessarily.
-#
-       retry_delay = 5
+       #
+       #  Note that as of 2.0, the "synchronous", "retry_delay",
+       #  "retry_count", and "dead_time" have all been deprecated.
+       #  For backwards compatibility, they are are still accepted
+       #  by the server, but they ONLY apply to the old-style realm
+       #  configuration.  i.e. realms with "authhost" and/or "accthost"
+       #  entries.
+       #
+       #  i.e. "retry_delay" and "retry_count" have been replaced
+       #  with per-home-server "pings".  See the "home_server" example
+       #  below for details.
+       #
+       #  i.e. "dead_time" has been replaced with a per-home-server
+       #  "revive_interval".  We strongly recommend that this not
+       #  be used, however.  The new "ping" method is much better.
+
+       #  Note that while we call these messages "pings" they are NOT
+       #  the same as the ICMP packets sent by the "ping" command.
+       #  These messages are normal RADIUS packets, sent to a home
+       #  server to determine if it is alive.
+
+       #
+       #  In 2.0, the server is always "synchronous", and setting
+       #  "synchronous = no" is impossible.  This simplifies the
+       #  server and increases the stability of the network.
+       #
+       #  If you need to set "synchronous = no", please send a
+       #  message to the list <freeradius-users@lists.freeradius.org>
+       #  explaining why this feature is vital for your network.
+
+       #
+       #  If a realm exists, but there are no live home servers for
+       #  it, we can fall back to using the "DEFAULT" realm.  This is
+       #  most useful for accounting, where the server can proxy
+       #  accounting requests to home servers, but if they're down,
+       #  use a DEFAULT realm that is LOCAL (i.e. accthost = LOCAL),
+       #  and then store the packets in the "detail" file.  That data
+       #  can be later proxied to the home servers by radrelay, when
+       #  those home servers come back up again.       
+
+       #  Setting this to "yes" may have issues for authentication.
+       #  i.e. If you are proxying for two different ISP's, and then
+       #  act as a general dial-up for Gric.  If one of the first two
+       #  ISP's has their RADIUS server go down, you do NOT want to
+       #  proxy those requests to GRIC.  Instead, you probably want
+       #  to just drop the requests on the floor.  In that case, set
+       #  this value to 'no'.
+       #
+       #  allowed values: {yes, no}
+       #
+       default_fallback = no
+
+}
 
+#######################################################################
 #
-#  The number of retries to send before giving up, and sending a reject
-#  message to the NAS.
+#  Configuration for the proxy realms.
 #
-       retry_count = 3
-
+#  As of 2.0. the old-style "realms" file is deprecated, and is not
+#  used by FreeRADIUS.
 #
-#  If the home server does not respond to any of the multiple retries,
-#  then FreeRADIUS will stop sending it proxy requests, and mark it 'dead'.
+#  As of 2.0, the "realm" configuration has changed.  Instead of
+#  specifying "authhost" and "accthost" in a realm section, the home
+#  servers are specified seperately in a "home_server" section.  For
+#  backwards compatibility, you can still use the "authhost" and
+#  "accthost" directives.  If you only have one home server for a
+#  realm, it is easier to use the old-style configuration.
 #
-#  If there are multiple entries configured for this realm, then the
-#  server will fail-over to the next one listed.  If no more are listed,
-#  then no requests will be proxied to that realm.
+#  However, if you have multiple servers for a realm, we STRONGLY
+#  suggest moving to the new-style configuration.
 #
 #
-#  After a configurable 'dead_time', in seconds, FreeRADIUS will
-#  speculatively mark the home server active, and start sending requests
-#  to it again.
+#  Load-balancing and failover between home servers is handled via
+#  a "server_pool" section.
 #
-#  If this dead time is set too low, then you will lose requests,
-#  as FreeRADIUS will quickly switch back to the home server, even if
-#  it isn't up again.
+#  Finally, The "realm" section defines the realm, some options, and
+#  indicates which server pool should be used for the realm.
 #
-#  If this dead time is set too high, then FreeRADIUS may take too long
-#  to switch back to the primary home server.
+#  This change means that simple configurations now require multiple
+#  ssections to define a realm.  However, complex configurations
+#  are much simpler than before, as multiple realms can share the same
+#  server pool.
 #
-#  Realistic values for this number are in the range of minutes to hours.
-#  (60 to 3600)
+#  That is, realms point to server pools, and server pools point to
+#  home servers.  Multiple realms can point to one server pool.  One
+#  server pool can point to multiple home servers.  Each home server
+#  can appear in one or more pools.
 #
-       dead_time = 120
 
-#  An ldflag attribute for all realms to be included in a round-robin 
-#  setup must be specified, and that ldflag must be the same for all
-#  realms of the same name.
-#  Currently (0 or fail_over) and (1 or round_robin) are the 
-#  supported values for ldflag.  Fail over is the default setup.
-#
-#  DO NOT INCLUDE LOCAL AUTH/ACCT HOST REALMS IN A ROUND-ROBIN QUEUE.
+######################################################################
+#
+#  This section defines a "Home Server" which is another RADIUS
+#  server that gets sent proxied requests.  In earlier versions
+#  of FreeRADIUS, home servers were defined in "realm" sections,
+#  which was awkward.  In 2.0, they have been made independent
+#  from realms, which is better for a number of reasons.
+#
+home_server localhost {
+       #
+       #  Home servers can be sent Access-Request packets
+       #  or Accounting-Request packets.
+       #
+       #  Allowed values are:
+       #       auth - send Access-Request packets
+       #       acct - send Accounting-Request packets
+       type = auth
 
+       #
+       #  Configure ONE OF the following three entries:
+       #
+       #       IPv4 address
+       #
+       ipaddr = 127.0.0.1
 
-#
-#  If all exact matching realms did not respond, we can try the
-#  DEFAULT realm, too.  This is what the server normally does.
-#
-#  This behaviour may be undesired for some cases.  e.g. You are proxying
-#  for two different ISP's, and then act as a general dial-up for Gric.
-#  If one of the first two ISP's has their RADIUS server go down, you do
-#  NOT want to proxy those requests to GRIC.  Instead, you probably want
-#  to just drop the requests on the floor.  In that case, set this value
-#  to 'no'.
-#
-#  allowed values: {yes, no}
-#
-       default_fallback = yes
+       #       OR IPv6 address
+       # ipv6addr = ::1
 
+       #       OR hostname, which will do address detection automatically
+       #
+       #       Note that we do NOT recommend using hostnames, because
+       #       it means that the server has to do a DNS lookup to
+       #       determine the IP address of the home server.  If the
+       #       DNS server is slow or unresponsible, it means that
+       #       FreeRADIUS will NOT be able to determine the IP
+       #       address, and will therefore NOT start.
+       #
+       # hostname = localhost
+
+       #
+       #  The port to which packets are sent.
+       #
+       #  Usually 1812 for type "auth", and  1813 for type "acct".
+       #  Older servers may use 1645 and 1646.
+       #
+       port = 1812
+
+       #
+       #  The shared secret use to "encrypt" and "sign" packets between
+       #  FreeRADIUS and the home server.
+       #
+       #  The secret can be any string, up to 8k characters in length.
+       #
+       #  Control codes can be entered vi octal encoding,
+       #       e.g. "\101\102" == "AB"
+       #  Quotation marks can be entered by escaping them,
+       #       e.g. "foo\"bar"
+       #  Spaces or other "special" characters can be entered
+       #  by putting quotes around the string.
+       #       e.g. "foo bar"
+       #            "foo;bar"
+       #
+       secret = testing123
+
+       ############################################################
+       #
+       #  The rest of the configuration items listed here are optional,
+       #  and do not have to appear in every home server definition.
+       #
+       ############################################################
+
+       #
+       #  If the home server doesn't respond to the request within
+       #  this time, this server will consider the request dead, and
+       #  respond to the NAS with an Access-Reject.
+       #
+       #  Useful range of values: 5 to 60
+       response_window = 20
+
+       #
+       #  If the home server does not respond to ANY packets for
+       #  a certain time, consider it dead.  This time period is
+       #  called the "zombie" period, because the server is neither
+       #  alive nor dead.
+       #
+       #  Useful range of values: 20 to 120
+       zombie_period = 40
+
+       ############################################################
+       #
+       #  As of 2.0, FreeRADIUS supports RADIUS layer "pings".  These
+       #  are used by a proxy server to see if a home server is alive.
+       #
+       #  Pings are sent ONLY if the proxying server believes that
+       #  the home server is dead.  Pings are NOT sent if the proxying
+       #  server believes that the home server is alive.  Pings are
+       #  NOT sent if the proxying server is not proxying packets.
+       #
+       #  If the home server responds to the "pings", then it is
+       #  marked "alive" again, and is returned to use.
+       #
+       ############################################################
+
+       #
+       #  Some home servers do not support RADIUS layer "pings" via
+       #  the Status-Server packet.  Others may not have a "test"
+       #  user configured that can be used to query the server, to
+       #  see if it is alive.  For those servers, we have NO WAY
+       #  of knowing when it becomes alive again.  Therefore, after
+       #  the server has been marked "dead", we wait a period of
+       #  time, and mark it "alive" again, in the hope that it has
+       #  come back to life.
+       #
+       #  If it has NOT come back to life, then FreeRADIUS will wait
+       #  for "zombie_period" before marking it dead again.  During
+       #  the "zombie_period", ALL AUTHENTICATIONS WILL FAIL, because
+       #  the home server is still dead.  There is NOTHING that can
+       #  be done about this, other than to enable "pings", as
+       #  documented below.
+       #
+       #  e.g. if "zombie_period" is 40 seconds, and "revive_interval"
+       #  is 300 seconds, the for 40 seconds out of every 340, or about
+       #  10% of the time, all authentications will fail.
+       #
+       #  If the "zombie_period" and "revive_interval" configurations
+       #  are set smaller, than it is possible for up to 50% of
+       #  authentications to fail.
+       #
+       #  As a result, we recommend enabling Status-Server "pings", and
+       #  we do NOT recommend using "revive_interval".
+       #
+       #  If the "ping_check" entry below is not "none", then the
+       #  "revive_interval" entry can be deleted, as it will not be
+       #  used.
+       #
+       #  Useful range of values: 60 to 3600
+       revive_interval = 120
+
+       #
+       #  The proxying server (i.e. this one) can do periodic "ping"
+       #  checks to see if a dead home server has come back alive.
+       #
+       #  If set to "none", then the ping configuration items listed
+       #  below are not used, and the "revive_interval" time is used
+       #  instead.
+       #
+       #  If set to "status-server", the Status-Server packets are
+       #  sent.  Many RADIUS servers support Status-Server.  If a
+       #  server does not support it, please contact the server
+       #  vendor and request that they add it.
+       #
+       #  If set to "request", then Access-Request, or Accounting-Request
+       #  packets are sent, depending on the "type" entry above (auth/acct).
+       #  
+       #  Allowed values: none, status-server, request
+       ping_check = status-server
+
+       #
+       #  If the home server does not support Status-Server "pings",
+       #  then the server can still send Access-Request or
+       #  Accounting-Request packets, with a pre-defined user name.
+       #
+       #  This practice is NOT recommended, as it may potentially let
+       #  users gain network access by using these "test" accounts!
+       #
+       #  If it is used, we recommend that the home server ALWAYS
+       #  respond to Access-Request "pings" with Access-Reject.  The
+       #  ping check just needs an answer, it does not need an
+       #  Access-Accept.
+       #
+       #  For Accounting-Request "pings", only the username needs to
+       #  be set.
+       #
+       # username = "test_user_please_reject_me"
+       # password = "this is really secret"
+
+       #
+       #  Configure the interval between sending ping packets.
+       #
+       #  Setting it too low increases the probability of spurious
+       #  fail-over and fallback attempts.
+       #
+       #  Useful range of values: 6 to 120
+       ping_interval = 30
+
+       #
+       #  Configure the number of pings in a row that the home
+       #  server needs to respond to before it is marked alive.
+       #
+       #  If you want to mark a home server as alive after a short
+       #  time period of being responsive, it is best to use a small
+       #  "ping_interval", and a large value for "num_pings_to_alive".
+       #  Using a long "ping_interval" and a small number for
+       #  "num_pings_to_alive" increases the probability of spurious
+       #  fail-over and fallback attempts.
+       #
+       #  Useful range of values: 3 to 10
+       num_pings_to_alive = 3
 }
 
-#######################################################################
-#
-#  Configuration for the proxy realms.
-#
-#  The information given here is used in conjunction with the 'realms'
-#  file.  This format is preferred, as it is more flexible.  The realms
-#  listed here take priority over those listed in the 'realms' file.
 
-#  A standard realm entry. A request from "user@company.com" will be
-#  sent to radius.company.com as "user", unless the 'nostrip'
-#  configuration item is specified.  If the 'nostrip' configuration
-#  item is specified, then the request will be proxied as
-#  "user@company.com"
-#
-#realm company.com {
-#      type            = radius
-#      authhost        = radius.company.com:1600
-#      accthost        = radius.company.com:1601
-#      secret          = testing123
-#}
+######################################################################
+#
+#  This section defines a pool of home servers that is used
+#  for fail-over and load-balancing.  In earlier versions of
+#  FreeRADIUS, fail-over and load-balancing were defined per-realm.
+#  As a result, if a server had 5 home servers, each of which served
+#  the same 10 realms, you would need 50 "realm" entries.
+#
+#  In version 2.0, you would need 5 "home_server" sections,
+#  10 'realm" sections, and one "server_pool" section to tie the
+#  two together.
+#
+server_pool my_auth_failover {
+       #
+       #  The type of this pool is either "fail-over" or "load-balance".
+       #
+       #  With "fail-over", the request is sent to the first live
+       #  home server in the list.
+       #
+       #  With "load-balance", the request is load-balanced (randomly)
+       #  between the live home servers.  This is equivalent to the
+       #  old per-realm configuration "round_robin".
+       #
+       type = fail-over
 
-#  A realm entry with an optional fail-over realm.  A request from
-#  "user@isp2.com" will be sent to radius.isp2.com as "user@isp2.com",
-#  because the 'nostrip' directive is specified for this realm.
-#
-#realm isp2.com {
-#      type        = radius
-#      authhost    = radius.isp2.com:1645
-#      accthost    = radius.isp2.com:1646
-#      secret      = TheirKey
-#      nostrip
-#}
-#
-#  The fail-over realm for isp2.com
-#
-#realm isp2.com {
-#      type        = radius
-#      authhost    = radius2.isp2.com:1645
-#      accthost    = radius2.isp2.com:1646
-#      secret      = TheirKey2
-#      nostrip
-#}
+       #
+       #  Next, a list of one or more home servers.  The names
+       #  of the home servers are NOT the hostnames, but the names
+       #  of the sections.  (e.g. home_server foo {...} has name "foo".
+       #
+       home_server = localhost
+
+       #  Additional home servers can be listed.
+       #  There is NO LIMIT to the number of home servers that can
+       #  be listed, though using more than 10 or so will become
+       #  difficult to manage.
+       #
+       # home_server = foo.example.com
+       # home_server = bar.example.com
+       # home_server = baz.example.com
+       # home_server = ...
+}
 
+######################################################################
 #
-#  1st node serv.com...set up for round-robin.
 #
-#  The load balancing 'ldflag' attribute can be used to perform
-#  load balancing.  Allowed values are 'fail_over' and 'round_robin'.
+#  This section defines a new-style "realm".  Note the in version 2.0,
+#  there are many fewer configuration items than in 1.x for a realm.
 #
-#  If there is no ldflag attribute, or it is set to 'fail_over', then
-#  the realms are treated as "fail-over".  That is, the first matching
-#  realm is used, unless it is down, in which case the realm "fails
-#  over" to the second matching realm.  The process continues until an
-#  active matching realm is found, OR the DEFAULT realm is returned.
+#  Automatic proxying is done via the "realms" module (see "man
+#  rlm_realm").  To manually proxy the request put this entry in the
+#  "users" file:
+
 #
-#  If the ldflag attribute is set to 'round_robin', then all active
-#  realms of the same name are put into a pool internally in the
-#  server, and the proxied requests are evenly divided among the
-#  realms in the pool.  For this to work, all realms of the same name
-#  MUST have the same value of their 'ldflag' attributes.  Mixing up
-#  different types of load balancing schemes for the same realm will
-#  cause problems.
 #
-#  The round_robin load balancing method is a probabilistic method
-#  which evenly scatters the requests among the home servers.
+#DEFAULT       Proxy-To-Realm := "realm_name"
 #
-#  Note that you CANNOT include local auth/acct host realms in a
-#  round-robin queue.  Having a server load balance requests to itself
-#  doesn't make any sense, as it only doubles the amount of work
-#  which is needed to be done.
 #
-#realm serv.com {
-#      type        = radius
-#      authhost    = radius.serv.com:1645
-#      accthost    = radius.serv.com:1646
-#      secret      = TheirKey
-#      ldflag      = round_robin
-#      nostrip
-#}
+realm example.com {
+       auth_pool = my_auth_failover
+#      acct_pool = acct
 
-#
-#  Another node for serv.com
-#
-#realm serv.com {
-#      type        = radius
-#      authhost    = radius2.serv.com:1645
-#      accthost    = radius2.serv.com:1646
-#      secret      = TheirKey2
-#      ldflag      = round_robin
-#      nostrip
-#}
+       #
+       #  Normally, when an incoming User-Name is matched against the
+       #  realm, the realm name is "stripped" off, and the "stripped"
+       #  user name is used to perform matches.
+       #
+       #  e.g. User-Name = "bob@example.com" will result in two new
+       #  attributes being created by the "realms" module:
+       #
+       #       Stripped-User-Name = "bob"
+       #       Realm = "example.com"
+       #
+       #  The Stripped-User-Name is then used as a key in the "users"
+       #  file, for example.
+       #
+       #  If you do not want this to happen, uncomment "nostrip" below.
+       #
+       # nostrip
 
-#
-#  A third round-robin node realm for serv.com
-#
-#realm serv.com {
-#      type        = radius
-#      authhost    = radius3.serv.com:1645
-#      accthost    = radius3.serv.com:1646
-#      secret      = TheirKey2
-#      ldflag      = round_robin
-#      nostrip
-#}
-#
-#
+       #  There are no more configuration entries for a realm.
+}
 
-#
-#  This is a local realm.  The requests are NOT proxied,
-#  but instead are authenticated by the RADIUS server itself.
-#
-#  You don't need a secret if BOTH 'authhost' and 'accthost' are
-#  set to LOCAL.
-#
-#realm bla.com {
-#      type            = radius
-#      authhost        = LOCAL
-#      accthost        = LOCAL
-#}
 
 #
 #  This is a sample entry for iPass.
+#  Note that you have to define "ipass_auth_pool" and
+#  "ipass_acct_pool", along with home_servers for them, too.
 #
 #realm IPASS {
-#      type            = radius
-#      authhost        = ipass.server.hostname:11812
-#      accthost        = ipass.server.hostname:11813
-#
-       #  The shared secret here must be the same
-       #  value as the secret of the NetServer found in the
-       #  /usr/ipass/raddb/clients file of your NetServer software.
-#      secret          = mysecret
 #      nostrip
+#
+#      auth_pool = ipass_auth_pool
+#      acct_pool = ipass_acct_pool
 #}
 
 #
@@ -227,9 +410,8 @@ proxy server {
 #  DEFAULT EAP-Type == PEAP, Proxy-To-Realm := LOCAL
 #
 realm LOCAL {
-       type            = radius
-       authhost        = LOCAL
-       accthost        = LOCAL
+       #  If we do not specify a server pool, the realm is LOCAL, and
+       #  requests are not proxied to it.
 }
 
 #
@@ -252,3 +434,4 @@ realm LOCAL {
 #      accthost        = radius.company.com:1601
 #      secret          = testing123
 #}
+
diff --git a/src/include/event.h b/src/include/event.h
new file mode 100644 (file)
index 0000000..c44b27d
--- /dev/null
@@ -0,0 +1,50 @@
+#ifndef LRAD_EVENT_H
+#define LRAD_EVENT_H
+
+/*
+ * event.h     Simple event queue
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2007 The FreeRADIUS server project
+ * Copyright 2007 Alan DeKok <aland@deployingradius.com>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSIDH(event_h, "$Id$")
+
+typedef struct lrad_event_list_t lrad_event_list_t;
+typedef        void (*lrad_event_callback_t)(void *);
+
+lrad_event_list_t *lrad_event_list_create(void);
+void lrad_event_list_free(lrad_event_list_t *el);
+
+int lrad_event_list_num_elements(lrad_event_list_t *el);
+
+int lrad_event_insert(lrad_event_list_t *el, lrad_event_callback_t callback,
+                     void *ctx, struct timeval *when);
+int lrad_event_delete(lrad_event_list_t *el, void *ctx);
+
+int lrad_event_callback(lrad_event_list_t *el, void *ctx,
+                       lrad_event_callback_t *pcallback);
+int lrad_event_when(lrad_event_list_t *el, void *ctx, struct timeval *when);
+
+int lrad_event_run(lrad_event_list_t *el, struct timeval *when);
+
+int lrad_event_now(lrad_event_list_t *el, struct timeval *when);
+
+#endif /* LRAD_HASH_H */
index 7e7f323..cd6d199 100644 (file)
@@ -16,6 +16,7 @@ RCSIDH(radiusd_h, "$Id$")
 #include <freeradius-devel/radpaths.h>
 #include <freeradius-devel/conf.h>
 #include <freeradius-devel/conffile.h>
+#include <freeradius-devel/realms.h>
 
 
 #ifdef HAVE_UNISTD_H
@@ -31,9 +32,6 @@ typedef pid_t child_pid_t;
 #define child_kill kill
 #endif
 
-#ifdef HAVE_NETINET_IN_H
-#endif
-
 #define NO_SUCH_CHILD_PID (child_pid_t) (0)
 
 #ifndef NDEBUG
@@ -50,11 +48,6 @@ typedef struct request_data_t request_data_t;
  */
 typedef struct rad_listen_t rad_listen_t;
 
-/*
- *     For request lists.
- */
-typedef struct request_list_t request_list_t;
-
 #define REQUEST_DATA_REGEX (0xadbeef00)
 #define REQUEST_MAX_REGEX (8)
 
@@ -79,37 +72,47 @@ typedef struct auth_req {
        rad_listen_t            *listener;
        rad_listen_t            *proxy_listener;
 
-       /*
-        *      We could almost keep a const char here instead of a
-        *      _copy_ of the secret... but what if the RADCLIENT
-        *      structure is freed because it was taken out of the
-        *      config file and SIGHUPed?
-        */
-       char                    proxysecret[32];
-       int                     proxy_try_count;
-       int                     proxy_outstanding;
-       time_t                  proxy_start_time;
-
        int                     simul_max;
        int                     simul_count;
        int                     simul_mpp; /* WEIRD: 1 is false, 2 is true */
 
-       int                     finished;
        int                     options; /* miscellanous options */
        const char              *module; /* for debugging unresponsive children */
        const char              *component; /* ditto */
-       void                    *container;
+
+       struct timeval          received;
+       struct timeval          when;           /* to wake up */
+       int                     delay;
+
+       int                     master_state;
+       int                     child_state;
+
+       struct timeval          next_when;
+       void                    *next_callback;
+
+       int                     in_request_hash;
+       int                     in_proxy_hash;
+
+       home_server             *home_server;
+
+       struct timeval          proxy_when;
+
+       int                     num_proxied_requests;
+       int                     num_proxied_responses;
+
 } REQUEST;
 
 #define RAD_REQUEST_OPTION_NONE            (0)
-#define RAD_REQUEST_OPTION_LOGGED_CHILD    (1 << 0)
-#define RAD_REQUEST_OPTION_DELAYED_REJECT  (1 << 1)
-#define RAD_REQUEST_OPTION_DONT_CACHE      (1 << 2)
-#define RAD_REQUEST_OPTION_FAKE_REQUEST    (1 << 3)
-#define RAD_REQUEST_OPTION_REJECTED        (1 << 4)
-#define RAD_REQUEST_OPTION_PROXIED         (1 << 5)
-#define RAD_REQUEST_OPTION_STOP_NOW        (1 << 6)
-#define RAD_REQUEST_OPTION_REPROCESS       (1 << 7)
+
+#define REQUEST_ACTIVE                 (1)
+#define REQUEST_STOP_PROCESSING (2)
+
+#define REQUEST_QUEUED         (1)
+#define REQUEST_RUNNING                (2)
+#define REQUEST_PROXIED                (3)
+#define REQUEST_REJECT_DELAY   (4)
+#define REQUEST_CLEANUP_DELAY  (5)
+#define REQUEST_DONE           (6)
 
 /*
  *  Function handler for requests.
@@ -130,27 +133,6 @@ typedef struct radclient {
 
 typedef struct radclient_list RADCLIENT_LIST;
 
-typedef struct _realm {
-       char                    realm[64];
-       char                    server[64];
-       char                    acct_server[64];
-       lrad_ipaddr_t           ipaddr; /* authentication */
-       lrad_ipaddr_t           acct_ipaddr;
-       char                    secret[32];
-       time_t                  last_reply; /* last time we saw a packet */
-       int                     auth_port;
-       int                     acct_port;
-       int                     striprealm;
-       int                     trusted; /* old */
-       int                     notrealm;
-       int                     active; /* is it dead? */
-       time_t                  wakeup; /* when we should try it again */
-       int                     acct_active;
-       time_t                  acct_wakeup;
-       int                     ldflag;
-       struct _realm           *next;
-} REALM;
-
 typedef struct pair_list {
        char                    *name;
        VALUE_PAIR              *check;
@@ -178,8 +160,9 @@ typedef enum RAD_LISTEN_TYPE {
 
 typedef int (*rad_listen_recv_t)(rad_listen_t *, RAD_REQUEST_FUNP *, REQUEST **);
 typedef int (*rad_listen_send_t)(rad_listen_t *, REQUEST *);
-typedef int (*rad_listen_update_t)(rad_listen_t *, time_t);
 typedef int (*rad_listen_print_t)(rad_listen_t *, char *, size_t);
+typedef int (*rad_listen_encode_t)(rad_listen_t *, REQUEST *);
+typedef int (*rad_listen_decode_t)(rad_listen_t *, REQUEST *);
 
 struct rad_listen_t {
        struct rad_listen_t *next; /* should be rbtree stuff */
@@ -190,11 +173,11 @@ struct rad_listen_t {
        RAD_LISTEN_TYPE type;
        int             fd;
        const char      *identity;
-       request_list_t  *rl;
 
        rad_listen_recv_t recv;
        rad_listen_send_t send;
-       rad_listen_update_t update;
+       rad_listen_encode_t encode;
+       rad_listen_decode_t decode;
        rad_listen_print_t print;
 
        void            *data;
@@ -250,7 +233,6 @@ typedef struct main_config_t {
        radlog_dest_t   radlog_dest;
        CONF_SECTION    *config;
        RADCLIENT_LIST  *clients;
-       REALM           *realms;
        const char      *radiusd_conf;
 } MAIN_CONFIG_T;
 
@@ -380,14 +362,9 @@ RADCLIENT  *client_find_old(const lrad_ipaddr_t *ipaddr);
 const char     *client_name_old(const lrad_ipaddr_t *ipaddr);
 
 /* files.c */
-REALM          *realm_find(const char *, int);
-REALM          *realm_findbyaddr(uint32_t ipno, int port);
-void           realm_free(REALM *cl);
-void           realm_disable(REQUEST *);
 int            pairlist_read(const char *file, PAIR_LIST **list, int complain);
 void           pairlist_free(PAIR_LIST **);
 int            read_config_files(void);
-int            read_realms_file(const char *file);
 
 /* version.c */
 void           version(void);
@@ -433,7 +410,6 @@ void                paircompare_unregister(int attr, RAD_COMPARE_FUNC func);
 int            paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check,
                            VALUE_PAIR **reply);
 int            simplepaircmp(REQUEST *, VALUE_PAIR *, VALUE_PAIR *);
-void           pair_builtincompare_init(void);
 void           pairxlatmove(REQUEST *, VALUE_PAIR **to, VALUE_PAIR **from);
 
 /* xlat.c */
@@ -471,4 +447,14 @@ void listen_free(rad_listen_t **head);
 int listen_init(const char *filename, rad_listen_t **head);
 rad_listen_t *proxy_new_listener(void);
 
+/* event.c */
+int radius_event_init(int spawn_flag);
+void radius_event_free(void);
+int radius_event_process(struct timeval **pptv);
+void radius_handle_request(REQUEST *request, RAD_REQUEST_FUNP fun);
+int received_request(rad_listen_t *listener,
+                    RADIUS_PACKET *packet, REQUEST **prequest,
+                    const RADCLIENT *client);
+REQUEST *received_proxy_response(RADIUS_PACKET *packet);
+
 #endif /*RADIUSD_H*/
diff --git a/src/include/realms.h b/src/include/realms.h
new file mode 100644 (file)
index 0000000..12f57ce
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef REALMS_H
+#define REALMS_H
+
+/*
+ * realms.h    Structures, prototypes and global variables
+ *             for realms
+ *
+ * Version:    $Id$
+ *
+ */
+
+#include <freeradius-devel/ident.h>
+RCSIDH(realms_h, "$Id$")
+
+#define HOME_TYPE_AUTH (1)
+#define HOME_TYPE_ACCT (2)
+
+#define HOME_PING_CHECK_NONE           (0)
+#define HOME_PING_CHECK_STATUS_SERVER  (1)
+#define HOME_PING_CHECK_REQUEST                (2)
+
+#define HOME_STATE_ALIVE               (0)
+#define HOME_STATE_ZOMBIE              (1)
+#define HOME_STATE_IS_DEAD             (2)
+
+typedef struct home_server {
+       const char      *name;
+
+       const char      *hostname;
+
+       lrad_ipaddr_t   ipaddr;
+
+       
+       int             port;
+       int             type;           /* auth/acct */
+
+       /*
+        *      Maybe also have list of source IP/ports, && socket?
+        */
+
+       const char      *secret;
+
+       struct timeval  when;
+       
+       int             response_window;
+       int             max_outstanding; /* don't overload it */
+       int             currently_outstanding;
+       
+       struct timeval  zombie_period_start;
+       int             zombie_period; /* unresponsive for T, mark it dead */
+
+       int             state;
+
+       int             ping_check;
+       const char      *ping_user_name;
+       const char      *ping_user_password;
+
+       int             ping_interval;
+       int             num_pings_to_alive;
+       int             num_received_pings;
+
+       int             revive_interval; /* if it doesn't support pings */
+} home_server;
+
+
+typedef enum home_pool_type_t {
+       HOME_POOL_INVALID = 0,
+       HOME_POOL_LOAD_BALANCE,
+       HOME_POOL_FAIL_OVER
+} home_pool_type_t;
+
+
+typedef struct home_pool_t {
+       const char              *name;
+       home_pool_type_t        type;
+
+       int                     server_type;
+
+       int                     num_home_servers;
+       home_server             *servers[1];
+} home_pool_t;
+
+
+typedef struct _realm {
+       const char              *name;
+
+       int                     striprealm;
+
+       home_pool_t             *auth_pool;
+       home_pool_t             *acct_pool;
+} REALM;
+
+int realms_init(const char *filename);
+void realms_free(void);
+int realm_add(const char *filename, CONF_SECTION *cs);
+REALM *realm_find(const char *name);
+
+home_server *home_server_ldb(REALM *realm, int code);
+home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port);
+
+#endif /* REALMS_H */
diff --git a/src/include/request_list.h b/src/include/request_list.h
deleted file mode 100644 (file)
index 3ab2d73..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef _REQUEST_LIST_H
-#define _REQUEST_LIST_H
-/*
- * request_list.h      Hide the handling of the REQUEST list from
- *                     the main server.
- *
- * Version:    $Id$
- *
- */
-
-#include <freeradius-devel/ident.h>
-RCSIDH(request_list_h, "$Id$")
-
-extern request_list_t *rl_init(void);
-extern void rl_deinit(request_list_t *);
-extern void rl_yank(request_list_t *, REQUEST *);
-extern void rl_delete(request_list_t *, REQUEST *);
-extern int rl_add(request_list_t *, REQUEST *);
-extern REQUEST *rl_find(request_list_t *, RADIUS_PACKET *);
-
-extern int rl_init_proxy(void);
-extern int rl_add_proxy(REQUEST *request);
-extern REQUEST *rl_find_proxy(RADIUS_PACKET *packet);
-extern REQUEST *rl_next(request_list_t *, REQUEST *);
-extern int rl_num_requests(request_list_t *);
-extern int rl_clean_list(request_list_t *, time_t now);
-
-#endif /* _REQUEST_LIST_H */
index e975982..f0e14e0 100644 (file)
@@ -9,7 +9,7 @@ include ../../Make.inc
 SRCS           = dict.c filters.c hash.c hmac.c hmacsha1.c isaac.c log.c \
                  misc.c missing.c md4.c md5.c print.c radius.c rbtree.c \
                  sha1.c snprintf.c strlcat.c strlcpy.c token.c udpfromto.c \
-                 valuepair.c fifo.c packet.c
+                 valuepair.c fifo.c packet.c event.c
 
 LT_OBJS                = $(SRCS:.c=.lo)
 
diff --git a/src/lib/event.c b/src/lib/event.c
new file mode 100644 (file)
index 0000000..11d9784
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ * event.c     Non-thread-safe event handling, specific to a RADIUS
+ *             server.
+ *
+ * Version:    $Id$
+ *
+ *   This library is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Lesser General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2.1 of the License, or (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *   Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public
+ *   License along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ *  Copyright 2007  The FreeRADIUS server project
+ *  Copyright 2007  Alan DeKok <aland@ox.org>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <freeradius-devel/missing.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/event.h>
+
+typedef struct lrad_event_fd_t {
+       int                     fd;
+       int                     priority;
+       lrad_event_callback_t   callback;
+       void                    *ctx;
+       struct lrad_event_fd_t  *next;
+} lrad_event_fd_t;
+
+struct lrad_event_list_t {
+       rbtree_t        *times;
+       rbtree_t        *contexts;
+
+       lrad_event_fd_t *fds;
+       int             maxfd;
+       fd_set          readfds;
+
+       int             exit;
+
+       struct timeval  now;
+       int             dispatch;
+};
+
+/*
+ *     Internal structure for managing events.
+ */
+typedef struct lrad_event_t {
+       lrad_event_callback_t   callback;
+       void                    *ctx;
+       struct timeval          when;
+} lrad_event_t;
+
+
+static int lrad_event_list_time_cmp(const void *one, const void *two)
+{
+       const lrad_event_t *a = one;
+       const lrad_event_t *b = two;
+
+       if (a->when.tv_sec < b->when.tv_sec) return -1;
+       if (a->when.tv_sec > b->when.tv_sec) return +1;
+
+       if (a->when.tv_usec < b->when.tv_usec) return -1;
+       if (a->when.tv_usec > b->when.tv_usec) return +1;
+
+       return 0;
+}
+
+static int lrad_event_list_ctx_cmp(const void *one, const void *two)
+{
+       const lrad_event_t *a = one;
+       const lrad_event_t *b = two;
+
+       if (a->ctx < b->ctx) return -1;
+       if (a->ctx > b->ctx) return +1;
+
+       return 0;
+}
+
+
+void lrad_event_list_free(lrad_event_list_t *el)
+{
+       if (!el) return;
+
+       rbtree_free(el->times);
+       rbtree_free(el->contexts);
+       free(el);
+}
+
+
+lrad_event_list_t *lrad_event_list_create(void)
+{
+       lrad_event_list_t *el;
+
+       el = malloc(sizeof(*el));
+       if (!el) return NULL;
+       memset(el, 0, sizeof(*el));
+
+       el->times = rbtree_create(lrad_event_list_time_cmp,
+                                 free, 0);
+       if (!el->times) {
+               lrad_event_list_free(el);
+               return NULL;
+       }
+
+       el->contexts = rbtree_create(lrad_event_list_ctx_cmp,
+                                    NULL, 0);
+       if (!el->contexts) {
+               lrad_event_list_free(el);
+               return NULL;
+       }
+
+       return el;
+}
+
+int lrad_event_list_num_elements(lrad_event_list_t *el)
+{
+       if (!el) return 0;
+
+       return rbtree_num_elements(el->times);
+}
+
+
+int lrad_event_delete(lrad_event_list_t *el, void *ctx)
+{
+       lrad_event_t my_ev, *ev;
+
+       if (!el || !ctx) return 0;
+
+       my_ev.ctx = ctx;
+       ev = rbtree_finddata(el->contexts, &my_ev);
+       if (!ev) return 0;
+
+       rbtree_deletebydata(el->contexts, ev);
+       rbtree_deletebydata(el->times, ev);
+
+       return 1;
+}
+
+                     
+int lrad_event_insert(lrad_event_list_t *el, lrad_event_callback_t callback,
+                     void *ctx, struct timeval *when)
+{
+       lrad_event_t *ev;
+
+       if (!el || !callback | !when) return 0;
+
+       lrad_event_delete(el, ctx); /* can only be 1 event per ctx */
+
+       ev = malloc(sizeof(*ev));
+       if (!ev) return 0;
+       memset(ev, 0, sizeof(*ev));
+
+       ev->callback = callback;
+       ev->ctx = ctx;
+       ev->when = *when;
+
+       if (!rbtree_insert(el->contexts, ev)) {
+               free(ev);
+               return 0;
+       }
+
+       /*
+        *      There's a tiny chance that two events will be
+        *      scheduled at the same time.  If this happens, we
+        *      increase the usec counter by 1, in order to avoid the
+        *      duplicate.  If we can't insert it after 10 tries, die.
+        */
+       if (!rbtree_insert(el->times, ev)) {
+               if (rbtree_finddata(el->times, ev)) {
+                       int i;
+
+                       for (i = 0; i < 10; i++) {
+                               ev->when.tv_usec++;
+                               if (ev->when.tv_usec >= 1000000) {
+                                       ev->when.tv_usec = 0;
+                                       ev->when.tv_sec++;
+                               }
+
+                               if (rbtree_finddata(el->times, ev)) {
+                                       continue;
+                               }
+
+                               if (!rbtree_insert(el->times, ev)) {
+                                       break;
+                               }
+
+                               return 1;
+                       }
+                               
+               }
+               rbtree_deletebydata(el->contexts, ev);
+               free(ev);
+               return 0;
+       }
+
+       return 1;
+}
+
+int lrad_event_callback(lrad_event_list_t *el, void *ctx,
+                       lrad_event_callback_t *pcallback)
+{
+       lrad_event_t my_ev, *ev;
+
+       if (!el || !ctx || !pcallback) return 0;
+
+       my_ev.ctx = ctx;
+       ev = rbtree_finddata(el->contexts, &my_ev);
+       if (!ev) return 0;
+
+       *pcallback = ev->callback;
+       return 1;
+}
+
+
+int lrad_event_when(lrad_event_list_t *el, void *ctx, struct timeval *when)
+{
+       lrad_event_t my_ev, *ev;
+
+       if (!el || !ctx) return 0;
+
+       my_ev.ctx = ctx;
+       ev = rbtree_finddata(el->contexts, &my_ev);
+       if (!ev) return 0;
+
+       *when = ev->when;
+       return 1;
+}
+
+
+typedef struct lrad_event_walk_t {
+       lrad_event_t *ev;
+       struct timeval when;
+} lrad_event_walk_t;
+
+
+static int lrad_event_find_earliest(void *ctx, void *data)
+{
+       lrad_event_t *ev = data;
+       lrad_event_walk_t *w = ctx;
+
+       if (ev->when.tv_sec > w->when.tv_sec) {
+               w->when = ev->when;
+               return 1;
+       }
+
+       if ((ev->when.tv_sec == w->when.tv_sec) &&
+           (ev->when.tv_usec > w->when.tv_usec)) {
+               w->when = ev->when;
+               return 1;
+       }
+
+       w->ev = ev;
+       return 1;       
+}
+
+
+int lrad_event_run(lrad_event_list_t *el, struct timeval *when)
+{
+       lrad_event_callback_t callback;
+       void *ctx;
+       lrad_event_walk_t w;
+
+       if (!el) return 0;
+
+       w.ev = NULL;
+       w.when = *when;
+
+       if (rbtree_num_elements(el->times) == 0) {
+               when->tv_sec = 0;
+               when->tv_usec = 0;
+               return 0;
+       }
+
+       rbtree_walk(el->times, InOrder, lrad_event_find_earliest, &w);
+       if (!w.ev) {
+               *when = w.when;
+               return 0;
+       }
+
+       callback = w.ev->callback;
+       ctx = w.ev->ctx;
+
+       /*
+        *      Delete the event before calling it.
+        */
+       rbtree_deletebydata(el->contexts, w.ev);
+       rbtree_deletebydata(el->times, w.ev);
+
+       callback(ctx);
+       return 1;
+}
+
+
+static int lrad_event_insert_fd(lrad_event_list_t *el, int fd, int priority,
+                        lrad_event_callback_t callback, void *ctx)
+{
+       lrad_event_fd_t **last, *ef;
+
+       if (!fd || (fd < 0) || (priority < 0) || !callback || !ctx) return 0;
+
+       ef = malloc(sizeof(*ef));
+       if (!ef) return 0;
+       memset(ef, 0, sizeof(*ef));
+
+       ef->fd = fd;
+       ef->priority = priority;
+       ef->callback = callback;
+       ef->ctx = ctx;
+
+       if (!el->fds) {
+               el->fds = ef;
+               el->maxfd = fd + 1;
+               FD_ZERO(&el->readfds);
+               FD_SET(fd, &el->readfds);
+               return 1;
+       }
+
+       for (last = &(el->fds);
+            *last != NULL;
+            last = &((*last)->next)) {
+               if ((*last)->priority < priority) {
+                       ef->next = *last;
+                       *last = ef;
+
+                       if (fd >= el->maxfd) {
+                               el->maxfd = fd + 1;
+                       }
+                       FD_SET(fd, &el->readfds);
+                       
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+static int lrad_event_delete_fd(lrad_event_list_t *el, int fd)
+{
+       lrad_event_fd_t **last;
+
+       if (!el || (fd < 0)) return 0;
+
+       for (last = &el->fds;
+            *last != NULL;
+            last = &((*last)->next)) {
+               if ((*last)->fd == fd) {
+                       lrad_event_fd_t *ef = *last;
+                       *last = (*last)->next;
+                       free(ef);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+int lrad_event_now(lrad_event_list_t *el, struct timeval *when)
+{
+       if (!el || !when || !el->dispatch) return 0;
+
+       *when = el->now;
+       return 1;
+}
+
+static void lrad_event_exit_loop_cb(void *data)
+{
+       lrad_event_list_t *el = data;
+
+       el->exit = 1;
+}
+
+static int lrad_event_exit_loop(lrad_event_list_t *el, struct timeval *when)
+{
+       if (!el || !when) return 0;
+
+       return lrad_event_insert(el, lrad_event_exit_loop_cb, el, when);
+}
+
+
+static int lrad_event_dispatch(lrad_event_list_t *el)
+{
+       if (!el || !el->fds) return 0;
+
+       el->dispatch = 1;
+
+       while (!el->exit) {
+               int rcode;
+               fd_set readfds;
+               struct timeval when, *timeout;
+               lrad_event_fd_t *ef;
+
+               gettimeofday(&el->now, NULL);
+               while (1) {
+                       when = el->now;
+                       
+                       if (!lrad_event_run(el, &when)) {
+                               break;
+                       }
+               }
+               gettimeofday(&el->now, NULL);
+               
+               if (rbtree_num_elements(el->times) == 0) {
+                       timeout = NULL;
+                       
+               } else if ((el->now.tv_sec >= when.tv_sec) ||
+                          ((el->now.tv_sec == when.tv_sec) &&
+                           (el->now.tv_usec >= when.tv_usec))) {
+                       timeout = &when;
+                       when.tv_sec = 0;
+                       when.tv_usec = 0;
+                       
+               } else {
+                       timeout = &when;
+                       
+                       when.tv_sec -= el->now.tv_sec;
+                       if (when.tv_sec == 0) {
+                               when.tv_usec -= el->now.tv_usec;
+                       } else if (when.tv_usec > el->now.tv_usec) {
+                               when.tv_usec -= el->now.tv_usec;
+                       } else {
+                               when.tv_sec--;
+                               when.tv_usec += 1000000;
+                               when.tv_usec -= el->now.tv_usec;
+                       }
+               }
+               
+               readfds = el->readfds;
+               
+               rcode = select(el->maxfd, &readfds, NULL, NULL, timeout);
+               if (rcode == 0) {
+                       continue;
+               }
+
+               if (rcode < 0) {
+                       el->dispatch = 0;
+                       el->exit = 0;
+                       return -1;
+               }
+               
+               for (ef = el->fds; ef != NULL; ef = ef->next) {
+                       if (FD_ISSET(ef->fd, &readfds)) {
+                               ef->callback(ef->ctx);
+                               continue; /* starve lower priority FD's! */
+                       }
+               }
+       }
+
+       el->exit = 0;
+       el->dispatch = 0;
+
+       return 1;
+}
+
+
+#ifdef TESTING
+
+/*
+ *  cc -g -I .. -c rbtree.c -o rbtree.o && cc -g -I .. -c isaac.c -o isaac.o && cc -DTESTING -I .. -c event.c  -o event_mine.o && cc event_mine.o rbtree.o isaac.o -o event
+ *
+ *  ./event
+ *
+ *  And hit CTRL-S to stop the output, CTRL-Q to continue.
+ *  It normally alternates printing the time and sleeping,
+ *  but when you hit CTRL-S/CTRL-Q, you should see a number
+ *  of events run right after each other.
+ *
+ *  OR
+ *
+ *   valgrind --tool=memcheck --leak-check=full --show-reachable=yes ./event
+ */
+
+static void print_time(void *ctx)
+{
+       struct timeval *when = ctx;
+
+       printf("%d.%06d\n", when->tv_sec, when->tv_usec);
+       fflush(stdout);
+}
+
+static lrad_randctx rand_pool;
+
+static uint32_t event_rand(void)
+{
+       uint32_t num;
+
+       num = rand_pool.randrsl[rand_pool.randcnt++];
+       if (rand_pool.randcnt == 256) {
+               lrad_isaac(&rand_pool);
+               rand_pool.randcnt = 0;
+       }
+
+       return num;
+}
+
+
+#define MAX 100
+int main(int argc, char **argv)
+{
+       int i, rcode;
+       struct timeval array[MAX];
+       struct timeval now, when;
+       lrad_event_list_t *el;
+
+       el = lrad_event_list_create();
+       if (!el) exit(1);
+       
+       memset(&rand_pool, 0, sizeof(rand_pool));
+       rand_pool.randrsl[1] = time(NULL);
+       
+       lrad_randinit(&rand_pool, 1);
+       rand_pool.randcnt = 0;
+
+       gettimeofday(&array[0], NULL);
+       for (i = 1; i < MAX; i++) {
+               array[i] = array[i - 1];
+
+               array[i].tv_usec += event_rand() & 0xffff;
+               if (array[i].tv_usec > 1000000) {
+                       array[i].tv_usec -= 1000000;
+                       array[i].tv_sec++;
+               }
+               lrad_event_insert(el, print_time, &array[i], &array[i]);
+       }
+       
+       while (lrad_event_list_num_elements(el)) {
+               gettimeofday(&now, NULL);
+               when = now;
+               if (!lrad_event_run(el, &when)) {
+                       int delay = (when.tv_sec - now.tv_sec) * 1000000;
+                       delay += when.tv_usec;
+                       delay -= now.tv_usec;
+
+                       printf("\tsleep %d\n", delay);
+                       fflush(stdout);
+                       usleep(delay);
+               }
+       }
+
+       lrad_event_list_free(el);
+
+       return 0;
+}
+#endif
index b28ad98..03b6e3d 100644 (file)
@@ -5,10 +5,10 @@
 include ../../Make.inc
 
 SERVER_SRCS    = acct.c auth.c client.c conffile.c crypt.c exec.c files.c \
-                 listen.c log.c mainconfig.c modules.c modcall.c proxy.c  \
-                 radiusd.c radius_snmp.c request_list.c request_process.c \
+                 listen.c log.c mainconfig.c modules.c modcall.c \
+                 radiusd.c radius_snmp.c \
                  session.c smux.c threads.c util.c valuepair.c version.c  \
-                 xlat.c
+                 xlat.c event.c realms.c
 
 SERVER_OBJS    += $(SERVER_SRCS:.c=.lo)
 
@@ -71,7 +71,7 @@ radiusd: $(SERVER_OBJS) $(MODULE_OBJS) ../lib/libradius.la
                $(MODULE_LIBS) $(LIBS) $(SNMP_LIBS) $(LCRYPT)     \
                $(PTHREADLIB) $(LIBLTDL) $(OPENSSL_LIBS)
 
-radiusd.lo: radiusd.c ../include/request_list.h ../include/modules.h ../include/modcall.h ../include/modpriv.h
+radiusd.lo: radiusd.c  ../include/modules.h ../include/modcall.h ../include/modpriv.h
        $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c radiusd.c
 
 acct.lo: acct.c ../include/modules.h
@@ -110,17 +110,14 @@ modcall.lo: modcall.c
 modules.lo: modules.c
        $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(VFLAGS) $(INCLTDL) -c modules.c
 
-proxy.lo: proxy.c
-       $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c proxy.c
-
 radius_snmp.lo: radius_snmp.c
        $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c radius_snmp.c
 
-request_list.lo: request_list.c
-       $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c request_list.c
+event.lo: event.c
+       $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c event.c
 
-request_process.lo: request_process.c
-       $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c request_process.c
+realms.lo: realms.c
+       $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c realms.c
 
 session.lo: session.c ../include/modules.h
        $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) -c session.c
index 6dc9305..0059c6f 100644 (file)
@@ -122,11 +122,9 @@ int rad_accounting(REQUEST *request)
                         *      Check whether Proxy-To-Realm is
                         *      a LOCAL realm.
                         */
-                       realm = realm_find(vp->vp_strvalue, TRUE);
-                       if (realm != NULL &&
-                           realm->acct_ipaddr.af == AF_INET &&
-                           realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) {
-                               DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->realm);
+                       realm = realm_find(vp->vp_strvalue);
+                       if (realm && !realm->acct_pool) {
+                               DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->name);
                                pairdelete(&request->config_items, PW_PROXY_TO_REALM);
                        } else {
                                /*
index ffa3a6b..94bed74 100644 (file)
@@ -621,10 +621,9 @@ autz_redo:
                 *      Catch users who set Proxy-To-Realm to a LOCAL
                 *      realm (sigh).
                 */
-               realm = realm_find(tmp->vp_strvalue, 0);
-               rad_assert((realm == NULL) || (realm->ipaddr.af == AF_INET));
-               if (realm && (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) {
-                       DEBUG2("  WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm!  Cancelling invalid proxy request.", realm->realm);
+               realm = realm_find(tmp->vp_strvalue);
+               if (realm && !realm->auth_pool) {
+                       DEBUG2("  WARNING: You set Proxy-To-Realm = %s, but it is a LOCAL realm!  Cancelling invalid proxy request.", realm->name);
                } else {
                        /*
                         *      Don't authenticate, as the request is
diff --git a/src/main/event.c b/src/main/event.c
new file mode 100644 (file)
index 0000000..6d048e4
--- /dev/null
@@ -0,0 +1,1841 @@
+/*
+ * event.c     Server event handling
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2007  The FreeRADIUS server project
+ * Copyright 2007  Alan DeKok <aland@deployingradius.com>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/event.h>
+#include <freeradius-devel/modules.h>
+
+#define USEC (1000000)
+
+/*
+ *     Ridiculous amounts of local state.
+ */
+static lrad_event_list_t       *el = NULL;
+static lrad_packet_list_t      *pl = NULL;
+static int                     request_num_counter = 0;
+static struct timeval          now;
+static time_t                  start_time;
+static int                     have_children;
+
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t proxy_mutex;
+
+#define PTHREAD_MUTEX_LOCK if (have_children) pthread_mutex_lock
+#define PTHREAD_MUTEX_UNLOCK if (have_children) pthread_mutex_unlock
+#else
+/*
+ *     This is easier than ifdef's throughout the code.
+ */
+#define PTHREAD_MUTEX_LOCK(_x)
+#define PTHREAD_MUTEX_UNLOCK(_x)
+#endif
+
+static lrad_packet_list_t *proxy_list = NULL;
+
+/*
+ *     We keep the proxy FD's here.  The RADIUS Id's are marked
+ *     "allocated" per Id, via a bit per proxy FD.
+ */
+static int             proxy_fds[32];
+static rad_listen_t    *proxy_listeners[32];
+
+
+static void _rad_panic(const char *file, unsigned int line, const char *msg)
+{
+       radlog(L_ERR, "]%s:%d] %s", file, line, msg);
+       _exit(1);
+}
+
+#define rad_panic(x) _rad_panic(__FILE__, __LINE__, x)
+
+
+/*
+ *     FIXME FIXME: Do SNMP, but smarter...  have an array[code].foo,
+ *     so we increment counters by just using code as an offset.  The
+ *     array should be the union of the server && client SNMP
+ *     variables, which should simplify it a lot.
+ */
+
+static void tv_add(struct timeval *tv, int usec_delay)
+{
+       if (usec_delay > USEC) {
+               tv->tv_sec += usec_delay / USEC;
+               usec_delay %= USEC;
+       }
+       tv->tv_usec += usec_delay;
+
+       if (tv->tv_usec > USEC) {
+               tv->tv_usec -= USEC;
+               tv->tv_sec++;
+       }
+}
+
+
+static void remove_from_request_hash(REQUEST *request)
+{
+#ifdef WITH_SNMP
+       /*
+        *      Update the SNMP statistics.
+        *
+        *      Note that we do NOT do this in a child thread.
+        *      Instead, we update the stats when a request is
+        *      deleted, because only the main server thread calls
+        *      this function...
+        */
+       if (mainconfig.do_snmp) {
+               switch (request->reply->code) {
+               case PW_AUTHENTICATION_ACK:
+                 rad_snmp.auth.total_responses++;
+                 rad_snmp.auth.total_access_accepts++;
+                 break;
+
+               case PW_AUTHENTICATION_REJECT:
+                 rad_snmp.auth.total_responses++;
+                 rad_snmp.auth.total_access_rejects++;
+                 break;
+
+               case PW_ACCESS_CHALLENGE:
+                 rad_snmp.auth.total_responses++;
+                 rad_snmp.auth.total_access_challenges++;
+                 break;
+
+               case PW_ACCOUNTING_RESPONSE:
+                 rad_snmp.acct.total_responses++;
+                 break;
+
+               default:
+                       break;
+               }
+       }
+#endif
+
+       if (!request->in_request_hash) return;
+
+       lrad_packet_list_yank(pl, request->packet);
+       request->in_request_hash = FALSE;
+}
+
+
+static REQUEST *lookup_in_proxy_hash(RADIUS_PACKET *reply)
+{
+       RADIUS_PACKET **proxy_p;
+       REQUEST *request;
+
+       PTHREAD_MUTEX_LOCK(&proxy_mutex);
+       proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
+
+       if (!proxy_p) {
+               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+               return NULL;
+       }
+
+       request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
+               
+       if (!request) {
+               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+               return NULL;
+       }
+
+       request->num_proxied_responses++;
+
+       /*
+        *      Catch the most common case of everything working
+        *      correctly.
+        */
+       if (request->num_proxied_requests == request->num_proxied_responses) {
+               /*
+                *      FIXME: remove from the event list, too?
+                */
+               lrad_packet_list_yank(proxy_list, request->proxy);
+               lrad_packet_list_id_free(proxy_list, request->proxy);
+               request->in_proxy_hash = FALSE;
+       }
+
+       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+       return request;
+}
+
+
+static void remove_from_proxy_hash(REQUEST *request)
+{
+       if (!request->in_proxy_hash) return;
+
+       PTHREAD_MUTEX_LOCK(&proxy_mutex);
+       request->home_server->currently_outstanding--;
+       lrad_packet_list_yank(proxy_list, request->proxy);
+       lrad_packet_list_id_free(proxy_list, request->proxy);
+       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+       request->in_proxy_hash = FALSE;
+}
+
+
+static int insert_into_proxy_hash(REQUEST *request)
+{
+       int i, proxy;
+       char buf[128];
+
+       rad_assert(request->proxy != NULL);
+       rad_assert(proxy_list != NULL);
+
+       request->proxy->sockfd = -1;
+
+       PTHREAD_MUTEX_LOCK(&proxy_mutex);
+
+       request->home_server->currently_outstanding++;
+
+       if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
+               int found;
+               rad_listen_t *proxy_listener;
+
+               /*
+                *      Allocate a new proxy fd.  This function adds it
+                *      into the list of listeners.
+                */
+               proxy_listener = proxy_new_listener();
+               if (!proxy_listener) {
+                       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+                       DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+                       return 0;
+               }
+
+               /*
+                *      Cache it locally.
+                */
+               found = -1;
+               proxy = proxy_listener->fd;
+               for (i = 0; i < 32; i++) {
+                       DEBUG2("PROXY %d %d", i, proxy_fds[(proxy + i) & 0x1f]);
+
+                       /*
+                        *      Found a free entry.  Save the socket,
+                        *      and remember where we saved it.
+                        */
+                       if (proxy_fds[(proxy + i) & 0x1f] == -1) {
+                               found = (proxy + i) & 0x1f;
+                               proxy_fds[found] = proxy;
+                               proxy_listeners[found] = proxy_listener;
+                               break;
+                       }
+               }
+               rad_assert(found >= 0);
+
+               if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
+                       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+                       DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+                       return 0; /* leak proxy_listener */
+                       
+               }
+                   
+               if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
+                       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+                       DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
+                       return 0;
+               }
+       }
+       rad_assert(request->proxy->sockfd >= 0);
+
+       /*
+        *      FIXME: Hack until we get rid of rad_listen_t, and put
+        *      the information into the packet_list.
+        */
+       proxy = -1;
+       for (i = 0; i < 32; i++) {
+               if (proxy_fds[i] == request->proxy->sockfd) {
+                       proxy = i;
+                       break;
+               }
+       }
+
+       if (proxy < 0) {
+               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+               DEBUG2("ERROR: All sockets are full.");
+               return 0;
+       }
+
+       rad_assert(proxy_fds[proxy] != -1);
+       rad_assert(proxy_listeners[proxy] != NULL);
+       request->proxy_listener = proxy_listeners[proxy];
+
+       if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
+               /* FIXME: free id? */
+               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+               DEBUG2("ERROR: Failed to insert entry into proxy list");
+               return 0;
+       }
+       
+       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+       DEBUG3(" proxy: allocating destination %s port %d - Id %d",
+              inet_ntop(request->proxy->dst_ipaddr.af,
+                        &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
+              request->proxy->dst_port,
+              request->proxy->id);
+       
+       request->in_proxy_hash = TRUE;
+
+       return 1;
+}
+
+
+/*
+ *     Called as BOTH an event, and in-line from other functions.
+ */
+static void wait_for_proxy_id_to_expire(void *ctx)
+{
+       REQUEST *request = ctx;
+       home_server *home = request->home_server;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+       rad_assert(request->proxy != NULL);
+
+       request->when = request->proxy_when;
+       request->when.tv_sec += home->response_window;
+
+       if ((request->num_proxied_requests == request->num_proxied_responses) ||
+           timercmp(&now, &request->when, >)) {
+               if (request->packet) {
+                       DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
+                              request->number, request->packet->id,
+                              (unsigned int) (request->timestamp - start_time));
+               } else {
+                       DEBUG2("Cleaning up request %d with timestamp +%d",
+                              request->number,
+                              (unsigned int) (request->timestamp - start_time));
+               }
+               lrad_event_delete(el, request);
+               remove_from_proxy_hash(request);
+               remove_from_request_hash(request);
+               request_free(&request);
+               return;
+       }
+
+       if (!lrad_event_insert(el, wait_for_proxy_id_to_expire,
+                              request, &request->when)) {
+               rad_panic("Failed to insert event");
+       }
+}
+
+
+static void wait_for_child_to_die(void *ctx)
+{
+       REQUEST *request = ctx;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+
+       if ((request->child_state == REQUEST_QUEUED) |
+           (request->child_state == REQUEST_RUNNING)) {
+               request->delay += (request->delay >> 1);
+               tv_add(&request->when, request->delay);
+               
+               DEBUG2("Child is still stuck for request %d", request->number);
+               if (!lrad_event_insert(el, wait_for_child_to_die,
+                                      request, &request->when)) {
+                       rad_panic("Failed to insert event");
+               }
+               return;
+       }
+       
+       DEBUG2("Child is finally responsive for request %d", request->number);
+       remove_from_request_hash(request);
+
+       if (request->proxy) {
+               return wait_for_proxy_id_to_expire(request);
+       }
+
+       request_free(&request);
+}
+
+
+static void cleanup_delay(void *ctx)
+{
+       REQUEST *request = ctx;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+       rad_assert(request->child_state == REQUEST_CLEANUP_DELAY);
+
+       remove_from_request_hash(request);
+
+       if (request->proxy) {
+               return wait_for_proxy_id_to_expire(request);
+       }
+
+       DEBUG2("Cleaning up request %d ID %d with timestamp +%d",
+              request->number, request->packet->id,
+              (unsigned int) (request->timestamp - start_time));
+
+       request_free(&request); 
+}
+
+
+static void reject_delay(void *ctx)
+{
+       REQUEST *request = ctx;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+       rad_assert(request->child_state == REQUEST_REJECT_DELAY);
+
+       DEBUG2("Sending delayed reject for request %d", request->number);
+
+       request->listener->send(request->listener, request);
+
+       request->when.tv_sec += mainconfig.cleanup_delay;
+       request->child_state = REQUEST_CLEANUP_DELAY;
+
+       if (!lrad_event_insert(el, cleanup_delay,
+                              request, &request->when)) {
+               rad_panic("Failed to insert event");
+       }
+}
+
+
+static void revive_home_server(void *ctx)
+{
+       home_server *home = ctx;
+
+       home->state = HOME_STATE_ALIVE;
+       DEBUG2("Marking home server alive again... we have no idea if it really is alive or not.");
+}
+
+
+static void no_response_to_ping(void *ctx)
+{
+       REQUEST *request = ctx;
+       home_server *home = request->home_server;
+       char buffer[128];
+
+       home->num_received_pings = 0;
+
+       DEBUG2("No response to ping %d from home server %s port %d",
+              request->number,
+              inet_ntop(request->proxy->dst_ipaddr.af,
+                        &request->proxy->dst_ipaddr.ipaddr,
+                        buffer, sizeof(buffer)),
+              request->proxy->dst_port);
+
+       wait_for_proxy_id_to_expire(request);   
+}
+
+
+static void received_response_to_ping(REQUEST *request)
+{
+       home_server *home = request->home_server;
+       char buffer[128];
+
+       home->num_received_pings++;
+
+       DEBUG2("Received response to ping %d (%d in current sequence)",
+              request->number, home->num_received_pings);
+
+       if (home->num_received_pings < home->num_pings_to_alive) {
+               wait_for_proxy_id_to_expire(request);
+               return;
+       }
+
+       DEBUG2("Marking home server %s port %d alive",
+              inet_ntop(request->proxy->dst_ipaddr.af,
+                        &request->proxy->dst_ipaddr.ipaddr,
+                        buffer, sizeof(buffer)),
+              request->proxy->dst_port);
+
+       if (!lrad_event_delete(el, home)) {
+               DEBUG2("Hmm... no event for home server, WTF?");
+       }
+
+       if (!lrad_event_delete(el, request)) {
+               DEBUG2("Hmm... no event for request, WTF?");
+       }
+
+       wait_for_proxy_id_to_expire(request);
+
+       home->state = HOME_STATE_ALIVE;
+}
+
+
+static void ping_home_server(void *ctx)
+{
+       uint32_t jitter;
+       home_server *home = ctx;
+       REQUEST *request;
+       VALUE_PAIR *vp;
+
+       if (home->state == HOME_STATE_ALIVE) {
+               radlog(L_INFO, "Suspicious proxy state... continuing");
+               return;
+       }
+
+       request = request_alloc();
+       request->number = request_num_counter++;
+
+       request->proxy = rad_alloc(1);
+       rad_assert(request->proxy != NULL);
+
+       gettimeofday(&request->when, NULL);
+       home->when = request->when;
+
+       if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
+               request->proxy->code = PW_STATUS_SERVER;
+
+               vp = pairmake("Message-Authenticator", "0x00", T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+       } else if (home->type == HOME_TYPE_AUTH) {
+               request->proxy->code = PW_AUTHENTICATION_REQUEST;
+
+               vp = pairmake("User-Name", home->ping_user_name, T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+               vp = pairmake("User-Password", home->ping_user_password, T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+               vp = pairmake("Service-Type", "Authenticate-Only", T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+               vp = pairmake("Message-Authenticator", "0x00", T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+       } else {
+               request->proxy->code = PW_ACCOUNTING_REQUEST;
+
+               vp = pairmake("User-Name", home->ping_user_name, T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+               vp = pairmake("Acct-Status-Type", "Stop", T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+               vp = pairmake("Acct-Session-Id", "00000000", T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               pairadd(&request->proxy->vps, vp);
+
+               vp = pairmake("Event-Timestamp", "0", T_OP_SET);
+               if (!vp) rad_panic("Out of memory");
+               vp->vp_date = now.tv_sec;
+               pairadd(&request->proxy->vps, vp);
+       }
+
+       vp = pairmake("NAS-Identifier", "Ping! Are you alive?", T_OP_SET);
+       if (!vp) rad_panic("Out of memory");
+       pairadd(&request->proxy->vps, vp);
+
+       request->proxy->dst_ipaddr = home->ipaddr;
+       request->proxy->dst_port = home->port;
+       request->home_server = home;
+
+       rad_assert(request->proxy_listener == NULL);
+
+       if (!insert_into_proxy_hash(request)) {
+               DEBUG2("Failed inserting ping %d into proxy hash.  Discarding it.",
+                      request->number);
+               request_free(&request);
+               return;
+       }
+       rad_assert(request->proxy_listener != NULL);
+       request->proxy_listener->send(request->proxy_listener,
+                                     request);
+
+       /*
+        *      FIXME: add a separate timeout for ping packets!
+        */
+       request->child_state = REQUEST_PROXIED;
+       request->when.tv_sec += mainconfig.cleanup_delay;
+
+       if (!lrad_event_insert(el, no_response_to_ping,
+                              request, &request->when)) {
+               rad_panic("Failed to insert event");
+       }
+
+       /*
+        *      Add +/- 2s of jitter, as suggested in RFC 3539
+        *      and in the Issues and Fixes draft.
+        */
+       home->when.tv_sec += home->ping_interval - 2;
+
+       jitter = lrad_rand();
+       jitter ^= (jitter >> 10);
+       jitter &= ((1 << 23) - 1); /* 22 bits of 1 */
+
+       tv_add(&home->when, jitter);
+
+       if (!lrad_event_insert(el, ping_home_server,
+                              home, &home->when)) {
+               rad_panic("Failed to insert event");
+       }
+}
+
+
+/* maybe check this against wait_for_proxy_id_to_expire? */
+static void no_response_to_proxied_request(void *ctx)
+{
+       REQUEST *request = ctx;
+       home_server *home;
+       struct timeval when;
+       char buffer[128];
+       
+       rad_assert(request->magic == REQUEST_MAGIC);
+       rad_assert(request->child_state == REQUEST_PROXIED);
+
+       radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
+              request->number,
+              inet_ntop(request->proxy->dst_ipaddr.af,
+                        &request->proxy->dst_ipaddr.ipaddr,
+                        buffer, sizeof(buffer)),
+              request->proxy->dst_port);
+
+       if ((request->proxy->code == PW_ACCOUNTING_REQUEST) ||
+           (request->proxy->code == PW_STATUS_SERVER)) {
+               remove_from_request_hash(request);
+               wait_for_proxy_id_to_expire(request);
+
+       } else {
+               /* FIXME: run it through post-auth reject section? */
+
+               request->reply->code = PW_AUTHENTICATION_REJECT;
+
+               request->listener->send(request->listener, request);
+
+               request->child_state = REQUEST_CLEANUP_DELAY;
+               request->when.tv_sec += mainconfig.cleanup_delay;
+                       
+               /* cleanup_delay calls wait_for_proxy_id_to_expire */
+               if (!lrad_event_insert(el, cleanup_delay,
+                                      request, &request->when)) {
+                       rad_panic("Failed to insert event");
+               }
+       }
+               
+       home = request->home_server;
+       if (home->state == HOME_STATE_IS_DEAD) {
+               /* FIXME: assert that some event is set for the home server */
+               return;
+       }
+
+       /*
+        *      Enable the zombie period when we notice that the home
+        *      server hasn't responded.  We also back-date the start
+        *      of the zombie period to when the proxied request was
+        *      sent.
+        */
+       if (home->state == HOME_STATE_ALIVE) {
+               DEBUG2("WARNING: Home server %s port %d may be dead.",
+                      inet_ntop(request->proxy->dst_ipaddr.af,
+                                &request->proxy->dst_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      request->proxy->dst_port);
+               home->state = HOME_STATE_ZOMBIE;
+               home->zombie_period_start = now;
+               home->zombie_period_start.tv_sec -= home->response_window;
+               return;
+       }
+
+       when = home->zombie_period_start;
+       when.tv_sec += home->zombie_period;
+
+       if (timercmp(&now, &when, <)) {
+               return;
+       }
+
+       /*
+        *      It's been a zombie for too long, mark it as
+        *      dead.
+        */
+       home->state = HOME_STATE_IS_DEAD;
+       home->num_received_pings = 0;
+       home->when = request->when;
+       
+       if (home->ping_check != HOME_PING_CHECK_NONE) {
+               rad_assert((home->ping_check == HOME_PING_CHECK_STATUS_SERVER) ||
+                          (home->ping_user_name != NULL));
+               home->when.tv_sec += home->ping_interval;
+               if (!lrad_event_insert(el, ping_home_server,
+                                              home, &home->when)) {
+                       rad_panic("Failed to insert event");
+               }
+       } else {
+               home->when.tv_sec += home->revive_interval;
+               if (!lrad_event_insert(el, revive_home_server,
+                                      home, &home->when)) {
+                       rad_panic("Failed to insert event");
+               }
+       }
+}
+
+
+static void wait_a_bit(void *ctx)
+{
+       struct timeval when;
+       REQUEST *request = ctx;
+       lrad_event_callback_t callback = NULL;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+
+       switch (request->child_state) {
+       case REQUEST_QUEUED:
+       case REQUEST_RUNNING:
+               when = request->received;
+               when.tv_sec += mainconfig.max_request_time;
+
+               if (timercmp(&now, &when, <)) {
+                       callback = wait_a_bit;
+               } else {
+                       /* FIXME: kill unresponsive children? */
+                       radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d, in module %s component %s",
+                              (unsigned long)request->child_pid, request->number,
+                              request->module ? request->module : "<server core>",
+                              request->component ? request->component : "<server core>");             
+
+                       request->master_state = REQUEST_STOP_PROCESSING;
+
+                       request->delay = 500000;
+                       tv_add(&request->when, request->delay);
+                       callback = wait_for_child_to_die;
+               }
+               request->delay += request->delay >> 1;
+               break;
+
+       case REQUEST_PROXIED:
+       case REQUEST_REJECT_DELAY:
+       case REQUEST_CLEANUP_DELAY:
+               rad_assert(request->next_callback != NULL);
+
+               request->when = request->next_when;
+               callback = request->next_callback;
+               request->next_callback = NULL;
+               break;
+
+       case REQUEST_DONE:
+               request->child_pid = NO_SUCH_CHILD_PID;
+               if (request->proxy) {
+                       return wait_for_proxy_id_to_expire(request);
+               }
+               return;
+
+       default:
+               rad_panic("Internal sanity check failure");
+               return;
+       }
+
+#if 0
+       /*
+        *      FIXME: Print time as "+%d.%06d", as that's more
+        *      understandable than 32-bit numbers.
+        */
+       DEBUG2("Inserting event for request %d at time %d.%06d",
+              request->number,
+              (int) request->when.tv_sec, (int) request->when.tv_usec);
+#endif
+
+       if (!lrad_event_insert(el, callback,
+                              request, &request->when)) {
+               rad_panic("Failed to insert event");
+       }
+}
+
+
+static int request_pre_handler(REQUEST *request)
+{
+       int rcode;
+
+       rad_assert(request->magic == REQUEST_MAGIC);
+       rad_assert(request->packet != NULL);
+       rad_assert(request->packet->dst_port != 0);
+
+       request->child_state = REQUEST_RUNNING;
+
+       /*
+        *      Don't decode the packet if it's an internal "fake"
+        *      request.  Instead, just return so that the caller can
+        *      process it.
+        */
+       if (request->packet->dst_port == 0) {
+               request->username = pairfind(request->packet->vps,
+                                            PW_USER_NAME);
+               request->password = pairfind(request->packet->vps,
+                                            PW_USER_PASSWORD);
+               return 1;
+       }
+
+       /*
+        *      Put the decoded packet into it's proper place.
+        */
+       if (request->proxy_reply != NULL) {
+               rcode = request->proxy_listener->decode(request->proxy_listener,
+                                                       request);
+       } else {
+               rcode = request->listener->decode(request->listener, request);
+       }
+
+       if (rcode < 0) {
+               radlog(L_ERR, "%s Dropping packet without response.", librad_errstr);
+               request->child_state = REQUEST_DONE;
+               return 0;
+       }
+
+       if (!request->proxy) {
+               request->username = pairfind(request->packet->vps,
+                                            PW_USER_NAME);
+       } else {
+               int post_proxy_type = 0;
+               VALUE_PAIR *vp;
+
+               /*
+                *      Delete any reply we had accumulated until now.
+                */
+               pairfree(&request->reply->vps);
+               
+               /*
+                *      Run the packet through the post-proxy stage,
+                *      BEFORE playing games with the attributes.
+                */
+               vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
+               if (vp) {
+                       DEBUG2("  Found Post-Proxy-Type %s", vp->vp_strvalue);
+                       post_proxy_type = vp->lvalue;
+               }
+               rcode = module_post_proxy(post_proxy_type, request);
+               
+               /*
+                *      Delete the Proxy-State Attributes from the reply.
+                *      These include Proxy-State attributes from us and
+                *      remote server.
+                */
+               pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
+               
+               /*
+                *      Add the attributes left in the proxy reply to
+                *      the reply list.
+                */
+               pairadd(&request->reply->vps, request->proxy_reply->vps);
+               request->proxy_reply->vps = NULL;
+               
+               /*
+                *      Free proxy request pairs.
+                */
+               pairfree(&request->proxy->vps);
+               
+               /*
+                *      FIXME: If the packet is an Access-Challenge,
+                *      THEN add it to a cache, which does:
+                *
+                *      (src IP, State) -> (home server ip/port)
+                *
+                *      This allows the load-balancing code to
+                *      work for EAP...
+                *
+                *      Alternately, we can delete the State from the home
+                *      server, and use our own..  that might be better.
+                */
+               
+               switch (rcode) {
+                default:  /* Don't Do Anything */
+                       break;
+                case RLM_MODULE_FAIL:
+                       /* FIXME: debug print stuff */
+                       request->child_state = REQUEST_DONE;
+                       return 0;
+
+                case RLM_MODULE_HANDLED:
+                       /* FIXME: debug print stuff */
+                       request->child_state = REQUEST_DONE;
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+
+/*
+ *     Return 1 if we did proxy it, or the proxy attempt failed
+ *     completely.  Either way, the caller doesn't touch the request
+ *     any more if we return 1.
+ */
+static int successfully_proxied_request(REQUEST *request)
+{
+       int rcode;
+       int pre_proxy_type = 0;
+       VALUE_PAIR *realmpair;
+       VALUE_PAIR *strippedname;
+       VALUE_PAIR *vp;
+       char *realmname;
+       home_server *home;
+       struct timeval when;
+       REALM *realm = NULL;
+       char buffer[128];
+
+       realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
+       if (!realmpair ||
+           (realmpair->length == 0)) {
+               return 0;
+       }
+
+       realmname = (char *) realmpair->vp_strvalue;
+
+       realm = realm_find(realmname);
+       if (!realm) {
+               DEBUG2("ERROR: Cannot proxy to unknown realm %s",
+                      realmname);
+       reject_request:
+               /*
+                *      Failed proxying means silently discard
+                *      accounting responses.
+                */
+               if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+                       request->child_state = REQUEST_DONE;
+                       return 1;
+               }
+               
+               rad_assert(request->packet->code == PW_AUTHENTICATION_REQUEST);
+               
+               request->reply->code = PW_AUTHENTICATION_REJECT;
+
+               return 0;
+       }
+
+       if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
+            !realm->auth_pool) ||
+           ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+            !realm->acct_pool)) {
+               DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
+                      realmname);
+               return 0;
+       }
+       
+       home = home_server_ldb(realm, request->packet->code);
+       if (!home) {
+               DEBUG2("ERROR: Failed to find live home server for realm %s",
+                      realmname);
+               goto reject_request;
+       }
+
+       /*
+        *      Remember that we sent the request to a Realm.
+        */
+       pairadd(&request->packet->vps,
+               pairmake("Realm", realmname, T_OP_EQ));
+
+       /*
+        *      We read the packet from a detail file, AND it came from
+        *      the server we're about to send it to.  Don't do that.
+        */
+       if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+           (request->listener->type == RAD_LISTEN_DETAIL) &&
+           (home->ipaddr.af == AF_INET) &&
+           (request->packet->src_ipaddr.af == AF_INET) &&
+           (home->ipaddr.ipaddr.ip4addr.s_addr == request->packet->src_ipaddr.ipaddr.ip4addr.s_addr)) {
+               DEBUG2("    rlm_realm: Packet came from realm %s, proxy cancelled", realmname);
+               return 0;
+       }
+
+       /*
+        *      Allocate the proxy packet, only if it wasn't already
+        *      allocated by a module.  This check is mainly to support
+        *      the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
+        *
+        *      In those cases, the EAP module creates a "fake"
+        *      request, and recursively passes it through the
+        *      authentication stage of the server.  The module then
+        *      checks if the request was supposed to be proxied, and
+        *      if so, creates a proxy packet from the TUNNELED request,
+        *      and not from the EAP request outside of the tunnel.
+        *
+        *      The proxy then works like normal, except that the response
+        *      packet is "eaten" by the EAP module, and encapsulated into
+        *      an EAP packet.
+        */
+       if (!request->proxy) {
+               if ((request->proxy = rad_alloc(TRUE)) == NULL) {
+                       radlog(L_ERR|L_CONS, "no memory");
+                       exit(1);
+               }
+
+               /*
+                *      Copy the request, then look up name and
+                *      plain-text password in the copy.
+                *
+                *      Note that the User-Name attribute is the
+                *      *original* as sent over by the client.  The
+                *      Stripped-User-Name attribute is the one hacked
+                *      through the 'hints' file.
+                */
+               request->proxy->vps =  paircopy(request->packet->vps);
+       }
+
+       /*
+        *      Strip the name, if told to.
+        *
+        *      Doing it here catches the case of proxied tunneled
+        *      requests.
+        */
+       if (realm->striprealm == TRUE &&
+          (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
+               /*
+                *      If there's a Stripped-User-Name attribute in
+                *      the request, then use THAT as the User-Name
+                *      for the proxied request, instead of the
+                *      original name.
+                *
+                *      This is done by making a copy of the
+                *      Stripped-User-Name attribute, turning it into
+                *      a User-Name attribute, deleting the
+                *      Stripped-User-Name and User-Name attributes
+                *      from the vps list, and making the new
+                *      User-Name the head of the vps list.
+                */
+               vp = pairfind(request->proxy->vps, PW_USER_NAME);
+               if (!vp) {
+                       vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
+                       if (!vp) {
+                               radlog(L_ERR|L_CONS, "no memory");
+                               exit(1);
+                       }
+                       vp->next = request->proxy->vps;
+                       request->proxy->vps = vp;
+               }
+               memcpy(vp->vp_strvalue, strippedname->vp_strvalue,
+                      sizeof(vp->vp_strvalue));
+               vp->length = strippedname->length;
+
+               /*
+                *      Do NOT delete Stripped-User-Name.
+                */
+       }
+       
+       /*
+        *      If there is no PW_CHAP_CHALLENGE attribute but
+        *      there is a PW_CHAP_PASSWORD we need to add it
+        *      since we can't use the request authenticator
+        *      anymore - we changed it.
+        */
+       if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
+           pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
+               vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
+               if (!vp) {
+                       radlog(L_ERR|L_CONS, "no memory");
+                       exit(1);
+               }
+               vp->length = AUTH_VECTOR_LEN;
+               memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
+               pairadd(&(request->proxy->vps), vp);
+       }
+
+       /*
+        *      The RFC's say we have to do this, but FreeRADIUS
+        *      doesn't need it.
+        */
+       vp = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
+       if (!vp) {
+               radlog(L_ERR|L_CONS, "no memory");
+               exit(1);
+       }
+       sprintf(vp->vp_strvalue, "%d", request->packet->id);
+       vp->length = strlen(vp->vp_strvalue);
+
+       pairadd(&request->proxy->vps, vp);
+
+       request->proxy->code = request->packet->code;
+       request->proxy->dst_ipaddr = home->ipaddr;
+       request->proxy->dst_port = home->port;
+       request->home_server = home;
+
+       /*
+        *      Call the pre-proxy routines.
+        */
+       vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
+       if (vp) {
+               DEBUG2("  Found Pre-Proxy-Type %s", vp->vp_strvalue);
+               pre_proxy_type = vp->lvalue;
+       }
+       rcode = module_pre_proxy(pre_proxy_type, request);
+       switch (rcode) {
+       case RLM_MODULE_FAIL:
+       case RLM_MODULE_INVALID:
+       case RLM_MODULE_NOTFOUND:
+       case RLM_MODULE_REJECT:
+       case RLM_MODULE_USERLOCK:
+       default:
+               /* FIXME: debug print failed stuff */
+               goto reject_request;
+
+       case RLM_MODULE_HANDLED:
+               return 0;
+
+       /*
+        *      Only proxy the packet if the pre-proxy code succeeded.
+        */
+       case RLM_MODULE_NOOP:
+       case RLM_MODULE_OK:
+       case RLM_MODULE_UPDATED:
+               break;
+       }
+
+       /*
+        *      If it's a fake request, don't send the proxy
+        *      packet.  The outer tunnel session will take
+        *      care of doing that.
+        */
+       if (request->packet->dst_port == 0) {
+               return 1;
+       }
+       
+       if (!insert_into_proxy_hash(request)) {
+               DEBUG("ERROR: Failed to proxy request %d", request->number);
+               goto reject_request;
+       }
+
+       request->proxy_listener->encode(request->proxy_listener, request);
+
+       when = request->received;
+       when.tv_sec += mainconfig.max_request_time;
+       
+       gettimeofday(&request->proxy_when, NULL);
+
+       request->next_when = request->proxy_when;
+       request->next_when.tv_sec += home->response_window;
+
+       rad_assert(home->response_window > 0);
+       
+       if (timercmp(&when, &request->next_when, <)) {
+               request->next_when = when;
+       }
+       request->next_callback = no_response_to_proxied_request;
+
+       DEBUG2("Proxying request %d to realm %s, home server %s port %d",
+              request->number, realmname,
+              inet_ntop(request->proxy->dst_ipaddr.af,
+                        &request->proxy->dst_ipaddr.ipaddr,
+                        buffer, sizeof(buffer)),
+              request->proxy->dst_port);
+       
+       /*
+        *      Note that we set proxied AFTER creating the packet,
+        *      but BEFORE actually sending it.
+        *
+        *      Once we send it, the request is tainted, as
+        *      another thread may have picked it up.  Don't
+        *      touch it!
+        */
+       request->num_proxied_requests = 1;
+       request->num_proxied_responses = 0;
+       request->child_state = REQUEST_PROXIED;
+       request->proxy_listener->send(request->proxy_listener,
+                                     request);
+       return 1;
+}
+
+
+static void request_post_handler(REQUEST *request)
+{
+       int child_state = -1;
+       struct timeval when;
+       VALUE_PAIR *vp;
+
+       if (request->master_state == REQUEST_STOP_PROCESSING) {
+               DEBUG2("Request %d was cancelled.", request->number);
+               request->child_state = REQUEST_DONE;
+               return;
+       }
+
+       if (request->child_state != REQUEST_RUNNING) {
+               rad_panic("Internal sanity check failed");
+       }
+
+       /*
+        *      FIXME: check for Auth-Type = Reject, and reject it if so?
+        *      this means don't proxy it.
+        *
+        *      FIXME: check if we can't proxy it because all of the
+        *      home servers are dead or busy, and set post-auth-type
+        *      "proxy fail", or something like that.
+        */
+
+       if (mainconfig.proxy_requests &&
+           (request->packet->code != PW_STATUS_SERVER) &&
+           (request->reply->code == 0) &&
+           !request->proxy &&
+           successfully_proxied_request(request)) {
+               return;
+       }
+
+       /*
+        *      Fake requests don't get encoded or signed.  The caller
+        *      also requires the reply VP's, so we don't free them
+        *      here!
+        */
+       if (request->packet->dst_port == 0) {
+               /* FIXME: DEBUG going to the next request */
+               request->child_state = REQUEST_DONE;
+               return;
+       }
+
+       /*
+        *      Copy Proxy-State from the request to the reply.
+        */
+       vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
+       if (vp) pairadd(&request->reply->vps, vp);
+
+       /*
+        *      Access-Requests get delayed or cached.
+        */
+       if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+               gettimeofday(&request->next_when, NULL);
+
+               if (request->reply->code == 0) {
+                       DEBUG2("There was no response configured: rejecting request %d",
+                              request->number);
+                       request->reply->code = PW_AUTHENTICATION_REJECT;
+               }
+               
+               /*
+                *      If configured, delay Access-Reject packets.
+                */
+               if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
+                   (mainconfig.reject_delay > 0)) {
+                       when = request->received;
+                       when.tv_sec += mainconfig.reject_delay;
+                       
+                       if (timercmp(&when, &request->next_when, >)) {
+                               DEBUG2("Delaying reject of request %d for %d seconds",
+                                      request->number,
+                                      mainconfig.reject_delay);
+                               request->next_when = when;
+                               request->next_callback = reject_delay;
+                               request->child_state = REQUEST_REJECT_DELAY;
+                               return;
+                       }
+               }
+
+               request->next_when.tv_sec += mainconfig.cleanup_delay;
+               request->next_callback = cleanup_delay;
+               child_state = REQUEST_CLEANUP_DELAY;
+
+       } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+               request->next_callback = NULL; /* just to be safe */
+               child_state = REQUEST_DONE;
+
+               /*
+                *      FIXME: Status-Server should probably not be
+                *      handled here...
+                */
+       } else if (request->packet->code == PW_STATUS_SERVER) {
+               request->next_callback = NULL;
+               child_state = REQUEST_DONE;
+
+       } else {
+               rad_panic("Unknown packet type");
+       }
+
+       /*
+        *      Encode, sign, and send.  The accounting request
+        *      handler takes care of suppressing responses when
+        *      request->reply->code == 0.
+        */
+       request->listener->send(request->listener, request);
+
+       /*
+        *      Clean up.  These are no longer needed.
+        */
+       pairfree(&request->config_items);
+
+       pairfree(&request->packet->vps);
+       request->username = NULL;
+       request->password = NULL;
+
+       pairfree(&request->reply->vps);
+
+       if (request->proxy) {
+               pairfree(&request->proxy->vps);
+       }
+       if (request->proxy_reply) {
+               pairfree(&request->proxy_reply->vps);
+       }
+
+       DEBUG2("Finished request %d state %d", request->number, child_state);
+
+       request->child_state = child_state;
+}
+
+
+static void received_retransmit(REQUEST *request, const RADCLIENT *client)
+{
+       char buffer[128];
+
+       switch (request->child_state) {
+       case REQUEST_QUEUED:
+       case REQUEST_RUNNING:
+               radlog(L_ERR, "Discarding duplicate request from "
+                      "client %s port %d - ID: %d due to unfinished request %d",
+                      client->shortname,
+                      request->packet->src_port,request->packet->id,
+                      request->number);
+               break;
+
+       case REQUEST_PROXIED:
+               DEBUG2("Sending duplicate proxied request to home server %s port %d - ID: %d",
+                      inet_ntop(request->proxy->dst_ipaddr.af,
+                                &request->proxy->dst_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                      request->proxy->dst_port,
+                      request->proxy->id);
+               request->num_proxied_requests++;
+               request->proxy_listener->send(request->proxy_listener,
+                                             request);
+               break;
+
+       case REQUEST_REJECT_DELAY:
+               DEBUG2("Waiting to send Access-Reject "
+                      "to client %s port %d - ID: %d",
+                      client->shortname,
+                      request->packet->src_port, request->packet->id);
+               break;
+
+       case REQUEST_CLEANUP_DELAY:
+       case REQUEST_DONE:
+               DEBUG2("Sending duplicate reply "
+                      "to client %s port %d - ID: %d",
+                      client->shortname,
+                      request->packet->src_port, request->packet->id);
+               request->listener->send(request->listener, request);
+               break;
+       }
+}
+
+
+static void received_conflicting_request(REQUEST *request,
+                                        const RADCLIENT *client)
+{
+       radlog(L_ERR, "Received conflicting packet from "
+              "client %s port %d - ID: %d due to unfinished request %d.  Giving up on old request.",
+              client->shortname,
+              request->packet->src_port, request->packet->id,
+              request->number);
+
+       switch (request->child_state) {
+       case REQUEST_QUEUED:
+       case REQUEST_RUNNING:
+               request->master_state = REQUEST_STOP_PROCESSING;
+               request->delay += request->delay >> 1;
+
+               tv_add(&request->when, request->delay);
+
+               if (!lrad_event_insert(el,
+                                      wait_for_child_to_die,
+                                      request, &request->when)) {
+                       rad_panic("Failed to insert event");
+               }
+               return;
+
+       default:
+               break;
+       }
+
+       remove_from_request_hash(request);
+
+       /*
+        *      The request stays in the event queue.  At some point,
+        *      the child will notice, and we can then delete it.
+        */
+}
+
+
+static int can_handle_new_request(RADIUS_PACKET *packet,
+                                 const RADCLIENT *client)
+{
+       /*
+        *      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 = lrad_packet_list_num_elements(pl);
+               
+               /*
+                *      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->shortname,
+                              packet->src_port, packet->id);
+                       radlog(L_INFO, "WARNING: Please check the %s file.\n"
+                              "\tThe value for 'max_requests' is probably set too low.\n", mainconfig.radiusd_conf);
+                       return 0;
+               } /* else there were a small number of requests */
+       } /* else there was no configured limit for requests */
+
+       /*
+        *      FIXME: Add per-client checks.  If one client is sending
+        *      too many packets, start discarding them.
+        *
+        *      We increment the counters here, and decrement them
+        *      when the response is sent... somewhere in this file.
+        */
+       
+       /*
+        *      FUTURE: 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 1;
+}
+
+
+int received_request(rad_listen_t *listener,
+                    RADIUS_PACKET *packet, REQUEST **prequest,
+                    const RADCLIENT *client)
+{
+       RADIUS_PACKET **packet_p;
+       REQUEST *request = NULL;
+
+       packet_p = lrad_packet_list_find(pl, packet);
+       if (packet_p) {
+               request = lrad_packet2myptr(REQUEST, packet, packet_p);
+
+               if (memcmp(request->packet->vector, packet->vector,
+                          sizeof(packet->vector)) == 0) {
+                       received_retransmit(request, client);
+                       return 0;
+               }
+               received_conflicting_request(request, client);
+               request = NULL;
+
+       } else if (!can_handle_new_request(packet, client)) {
+               return 0;
+       }
+
+       /*
+        *      Create and initialize the new request.
+        */
+       request = request_alloc(); /* never fails */
+       
+       if ((request->reply = rad_alloc(0)) == NULL) {
+               radlog(L_ERR, "No memory");
+               exit(1);
+       }
+
+       request->listener = listener;
+       request->packet = packet;
+       request->packet->timestamp = request->timestamp;
+       request->number = request_num_counter++;
+       strlcpy(request->secret, client->secret, sizeof(request->secret));
+       
+       /*
+        *      Remember the request in the list.
+        */
+       if (!lrad_packet_list_insert(pl, &request->packet)) {
+               radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", request->number);
+               request_free(&request);
+               return 0;
+       }
+       request->in_request_hash = TRUE;
+       
+       /*
+        *      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.
+        */
+
+       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;
+
+       request->master_state = REQUEST_ACTIVE;
+       request->child_state = REQUEST_QUEUED;
+       request->next_callback = NULL;
+
+       gettimeofday(&request->received, NULL);
+       request->when = request->received;
+       request->delay = USEC / 10;
+
+       tv_add(&request->when, request->delay);
+
+       if (!lrad_event_insert(el, wait_a_bit,
+                              request, &request->when)) {
+               rad_panic("Failed to insert event");
+       }
+
+       *prequest = request;
+       return 1;
+}
+
+
+REQUEST *received_proxy_response(RADIUS_PACKET *packet)
+{
+       char            buffer[128];
+       home_server     *home;
+       REQUEST         *request;
+
+       if (!home_server_find(&packet->src_ipaddr, packet->src_port)) {
+               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 NULL;
+       }
+
+       /*
+        *      Also removes from the proxy hash if responses == requests
+        */
+       request = lookup_in_proxy_hash(packet);
+
+       if (!request) {
+               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 NULL;
+       }
+
+       home = request->home_server;
+
+       gettimeofday(&now, NULL);
+       home->state = HOME_STATE_ALIVE;
+
+       if (request->reply && request->reply->code != 0) {
+               DEBUG2("We already replied to this request.  Discarding response from home server.");
+               rad_free(&packet);
+               return NULL;
+       }
+
+       /*
+        *      We had previously received a reply, so we don't need
+        *      to do anything here.
+        */
+       if (request->proxy_reply) {
+               if (memcmp(request->proxy_reply->vector,
+                          packet->vector,
+                          sizeof(request->proxy_reply->vector)) == 0) {
+                       DEBUG2("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,
+                              request->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");
+               }
+
+               /* assert that there's an event queued for request? */
+               rad_free(&packet);
+               return NULL;
+       }
+
+       switch (request->child_state) {
+       case REQUEST_QUEUED:
+       case REQUEST_RUNNING:
+               rad_panic("Internal sanity check failed for child state");
+               break;
+
+       case REQUEST_REJECT_DELAY:
+       case REQUEST_CLEANUP_DELAY:
+       case REQUEST_DONE:
+               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,
+                      request->number);
+               /* assert that there's an event queued for request? */
+               rad_free(&packet);
+               return NULL;
+               
+       case REQUEST_PROXIED:
+               break;
+       }
+
+       request->proxy_reply = packet;
+
+       /*
+        *      There's no incoming request, so it's a proxied packet
+        *      we originated.
+        */
+       if (!request->packet) {
+               received_response_to_ping(request);
+               return NULL;
+       }
+
+       request->child_state = REQUEST_QUEUED;
+       request->when = now;
+       request->delay = USEC / 10;
+       tv_add(&request->when, request->delay);
+
+       /*
+        *      Wait a bit will take care of max_request_time
+        */
+       if (!lrad_event_insert(el, wait_a_bit,
+                              request, &request->when)) {
+               rad_panic("Failed to insert event");
+       }
+
+       return request;
+}
+
+
+/*
+ *     Externally-visibly functions.
+ */
+int radius_event_init(int spawn_flag)
+{
+       if (el) return 0;
+
+       time(&start_time);
+
+       el = lrad_event_list_create();
+       if (!el) return 0;
+
+       pl = lrad_packet_list_create(0);
+       if (!el) return 0;
+
+       request_num_counter = 0;
+
+       /*
+        *      Move all of the thread calls to this file?
+        *
+        *      It may be best for the mutexes to be in this file...
+        */
+       have_children = spawn_flag;
+
+       if (mainconfig.proxy_requests) {
+               int i;
+               rad_listen_t *listener;
+                       
+               /*
+                *      Create the tree for managing proxied requests and
+                *      responses.
+                */
+               proxy_list = lrad_packet_list_create(1);
+               if (!proxy_list) return 0;
+               
+#ifdef HAVE_PTHREAD_H
+               if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
+                       radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
+                              strerror(errno));
+                       exit(1);
+               }
+#endif
+
+               /*
+                *      Mark the Fd's as unused.
+                */
+               for (i = 0; i < 32; i++) proxy_fds[i] = -1;
+               
+               i = -1;
+
+               for (listener = mainconfig.listen;
+                    listener != NULL;
+                    listener = listener->next) {
+                       if (listener->type == RAD_LISTEN_PROXY) {
+                               /*
+                                *      FIXME: This works only because we
+                                *      start off with one proxy socket.
+                                */
+                               rad_assert(proxy_fds[listener->fd & 0x1f] == -1);
+                               rad_assert(proxy_listeners[listener->fd & 0x1f] == NULL);
+
+                               proxy_fds[listener->fd & 0x1f] = listener->fd;
+                               proxy_listeners[listener->fd & 0x1f] = listener;
+                               if (!lrad_packet_list_socket_add(proxy_list, listener->fd)) {
+                                       rad_assert(0 == 1);
+                               }
+                               i = listener->fd;
+                       }
+               }
+
+               if (mainconfig.proxy_requests) rad_assert(i >= 0);
+       }
+
+       thread_pool_init(spawn_flag);
+
+       return 1;
+}
+
+
+static int request_hash_cb(void *ctx, void *data)
+{
+       ctx = ctx;              /* -Wunused */
+       REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
+
+       rad_assert(request->in_proxy_hash == FALSE);
+
+       lrad_event_delete(el, request);
+       remove_from_request_hash(request);
+       request_free(&request);
+
+       return 0;
+}
+
+
+static int proxy_hash_cb(void *ctx, void *data)
+{
+       ctx = ctx;              /* -Wunused */
+       REQUEST *request = lrad_packet2myptr(REQUEST, proxy, data);
+
+       lrad_packet_list_yank(proxy_list, request->proxy);
+       request->in_proxy_hash = FALSE;
+
+       if (!request->in_request_hash) {
+               lrad_event_delete(el, request);
+               request_free(&request);
+       }
+
+       return 0;
+}
+
+
+void radius_event_free(void)
+{
+       /*
+        *      FIXME: Stop all threads, or at least check that
+        *      they're all waiting on the semaphore, and the queues
+        *      are empty.
+        */
+
+       /*
+        *      There are requests in the proxy hash that aren't
+        *      referenced from anywhere else.  Remove them first.
+        */
+       if (proxy_list) {
+               PTHREAD_MUTEX_LOCK(&proxy_mutex);
+               lrad_packet_list_walk(proxy_list, NULL, proxy_hash_cb);
+               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+               lrad_packet_list_free(proxy_list);
+               proxy_list = NULL;
+       }
+
+       lrad_packet_list_walk(pl, NULL, request_hash_cb);
+
+       lrad_packet_list_free(pl);
+       pl = NULL;
+
+       lrad_event_list_free(el);
+}
+
+int radius_event_process(struct timeval **pptv)
+{
+       int rcode;
+       struct timeval when;
+
+       if (!el) return 0;
+
+       if (lrad_event_list_num_elements(el) == 0) {
+               *pptv = NULL;
+               return 1;
+       }
+
+       gettimeofday(&now, NULL);
+       when = now;
+
+       do {
+               rcode = lrad_event_run(el, &when);
+       } while (rcode == 1);
+
+       gettimeofday(&now, NULL);
+
+       if ((when.tv_sec == 0) && (when.tv_usec == 0)) {
+               if (lrad_event_list_num_elements(el) == 0) {
+                       *pptv = NULL;
+                       return 1;
+               }
+               rad_panic("Internal sanity check failed");
+               
+       } else if (timercmp(&now, &when, >)) {
+               DEBUG2("WTF?");
+               when.tv_sec = 0;
+               when.tv_usec = 1;
+
+       } else {
+               when.tv_sec -= now.tv_sec;
+               when.tv_usec -= now.tv_usec;
+               if (when.tv_usec < 0) {
+                       when.tv_sec--;
+                       when.tv_usec += USEC;
+               }
+       }
+       **pptv = when;
+
+       return 1;
+}
+
+void radius_handle_request(REQUEST *request, RAD_REQUEST_FUNP fun)
+{
+       if (!request_pre_handler(request)) {
+               DEBUG2("Going to the next request at X");
+               return;
+       }
+       
+       rad_assert(fun != NULL);
+       rad_assert(request != NULL);
+       
+       fun(request);
+       
+       request_post_handler(request);
+       DEBUG2("Going to the next request");
+       return;
+}
+
index cf4d24e..b409baf 100644 (file)
@@ -301,371 +301,3 @@ static void debug_pair_list(PAIR_LIST *pl)
        }
 }
 #endif
-
-#ifndef BUILDDBM /* HACK HACK */
-
-/*
- *     Free a REALM list.
- */
-void realm_free(REALM *cl)
-{
-       REALM *next;
-
-       while(cl) {
-               next = cl->next;
-               free(cl);
-               cl = next;
-       }
-}
-
-/*
- *     Read the realms file.
- */
-int read_realms_file(const char *file)
-{
-       FILE *fp;
-       char buffer[256];
-       char realm[256];
-       char hostnm[256];
-       char opts[256];
-       char *s, *p;
-       int lineno = 0;
-       REALM *c, **tail;
-       int got_realm = FALSE;
-
-       realm_free(mainconfig.realms);
-       mainconfig.realms = NULL;
-       tail = &mainconfig.realms;
-
-       if ((fp = fopen(file, "r")) == NULL) {
-               /* The realms file is not mandatory.  If it exists it will
-                  be used, however, since the new style config files are
-                  more robust and flexible they are more likely to get used.
-                  So this is a non-fatal error.  */
-               return 0;
-       }
-
-       while(fgets(buffer, 256, fp) != NULL) {
-               lineno++;
-               if (!feof(fp) && (strchr(buffer, '\n') == NULL)) {
-                       radlog(L_ERR, "%s[%d]: line too long", file, lineno);
-                       return -1;
-               }
-               if (buffer[0] == '#' || buffer[0] == '\n')
-                       continue;
-               p = buffer;
-               if (!getword(&p, realm, sizeof(realm)) ||
-                               !getword(&p, hostnm, sizeof(hostnm))) {
-                       radlog(L_ERR, "%s[%d]: syntax error", file, lineno);
-                       continue;
-               }
-
-               got_realm = TRUE;
-               c = rad_malloc(sizeof(REALM));
-               memset(c, 0, sizeof(REALM));
-
-               if ((s = strchr(hostnm, ':')) != NULL) {
-                       *s++ = 0;
-                       c->auth_port = atoi(s);
-                       c->acct_port = c->auth_port + 1;
-               } else {
-                       c->auth_port = PW_AUTH_UDP_PORT;
-                       c->acct_port = PW_ACCT_UDP_PORT;
-               }
-
-               if (strcmp(hostnm, "LOCAL") == 0) {
-                       /*
-                        *      Local realms don't have an IP address,
-                        *      secret, or port.
-                        */
-                       c->ipaddr.af = c->acct_ipaddr.af = AF_INET;
-                       c->ipaddr.ipaddr.ip4addr.s_addr = c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-                       c->secret[0] = '\0';
-                       c->auth_port = 0;
-                       c->acct_port = 0;
-
-               } else {
-                       RADCLIENT *client;
-
-                       if (ip_hton(hostnm, AF_INET, &c->ipaddr) < 0) {
-                               radlog(L_CONS|L_ERR, "%s[%d]: Failed to look up hostname %s",
-                                      file, lineno, hostnm);
-                               return -1;
-                       }
-                       c->acct_ipaddr = c->ipaddr;
-
-                       /*
-                        *      Find the remote server in the "clients" list.
-                        *      If we can't find it, there's a big problem...
-                        */
-                       client = client_find_old(&c->ipaddr);
-                       if (client == NULL) {
-                         radlog(L_CONS|L_ERR, "%s[%d]: Cannot find 'clients' file entry of remote server %s for realm \"%s\"",
-                                file, lineno, hostnm, realm);
-                         return -1;
-                       }
-                       memcpy(c->secret, client->secret, sizeof(c->secret));
-               }
-
-               /*
-                *      Double-check lengths to be sure they're sane
-                */
-               if (strlen(hostnm) >= sizeof(c->server)) {
-                       radlog(L_ERR, "%s[%d]: server name of length %u is greater than the allowed maximum of %u.",
-                              file, lineno,
-                              strlen(hostnm),
-                              sizeof(c->server) - 1);
-                       return -1;
-               }
-               if (strlen(realm) > sizeof(c->realm)) {
-                       radlog(L_ERR, "%s[%d]: realm of length %u is greater than the allowed maximum of %u.",
-                              file, lineno,
-                              strlen(realm),
-                              sizeof(c->realm) - 1);
-                       return -1;
-               }
-
-               /*
-                *      OK, they're sane, copy them over.
-                */
-               strcpy(c->realm, realm);
-               strcpy(c->server, hostnm);
-               c->striprealm = TRUE;
-               c->active = TRUE;
-               c->acct_active = TRUE;
-
-               while (getword(&p, opts, sizeof(opts))) {
-                       if (strcmp(opts, "nostrip") == 0)
-                               c->striprealm = FALSE;
-                       if (strstr(opts, "noacct") != NULL)
-                               c->acct_port = 0;
-                       if (strstr(opts, "trusted") != NULL)
-                               c->trusted = 1;
-                       if (strstr(opts, "notrealm") != NULL)
-                               c->notrealm = 1;
-                       if (strstr(opts, "notsuffix") != NULL)
-                               c->notrealm = 1;
-               }
-
-               c->next = NULL;
-               *tail = c;
-               tail = &c->next;
-       }
-       fclose(fp);
-
-       /*
-        *      Complain only if the realms file has content.
-        */
-       if (got_realm) {
-               radlog(L_INFO, "Using deprecated realms file.  Support for this will go away soon.");
-       }
-
-       return 0;
-}
-#endif /* BUILDDBM */
-
-/*
- * Mark a host inactive
- */
-void realm_disable(REQUEST *request)
-{
-       REALM *cl;
-       time_t now;
-       lrad_ipaddr_t *ipaddr;
-       int port;
-
-       now = time(NULL);
-
-       if (!request || !request->proxy ||
-           (request->proxy->dst_ipaddr.af != AF_INET)) return;
-       
-       ipaddr = &request->proxy->dst_ipaddr;
-       port = request->proxy->dst_port;
-
-       for (cl = mainconfig.realms; cl; cl = cl->next) {
-               if (ipaddr->af != cl->ipaddr.af) continue;
-
-               /*
-                *      FIXME: Switch over AF
-                */
-               if ((ipaddr->ipaddr.ip4addr.s_addr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
-                   (port == cl->auth_port)) {
-                       /*
-                        *      If we've received a reply (any reply)
-                        *      from the home server in the time spent
-                        *      re-sending this request, then don't mark
-                        *      the realm as dead.
-                        */
-                       if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
-                               continue;
-                       }
-
-                       cl->active = FALSE;
-                       cl->wakeup = now + mainconfig.proxy_dead_time;
-                       radlog(L_PROXY, "marking authentication server %s:%d for realm %s dead",
-                               cl->server, port, cl->realm);
-               } else if ((ipaddr->ipaddr.ip4addr.s_addr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
-                          (port == cl->acct_port)) {
-                       if (cl->last_reply > (( now - mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count ))) {
-                               continue;
-                       }
-
-                       cl->acct_active = FALSE;
-                       cl->acct_wakeup = now + mainconfig.proxy_dead_time;
-                       radlog(L_PROXY, "marking accounting server %s:%d for realm %s dead",
-                               cl->acct_server, port, cl->realm);
-               }
-       }
-}
-
-/*
- *     Find a realm in the REALM list.
- */
-REALM *realm_find(const char *realm, int accounting)
-{
-       REALM *cl;
-       REALM *default_realm = NULL;
-       time_t now;
-       int dead_match = 0;
-
-       now = time(NULL);
-
-       /*
-        *      If we're passed a NULL realm pointer,
-        *      then look for a "NULL" realm string.
-        */
-       if (realm == NULL) {
-               realm = "NULL";
-       }
-
-       for (cl = mainconfig.realms; cl; cl = cl->next) {
-               /*
-                *      Wake up any sleeping realm.
-                */
-               if (cl->wakeup <= now) {
-                       cl->active = TRUE;
-               }
-               if (cl->acct_wakeup <= now) {
-                       cl->acct_active = TRUE;
-               }
-
-               /*
-                *      Asked for auth/acct, and the auth/acct server
-                *      is not active.  Skip it.
-                */
-               if ((!accounting && !cl->active) ||
-                   (accounting && !cl->acct_active)) {
-
-                       /*
-                        *      We've been asked to NOT fall through
-                        *      to the DEFAULT realm if there are
-                        *      exact matches for this realm which are
-                        *      dead.
-                        */
-                       if ((!mainconfig.proxy_fallback) &&
-                           (strcasecmp(cl->realm, realm) == 0)) {
-                               dead_match = 1;
-                       }
-                       continue;
-               }
-
-               /*
-                *      If it matches exactly, return it.
-                *
-                *      Note that we just want ONE live realm
-                *      here.  We don't care about round-robin, or
-                *      scatter techniques, as that's more properly
-                *      the responsibility of the proxying code.
-                */
-               if (strcasecmp(cl->realm, realm) == 0) {
-                       return cl;
-               }
-
-               /*
-                *      No default realm, try to set one.
-                */
-               if ((default_realm == NULL) &&
-                   (strcmp(cl->realm, "DEFAULT") == 0)) {
-                 default_realm = cl;
-               }
-       } /* loop over all realms */
-
-       /*
-        *      There WAS one or more matches which were marked dead,
-        *      AND there were NO live matches, AND we've been asked
-        *      to NOT fall through to the DEFAULT realm.  Therefore,
-        *      we return NULL, which means "no match found".
-        */
-       if (!mainconfig.proxy_fallback && dead_match) {
-               if (mainconfig.wake_all_if_all_dead) {
-                       REALM *rcl = NULL;
-                       for (cl = mainconfig.realms; cl; cl = cl->next) {
-                               if(strcasecmp(cl->realm,realm) == 0) {
-                                       if (!accounting && !cl->active) {
-                                               cl->active = TRUE;
-                                               rcl = cl;
-                                       }
-                                       else if (accounting &&
-                                                !cl->acct_active) {
-                                               cl->acct_active = TRUE;
-                                               rcl = cl;
-                                       }
-                               }
-                       }
-                       return rcl;
-               }
-               else {
-                       return NULL;
-               }
-       }
-
-       /*      If we didn't find the realm 'NULL' don't return the
-        *      DEFAULT entry.
-        */
-       if ((strcmp(realm, "NULL")) == 0) {
-         return NULL;
-       }
-
-       /*
-        *      Didn't find anything that matched exactly, return the
-        *      DEFAULT realm.  We also return the DEFAULT realm if
-        *      all matching realms were marked dead, and we were
-        *      asked to fall through to the DEFAULT realm in this
-        *      case.
-        */
-       return default_realm;
-}
-
-/*
- *     Find a realm for a proxy reply by proxy's IP
- *
- *     Note that we don't do anything else.
- */
-REALM *realm_findbyaddr(uint32_t ipaddr, int port)
-{
-       REALM *cl;
-
-       /*
-        *      Note that we do NOT check for inactive realms!
-        *
-        *      The purpose of this code is simply to find a matching
-        *      source IP/Port pair, for a home server which is allowed
-        *      to send us proxy replies.  If we get a reply, then it
-        *      doesn't matter if we think the realm is inactive.
-        */
-       for (cl = mainconfig.realms; cl != NULL; cl = cl->next) {
-               if (cl->ipaddr.af != AF_INET) rad_assert(0 == 1);
-
-               if ((ipaddr == cl->ipaddr.ipaddr.ip4addr.s_addr) &&
-                   (port == cl->auth_port)) {
-                       return cl;
-
-               } else if ((ipaddr == cl->acct_ipaddr.ipaddr.ip4addr.s_addr) &&
-                          (port == cl->acct_port)) {
-                       return cl;
-               }
-       }
-
-       return NULL;
-}
-
index 393e3fa..d6f879f 100644 (file)
@@ -61,7 +61,6 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #include <freeradius-devel/radius_snmp.h>
-#include <freeradius-devel/request_list.h>
 
 static time_t start_time = 0;
 
@@ -81,8 +80,9 @@ typedef struct rad_listen_master_t {
        rad_listen_free_t       free;
        rad_listen_recv_t       recv;
        rad_listen_send_t       send;
-       rad_listen_update_t     update;
        rad_listen_print_t      print;
+       rad_listen_encode_t     encode;
+       rad_listen_decode_t     decode;
 } rad_listen_master_t;
 
 typedef struct listen_socket_t {
@@ -182,266 +182,6 @@ static int rad_status_server(REQUEST *request)
        return 0;
 }
 
-static int request_num_counter = 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 RADCLIENT *client)
-{
-       REQUEST *curreq;
-       char buffer[128];
-
-       rad_assert(listener->rl != NULL);
-
-       /*
-        *      If there is no existing request of id, code, etc.,
-        *      then we can return, and let it be processed.
-        */
-       if ((curreq = rl_find(listener->rl, 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) {
-                       /*
-                        *      FIXME: This is now per-socket,
-                        *      when it should really be global
-                        *      to the server!
-                        */
-                       int request_count = rl_num_requests(listener->rl);
-
-                       /*
-                        *      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->shortname,
-                                      packet->src_port, packet->id);
-                               radlog(L_INFO, "WARNING: Please check the %s file.\n"
-                                      "\tThe value for 'max_requests' is probably set too low.\n", mainconfig.radiusd_conf);
-                               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.
-                        *
-                        *      This code will never get hit for
-                        *      accounting packets, as they're always
-                        *      updated, and never re-transmitted.
-                        */
-                       if (curreq->proxy && !curreq->proxy_reply) {
-                               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_listener->send(curreq->proxy_listener, curreq);
-                               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->shortname,
-                              packet->src_port, packet->id,
-                              curreq->number);
-                       return 0;
-               } /* else the authentication vectors were different */
-
-               /*
-                *      We're waiting for a proxy reply, but no one is
-                *      currently processing the request.  We can
-                *      discard the old request, and ignore any reply
-                *      from the home server, because the NAS will
-                *      never care...
-                */
-               if (curreq->proxy && !curreq->proxy_reply &&
-                   (curreq->child_pid == NO_SUCH_CHILD_PID)) {
-                       radlog(L_ERR, "Discarding old proxied request %d from "
-                              "client %s port %d - ID: %d due to new request from the client",
-                              curreq->number, client->shortname,
-                              packet->src_port, packet->id);
-               } else {
-                       /*
-                        *      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->shortname,
-                              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_assert(curreq->listener == listener);
-                       listener->send(listener, curreq);
-                       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->shortname,
-                              packet->src_port, packet->id);
-                       rad_assert(curreq->listener == listener);
-                       listener->send(listener, curreq);
-                       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->shortname, 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_yank(listener->rl, curreq);
-               request_free(&curreq);
-       }
-
-       /*
-        *      A unique per-request counter.
-        */
-       curreq = request_alloc(); /* never fails */
-       
-       if ((curreq->reply = rad_alloc(0)) == NULL) {
-               radlog(L_ERR, "No memory");
-               exit(1);
-       }
-       curreq->listener = listener;
-       curreq->packet = packet;
-       curreq->packet->timestamp = curreq->timestamp;
-       curreq->number = request_num_counter++;
-       strlcpy(curreq->secret, client->secret, sizeof(curreq->secret));
-       
-       /*
-        *      Remember the request in the list.
-        */
-       if (!rl_add(listener->rl, curreq)) {
-               radlog(L_ERR, "Failed to insert request %d in the list of live requests: discarding", curreq->number);
-               request_free(&curreq);
-               return 0;
-       }
-       
-       /*
-        *      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.
-        */
-
-       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;
-}
-
-
 static int socket_print(rad_listen_t *this, char *buffer, size_t bufsize)
 {
        size_t len;
@@ -580,32 +320,6 @@ static int auth_socket_send(rad_listen_t *listener, REQUEST *request)
        rad_assert(request->listener == listener);
        rad_assert(listener->send == auth_socket_send);
 
-       /*
-        *      Ensure that the reply is sane
-        */
-       if (request->reply->code == 0) {
-               DEBUG2("There was no response configured: rejecting request %d", request->number);
-               request->reply->code = PW_AUTHENTICATION_REJECT;
-       }
-
-       /*
-        *      If we're delaying authentication rejects, then
-        *      mark the request as delayed, and do NOT send a
-        *      response right now.
-        *
-        *      However, if it's already marked as delayed, then
-        *      send it now.
-        */
-       if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
-           ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) == 0) &&
-           (mainconfig.reject_delay > 0) &&
-           ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0)) {
-               DEBUG2("Delaying request %d for %d seconds",
-                      request->number, mainconfig.reject_delay);
-               request->options |= RAD_REQUEST_OPTION_DELAYED_REJECT;
-               return 0;
-       }
-
        return rad_send(request->reply, request->packet, request->secret);
 }
 
@@ -645,7 +359,8 @@ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
        request->proxy->src_ipaddr = sock->ipaddr;
        request->proxy->src_port = sock->port;
 
-       return rad_send(request->proxy, request->packet, request->proxysecret);
+       return rad_send(request->proxy, request->packet,
+                       request->home_server->secret);
 }
 
 
@@ -714,7 +429,7 @@ static int auth_socket_recv(rad_listen_t *listener,
                break;
        } /* switch over packet types */
        
-       if (!common_checks(listener, packet, prequest, client)) {
+       if (!received_request(listener, packet, prequest, client)) {
                rad_free(&packet);
                return 0;
        }
@@ -787,7 +502,7 @@ static int acct_socket_recv(rad_listen_t *listener,
         *      FIXME: Accounting duplicates should be handled
         *      differently than authentication duplicates.
         */
-       if (!common_checks(listener, packet, prequest, client)) {
+       if (!received_request(listener, packet, prequest, client)) {
                rad_free(&packet);
                return 0;
        }
@@ -803,8 +518,7 @@ static int acct_socket_recv(rad_listen_t *listener,
 static int proxy_socket_recv(rad_listen_t *listener,
                              RAD_REQUEST_FUNP *pfun, REQUEST **prequest)
 {
-       REALM           *cl;
-       REQUEST         *oldreq;
+       REQUEST         *request;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
        char            buffer[128];
@@ -816,27 +530,6 @@ static int proxy_socket_recv(rad_listen_t *listener,
        }
 
        /*
-        *      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) {
@@ -863,132 +556,60 @@ static int proxy_socket_recv(rad_listen_t *listener,
                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);
+       request = received_proxy_response(packet);
+       if (!request) {
                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;
-       }
+       rad_assert(fun != NULL);
+       *pfun = fun;
+       *prequest = request;
 
-       /*
-        *      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");
-               }
+       return 1;
+}
 
-               /*
-                *      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;
+static int client_socket_encode(rad_listen_t *listener, REQUEST *request)
+{
+       if (!request->reply->code) return 0;
 
-       /*
-        *      FIXME: we should really verify the digest here,
-        *      before marking this packet as a valid response.
-        *
-        *      This is a security problem, I think...
-        */
+       rad_encode(request->reply, request->packet, request->secret);
+       rad_sign(request->reply, request->packet, request->secret);
 
-       /*
-        *      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;
-                       }
-               }
+       return 0;
+}
+
+
+static int client_socket_decode(rad_listen_t *listener, REQUEST *request)
+{
+       if (rad_verify(request->packet, NULL, request->secret) < 0) {
+               return -1;
        }
 
-       rad_assert(fun != NULL);
-       *pfun = fun;
-       *prequest = oldreq;
+       return rad_decode(request->packet, NULL, request->secret);
+}
 
-       return 1;
+static int proxy_socket_encode(rad_listen_t *listener, REQUEST *request)
+{
+       rad_encode(request->proxy, NULL, request->home_server->secret);
+       rad_sign(request->proxy, NULL, request->home_server->secret);
+
+       return 0;
 }
 
+
+static int proxy_socket_decode(rad_listen_t *listener, REQUEST *request)
+{
+       if (rad_verify(request->proxy_reply, request->proxy,
+                      request->home_server->secret) < 0) {
+               return -1;
+       }
+
+       return rad_decode(request->proxy_reply, request->proxy,
+                          request->home_server->secret);
+}
+
+
 #define STATE_UNOPENED (0)
 #define STATE_UNLOCKED (1)
 #define STATE_HEADER   (2)
@@ -1164,27 +785,6 @@ static int detail_recv(rad_listen_t *listener,
        }
 
        /*
-        *      If we keep track of the outstanding requests, do so
-        *      here.  Note that to minimize potential work, we do
-        *      so only once the file is opened & locked.
-        */
-       if (data->max_outstanding) {
-               int i;
-
-               for (i = 0; i < data->max_outstanding; i++) {
-                       if (!data->outstanding[i]) {
-                               free_slot = i;
-                               break;
-                       }
-               }
-
-               /*
-                *      All of the slots are full, don't read data.
-                */
-               if (free_slot < 0) return 0;
-       }
-
-       /*
         *      Catch an out of memory condition which will most likely
         *      never be met.
         */
@@ -1363,6 +963,34 @@ static int detail_recv(rad_listen_t *listener,
         *      problem.
         */
  alloc_packet:
+       /*
+        *      If we keep track of the outstanding requests, do so
+        *      here.  Note that to minimize potential work, we do
+        *      so only once the file is opened & locked.
+        */
+       if (data->max_outstanding) {
+               int i;
+
+               for (i = 0; i < data->max_outstanding; i++) {
+                       if (!data->outstanding[i]) {
+                               free_slot = i;
+                               break;
+                       }
+               }
+
+               /*
+                *      All of the slots are full.  Put the LF back,
+                *      so select will say that data is ready, and set
+                *      the state back to reading.  Then, return so
+                *      that we don't overload the server.
+                */
+               if (free_slot < 0) {
+                       ungetc('\n', data->fp);
+                       data->state = STATE_READING;
+                       return 0;
+               }
+       }
+
        packet = rad_alloc(1);
        if (!packet) {
                return 0;       /* maybe memory will magically free up... */
@@ -1428,15 +1056,14 @@ static int detail_recv(rad_listen_t *listener,
         *      However, we don't want to keep track of used/unused
         *      id's and ports, as that's a lot of work.  This hack
         *      ensures that (if we have real random numbers), that
-        *      there will be a collision on every (2^(16+16+2+24))/2
-        *      packets, on average.  That means we can read 2^32 (4G)
+        *      there will be a collision on every 2^(16+15+15+24 - 1)
+        *      packets, on average.  That means we can read 2^37
         *      packets before having a collision, which means it's
-        *      effectively impossible.  Having 4G packets currently
-        *      being process is ridiculous.
+        *      effectively impossible.
         */
-       packet->id = lrad_rand() & 0xff;
-       packet->src_port = lrad_rand() & 0xffff;
-       packet->dst_port = lrad_rand() & 0xffff;
+       packet->id = lrad_rand() & 0xffff;
+       packet->src_port = 1024 + (lrad_rand() & 0x7fff);
+       packet->dst_port = 1024 + (lrad_rand() & 0x7fff);
 
        packet->dst_ipaddr.af = AF_INET;
        packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl((INADDR_LOOPBACK & ~0xffffff) | (lrad_rand() & 0xffffff));
@@ -1450,19 +1077,11 @@ static int detail_recv(rad_listen_t *listener,
        data->state = STATE_HEADER;
 
        /*
-        *      FIXME: many of these checks may not be necessary...
-        */
-       if (!common_checks(listener, packet, prequest, &detail_client)) {
-               rad_free(&packet);
-               return 0;
-       }
-
-       /*
         *      Keep track of free slots, as a hack, in an otherwise
         *      unused 'int'
         */
        (*prequest)->simul_max = free_slot;
-       if (free_slot) data->outstanding[free_slot] = 1;
+       data->outstanding[free_slot] = 1;
 
        *pfun = rad_accounting;
 
@@ -1475,6 +1094,15 @@ static int detail_recv(rad_listen_t *listener,
                }
        }
 
+       /*
+        *      FIXME: many of these checks may not be necessary when
+        *      reading from the detail file.
+        */
+       if (!received_request(listener, packet, prequest, &detail_client)) {
+               rad_free(&packet);
+               return 0;
+       }
+
        return 1;
 }
 
@@ -1500,6 +1128,22 @@ static int detail_print(rad_listen_t *this, char *buffer, size_t bufsize)
                        ((listen_detail_t *)(this->data))->detail);
 }
 
+static int detail_encode(rad_listen_t *this, REQUEST *request)
+{
+       /*
+        *      We never encode responses "sent to" the detail file.
+        */
+       return 0;
+}
+
+static int detail_decode(rad_listen_t *this, REQUEST *request)
+{
+       /*
+        *      We never decode responses read from the detail file.
+        */
+       return 0;
+}
+
 
 static const CONF_PARSER detail_config[] = {
        { "detail",   PW_TYPE_STRING_PTR,
@@ -1550,48 +1194,32 @@ static int detail_parse(const char *filename, int lineno,
        return 0;
 }
 
-
-/*
- *     See radiusd.c & request_list.c
- */
-#define SLEEP_FOREVER (65536)
-/*
- *     A generic "update the request list once a second" function.
- */
-static int generic_update(rad_listen_t *this, time_t now)
-{
-       if (!this->rl) return SLEEP_FOREVER;
-
-       return rl_clean_list(this->rl, now);
-}
-
-
-
 static const rad_listen_master_t master_listen[RAD_LISTEN_MAX] = {
-       { NULL, NULL, NULL, NULL, NULL, NULL},  /* RAD_LISTEN_NONE */
+       { NULL, NULL, NULL, NULL, NULL, NULL, NULL},    /* RAD_LISTEN_NONE */
 
        /* authentication */
        { common_socket_parse, NULL,
          auth_socket_recv, auth_socket_send,
-         generic_update, socket_print },
+         socket_print, client_socket_encode, client_socket_decode },
 
        /* accounting */
        { common_socket_parse, NULL,
          acct_socket_recv, acct_socket_send,
-         generic_update, socket_print},
+         socket_print, client_socket_encode, client_socket_decode},
 
        /* proxying */
        { NULL, NULL,
          proxy_socket_recv, proxy_socket_send,
-         generic_update, socket_print }, /* FIXME: update func is wrong! */
+         socket_print, proxy_socket_encode, proxy_socket_decode },
 
        /* detail */
        { detail_parse, detail_free,
          detail_recv, detail_send,
-         generic_update, detail_print }
+         detail_print, detail_encode, detail_decode }
 };
 
 
+
 /*
  *     Binds a listener to a socket.
  */
@@ -1656,10 +1284,8 @@ static int listen_bind(rad_listen_t *this)
                        }
                        
                        if (equal) {
-                               this->rl = (*last)->rl;
                                this->fd = (*last)->fd;
                                (*last)->fd = -1;
-                               (*last)->rl = NULL;
                                return 0;
                        }
                }
@@ -1672,6 +1298,21 @@ static int listen_bind(rad_listen_t *this)
                return -1;
        }
 
+#if 0
+#ifdef O_NONBLOCK
+       if ((flags = fcntl(this->fd, F_GETFL, NULL)) < 0)  { 
+               radlog(L_ERR, "Failure in fcntl: %s)\n", strerror(errno)); 
+               return -1;
+       }
+
+       flags |= O_NONBLOCK; 
+       if( fcntl(this->fd, F_SETFL, flags) < 0) { 
+               radlog(L_ERR, "Failure in fcntl: %s)\n", strerror(errno)); 
+               return -1;
+       }
+#endif
+#endif
+
        return 0;
 }
 
@@ -1689,8 +1330,9 @@ static rad_listen_t *listen_alloc(RAD_LISTEN_TYPE type)
        this->type = type;
        this->recv = master_listen[this->type].recv;
        this->send = master_listen[this->type].send;
-       this->update = master_listen[this->type].update;
        this->print = master_listen[this->type].print;
+       this->encode = master_listen[this->type].encode;
+       this->decode = master_listen[this->type].decode;
 
        switch (type) {
        case RAD_LISTEN_AUTH:
@@ -2070,20 +1712,6 @@ int listen_init(const char *filename, rad_listen_t **head)
         */
        rcode = 0;
        for (this = *head; this != NULL; this = this->next) {
-               if ((this->type != RAD_LISTEN_PROXY) &&
-                   !this->rl) {
-                       /*
-                        *      FIXME: Pass type to rl_init, so that
-                        *      it knows how to deal with accounting
-                        *      packets.  i.e. it caches them, but
-                        *      doesn't bother trying to re-transmit.
-                        */
-                       this->rl = rl_init();
-                       if (!this->rl) {
-                               rad_assert(0 == 1); /* FIXME: */
-                       }
-               }
-
                if (((this->type == RAD_LISTEN_ACCT) &&
                     (rcode == RAD_LISTEN_DETAIL)) ||
                    ((this->type == RAD_LISTEN_DETAIL) &&
@@ -2118,8 +1746,6 @@ void listen_free(rad_listen_t **head)
                
                free(this->identity);
 
-               rl_deinit(this->rl);
-
                /*
                 *      Other code may have eaten the FD.
                 */
index 34d9628..bc882c2 100644 (file)
@@ -40,7 +40,6 @@ RCSID("$Id$")
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/rad_assert.h>
 #include <freeradius-devel/modules.h>
-#include <freeradius-devel/request_list.h>
 
 #include <sys/resource.h>
 #include <netdb.h>
@@ -611,250 +610,6 @@ static int switch_users(void)
 }
 
 
-/*
- * Create the linked list of realms from the new configuration type
- * This way we don't have to change to much in the other source-files
- */
-static int generate_realms(const char *filename)
-{
-       CONF_SECTION *cs;
-       REALM *my_realms = NULL;
-       REALM *c, **tail;
-       char *s, *t, *authhost, *accthost;
-       const char *name2;
-
-       tail = &my_realms;
-       for (cs = cf_subsection_find_next(mainconfig.config, NULL, "realm");
-            cs != NULL;
-            cs = cf_subsection_find_next(mainconfig.config, cs, "realm")) {
-               name2 = cf_section_name2(cs);
-               if (!name2) {
-                       radlog(L_CONS|L_ERR, "%s[%d]: Missing realm name",
-                              filename, cf_section_lineno(cs));
-                       return -1;
-               }
-               /*
-                * We've found a realm, allocate space for it
-                */
-               c = rad_malloc(sizeof(REALM));
-               memset(c, 0, sizeof(REALM));
-
-               c->secret[0] = '\0';
-
-               /*
-                *      No authhost means LOCAL.
-                */
-               if ((authhost = cf_section_value_find(cs, "authhost")) == NULL) {
-                       c->ipaddr.af = AF_INET;
-                       c->ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-                       c->auth_port = 0;
-               } else {
-                       if ((s = strchr(authhost, ':')) != NULL) {
-                               *s++ = 0;
-                               c->auth_port = atoi(s);
-                       } else {
-                               c->auth_port = PW_AUTH_UDP_PORT;
-                       }
-                       if (strcmp(authhost, "LOCAL") == 0) {
-                               /*
-                                *      Local realms don't have an IP address,
-                                *      secret, or port.
-                                */
-                               c->ipaddr.af = AF_INET;
-                               c->ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-                               c->auth_port = 0;
-                       } else {
-                               if (ip_hton(authhost, AF_INET,
-                                           &c->ipaddr) < 0) {
-                                       radlog(L_ERR, "%s[%d]: Host %s not found",
-                                              filename, cf_section_lineno(cs),
-                                              authhost);
-                                       return -1;
-                               }
-                       }
-
-                       /*
-                        * Double check length, just to be sure!
-                        */
-                       if (strlen(authhost) >= sizeof(c->server)) {
-                               radlog(L_ERR, "%s[%d]: Server name of length %u is greater than allowed: %u",
-                                      filename, cf_section_lineno(cs),
-                                      strlen(authhost),
-                                      sizeof(c->server) - 1);
-                               return -1;
-                       }
-               }
-
-               /*
-                *      No accthost means LOCAL
-                */
-               if ((accthost = cf_section_value_find(cs, "accthost")) == NULL) {
-                       c->acct_ipaddr.af = AF_INET;
-                       c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-                       c->acct_port = 0;
-               } else {
-                       if ((s = strchr(accthost, ':')) != NULL) {
-                               *s++ = 0;
-                               c->acct_port = atoi(s);
-                       } else {
-                               c->acct_port = PW_ACCT_UDP_PORT;
-                       }
-                       if (strcmp(accthost, "LOCAL") == 0) {
-                               /*
-                                *      Local realms don't have an IP address,
-                                *      secret, or port.
-                                */
-                               c->acct_ipaddr.af = AF_INET;
-                               c->acct_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-                               c->acct_port = 0;
-                       } else {
-                               if (ip_hton(accthost, AF_INET,
-                                           &c->acct_ipaddr) < 0) {
-                                       radlog(L_ERR, "%s[%d]: Host %s not found",
-                                              filename, cf_section_lineno(cs),
-                                              accthost);
-                                       return -1;
-                               }
-                       }
-
-                       if (strlen(accthost) >= sizeof(c->acct_server)) {
-                               radlog(L_ERR, "%s[%d]: Server name of length %u is greater than allowed: %u",
-                                      filename, cf_section_lineno(cs),
-                                      strlen(accthost),
-                                      sizeof(c->acct_server) - 1);
-                               return -1;
-                       }
-               }
-
-               if (strlen(name2) >= sizeof(c->realm)) {
-                       radlog(L_ERR, "%s[%d]: Realm name of length %u is greater than allowed %u",
-                              filename, cf_section_lineno(cs),
-                              strlen(name2),
-                              sizeof(c->server) - 1);
-                       return -1;
-               }
-
-               strcpy(c->realm, name2);
-                if (authhost) strcpy(c->server, authhost);
-               if (accthost) strcpy(c->acct_server, accthost);
-
-               /*
-                *      If one or the other of authentication/accounting
-                *      servers is set to LOCALHOST, then don't require
-                *      a shared secret.
-                */
-               rad_assert(c->ipaddr.af == AF_INET);
-               rad_assert(c->acct_ipaddr.af == AF_INET);
-               if ((c->ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE)) ||
-                   (c->acct_ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE))) {
-                       if ((s = cf_section_value_find(cs, "secret")) == NULL ) {
-                               radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
-                                      filename, cf_section_lineno(cs), name2);
-                               return -1;
-                       }
-
-                       if (strlen(s) >= sizeof(c->secret)) {
-                               radlog(L_ERR, "%s[%d]: Secret of length %u is greater than the allowed maximum of %u.",
-                                      filename, cf_section_lineno(cs),
-                                      strlen(s), sizeof(c->secret) - 1);
-                               return -1;
-                       }
-                       strlcpy((char *)c->secret, s, sizeof(c->secret));
-               }
-
-               c->striprealm = 1;
-
-               if ((cf_section_value_find(cs, "nostrip")) != NULL)
-                       c->striprealm = 0;
-               if ((cf_section_value_find(cs, "noacct")) != NULL)
-                       c->acct_port = 0;
-               if ((cf_section_value_find(cs, "trusted")) != NULL)
-                       c->trusted = 1;
-               if ((cf_section_value_find(cs, "notrealm")) != NULL)
-                       c->notrealm = 1;
-               if ((cf_section_value_find(cs, "notsuffix")) != NULL)
-                       c->notrealm = 1;
-               if ((t = cf_section_value_find(cs,"ldflag")) != NULL) {
-                       static const LRAD_NAME_NUMBER ldflags[] = {
-                               { "fail_over",   0 },
-                               { "round_robin", 1 },
-                               { NULL, 0 }
-                       };
-
-                       c->ldflag = lrad_str2int(ldflags, t, -1);
-                       if (c->ldflag == -1) {
-                               radlog(L_ERR, "%s[%d]: Unknown value \"%s\" for ldflag",
-                                      filename, cf_section_lineno(cs),
-                                      t);
-                               return -1;
-                       }
-
-               } else {
-                       c->ldflag = 0; /* non, make it fail-over */
-               }
-               c->active = TRUE;
-               c->acct_active = TRUE;
-
-               c->next = NULL;
-               *tail = c;
-               tail = &c->next;
-       }
-
-       /*
-        *      And make these realms preferred over the ones
-        *      in the 'realms' file.
-        */
-       *tail = mainconfig.realms;
-       mainconfig.realms = my_realms;
-
-       /*
-        *  Ensure that all of the flags agree for the realms.
-        *
-        *      Yeah, it's O(N^2), but it's only once, and the
-        *      maximum number of realms is small.
-        */
-       for(c = mainconfig.realms; c != NULL; c = c->next) {
-               REALM *this;
-
-               /*
-                *      Check that we cannot load balance to LOCAL
-                *      realms, as that doesn't make any sense.
-                */
-               rad_assert(c->ipaddr.af == AF_INET);
-               rad_assert(c->acct_ipaddr.af == AF_INET);
-               if ((c->ldflag == 1) &&
-                   ((c->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) ||
-                    (c->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)))) {
-                       radlog(L_ERR | L_CONS, "ERROR: Realm %s cannot be load balanced to LOCAL",
-                              c->realm);
-                       exit(1);
-               }
-
-               /*
-                *      Compare this realm to all others, to ensure
-                *      that the configuration is consistent.
-                */
-               for (this = c->next; this != NULL; this = this->next) {
-                       if (strcasecmp(c->realm, this->realm) != 0) {
-                               continue;
-                       }
-
-                       /*
-                        *      Same realm: Different load balancing
-                        *      flag: die.
-                        */
-                       if (c->ldflag != this->ldflag) {
-                               radlog(L_ERR | L_CONS, "ERROR: Inconsistent value in realm %s for load balancing 'ldflag' attribute",
-                                      c->realm);
-                               exit(1);
-                       }
-               }
-       }
-
-       return 0;
-}
-
-
 static const LRAD_NAME_NUMBER str2dest[] = {
        { "null", RADLOG_NULL },
        { "files", RADLOG_FILES },
@@ -1023,22 +778,9 @@ int read_mainconfig(int reload)
        mainconfig.config = cs;
        cf_section_free(&oldcs);
 
-       /*
-        *      Old-style realms file.
-        */
-       snprintf(buffer, sizeof(buffer), "%.200s/%.50s", radius_dir, RADIUS_REALMS);
-       DEBUG2("read_config_files:  reading realms");
-       if (read_realms_file(buffer) < 0) {
-               radlog(L_ERR|L_CONS, "Errors reading realms");
-               return -1;
-       }
-
-       /*
-        *      If there isn't any realms it isn't fatal..
-        */
        snprintf(buffer, sizeof(buffer), "%.200s/%.50s",
                 radius_dir, mainconfig.radiusd_conf);
-       if (generate_realms(buffer) < 0) {
+       if (!realms_init(buffer)) {
                return -1;
        }
 
@@ -1152,8 +894,6 @@ int read_mainconfig(int reload)
                clients_free(old_clients);
        }
 
-       rl_init_proxy();
-
        /*  Reload the modules.  */
        DEBUG2("radiusd:  entering modules setup");
        if (setup_modules(reload) < 0) {
@@ -1174,7 +914,7 @@ int free_mainconfig(void)
         */
        cf_section_free(&mainconfig.config);
        free(mainconfig.radiusd_conf);
-       realm_free(mainconfig.realms);
+       realms_free();
        listen_free(&mainconfig.listen);
        xlat_free();
        dict_free();
index 3dcd7a4..c5a3b83 100644 (file)
@@ -328,7 +328,7 @@ int modcall(int component, modcallable *c, REQUEST *request)
                 *      A module has taken too long to process the request,
                 *      and we've been told to stop processing it.
                 */
-               if (request->options & RAD_REQUEST_OPTION_STOP_NOW) {
+               if (request->master_state == REQUEST_STOP_PROCESSING) {
                        myresult = RLM_MODULE_FAIL;
                        break;
                }
index 7324b81..25971af 100644 (file)
@@ -724,6 +724,7 @@ int setup_modules(int reload)
                        do_component[RLM_COMPONENT_POST_PROXY] = 1;
                        break;
 
+
                default:
                        rad_assert(0 == 1);
                        break;
@@ -948,20 +949,6 @@ int setup_modules(int reload)
  */
 int module_authorize(int autz_type, REQUEST *request)
 {
-       /*
-        *      Older versions of the server would pass proxy requests
-        *      through the 'authorize' sections twice; once when the
-        *      packet was received from the NAS, and again after the
-        *      reply was received from the home server.  Now that we
-        *      have a 'post_proxy' section, the replies from the home
-        *      server should be sent through that, instead of through
-        *      the 'authorize' section again.
-        */
-       if (request->proxy != NULL) {
-               DEBUG2(" authorize: Skipping authorize in post-proxy stage");
-               return RLM_MODULE_NOOP;
-       }
-
        return indexed_modcall(RLM_COMPONENT_AUTZ, autz_type, request);
 }
 
diff --git a/src/main/proxy.c b/src/main/proxy.c
deleted file mode 100644 (file)
index 4610906..0000000
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
- * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Copyright 2000,2006  The FreeRADIUS server project
- * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
- * Copyright 2000  Chris Parker <cparker@starnetusa.com>
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include <freeradius-devel/autoconf.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 <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/modules.h>
-#include <freeradius-devel/request_list.h>
-
-/*
- *     We received a response from a remote radius server.
- *     Call the post-proxy modules.
- */
-int proxy_receive(REQUEST *request)
-{
-        int rcode;
-       int post_proxy_type = 0;
-       VALUE_PAIR *vp;
-
-        /*
-         *     Delete any reply we had accumulated until now.
-        */
-        pairfree(&request->reply->vps);
-
-       /*
-        *      Run the packet through the post-proxy stage,
-        *      BEFORE playing games with the attributes.
-        */
-       vp = pairfind(request->config_items, PW_POST_PROXY_TYPE);
-       if (vp) {
-               DEBUG2("  Found Post-Proxy-Type %s", vp->vp_strvalue);
-               post_proxy_type = vp->lvalue;
-       }
-       rcode = module_post_proxy(post_proxy_type, request);
-
-        /*
-         *     Delete the Proxy-State Attributes from the reply.
-         *     These include Proxy-State attributes from us and
-         *     remote server.
-        */
-        pairdelete(&request->proxy_reply->vps, PW_PROXY_STATE);
-
-       /*
-        *      Add the attributes left in the proxy reply to
-        *      the reply list.
-        */
-        pairadd(&request->reply->vps, request->proxy_reply->vps);
-        request->proxy_reply->vps = NULL;
-
-        /*
-        *      Free proxy request pairs.
-         */
-        pairfree(&request->proxy->vps);
-
-       /*
-        *      FIXME: If the packet is an Access-Challenge,
-        *      THEN add it to a cache, which does:
-        *
-        *      (src IP, State) -> (home server ip/port)
-        *
-        *      This allows the load-balancing code to
-        *      work for EAP...
-        *
-        *      Alternately, we can delete the State from the home
-        *      server, and use our own..  that might be better.
-        */
-
-        return rcode;
-}
-
-/*
- *     Add a proxy-pair to the end of the request.
- */
-static void proxy_addinfo(REQUEST *request)
-{
-       VALUE_PAIR *proxy_pair;
-
-       proxy_pair = paircreate(PW_PROXY_STATE, PW_TYPE_STRING);
-       if (proxy_pair == NULL) {
-               radlog(L_ERR|L_CONS, "no memory");
-               exit(1);
-       }
-       sprintf(proxy_pair->vp_strvalue, "%d", request->packet->id);
-       proxy_pair->length = strlen(proxy_pair->vp_strvalue);
-
-       pairadd(&request->proxy->vps, proxy_pair);
-}
-
-
-/*
- *     Like realm find, but does load balancing, and we don't
- *     wake up any sleeping realms.  Someone should already have
- *     done that.
- *
- *     It also does NOT do fail-over to default if the realms are dead,
- *     as that decision has already been made.
- */
-static REALM *proxy_realm_ldb(REQUEST *request, const char *realm_name,
-                             int accounting)
-{
-       REALM           *cl, *lb;
-       uint32_t        count;
-
-       /*
-        *      FIXME: If the packet contains a State attribute,
-        *      AND the realm is load-balance,
-        *      AND there is a matching
-        *      State attribute in the cached entry, THEN proxy it to
-        *      that realm.
-        */
-
-       lb = NULL;
-       count = 0;
-       for (cl = mainconfig.realms; cl; cl = cl->next) {
-               /*
-                *      Wake up any sleeping realm.
-                *
-                *      Note that the 'realm find' function will only
-                *      wake up the FIRST realm which matches.  We've
-                *      got to wake up ALL of the matching realms.
-                */
-               if (cl->wakeup <= request->timestamp) {
-                       cl->active = TRUE;
-               }
-               if (cl->acct_wakeup <= request->timestamp) {
-                       cl->acct_active = TRUE;
-               }
-
-               /*
-                *      Asked for auth/acct, and the auth/acct server
-                *      is not active.  Skip it.
-                */
-               if ((!accounting && !cl->active) ||
-                   (accounting && !cl->acct_active)) {
-                       continue;
-               }
-
-               /*
-                *      The realm name doesn't match, skip it.
-                */
-               if (strcasecmp(cl->realm, realm_name) != 0) {
-                       continue;
-               }
-
-               /*
-                *      Fail-over, pick the first one that matches.
-                */
-               if ((count == 0) && /* if size > 0, we have round-robin */
-                   (cl->ldflag == 0)) {
-                       return cl;
-               }
-
-               /*
-                *      We're doing load-balancing.  Pick a random
-                *      number, which will be used to determine which
-                *      home server is chosen.
-                */
-               if (!lb) {
-                       lb = cl;
-                       count = 1;
-                       continue;
-               }
-
-               /*
-                *      Keep track of how many load balancing servers
-                *      we've gone through.
-                */
-               count++;
-
-               /*
-                *      See the "camel book" for why this works.
-                *
-                *      If (rand(0..n) < 1), pick the current realm.
-                *      We add a scale factor of 65536, to avoid
-                *      floating point.
-                */
-               if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
-                       lb = cl;
-               }
-       } /* loop over the realms */
-
-       /*
-        *      Return the load-balanced realm.
-        */
-       return lb;
-}
-
-/*
- *     Relay the request to a remote server.
- *     Returns:
- *
- *      RLM_MODULE_FAIL: we don't reply, caller returns without replying
- *      RLM_MODULE_NOOP: caller falls through to normal processing
- *      RLM_MODULE_HANDLED  : we reply, caller returns without replying
- */
-int proxy_send(REQUEST *request)
-{
-       int rcode;
-       int pre_proxy_type = 0;
-       VALUE_PAIR *realmpair;
-       VALUE_PAIR *strippedname;
-       VALUE_PAIR *vp;
-       REALM *realm;
-       char *realmname;
-
-       /*
-        *      Not authentication or accounting.  Stop it.
-        */
-       if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
-           (request->packet->code != PW_ACCOUNTING_REQUEST)) {
-               DEBUG2("  ERROR: Cannot proxy packets of type %d",
-                      request->packet->code);
-               return RLM_MODULE_FAIL;
-       }
-
-       /*
-        *      The timestamp is used below to figure the
-        *      next_try. The request needs to "hang around" until
-        *      either the other server sends a reply or the retry
-        *      count has been exceeded.  Until then, it should not
-        *      be eligible for the time-based cleanup.  --Pac. */
-
-       realmpair = pairfind(request->config_items, PW_PROXY_TO_REALM);
-       if (!realmpair) {
-               /*
-                *      Not proxying, so we can exit from the proxy
-                *      code.
-                */
-               return RLM_MODULE_NOOP;
-       }
-
-       /*
-        *      If the server has already decided to reject the request,
-        *      then don't try to proxy it.
-        */
-       if (request->reply->code == PW_AUTHENTICATION_REJECT) {
-               DEBUG2("Cancelling proxy as request was already rejected");
-               return RLM_MODULE_REJECT;
-       }
-       if (((vp = pairfind(request->config_items, PW_AUTH_TYPE)) != NULL) &&
-           (vp->lvalue == PW_AUTHTYPE_REJECT)) {
-               DEBUG2("Cancelling proxy as request was already rejected");
-               return RLM_MODULE_REJECT;
-       }
-       /*
-        *      Length == 0 means it exists, but there's no realm.
-        *      Don't proxy it.
-        */
-       if (realmpair->length == 0) {
-               return RLM_MODULE_NOOP;
-       }
-
-       realmname = (char *)realmpair->vp_strvalue;
-
-       /*
-        *      Look for the realm, using the load balancing
-        *      version of realm find.
-        */
-       realm = proxy_realm_ldb(request, realmname,
-                               (request->packet->code == PW_ACCOUNTING_REQUEST));
-       if (realm == NULL) {
-               DEBUG2("  ERROR: Failed to find live home server for realm %s",
-                      realmname);
-               return RLM_MODULE_FAIL;
-       }
-
-       /*
-        *      Remember that we sent the request to a Realm.
-        */
-       pairadd(&request->packet->vps,
-               pairmake("Realm", realm->realm, T_OP_EQ));
-
-       /*
-        *      Access-Request: look for LOCAL realm.
-        *      Accounting-Request: look for LOCAL realm.
-        */
-       if (((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
-            (realm->ipaddr.af == AF_INET) &&
-            (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) ||
-           ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
-            (realm->acct_ipaddr.af == AF_INET) &&
-            (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)))) {
-               DEBUG2(" WARNING: Cancelling proxy to Realm %s, as the realm is local.",
-                      realm->realm);
-               return RLM_MODULE_NOOP;
-       }
-
-       /*
-        *      This is mainly for radrelay.  Don't proxy packets back
-        *      to servers which sent them to us.
-        */
-       if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
-           (request->listener->type == RAD_LISTEN_DETAIL) &&
-           (realm->acct_ipaddr.af == AF_INET) &&
-           (request->packet->src_ipaddr.af == AF_INET) &&
-           (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == request->packet->src_ipaddr.ipaddr.ip4addr.s_addr)) {
-               DEBUG2("    rlm_realm: Packet came from realm %s, proxy cancelled", realm->realm);
-               return RLM_MODULE_NOOP;
-       }
-
-       /*
-        *      Allocate the proxy packet, only if it wasn't already
-        *      allocated by a module.  This check is mainly to support
-        *      the proxying of EAP-TTLS and EAP-PEAP tunneled requests.
-        *
-        *      In those cases, the EAP module creates a "fake"
-        *      request, and recursively passes it through the
-        *      authentication stage of the server.  The module then
-        *      checks if the request was supposed to be proxied, and
-        *      if so, creates a proxy packet from the TUNNELED request,
-        *      and not from the EAP request outside of the tunnel.
-        *
-        *      The proxy then works like normal, except that the response
-        *      packet is "eaten" by the EAP module, and encapsulated into
-        *      an EAP packet.
-        */
-       if (!request->proxy) {
-               /*
-                *      Now build a new RADIUS_PACKET.
-                *
-                *      FIXME: it could be that the id wraps around
-                *      too fast if we have a lot of requests, it
-                *      might be better to keep a seperate ID value
-                *      per remote server.
-                *
-                *      OTOH the remote radius server should be smart
-                *      enough to compare _both_ ID and vector.
-                *      Right?
-                */
-               if ((request->proxy = rad_alloc(TRUE)) == NULL) {
-                       radlog(L_ERR|L_CONS, "no memory");
-                       exit(1);
-               }
-
-               /*
-                *      We now massage the attributes to be proxied...
-                */
-
-               /*
-                *      Copy the request, then look up name and
-                *      plain-text password in the copy.
-                *
-                *      Note that the User-Name attribute is the
-                *      *original* as sent over by the client.  The
-                *      Stripped-User-Name attribute is the one hacked
-                *      through the 'hints' file.
-                */
-               request->proxy->vps =  paircopy(request->packet->vps);
-       }
-
-       /*
-        *      Strip the name, if told to.
-        *
-        *      Doing it here catches the case of proxied tunneled
-        *      requests.
-        */
-       if (realm->striprealm == TRUE &&
-          (strippedname = pairfind(request->proxy->vps, PW_STRIPPED_USER_NAME)) != NULL) {
-               /*
-                *      If there's a Stripped-User-Name attribute in
-                *      the request, then use THAT as the User-Name
-                *      for the proxied request, instead of the
-                *      original name.
-                *
-                *      This is done by making a copy of the
-                *      Stripped-User-Name attribute, turning it into
-                *      a User-Name attribute, deleting the
-                *      Stripped-User-Name and User-Name attributes
-                *      from the vps list, and making the new
-                *      User-Name the head of the vps list.
-                */
-               vp = pairfind(request->proxy->vps, PW_USER_NAME);
-               if (!vp) {
-                       vp = paircreate(PW_USER_NAME, PW_TYPE_STRING);
-                       if (!vp) {
-                               radlog(L_ERR|L_CONS, "no memory");
-                               exit(1);
-                       }
-                       vp->next = request->proxy->vps;
-                       request->proxy->vps = vp;
-               }
-               memcpy(vp->vp_strvalue, strippedname->vp_strvalue,
-                      sizeof(vp->vp_strvalue));
-               vp->length = strippedname->length;
-
-               /*
-                *      Do NOT delete Stripped-User-Name.
-                */
-       }
-       
-       /*
-        *      If there is no PW_CHAP_CHALLENGE attribute but
-        *      there is a PW_CHAP_PASSWORD we need to add it
-        *      since we can't use the request authenticator
-        *      anymore - we changed it.
-        */
-       if (pairfind(request->proxy->vps, PW_CHAP_PASSWORD) &&
-           pairfind(request->proxy->vps, PW_CHAP_CHALLENGE) == NULL) {
-               vp = paircreate(PW_CHAP_CHALLENGE, PW_TYPE_STRING);
-               if (!vp) {
-                       radlog(L_ERR|L_CONS, "no memory");
-                       exit(1);
-               }
-               vp->length = AUTH_VECTOR_LEN;
-               memcpy(vp->vp_strvalue, request->packet->vector, AUTH_VECTOR_LEN);
-               pairadd(&(request->proxy->vps), vp);
-       }
-
-       request->proxy->code = request->packet->code;
-       if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
-               request->proxy->dst_port = realm->auth_port;
-               request->proxy->dst_ipaddr = realm->ipaddr;
-       } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
-               request->proxy->dst_port = realm->acct_port;
-               request->proxy->dst_ipaddr = realm->acct_ipaddr;
-       }
-
-       /*
-        *      Add PROXY_STATE attribute, before pre-proxy stage,
-        *      so the pre-proxy modules have access to it.
-        *
-        *      Note that, at this point, the proxied request HAS NOT
-        *      been assigned a RADIUS Id.
-        */
-       proxy_addinfo(request);
-
-       /*
-        *      Set up for sending the request.
-        */
-       memcpy(request->proxysecret, realm->secret, sizeof(request->proxysecret));
-       request->proxy_try_count = mainconfig.proxy_retry_count - 1;
-
-       vp = NULL;
-       if (request->packet->code == PW_ACCOUNTING_REQUEST) {
-               vp = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME);
-       }
-       if (vp) {
-               request->proxy->timestamp = request->timestamp - vp->lvalue;
-       } else {
-               request->proxy->timestamp = request->timestamp;
-       }
-       request->proxy_start_time = request->timestamp;
-
-       /*
-        *      Do pre-proxying.
-        */
-       vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE);
-       if (vp) {
-               DEBUG2("  Found Pre-Proxy-Type %s", vp->vp_strvalue);
-               pre_proxy_type = vp->lvalue;
-       }
-       rcode = module_pre_proxy(pre_proxy_type, request);
-       switch (rcode) {
-       /*
-        *      Only proxy the packet if the pre-proxy code succeeded.
-        */
-       case RLM_MODULE_NOOP:
-       case RLM_MODULE_OK:
-       case RLM_MODULE_UPDATED:
-               /*
-                *      Delay sending the proxy packet until after we've
-                *      done the work above, playing with the request.
-                *
-                *      After this point, it becomes dangerous to play with
-                *      the request data structure, as the reply MAY come in
-                *      and get processed before we're done with it here.
-                */
-               request->options |= RAD_REQUEST_OPTION_PROXIED;
-
-               /*
-                *      If it's a fake request, don't send the proxy
-                *      packet.  The outer tunnel session will take
-                *      care of doing that.
-                */
-               if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) == 0) {
-                       /*
-                        *      Add the proxied request to the
-                        *      list of outstanding proxied
-                        *      requests, BEFORE we send it, so
-                        *      we have fewer problems with race
-                        *      conditions when the responses come
-                        *      back very quickly.
-                        */
-                       if (!rl_add_proxy(request)) {
-                               DEBUG("ERROR: Failed to proxy request %d",
-                                     request->number);
-                               return RLM_MODULE_FAIL; /* caller doesn't reply */
-                       }
-
-                       /*
-                        *      We're still running, encode & sign the
-                        *      packet outside of the critical section.
-                        */
-                       if (request->child_pid != NO_SUCH_CHILD_PID) {
-                               rad_encode(request->proxy, NULL,
-                                          (char *)request->proxysecret);
-                               rad_sign(request->proxy, NULL,
-                                        (char *)request->proxysecret);
-                       } else {
-                               request->proxy_listener->send(request->proxy_listener,
-                                                             request);
-                       }
-               }
-               rcode = RLM_MODULE_HANDLED; /* caller doesn't reply */
-               break;
-       /*
-        *      The module handled the request, don't reply.
-        */
-       case RLM_MODULE_HANDLED:
-               break;
-       /*
-        *      Neither proxy, nor reply to invalid requests.
-        */
-       case RLM_MODULE_FAIL:
-       case RLM_MODULE_INVALID:
-       case RLM_MODULE_NOTFOUND:
-       case RLM_MODULE_REJECT:
-       case RLM_MODULE_USERLOCK:
-       default:
-               rcode = RLM_MODULE_FAIL; /* caller doesn't reply */
-               break;
-       }
-
-       /*
-        *      Do NOT free request->proxy->vps, the pairs are needed
-        *      for the retries!
-        */
-       return rcode;
-}
index ce19e46..b6d623c 100644 (file)
@@ -68,11 +68,8 @@ RCSID("$Id$")
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/rad_assert.h>
 #include <freeradius-devel/modules.h>
-#include <freeradius-devel/request_list.h>
 #include <freeradius-devel/radius_snmp.h>
 
-#define SLEEP_FOREVER (65536)
-
 /*
  *  Global variables.
  */
@@ -117,11 +114,10 @@ int main(int argc, char *argv[])
        int pid;
        int max_fd;
        int status;
-       int sleep_time = SLEEP_FOREVER;
        int spawn_flag = TRUE;
        int dont_fork = FALSE;
        int sig_hup_block = FALSE;
-       time_t last_cleaned_lists = 0;
+       struct timeval tv, *ptv = NULL;
 
 #ifdef HAVE_SIGACTION
        struct sigaction act;
@@ -397,7 +393,7 @@ int main(int argc, char *argv[])
         *      It's called the thread pool, but it does a little
         *      more than that.
         */
-       thread_pool_init(spawn_flag);
+       radius_event_init(spawn_flag);
 
        /*
         *  Use linebuffered or unbuffered stdout if
@@ -521,6 +517,8 @@ int main(int argc, char *argv[])
                         */
                        detach_modules();
 
+                       radius_event_free();
+
                        free(radius_dir);
 
                        /*
@@ -588,18 +586,18 @@ int main(int argc, char *argv[])
                }
 #endif
 
-               if (sleep_time == SLEEP_FOREVER) {
+               if (!ptv) {
                        DEBUG2("Nothing to do.  Sleeping until we see a request.");
-                       status = select(max_fd + 1, &readfds, NULL, NULL, NULL);
-               } else {
-                       struct timeval tv;
-
-                       DEBUG2("Waking up in %d seconds...", sleep_time);
-
-                       tv.tv_sec = sleep_time;
-                       tv.tv_usec = 0;
-                       status = select(max_fd + 1, &readfds, NULL, NULL, &tv);
+               } else if (tv.tv_sec) {
+#if 0
+                               DEBUG2("Waking up in %d.%06d seconds...",
+                                      (int) tv.tv_sec, (int) tv.tv_usec);
+#else
+                               DEBUG2("Waking up in %d seconds...",
+                                      (int) tv.tv_sec);
+#endif
                }
+               status = select(max_fd + 1, &readfds, NULL, NULL, ptv);
                if (status == -1) {
                        /*
                         *      On interrupts, we clean up the request
@@ -662,25 +660,25 @@ int main(int argc, char *argv[])
                        }
 
                        /*
-                        *      Do per-socket receive processing of the
-                        *      packet.
+                        *      Do per-socket receive processing of
+                        *      the packet.  This also takes care of
+                        *      inserting the request into the event
+                        *      tree, and adding it to the queue for
+                        *      threads.
                         */
                        if (!listener->recv(listener, &fun, &request)) {
                                continue;
                        }
                        
+                       // EVENT FIX FIXME! Nuke this!
+
                        /*
                         *      Drop the request into the thread pool,
                         *      and let the thread pool take care of
                         *      doing something with it.
                         */
                        if (!thread_pool_addrequest(request, fun)) {
-                               /*
-                                *      FIXME: Maybe just drop
-                                *      the packet on the floor?
-                                */
-                               request_reject(request, REQUEST_FAIL_NO_THREADS);
-                               request->finished = TRUE;
+                               request->child_state = REQUEST_DONE;
                        }
                } /* loop over listening sockets*/
 
@@ -711,26 +709,8 @@ int main(int argc, char *argv[])
                }
 #endif
 
-               /*
-                *      Loop through the request lists once per
-                *      second, to clean up old requests.
-                */
-               if (last_cleaned_lists != time_now) {
-                       last_cleaned_lists = time_now;
-
-                       DEBUG2("--- Walking the entire request list ---");
-                       sleep_time = SLEEP_FOREVER;
-                       for (listener = mainconfig.listen;
-                            listener != NULL;
-                            listener = listener->next) {
-                               int next;
-                               
-                               next = listener->update(listener, time_now);
-                               if (next < sleep_time) {
-                                       sleep_time = next;
-                               }
-                       }
-               }
+               ptv = &tv;
+               radius_event_process(&ptv);
 
 #ifdef HAVE_PTHREAD_H
 
diff --git a/src/main/realms.c b/src/main/realms.c
new file mode 100644 (file)
index 0000000..6ed552a
--- /dev/null
@@ -0,0 +1,1129 @@
+/*
+ * realms.c    Realm handling code
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ *
+ * Copyright 2000,2006  The FreeRADIUS server project
+ * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
+ * Copyright 2000  Alan DeKok <aland@ox.org>
+ */
+
+#include <freeradius-devel/ident.h>
+RCSID("$Id$")
+
+#include <freeradius-devel/autoconf.h>
+
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+static rbtree_t *realms_byname = NULL;
+
+static rbtree_t        *home_servers_byaddr = NULL;
+static rbtree_t        *home_servers_byname = NULL;
+
+static rbtree_t        *home_pools_byname = NULL;
+
+static int realm_name_cmp(const void *one, const void *two)
+{
+       const REALM *a = one;
+       const REALM *b = two;
+
+       return strcasecmp(a->name, b->name);
+}
+
+
+static int home_server_name_cmp(const void *one, const void *two)
+{
+       const home_server *a = one;
+       const home_server *b = two;
+
+       return strcasecmp(a->name, b->name);
+}
+
+static int home_server_addr_cmp(const void *one, const void *two)
+{
+       const home_server *a = one;
+       const home_server *b = two;
+
+       if (a->ipaddr.af < b->ipaddr.af) return -1;
+       if (a->ipaddr.af > b->ipaddr.af) return +1;
+
+       if (a->port < b->port) return -1;
+       if (a->port > b->port) return +1;
+
+       switch (a->ipaddr.af) {
+       case AF_INET:
+               return memcmp(&a->ipaddr.ipaddr.ip4addr,
+                             &b->ipaddr.ipaddr.ip4addr,
+                             sizeof(a->ipaddr.ipaddr.ip4addr));
+               break;
+       case AF_INET6:
+               return memcmp(&a->ipaddr.ipaddr.ip6addr,
+                             &b->ipaddr.ipaddr.ip6addr,
+                             sizeof(a->ipaddr.ipaddr.ip6addr));
+               break;
+       default:
+               break;
+       }
+       
+       return -1;
+}
+
+
+static int home_pool_name_cmp(const void *one, const void *two)
+{
+       const home_pool_t *a = one;
+       const home_pool_t *b = two;
+
+       return strcasecmp(a->name, b->name);
+}
+
+
+void realms_free(void)
+{
+       rbtree_free(home_servers_byname);
+       home_servers_byname = NULL;
+
+       rbtree_free(home_servers_byaddr);
+       home_servers_byaddr = NULL;
+
+       rbtree_free(home_pools_byname);
+       home_pools_byname = NULL;
+
+       rbtree_free(realms_byname);
+       realms_byname = NULL;
+}
+
+
+int realms_init(const char *filename)
+{
+       CONF_SECTION *cs;
+
+       if (realms_byname) return 1;
+
+       realms_byname = rbtree_create(realm_name_cmp, free, 0);
+       if (!realms_byname) {
+               realms_free();
+               return 0;
+       }
+
+       home_servers_byaddr = rbtree_create(home_server_addr_cmp, free, 0);
+       if (!home_servers_byaddr) {
+               realms_free();
+               return 0;
+       }
+
+       home_servers_byname = rbtree_create(home_server_name_cmp, NULL, 0);
+       if (!home_servers_byname) {
+               realms_free();
+               return 0;
+       }
+
+       home_pools_byname = rbtree_create(home_pool_name_cmp, free, 0);
+       if (!home_pools_byname) {
+               realms_free();
+               return 0;
+       }
+
+       for (cs = cf_subsection_find_next(mainconfig.config, NULL, "realm");
+            cs != NULL;
+            cs = cf_subsection_find_next(mainconfig.config, cs, "realm")) {
+               if (!realm_add(filename, cs)) {
+                       realms_free();
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static struct in_addr hs_ip4addr;
+static struct in6_addr hs_ip6addr;
+static char *hs_type = NULL;
+static char *hs_check = NULL;
+
+static CONF_PARSER home_server_config[] = {
+       { "ipaddr",  PW_TYPE_IPADDR,
+         0, &hs_ip4addr,  NULL },
+       { "ipv6addr",  PW_TYPE_IPV6ADDR,
+         0, &hs_ip6addr, NULL },
+
+       { "hostname",  PW_TYPE_STRING_PTR,
+         offsetof(home_server,hostname), NULL,  NULL},
+       { "port", PW_TYPE_INTEGER,
+         offsetof(home_server,port), NULL,   "0" },
+
+       { "type",  PW_TYPE_STRING_PTR,
+         0, &hs_type, NULL },
+
+       { "secret",  PW_TYPE_STRING_PTR,
+         offsetof(home_server,secret), NULL,  NULL},
+
+       { "response_window", PW_TYPE_INTEGER,
+         offsetof(home_server,response_window), NULL,   "30" },
+       { "max_outstanding", PW_TYPE_INTEGER,
+         offsetof(home_server,max_outstanding), NULL,   "65536" },
+
+       { "zombie_period", PW_TYPE_INTEGER,
+         offsetof(home_server,zombie_period), NULL,   "40" },
+       { "ping_check", PW_TYPE_STRING_PTR,
+         0, &hs_check,   "none" },
+
+       { "ping_interval", PW_TYPE_INTEGER,
+         offsetof(home_server,ping_interval), NULL,   "30" },
+       { "num_pings_to_alive", PW_TYPE_INTEGER,
+         offsetof(home_server,num_pings_to_alive), NULL,   "3" },
+       { "revive_interval", PW_TYPE_INTEGER,
+         offsetof(home_server,revive_interval), NULL,   "300" },
+
+       { "username",  PW_TYPE_STRING_PTR,
+         offsetof(home_server,ping_user_name), NULL,  NULL},
+       { "password",  PW_TYPE_STRING_PTR,
+         offsetof(home_server,ping_user_password), NULL,  NULL},
+       
+       { NULL, -1, 0, NULL, NULL }             /* end the list */
+
+};
+
+
+static int home_server_add(const char *filename, CONF_SECTION *cs)
+{
+       const char *name2;
+       home_server *home;
+
+       name2 = cf_section_name1(cs);
+       if (!name2 || (strcasecmp(name2, "home_server") != 0)) {
+               radlog(L_ERR, "%s[%d]: Section is not a home_server.",
+                      filename, cf_section_lineno(cs));
+               return 0;
+       }
+
+       name2 = cf_section_name2(cs);
+       if (!name2) {
+               radlog(L_ERR, "%s[%d]: Home server section is missing a name.",
+                      filename, cf_section_lineno(cs));
+               return 0;
+       }
+
+       home = rad_malloc(sizeof(*home));
+       memset(home, 0, sizeof(*home));
+
+       home->name = name2;
+       
+       memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
+       memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
+       cf_section_parse(cs, home, home_server_config);
+
+       if (!home->hostname && (htonl(hs_ip4addr.s_addr) == INADDR_NONE) &&
+           IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
+               radlog(L_ERR, "%s[%d]: No hostname, IPv4 address, or IPv6 address defined for home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               free(hs_type);
+               hs_type = NULL;
+               free(hs_check);
+               hs_check = NULL;
+               return 0;
+       }
+
+       /*
+        *      FIXME: Parse home->hostname!
+        *
+        *      Right now, only ipaddr && ip6addr are used.
+        *      The old-style parsing still allows hostnames.
+        */
+       if (htonl(hs_ip4addr.s_addr) != INADDR_NONE) {
+               home->ipaddr.af = AF_INET;
+               home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
+
+       } else if (!IN6_IS_ADDR_UNSPECIFIED(&hs_ip6addr)) {
+               home->ipaddr.af = AF_INET6;
+               home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
+
+       } else {
+               radlog(L_ERR, "%s[%d]: FIXME: parse hostname for home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               free(hs_type);
+               hs_type = NULL;
+               free(hs_check);
+               hs_check = NULL;
+               return 0;
+       }
+
+       if (!home->port || (home->port > 65535)) {
+               radlog(L_ERR, "%s[%d]: No port, or invalid port defined for home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               free(hs_type);
+               hs_type = NULL;
+               free(hs_check);
+               hs_check = NULL;
+               return 0;
+       }
+
+       if (0) {
+               radlog(L_ERR, "%s[%d]: Fatal error!  Home server %s is ourselves!",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               free(hs_type);
+               hs_type = NULL;
+               free(hs_check);
+               hs_check = NULL;
+               return 0;
+       }
+
+       if (strcasecmp(hs_type, "auth") == 0) {
+               home->type = HOME_TYPE_AUTH;
+
+       } else if (strcasecmp(hs_type, "acct") == 0) {
+               home->type = HOME_TYPE_ACCT;
+
+       } else {
+               radlog(L_ERR, "%s[%d]: Invalid type \"%s\" for home server %s.",
+                      filename, cf_section_lineno(cs), hs_type, name2);
+               free(home);
+               free(hs_type);
+               hs_type = NULL;
+               free(hs_check);
+               hs_check = NULL;
+               return 0;
+       }
+       free(hs_type);
+       hs_type = NULL;
+
+       if (!home->secret) {
+               radlog(L_ERR, "%s[%d]: No shared secret defined for home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               return 0;
+       }
+
+       if (strcasecmp(hs_check, "none") == 0) {
+               home->ping_check = HOME_PING_CHECK_NONE;
+
+       } else if (strcasecmp(hs_check, "status-server") == 0) {
+               home->ping_check = HOME_PING_CHECK_STATUS_SERVER;
+
+       } else if (strcasecmp(hs_check, "request") == 0) {
+               home->ping_check = HOME_PING_CHECK_REQUEST;
+
+       } else {
+               radlog(L_ERR, "%s[%d]: Invalid ping_check \"%s\" for home server %s.",
+                      filename, cf_section_lineno(cs), hs_check, name2);
+               free(home);
+               free(hs_check);
+               hs_check = NULL;
+               return 0;
+       }
+       free(hs_check);
+       hs_check = NULL;
+
+       if ((home->ping_check != HOME_PING_CHECK_NONE) &&
+           (home->ping_check != HOME_PING_CHECK_STATUS_SERVER)) {
+               if (!home->ping_user_name) {
+                       radlog(L_INFO, "%s[%d]: You must supply a user name to enable ping checks",
+                              filename, cf_section_lineno(cs));
+                       free(home);
+                       return 0;
+               }
+
+               if ((home->type == HOME_TYPE_AUTH) && 
+                   !home->ping_user_password) {        
+                       radlog(L_INFO, "%s[%d]: You must supply a password to enable ping checks",
+                              filename, cf_section_lineno(cs));
+                       free(home);
+                       return 0;
+               }
+       }
+
+       if (rbtree_finddata(home_servers_byaddr, home)) {
+               radlog(L_INFO, "%s[%d]: Ignoring duplicate home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               return 1;
+       }
+
+       if (!rbtree_insert(home_servers_byname, home)) {
+               radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               return 0;
+       }
+
+       if (!rbtree_insert(home_servers_byaddr, home)) {
+               rbtree_deletebydata(home_servers_byname, home);
+               radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+                      filename, cf_section_lineno(cs), name2);
+               free(home);
+               return 0;
+       }
+
+       if (home->response_window < 5) home->response_window = 5;
+       if (home->response_window > 60) home->response_window = 60;
+
+       if (home->max_outstanding < 8) home->max_outstanding = 8;
+       if (home->max_outstanding > 65536*16) home->max_outstanding = 65536*16;
+
+       if (home->ping_interval < 6) home->ping_interval = 6;
+       if (home->ping_interval > 120) home->ping_interval = 120;
+
+       if (home->zombie_period < 20) home->zombie_period = 20;
+       if (home->zombie_period > 120) home->zombie_period = 120;
+
+       if (home->zombie_period < home->response_window) {
+               home->zombie_period = home->response_window;
+       }
+
+       if (home->num_pings_to_alive < 3) home->num_pings_to_alive = 3;
+       if (home->num_pings_to_alive > 10) home->num_pings_to_alive = 10;
+
+       if (home->revive_interval < 60) home->revive_interval = 60;
+       if (home->revive_interval > 3600) home->revive_interval = 3600;
+
+       return 1;
+}
+
+
+static int server_pool_add(const char *filename, CONF_SECTION *cs)
+{
+       const char *name2;
+       home_pool_t *pool;
+       const char *value;
+       CONF_PAIR *cp;
+       int num_home_servers;
+
+       name2 = cf_section_name1(cs);
+       if (!name2 || (strcasecmp(name2, "server_pool") != 0)) {
+               radlog(L_ERR, "%s[%d]: Section is not a server_pool.",
+                      filename, cf_section_lineno(cs));
+               return 0;
+       }
+
+       name2 = cf_section_name2(cs);
+       if (!name2) {
+               radlog(L_ERR, "%s[%d]: Server pool section is missing a name.",
+                      filename, cf_section_lineno(cs));
+               return 0;
+       }
+
+       num_home_servers = 0;
+       for (cp = cf_pair_find(cs, "home_server");
+            cp != NULL;
+            cp = cf_pair_find_next(cs, cp, "home_server")) {
+               num_home_servers++;
+       }
+
+       if (num_home_servers == 0) {
+               radlog(L_ERR, "%s[%d]: No home servers defined in pool %s",
+                      filename, cf_section_lineno(cs), name2);
+               return 0;
+       }
+
+       pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+       memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+
+       pool->type = HOME_POOL_FAIL_OVER;
+       pool->name = name2;
+
+       cp = cf_pair_find(cs, "type");
+       if (cp) {
+               value = cf_pair_value(cp);
+               if (!value) {
+                       radlog(L_ERR, "%s[%d]: No value given for type.",
+                              filename, cf_pair_lineno(cp));
+                       free(pool);
+                       return 0;
+               }
+
+               if (strcmp(value, "load-balance") == 0) {
+                       pool->type = HOME_POOL_LOAD_BALANCE;
+
+               } else if (strcmp(value, "fail-over") == 0) {
+                       pool->type = HOME_POOL_FAIL_OVER;
+
+               } else {
+                       radlog(L_ERR, "%s[%d]: Unknown type \"%s\".",
+                              filename, cf_pair_lineno(cp), value);
+                       free(pool);
+                       return 0;
+               }
+
+               DEBUG2(" server_pool %s: type = %s", name2, value);
+       }
+
+       for (cp = cf_pair_find(cs, "home_server");
+            cp != NULL;
+            cp = cf_pair_find_next(cs, cp, "home_server")) {
+               home_server myhome, *home;
+
+               value = cf_pair_value(cp);
+               if (!value) {
+                       radlog(L_ERR, "%s[%d]: No value given for home_server.",
+                              filename, cf_pair_lineno(cp));
+                       free(pool);
+                       return 0;
+               }
+
+               myhome.name = value;
+
+               home = rbtree_finddata(home_servers_byname, &myhome);
+               if (!home) {
+                       CONF_SECTION *server_cs;
+
+                       server_cs = cf_section_sub_find_name2(NULL,
+                                                             "home_server",
+                                                             value);
+                       if (!server_cs) {
+                               radlog(L_ERR, "%s[%d]: Unknown home_server \"%s\".",
+                                      filename, cf_pair_lineno(cp), value);
+                               free(pool);
+                               return 0;
+                       }
+                       
+                       if (!home_server_add(filename, server_cs)) {
+                               free(pool);
+                               return 0;
+                       }
+
+                       home = rbtree_finddata(home_servers_byname, &myhome);
+                       if (!home) {
+                               rad_assert("Internal sanity check failed");
+                               return 0;
+                       }
+               }
+
+               if (!pool->server_type) {
+                       rad_assert(home->type != 0);
+                       pool->server_type = home->type;
+
+               } else if (pool->server_type != home->type) {
+                       radlog(L_ERR, "%s[%d]: Home server \"%s\" is not of the same type as previous servers in server pool %s",
+                              filename, cf_pair_lineno(cp), value, pool->name);
+                       free(pool);
+                       return 0;
+               }
+
+               if (0) {
+                       DEBUG2("Warning: Duplicate home server %s in server pool %s", home->name, pool->name);
+                       continue;
+               }
+
+               DEBUG2(" server_pool %s: home_server = %s", name2, home->name);
+               pool->servers[pool->num_home_servers] = home;
+               pool->num_home_servers++;
+
+       } /* loop over home_server's */
+
+       if (!rbtree_insert(home_pools_byname, pool)) {
+               rad_assert("Internal sanity check failed");
+               return 0;
+       }
+
+       rad_assert(pool->server_type != 0);
+       
+       return 1;
+}
+
+
+static int old_server_add(const char *filename, int lineno,
+                         const char *name, const char *secret,
+                         home_pool_type_t ldflag, home_pool_t **pool_p,
+                         int type)
+{
+       int i, insert_point, num_home_servers;
+       home_server myhome, *home;
+       home_pool_t mypool, *pool;
+       CONF_SECTION *cs;
+
+       /*
+        *      LOCAL realms get sanity checked, and nothing else happens.
+        */
+       if (strcmp(name, "LOCAL") == 0) {
+               if (*pool_p) {
+                       radlog(L_ERR, "%s[%d]: Realm \"%s\" cannot be both LOCAL and remote", filename, lineno, name);
+                       return 0;
+               }
+               return 1;
+       }
+
+       mypool.name = name;
+       pool = rbtree_finddata(home_pools_byname, &mypool);
+       if (pool) {
+               if (pool->type != ldflag) {
+                       radlog(L_ERR, "%s[%d]: Inconsistent ldflag for server pool \"%s\"", filename, lineno, name);
+                       return 0;
+               }
+
+               if (pool->server_type != type) {
+                       radlog(L_ERR, "%s[%d]: Inconsistent home server type for server pool \"%s\"", filename, lineno, name);
+                       return 0;
+               }
+       }
+
+       myhome.name = name;
+       home = rbtree_finddata(home_servers_byname, &myhome);
+       if (home) {
+               if (strcmp(home->secret, secret) != 0) {
+                       radlog(L_ERR, "%s[%d]: Inconsistent shared secret for home server \"%s\"", filename, lineno, name);
+                       return 0;
+               }
+
+               if (home->type != type) {
+                       radlog(L_ERR, "%s[%d]: Inconsistent type for home server \"%s\"", filename, lineno, name);
+                       return 0;
+               }
+               
+               /*
+                *      See if the home server is already listed
+                *      in the pool.  If so, do nothing else.
+                */
+               if (pool) for (i = 0; i < pool->num_home_servers; i++) {
+                       if (pool->servers[i] == home) {
+                               return 1;
+                       }
+               }
+       }
+
+       /*
+        *      If we do have a pool, check that there is room to
+        *      insert the home server we've found, or the one that we
+        *      create here.
+        *
+        *      Note that we insert it into the LAST available
+        *      position, in order to maintain the same order as in
+        *      the configuration files.
+        */
+       insert_point = -1;
+       if (pool) {
+               for (i = pool->num_home_servers - 1; i >= 0; i--) {
+                       if (pool->servers[i]) break;
+
+                       if (!pool->servers[i]) {
+                               insert_point = i;
+                       }
+               }
+
+               if (insert_point < 0) {
+                       radlog(L_ERR, "%s[%d]: No room in pool to add home server \"%s\".  Please update the realm configuration to use the new-style home servers and server pools.", filename, lineno, name);
+                       return 0;
+               }
+       }
+
+       /*
+        *      No home server, allocate one.
+        */
+       if (!home) {         
+               const char *p;
+               char *q;
+
+               home = rad_malloc(sizeof(*home));
+               memset(home, 0, sizeof(*home));
+
+               home->name = name;
+               home->hostname = name;
+               home->type = type;
+               home->secret = secret;
+
+               p = strchr(name, ':');
+               if (!p) {
+                       if (type == HOME_TYPE_AUTH) {
+                               home->port = PW_AUTH_UDP_PORT;
+                       } else {
+                               home->port = PW_ACCT_UDP_PORT;
+                       }
+
+                       p = name;
+                       q = NULL;
+
+               } else if (p == name) {
+                               radlog(L_ERR, "%s[%d]: Invalid hostname %s.",
+                                      filename, lineno, name);
+                               free(home);
+                               return 0;
+
+               } else {
+                       home->port = atoi(p + 1);
+                       if ((home->port == 0) || (home->port > 65535)) {
+                               radlog(L_ERR, "%s[%d]: Invalid port %s.",
+                                      filename, lineno, p + 1);
+                               free(home);
+                               return 0;
+                       }
+
+                       q = rad_malloc((p - name) + 1);
+                       memcpy(q, name, (p - name));
+                       q[p - name] = '\0';
+                       p = q;
+               }
+
+               if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
+                       radlog(L_ERR, "%s[%d]: Failed looking up hostname %s.",
+                              filename, lineno, p);
+                       free(home);
+                       free(q);
+                       return 0;
+               }
+               free(q);
+
+               /*
+                *      Use the old-style configuration.
+                */
+               home->max_outstanding = 65535*16;
+               home->zombie_period = mainconfig.proxy_retry_delay * mainconfig.proxy_retry_count;
+               if (home->zombie_period == 0) home->zombie_period =30;
+               home->response_window = home->zombie_period - 1;
+
+               home->ping_check = HOME_PING_CHECK_NONE;
+
+               home->revive_interval = mainconfig.proxy_dead_time;
+
+               if (rbtree_finddata(home_servers_byaddr, home)) {
+                       radlog(L_ERR, "%s[%d]: Home server %s has the same IP address as another home server.",
+                              filename, lineno, name);
+                       free(home);
+                       return 0;
+               }
+
+               if (!rbtree_insert(home_servers_byname, home)) {
+                       radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+                              filename, lineno, name);
+                       free(home);
+                       return 0;
+               }
+               
+               if (!rbtree_insert(home_servers_byaddr, home)) {
+                       rbtree_deletebydata(home_servers_byname, home);
+                       radlog(L_ERR, "%s[%d]: Internal error adding home server %s.",
+                              filename, lineno, name);
+                       free(home);
+                       return 0;
+               }
+       }
+
+       /*
+        *      We now have a home server, see if we can insert it
+        *      into pre-existing pool.
+        */
+       if (insert_point >= 0) {
+               rad_assert(pool != NULL);
+               pool->servers[insert_point] = home;
+               return 1;
+       }
+
+       rad_assert(pool == NULL);
+       rad_assert(home != NULL);
+
+       /*
+        *      Count the old-style realms of this name.
+        */
+       num_home_servers = 0;
+       for (cs = cf_section_sub_find_name2(mainconfig.config, "realm", name);
+            cs != NULL;
+            cs = cf_section_sub_find_name2(cs, "realm", name)) {
+               num_home_servers++;
+       }
+
+
+       pool = rad_malloc(sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+       memset(pool, 0, sizeof(*pool) + num_home_servers * sizeof(pool->servers[0]));
+
+       pool->name = name;
+       pool->type = ldflag;
+       pool->server_type = type;
+       pool->num_home_servers = num_home_servers;
+       pool->servers[0] = home;
+
+       if (!rbtree_insert(home_pools_byname, pool)) {
+               rad_assert("Internal sanity check failed");
+               return 0;
+       }
+
+       *pool_p = pool;
+
+       return 1;
+}
+
+static int old_realm_config(const char *filename, CONF_SECTION *cs, REALM *r)
+{
+       char *host;
+       const char *secret;
+       home_pool_type_t ldflag;
+
+       secret = cf_section_value_find(cs, "secret");
+
+       host = cf_section_value_find(cs, "ldflag");
+       if (!host ||
+           (strcasecmp(host, "fail_over") == 0)) {
+               ldflag = HOME_POOL_FAIL_OVER;
+               DEBUG2("  realm %s: ldflag = fail_over", r->name);
+
+       } else if (strcasecmp(host, "round_robin") == 0) {
+               ldflag = HOME_POOL_LOAD_BALANCE;
+               DEBUG2("  realm %s: ldflag = round_robin", r->name);
+
+       } else {
+               radlog(L_ERR, "%s[%d]: Unknown value \"%s\" for ldflag",
+                      filename, cf_section_lineno(cs), host);
+               return 0;
+       }
+
+       /*
+        *      Allow old-style if it doesn't exist, or if it exists and
+        *      it's LOCAL.
+        */
+       if (((host = cf_section_value_find(cs, "authhost")) != NULL) &&
+           (strcmp(host, "LOCAL") != 0)) {
+               if (!secret) {
+                       radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
+                              filename, cf_section_lineno(cs), r->name);
+                       return 0;
+               }
+
+               DEBUG2("  realm %s: authhost = %s",  r->name, host);
+
+               if (!old_server_add(filename, cf_section_lineno(cs),
+                                   host, secret, ldflag,
+                                   &r->auth_pool, HOME_TYPE_AUTH)) {
+                       return 0;
+               }
+       }
+
+       if (((host = cf_section_value_find(cs, "accthost")) != NULL) &&
+           (strcmp(host, "LOCAL") != 0)) {
+               if (!secret) {
+                       radlog(L_ERR, "%s[%d]: No shared secret supplied for realm: %s",
+                              filename, cf_section_lineno(cs), r->name);
+                       return 0;
+               }
+
+               DEBUG2("  realm %s: accthost = %s", r->name, host);
+
+               if (!old_server_add(filename, cf_section_lineno(cs),
+                                   host, secret, ldflag,
+                                   &r->auth_pool, HOME_TYPE_ACCT)) {
+                       return 0;
+               }
+       }
+
+       if (secret) DEBUG2("  realm %s: secret = %s", r->name, secret);
+
+       return 1;
+       
+}
+
+
+static int add_pool_to_realm(const char *filename, int lineno,
+                            const char *name, home_pool_t **dest,
+                            int server_type)
+{
+       home_pool_t mypool, *pool;
+
+       mypool.name = name;
+       pool = rbtree_finddata(home_pools_byname, &mypool);
+       if (!pool) {
+               CONF_SECTION *pool_cs;
+
+               pool_cs = cf_section_sub_find_name2(NULL, "server_pool",
+                                                   name);
+               if (!pool_cs) {
+                       radlog(L_ERR, "%s[%d]: Failed to find server_pool \"%s\"",
+                              filename, lineno, name);
+                       return 0;
+               }
+
+               if (!server_pool_add(filename, pool_cs)) {
+                       return 0;
+               }
+               
+               pool = rbtree_finddata(home_pools_byname, &mypool);
+               if (!pool) {
+                       rad_assert("Internal sanity check failed");
+                       return 0;
+               }
+       }
+
+       if (pool->server_type != server_type) {
+               radlog(L_ERR, "%s[%d]: Incompatible server_pool \"%s\" (mixed auth_pool / acct_pool)",
+                      filename, lineno, name);
+               return 0;
+       }
+
+       *dest = pool;
+
+       return 1;
+}
+
+int realm_add(const char *filename, CONF_SECTION *cs)
+{
+       const char *name2;
+       char *pool = NULL;
+       REALM *r;
+       CONF_PAIR *cp;
+
+       name2 = cf_section_name1(cs);
+       if (!name2 || (strcasecmp(name2, "realm") != 0)) {
+               radlog(L_ERR, "%s[%d]: Section is not a realm.",
+                      filename, cf_section_lineno(cs));
+               return 0;
+       }
+
+       name2 = cf_section_name2(cs);
+       if (!name2) {
+               radlog(L_ERR, "%s[%d]: Realm section is missing the realm name.",
+                      filename, cf_section_lineno(cs));
+               return 0;
+       }
+
+       /*
+        *      The realm MAY already exist if it's an old-style realm.
+        *      In that case, merge the old-style realm with this one.
+        */
+       r = realm_find(name2);
+       if (r) {
+               if (cf_pair_find(cs, "auth_pool") ||
+                   cf_pair_find(cs, "acct_pool")) {
+                       radlog(L_ERR, "%s[%d]: Duplicate realm \"%s\"",
+                              filename, cf_section_lineno(cs), name2);
+                       return 0;
+               }
+
+               if (!old_realm_config(filename, cs, r)) {
+                       return 0;
+               }
+
+               return 1;
+       }
+
+       r = rad_malloc(sizeof(*r));
+       memset(r, 0, sizeof(*r));
+
+       r->name = name2;
+
+       /*
+        *      Prefer new configuration to old one.
+        */
+       cp = cf_pair_find(cs, "auth_pool");
+       if (cp) pool = cf_pair_value(cp);
+       if (cp && pool) {
+               if (!add_pool_to_realm(filename, cf_pair_lineno(cp),
+                                      pool, &r->auth_pool, HOME_TYPE_AUTH)) {
+                       free(r);
+                       return 0;
+               }
+               DEBUG2(" realm %s: auth_pool = %s", name2, pool);
+       }
+
+       cp = cf_pair_find(cs, "acct_pool");
+       if (cp) pool = cf_pair_value(cp);
+       if (cp && pool) {
+               if (!add_pool_to_realm(filename, cf_pair_lineno(cp),
+                                      pool, &r->acct_pool, HOME_TYPE_ACCT)) {
+                       free(r);
+                       return 0;
+               }
+               DEBUG2(" realm %s: acct_pool = %s", name2, pool);
+       }
+
+       r->striprealm = 1;
+       
+       if ((cf_section_value_find(cs, "nostrip")) != NULL) {
+               r->striprealm = 0;
+               DEBUG2(" realm %s: nostrip", name2);
+       }
+
+       /*
+        *      We're a new-style realm.  Complain if we see the old
+        *      directives.
+        */
+       if (r->auth_pool || r->acct_pool) {
+               if (((cp = cf_pair_find(cs, "authhost")) != NULL) ||
+                   ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
+                   ((cp = cf_pair_find(cs, "secret")) != NULL) ||
+                   ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
+                       DEBUG2("WARNING: Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
+               }
+
+
+               /*
+                *      The realm MAY be an old-style realm, as there
+                *      was no auth_pool or acct_pool.  Double-check
+                *      it, just to be safe.
+                */
+       } else if (!old_realm_config(filename, cs, r)) {
+               free(r);
+               return 0;
+       }
+
+       if (!rbtree_insert(realms_byname, r)) {
+               rad_assert("Internal sanity check failed");
+               free(r);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/*
+ *     Find a realm in the REALM list.
+ */
+REALM *realm_find(const char *name)
+{
+       REALM myrealm;
+
+       if (!name) name = "NULL";
+
+       myrealm.name = name;
+       return rbtree_finddata(realms_byname, &myrealm);
+}
+
+
+home_server *home_server_ldb(REALM *r, int code)
+{
+       uint32_t        count;
+       home_server     *lb;
+       home_pool_t     *pool;
+
+       if (code == PW_AUTHENTICATION_REQUEST) {
+               pool = r->auth_pool;
+
+       } else if (code == PW_ACCOUNTING_REQUEST) {
+               pool = r->acct_pool;
+
+       } else {
+               rad_assert("Internal sanity check failed");
+               return NULL;
+       }
+
+       if (!pool) {
+               rad_assert("Internal sanity check failed");
+               return NULL;
+       }
+
+       lb = NULL;
+
+       for (count = 0; count < pool->num_home_servers; count++) {
+               home_server *home = pool->servers[count];
+
+               if (home->state == HOME_STATE_IS_DEAD) {
+                       continue;
+               }
+
+               /*
+                *      This home server is too busy.  Choose another one.
+                */
+               if (home->currently_outstanding >= home->max_outstanding) {
+                       continue;
+               }
+               
+               /*
+                *      Fail-over, pick the first one that matches.
+                */
+               if (pool->type == HOME_POOL_FAIL_OVER) {
+                       return home;
+               }
+
+               /*
+                *      FUTURE: If we're well into "zombie_period",
+                *      then we probably want to think about
+                *      load-balancing the packet somewhere else, as
+                *      the home server is probably dead.
+                */
+
+               /*
+                *      We're doing load-balancing.  Pick a random
+                *      number, which will be used to determine which
+                *      home server is chosen.
+                */
+               if (!lb) {
+                       lb = home;
+                       continue;
+               }
+
+               /*
+                *      See the "camel book" for why this works.
+                *
+                *      If (rand(0..n) < 1), pick the current server.
+                *      We add a scale factor of 65536, to avoid
+                *      floating point.
+                */
+               if (((count + 1) * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
+                       lb = home;
+               }
+       } /* loop over the realms */
+
+       /*
+        *      No live match found, and no fallback to the "DEFAULT"
+        *      realm.  We fix this by blindly marking all servers as
+        *      "live".  But only do it for ones that don't support
+        *      "pings", as they will be marked live when they
+        *      actually are live.
+        */
+       if (!lb &&
+           !mainconfig.proxy_fallback &&
+           mainconfig.wake_all_if_all_dead) {
+               for (count = 0; count < pool->num_home_servers; count++) {
+                       home_server *home = pool->servers[count];
+
+                       if ((home->state == HOME_STATE_IS_DEAD) &&
+                           (home->ping_check == HOME_PING_CHECK_NONE)) {
+                               home->state = HOME_STATE_ALIVE;
+                               if (!lb) lb = home;
+                       }
+               }
+       }
+
+       /*
+        *      Still nothing.  Look up the DEFAULT realm, but only
+        *      if we weren't looking up the NULL or DEFAULT realms.
+        */
+       if (!lb &&
+           mainconfig.proxy_fallback &&
+           (strcmp(r->name, "NULL") != 0) &&
+           (strcmp(r->name, "DEFAULT") != 0)) {
+               REALM *rd = realm_find("DEFAULT");
+
+               if (rd) {
+                       DEBUG2("  Realm %s has no live home servers.  Falling back to the DEFAULT realm.", r->name);
+                       return home_server_ldb(rd, code);
+               }
+       }
+
+       /*
+        *      Return the appropriate home server.
+        */
+       return lb;
+}
+
+
+home_server *home_server_find(lrad_ipaddr_t *ipaddr, int port)
+{
+       home_server myhome;
+
+       myhome.ipaddr = *ipaddr;
+       myhome.port = port;
+
+       return rbtree_finddata(home_servers_byaddr, &myhome);
+}
diff --git a/src/main/request_list.c b/src/main/request_list.c
deleted file mode 100644 (file)
index 0173ea6..0000000
+++ /dev/null
@@ -1,703 +0,0 @@
-/*
- * request_list.c      Hide the handling of the REQUEST list from
- *                     the main server.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Copyright 2003-2004,2006  The FreeRADIUS server project
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include <freeradius-devel/autoconf.h>
-
-#include <stdlib.h>
-#include <string.h>
-#include <signal.h>
-
-#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/request_list.h>
-#include <freeradius-devel/radius_snmp.h>
-
-struct request_list_t {
-       lrad_packet_list_t *pl;
-};
-
-#ifdef HAVE_PTHREAD_H
-static pthread_mutex_t proxy_mutex;
-#else
-/*
- *     This is easier than ifdef's throughout the code.
- */
-#define pthread_mutex_lock(_x)
-#define pthread_mutex_unlock(_x)
-#endif
-
-static lrad_packet_list_t *proxy_list = NULL;
-
-/*
- *     We keep the proxy FD's here.  The RADIUS Id's are marked
- *     "allocated" per Id, via a bit per proxy FD.
- */
-static int             proxy_fds[32];
-static rad_listen_t    *proxy_listeners[32];
-
-/*
- *     Initialize the request list.
- */
-request_list_t *rl_init(void)
-{
-       request_list_t *rl = rad_malloc(sizeof(*rl));
-
-       /*
-        *      Initialize the request_list[] array.
-        */
-       memset(rl, 0, sizeof(*rl));
-
-       rl->pl = lrad_packet_list_create(0);
-       if (!rl->pl) {
-               rad_assert("FAIL" == NULL);
-       }
-
-       return rl;
-}
-
-int rl_init_proxy(void)
-{
-       /*
-        *      Hacks, so that multiple users can call rl_init,
-        *      and it won't get excited.
-        *
-        *      FIXME: Move proxy stuff to another struct entirely.
-        */
-       if (proxy_list) return 0;
-
-       /*
-        *      Create the tree for managing proxied requests and
-        *      responses.
-        */
-       proxy_list = lrad_packet_list_create(1);
-       if (!proxy_list) {
-               rad_assert("FAIL" == NULL);
-       }
-
-#ifdef HAVE_PTHREAD_H
-       /*
-        *      For now, always create the mutex.
-        *
-        *      Later, we can only create it if there are multiple threads.
-        */
-       if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
-               radlog(L_ERR, "FATAL: Failed to initialize proxy mutex: %s",
-                      strerror(errno));
-               exit(1);
-       }
-#endif
-
-       {
-               int i;
-               rad_listen_t *listener;
-
-               /*
-                *      Mark the Fd's as unused.
-                */
-               for (i = 0; i < 32; i++) proxy_fds[i] = -1;
-
-               for (listener = mainconfig.listen;
-                    listener != NULL;
-                    listener = listener->next) {
-                       if (listener->type == RAD_LISTEN_PROXY) {
-                               /*
-                                *      FIXME: This works only because we
-                                *      start off with one proxy socket.
-                                */
-                               proxy_fds[listener->fd & 0x1f] = listener->fd;
-                               proxy_listeners[listener->fd & 0x1f] = listener;
-                               lrad_packet_list_socket_add(proxy_list, listener->fd);
-                               break;
-                       }
-               }
-       }
-
-       return 1;
-}
-
-static int rl_free_entry(void *ctx, void *data)
-{
-       ctx = ctx;              /* -Wunused */
-       REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
-
-#ifdef HAVE_PTHREAD_H 
-       /*
-        *      If someone is processing this request, kill
-        *      them, and mark the request as not being used.
-        *
-        *      FIXME: Move the request to the "dead pool",
-        *      and don't kill the thread.
-        */
-       if (request->child_pid != NO_SUCH_CHILD_PID) {
-               pthread_kill(request->child_pid, SIGKILL);
-               request->child_pid = NO_SUCH_CHILD_PID;
-       }
-#endif
-       request_free(&request);
-
-       return 0;
-}
-
-
-/*
- *     Delete everything in the request list.
- *
- *     This should be called only when debugging the server...
- */
-void rl_deinit(request_list_t *rl)
-{
-       if (!rl) return;
-
-       if (proxy_list) {
-               lrad_packet_list_free(proxy_list);
-               proxy_list = NULL;
-       }
-
-       /*
-        *      Delete everything in the table, too.
-        */
-       lrad_packet_list_walk(rl->pl, NULL, rl_free_entry);
-
-       lrad_packet_list_free(rl->pl);
-
-       /*
-        *      Just to ensure no one is using the memory.
-        */
-       memset(rl, 0, sizeof(*rl));
-       free(rl);
-}
-
-
-/*
- *     Yank a request from the tree, without free'ing it.
- */
-void rl_yank(request_list_t *rl, REQUEST *request)
-{
-#ifdef WITH_SNMP
-       /*
-        *      Update the SNMP statistics.
-        *
-        *      Note that we do NOT do this in rad_respond(),
-        *      as that function is called from child threads.
-        *      Instead, we update the stats when a request is
-        *      deleted, because only the main server thread calls
-        *      this function...
-        */
-       if (mainconfig.do_snmp) {
-               switch (request->reply->code) {
-               case PW_AUTHENTICATION_ACK:
-                 rad_snmp.auth.total_responses++;
-                 rad_snmp.auth.total_access_accepts++;
-                 break;
-
-               case PW_AUTHENTICATION_REJECT:
-                 rad_snmp.auth.total_responses++;
-                 rad_snmp.auth.total_access_rejects++;
-                 break;
-
-               case PW_ACCESS_CHALLENGE:
-                 rad_snmp.auth.total_responses++;
-                 rad_snmp.auth.total_access_challenges++;
-                 break;
-
-               case PW_ACCOUNTING_RESPONSE:
-                 rad_snmp.acct.total_responses++;
-                 break;
-
-               default:
-                       break;
-               }
-       }
-#endif
-
-       /*
-        *      Delete the request from the list.
-        */
-       lrad_packet_list_yank(rl->pl, request->packet);
-       
-       /*
-        *      If there's a proxied packet, and we're still
-        *      waiting for a reply, then delete the packet
-        *      from the list of outstanding proxied requests.
-        */
-       if (request->proxy &&
-           (request->proxy_outstanding > 0)) {
-               pthread_mutex_lock(&proxy_mutex);
-               lrad_packet_list_yank(proxy_list, request->proxy);
-               lrad_packet_list_id_free(proxy_list, request->proxy);
-               pthread_mutex_unlock(&proxy_mutex);
-       }
-}
-
-
-/*
- *     Delete a request from the tree.
- */
-void rl_delete(request_list_t *rl, REQUEST *request)
-{
-       rl_yank(rl, request);
-       request_free(&request);
-}
-
-
-/*
- *     Add a request to the request list.
- */
-int rl_add(request_list_t *rl, REQUEST *request)
-{
-       return lrad_packet_list_insert(rl->pl, &request->packet);
-}
-
-/*
- *     Look up a particular request, using:
- *
- *     Request ID, request code, source IP, source port,
- *
- *     Note that we do NOT use the request vector to look up requests.
- *
- *     We MUST NOT have two requests with identical (id/code/IP/port), and
- *     different vectors.  This is a serious error!
- */
-REQUEST *rl_find(request_list_t *rl, RADIUS_PACKET *packet)
-{
-       RADIUS_PACKET **packet_p;
-
-       packet_p = lrad_packet_list_find(rl->pl, packet);
-       if (!packet_p) return NULL;
-
-       return lrad_packet2myptr(REQUEST, packet, packet_p);
-}
-
-/*
- *     Add an entry to the proxy tree.
- *
- *     This is the ONLY function in this source file which may be called
- *     from a child thread.  It therefore needs mutexes...
- */
-int rl_add_proxy(REQUEST *request)
-{
-       int i, proxy;
-       char buf[128];
-
-       request->proxy_outstanding = 1;
-       request->proxy->sockfd = -1;
-
-       pthread_mutex_lock(&proxy_mutex);
-
-       if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
-               int found;
-               rad_listen_t *proxy_listener;
-
-               /*
-                *      Allocate a new proxy Fd.  This function adds it
-                *      into the list of listeners.
-                */
-               proxy_listener = proxy_new_listener();
-               if (!proxy_listener) {
-                       pthread_mutex_unlock(&proxy_mutex);
-                       DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
-                       return 0;
-               }
-
-               /*
-                *      Cache it locally.
-                */
-               found = -1;
-               proxy = proxy_listener->fd;
-               for (i = 0; i < 32; i++) {
-                       /*
-                        *      Found a free entry.  Save the socket,
-                        *      and remember where we saved it.
-                        */
-                       if (proxy_fds[(proxy + i) & 0x1f] == -1) {
-                               found = (proxy + i) & 0x1f;
-                               proxy_fds[found] = proxy;
-                               proxy_listeners[found] = proxy_listener;
-                               break;
-                       }
-               }
-               rad_assert(found >= 0);
-
-               if (!lrad_packet_list_socket_add(proxy_list, proxy_listener->fd)) {
-                       pthread_mutex_unlock(&proxy_mutex);
-                       DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
-                       return 0; /* leak proxy_listener */
-                       
-               }
-                   
-               if (!lrad_packet_list_id_alloc(proxy_list, request->proxy)) {
-                       pthread_mutex_unlock(&proxy_mutex);
-                       DEBUG2("ERROR: Failed to create a new socket for proxying requests.");
-                       return 0;
-               }
-       }
-
-       /*
-        *      FIXME: Hack until we get rid of rad_listen_t, and put
-        *      the information into the packet_list.
-        */
-       proxy = -1;
-       for (i = 0; i < 32; i++) {
-               if (proxy_fds[i] == request->proxy->sockfd) {
-                       proxy = i;
-                       break;
-               }
-       }
-       rad_assert(proxy >= 0);
-
-       rad_assert(proxy_fds[proxy] != -1);
-       request->proxy_listener = proxy_listeners[proxy];
-
-       if (!lrad_packet_list_insert(proxy_list, &request->proxy)) {
-               pthread_mutex_unlock(&proxy_mutex);
-               DEBUG2("ERROR: Failed to insert entry into proxy list");
-               return 0;
-       }
-       
-       pthread_mutex_unlock(&proxy_mutex);
-
-       DEBUG3(" proxy: allocating destination %s port %d - Id %d",
-              inet_ntop(request->proxy->dst_ipaddr.af,
-                        &request->proxy->dst_ipaddr.ipaddr, buf, sizeof(buf)),
-              request->proxy->dst_port,
-              request->proxy->id);
-       
-       return 1;
-}
-
-
-/*
- *     Look up a particular request, using:
- *
- *     Request Id, request code, source IP, source port,
- *
- *     Note that we do NOT use the request vector to look up requests.
- *
- *     We MUST NOT have two requests with identical (id/code/IP/port), and
- *     different vectors.  This is a serious error!
- */
-REQUEST *rl_find_proxy(RADIUS_PACKET *reply)
-{
-       RADIUS_PACKET **proxy_p;
-       REQUEST *request;
-
-       pthread_mutex_lock(&proxy_mutex);
-       proxy_p = lrad_packet_list_find_byreply(proxy_list, reply);
-
-       if (!proxy_p) {
-               pthread_mutex_unlock(&proxy_mutex);
-               return NULL;
-       }
-
-       request = lrad_packet2myptr(REQUEST, proxy, proxy_p);
-       rad_assert(request->proxy_outstanding > 0);
-       request->proxy_outstanding--;
-               
-       /*
-        *      Received all of the replies we expect.
-        *      delete it from the managed list.
-        */
-       if (request->proxy_outstanding == 0) {
-               lrad_packet_list_yank(proxy_list, request->proxy);
-               lrad_packet_list_id_free(proxy_list, request->proxy);
-       }
-       pthread_mutex_unlock(&proxy_mutex);
-
-       return request;
-}
-
-
-/*
- *     Return the number of requests in the request list.
- */
-int rl_num_requests(request_list_t *rl)
-{
-       return lrad_packet_list_num_elements(rl->pl);
-}
-
-
-/*
- *     See also radiusd.c
- */
-#define SLEEP_FOREVER (65536)
-typedef struct rl_walk_t {
-       time_t  now;
-       int     sleep_time;
-       request_list_t *rl;
-} rl_walk_t;
-
-
-/*
- *  Refresh a request, by using cleanup_delay, max_request_time, etc.
- *
- *  When walking over the request list, all of the per-request
- *  magic is done here.
- */
-static int refresh_request(void *ctx, void *data)
-{
-       int time_passed;
-       rl_walk_t *info = (rl_walk_t *) ctx;
-       child_pid_t child_pid;
-       request_list_t *rl = info->rl;
-       REQUEST *request = lrad_packet2myptr(REQUEST, packet, data);
-
-       rad_assert(request->magic == REQUEST_MAGIC);
-
-       time_passed = (int) (info->now - request->timestamp);
-       
-       /*
-        *      If the request is marked as a delayed reject, AND it's
-        *      time to send the reject, then do so now.
-        */
-       if (request->finished &&
-           ((request->options & RAD_REQUEST_OPTION_DELAYED_REJECT) != 0)) {
-               rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
-               if (time_passed < mainconfig.reject_delay) {
-                       goto reject_delay;
-               }
-
-       reject_packet:
-               /*
-                *      Clear the 'delayed reject' bit, so that we
-                *      don't do this again, and fall through to
-                *      setting cleanup delay.
-                */
-               request->listener->send(request->listener, request);
-               request->options &= ~RAD_REQUEST_OPTION_DELAYED_REJECT;
-
-               /*
-                *      FIXME: Beware interaction with cleanup_delay,
-                *      where we might send a reject, and immediately
-                *      there-after clean it up!
-                */
-       }
-
-       /*
-        *      If the request is finished, THEN
-        *      check that more than cleanup_delay seconds have passed
-        *      since it was received
-        *      OR, if this is a request which had the "don't cache"
-        *      option set, then it CANNOT have a duplicate
-        *      SO, clean it up
-        */
-       if (request->finished &&
-           ((time_passed >= mainconfig.cleanup_delay) ||
-           ((request->options & RAD_REQUEST_OPTION_DONT_CACHE) != 0))) {
-               rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
-               /*
-                *  Request completed, delete it, and unlink it
-                *  from the currently 'alive' list of requests.
-                */
-       cleanup:
-               DEBUG2("Cleaning up request %d ID %d with timestamp %08lx",
-                               request->number, request->packet->id,
-                               (unsigned long) request->timestamp);
-
-               /*
-                *  Delete the request.
-                */
-               rl_delete(rl, request);
-               return 0;
-       }
-
-       /*
-        *      If more than max_request_time has passed since
-        *      we received the request, kill it.
-        */
-       if (time_passed >= mainconfig.max_request_time) {
-               int number;
-
-               child_pid = request->child_pid;
-               number = request->number;
-
-               /*
-                *      There MUST be a RAD_PACKET reply.
-                */
-               rad_assert(request->reply != NULL);
-
-               /*
-                *      If we've tried to proxy the request, and
-                *      the proxy server hasn't responded, then
-                *      we send a REJECT back to the caller.
-                *
-                *      For safety, we assert that there is no child
-                *      handling the request.  If the assertion fails,
-                *      it means that we've sent a proxied request to
-                *      the home server, and the child thread is still
-                *      sitting on the request!
-                */
-               if (request->proxy && !request->proxy_reply) {
-                       rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
-
-                       radlog(L_ERR, "Rejecting request %d due to lack of any response from home server %s port %d",
-                              request->number,
-                              client_name_old(&request->packet->src_ipaddr),
-                              request->packet->src_port);
-                       request_reject(request, REQUEST_FAIL_HOME_SERVER);
-                       request->finished = TRUE;
-                       return 0;
-               }
-
-#ifdef DELETE_BLOCKED_REQUESTS
-       /*
-        * Calling pthread_cancel() without a cancel handler is an
-        * exceedingly bad idea.  This code is left here in case
-        * we implement per-module cancel handlers later.
-        * See freeradius-devel archives,
-        * "cancelling requests due to max_request_time"
-        *
-        * If implemented, just remove the #ifdef's for DELETE_BLOCKED_REQUESTS
-        * scattered throughout the code (this file and others), and
-        * add the option back to radiusd.conf.in.
-        */
-               if (mainconfig.kill_unresponsive_children) {
-                       if (child_pid != NO_SUCH_CHILD_PID) {
-                               /*
-                                *  This request seems to have hung
-                                *   - kill it
-                                */
-#ifdef HAVE_PTHREAD_H
-                               radlog(L_ERR, "Killing unresponsive thread for request %d",
-                                      request->number);
-                               pthread_cancel(child_pid);
-#endif
-                       } /* else no proxy reply, quietly fail */
-
-                       /*
-                        *      Maybe we haven't killed it.  In that
-                        *      case, print a warning.
-                        */
-               } else
-#endif
-               if ((child_pid != NO_SUCH_CHILD_PID) &&
-                       ((request->options & RAD_REQUEST_OPTION_LOGGED_CHILD) == 0)) {
-                       radlog(L_ERR, "WARNING: Unresponsive child (id %lu) for request %d",
-                              (unsigned long)child_pid, number);
-
-                       /*
-                        *  Set the option that we've sent a log message,
-                        *  so that we don't send more than one message
-                        *  per request.
-                        */
-                       request->options |= RAD_REQUEST_OPTION_LOGGED_CHILD;
-               }
-
-               /*
-                *      Send a reject message for the request, mark it
-                *      finished, and forget about the child.
-                */
-               request_reject(request, REQUEST_FAIL_SERVER_TIMEOUT);
-               
-               request->child_pid = NO_SUCH_CHILD_PID;
-
-#ifdef DELETE_BLOCKED_REQUESTS
-               if (mainconfig.kill_unresponsive_children)
-                       request->finished = TRUE;
-#endif
-               return 0;
-       } /* else the request is still allowed to be in the queue */
-
-       /*
-        *      If the request is finished, set the cleanup delay.
-        */
-       if (request->finished) {
-               time_passed = mainconfig.cleanup_delay - time_passed;
-               goto setup_timeout;
-       }
-
-       /*
-        *      Accounting request.  Don't re-send them, since the NAS
-        *      will take care of doing that, and we're not a NAS.
-        *      Instead, simply clean them up once we're pretty sure
-        *      that the home server won't be responding.
-        */
-       if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
-           request->proxy && !request->proxy_reply &&
-           (request->child_pid == NO_SUCH_CHILD_PID) &&
-           ((info->now - request->proxy_start_time) > (mainconfig.proxy_retry_delay * 2))) {
-               goto cleanup;
-       }
-
-
-       /*
-        *      Set reject delay, if appropriate.
-        */
-       if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
-           (mainconfig.reject_delay > 0)) {
-       reject_delay:
-               time_passed = mainconfig.reject_delay - time_passed;
-               
-               /*
-                *      This catches a corner case, apparently.
-                */
-               if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
-                   (time_passed == 0)) goto reject_packet;
-               if (time_passed <= 0) time_passed = 1;
-               goto setup_timeout;
-       }
-
-       /*
-        *      The request is still alive, wake up when it's
-        *      taken too long.
-        */
-       time_passed = mainconfig.max_request_time - time_passed;
-
-setup_timeout:         
-       if (time_passed < 0) time_passed = 1;
-
-       if (time_passed < info->sleep_time) {
-               info->sleep_time = time_passed;
-       }
-
-       return 0;
-}
-
-
-/*
- *  Clean up the request list, every so often.
- *
- *  This is done by walking through ALL of the list, and
- *  - marking any requests which are finished, and expired
- *  - killing any processes which are NOT finished after a delay
- *  - deleting any marked requests.
- *
- *     Returns the number of millisends to sleep, before processing
- *     something.
- */
-int rl_clean_list(request_list_t *rl, time_t now)
-{
-       rl_walk_t info;
-
-       info.now = now;
-       info.sleep_time = SLEEP_FOREVER;
-       info.rl = rl;
-
-       lrad_packet_list_walk(rl->pl, &info, refresh_request);
-
-       if (info.sleep_time < 0) info.sleep_time = 0;
-
-       return info.sleep_time;
-}
diff --git a/src/main/request_process.c b/src/main/request_process.c
deleted file mode 100755 (executable)
index a9cdf35..0000000
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
- *
- * Copyright 2000,2006  The FreeRADIUS server project
- * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
- * Copyright 2000  Chris Parker <cparker@starnetusa.com>
- */
-
-#include <freeradius-devel/ident.h>
-RCSID("$Id$")
-
-#include <freeradius-devel/autoconf.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 <freeradius-devel/radiusd.h>
-#include <freeradius-devel/rad_assert.h>
-#include <freeradius-devel/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;
-               
-               /*
-                *  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;
-       }
-       
-       /*
-        *      Send the reply.  The sender takes care of quenching
-        *      packets.
-        */
-       request->listener->send(request->listener, request);
-
-       return 0;               /* ignored for now */
-}
-
-/*
- *     For debugging
- */
-static const 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:
-               /*
-                *      Conditionally disable the home server we sent
-                *      packets to.
-                */
-               realm_disable(request);
-               
-               /*
-                *      Not supposed to re-process it, 
-                */
-               if (mainconfig.proxy_fail_type) {
-                       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);
-                       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>");
-               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;
-
-                       /*
-                        *  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;
-       }
-
-       /*
-        *      Reject the request.  The sender will take care of delaying
-        *      or quenching rejects.
-        */
-       request->listener->send(request->listener, request);
-}
-
-
-/*
- *  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;
-
-       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) {
-               int decoderesult;
-
-               /*
-                *      Fails verification: silently discard it.
-                */
-               decoderesult = rad_verify(packet, original, secret);
-               if (decoderesult < 0) {
-                       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;
-               }
-
-               /*
-                *      Can't decode it.  This usually means we're out
-                *      of memory.
-                */
-               decoderesult = rad_decode(packet, original, secret);
-               if (decoderesult < 0) {
-                       radlog(L_ERR, "%s", librad_errstr);
-                       request_reject(request, REQUEST_FAIL_DECODE);
-                       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);
-       }
-
-       (*fun)(request);
-
-       /*
-        *      If the request took too long to process, don't do
-        *      anything else.
-        */
-       if (request->options & RAD_REQUEST_OPTION_STOP_NOW) {
-               finished = TRUE;
-               goto postpone_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.
-                */
-               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;
-
-               /*
-                *      Need to copy Proxy-State from request->packet->vps
-                */
-               vp = paircopy2(request->packet->vps, PW_PROXY_STATE);
-               if (vp) pairadd(&(request->reply->vps), vp);
-       }
-
-       /*
-        *      ALWAYS call the sender to send the reply.  The sender
-        *      will take care of doing the appropriate work to
-        *      suppress packets which aren't supposed to be sent over
-        *      the wire, or to be delayed.
-        */
-       request->listener->send(request->listener, request);
-
-       /*
-        *  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:
-       return finished;
-}
index fe686a9..c29fa46 100644 (file)
@@ -301,7 +301,7 @@ static int request_enqueue(REQUEST *request, RAD_REQUEST_FUNP fun)
                 *      Mark the request as done.
                 */
                radlog(L_ERR|L_CONS, "!!! ERROR !!! The server is blocked: discarding new request %d", request->number);
-               request->finished = TRUE;
+               request->child_state = REQUEST_DONE;
                return 0;
        }
 
@@ -327,7 +327,7 @@ static int request_enqueue(REQUEST *request, RAD_REQUEST_FUNP fun)
        if (!lrad_fifo_push(thread_pool.fifo[fifo], entry)) {
                pthread_mutex_unlock(&thread_pool.queue_mutex);
                radlog(L_ERR, "!!! ERROR !!! Failed inserting request %d into the queue", request->number);
-               request->finished = TRUE;
+               request->child_state = REQUEST_DONE;
                return 0;
        }
 
@@ -398,8 +398,8 @@ static int request_dequeue(REQUEST **request, RAD_REQUEST_FUNP *fun)
         *      The main clean-up code won't delete the request from
         *      the request list, until it's marked "finished"
         */
-       if ((*request)->options & RAD_REQUEST_OPTION_STOP_NOW) {
-               (*request)->finished = 1;
+       if ((*request)->master_state == REQUEST_STOP_PROCESSING) {
+               (*request)->child_state = REQUEST_DONE;
                goto retry;
        }
 
@@ -451,8 +451,6 @@ static void *request_handler_thread(void *arg)
         *      Loop forever, until told to exit.
         */
        do {
-               int finished;
-
                /*
                 *      Wait to be signalled.
                 */
@@ -491,37 +489,12 @@ static void *request_handler_thread(void *arg)
                       self->thread_num, self->request->number,
                       self->request_count);
 
-               /*
-                *      Respond, and reset request->child_pid
-                */
-               finished = rad_respond(self->request, fun);
+               radius_handle_request(self->request, fun);
 
                /*
                 *      Update the active threads.
                 */
                pthread_mutex_lock(&thread_pool.queue_mutex);
-
-               /*
-                *      We haven't replied to the client, but we HAVE
-                *      sent a proxied packet, and we have NOT
-                *      received a proxy response.  In that case, send
-                *      the proxied packet now.  Doing this in the mutex
-                *      avoids race conditions.
-                *
-                *      FIXME: this work should really depend on a
-                *      "state", and "next handler", rather than
-                *      horrid hacks like thise.
-                */
-               if (!self->request->reply->data &&
-                   self->request->proxy && self->request->proxy->data
-                   && !self->request->proxy_reply)
-                       self->request->proxy_listener->send(self->request->proxy_listener,
-                                                           self->request);
-
-               self->request->child_pid = NO_SUCH_CHILD_PID;
-               self->request->finished = finished;
-               self->request = NULL;
-               
                rad_assert(thread_pool.active_threads > 0);
                thread_pool.active_threads--;
                pthread_mutex_unlock(&thread_pool.queue_mutex);
@@ -852,7 +825,7 @@ int thread_pool_addrequest(REQUEST *request, RAD_REQUEST_FUNP fun)
         *      We've been told not to spawn threads, so don't.
         */
        if (!thread_pool.spawn_flag) {
-               request->finished = rad_respond(request, fun);
+               radius_handle_request(request, fun);
                
                /*
                 *      Requests that care about child process exit
index 8bc3362..960d470 100644 (file)
@@ -245,7 +245,6 @@ void request_free(REQUEST **request_ptr)
 #ifndef NDEBUG
        request->magic = 0x01020304;    /* set the request to be nonsense */
        strcpy(request->secret, "REQUEST-DELETED");
-       strcpy(request->proxysecret, "REQUEST-DELETED");
 #endif
        free(request);
 
@@ -369,7 +368,6 @@ REQUEST *request_alloc(void)
        request->password = NULL;
        request->timestamp = time(NULL);
        request->child_pid = NO_SUCH_CHILD_PID;
-       request->container = NULL;
        request->options = RAD_REQUEST_OPTION_NONE;
 
        return request;
@@ -390,7 +388,6 @@ REQUEST *request_alloc_fake(REQUEST *oldreq)
 
   request->number = oldreq->number;
   request->child_pid = NO_SUCH_CHILD_PID;
-  request->options = RAD_REQUEST_OPTION_FAKE_REQUEST;
 
   request->packet = rad_alloc(0);
   rad_assert(request->packet != NULL);
@@ -406,6 +403,11 @@ REQUEST *request_alloc_fake(REQUEST *oldreq)
   request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
   request->packet->dst_ipaddr = request->packet->src_ipaddr;
   request->packet->src_port = request->number >> 8;
+
+  /*
+   *   This field is used by the rest of the code to notice that the
+   *   request is "internal", and not from a real client.
+   */
   request->packet->dst_port = 0;
 
   /*
index e7b4805..e023217 100644 (file)
@@ -205,9 +205,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
        int             locked;
        int             lock_count;
        struct timeval  tv;
-       REALM           *proxy_realm;
-       char            proxy_buffer[16];
-       VALUE_PAIR      *pair = packet->vps;
+       VALUE_PAIR      *pair;
 
        struct detail_instance *inst = instance;
 
@@ -428,7 +426,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
        }
 
        /* Write each attribute/value to the log file */
-       for (; pair != NULL; pair = pair->next) {
+       for (pair = packet->vps; pair != NULL; pair = pair->next) {
                DICT_ATTR da;
                da.attr = pair->attribute;
 
@@ -452,23 +450,18 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
         *      Add non-protocol attibutes.
         */
        if (compat) {
-               if ((pair = pairfind(request->config_items,
-                                    PW_PROXY_TO_REALM)) != NULL) {
-                       proxy_realm = realm_find(pair->vp_strvalue, TRUE);
-                       if (proxy_realm) {
-                               memset((char *) proxy_buffer, 0, 16);
-
-                               rad_assert(proxy_realm->acct_ipaddr.af == AF_INET);
-
-                               inet_ntop(proxy_realm->acct_ipaddr.af,
-                                         &proxy_realm->acct_ipaddr.ipaddr,
-                                         proxy_buffer, sizeof(proxy_buffer));
-                               fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
+               if (request->proxy) {
+                       char proxy_buffer[128];
+
+                       inet_ntop(request->proxy->dst_ipaddr.af,
+                                 &request->proxy->dst_ipaddr.ipaddr,
+                                 proxy_buffer, sizeof(proxy_buffer));
+                       fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
                                        proxy_buffer);
-                               DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
+                               DEBUG("rlm_detail: Freeradius-Proxied-To = %s",
                                      proxy_buffer);
-                       }
                }
+
                fprintf(outfp, "\tTimestamp = %ld\n",
                        (unsigned long) request->timestamp);
 
index 0cdd32e..3486ad0 100644 (file)
@@ -252,7 +252,7 @@ int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
                 *      We don't do TLS inside of TLS, as it's a bad
                 *      idea...
                 */
-               if (((handler->request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) &&
+               if ((handler->request->packet->dst_port == 0) &&
                    (default_eap_type == PW_EAP_TLS)) {
                        DEBUG2(" rlm_eap: Unable to tunnel TLS inside of TLS");
                        return EAP_INVALID;
@@ -645,9 +645,8 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
                 *      If it's a LOCAL realm, then we're not proxying
                 *      to it.
                 */
-               realm = realm_find(proxy->vp_strvalue, 0);
-               rad_assert(realm->ipaddr.af == AF_INET);
-               if (realm && (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE))) {
+               realm = realm_find(proxy->vp_strvalue);
+               if (realm && (realm->auth_pool == NULL)) {
                        proxy = NULL;
                }
        }
index 591361e..dcaf53c 100644 (file)
@@ -261,7 +261,7 @@ static int eap_authenticate(void *instance, REQUEST *request)
         *      If it's a recursive request, then disallow
         *      TLS, TTLS, and PEAP, inside of the TLS tunnel.
         */
-       if ((request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) {
+       if (request->packet->dst_port == 0) {
                switch(handler->eap_ds->response->type.type) {
                case PW_EAP_TLS:
                case PW_EAP_TTLS:
@@ -625,7 +625,7 @@ static int eap_post_proxy(void *inst, REQUEST *request)
         */
        i = 34;                 /* starts off with 34 octets */
        len = rad_tunnel_pwdecode(vp->vp_strvalue + 17, &i,
-                                 request->proxysecret,
+                                 request->home_server->secret,
                                  request->proxy->vector);
 
        /*
index 504df4d..ec0c8c4 100644 (file)
@@ -376,10 +376,8 @@ static int pap_authorize(void *instance, REQUEST *request)
                         */
                case PW_PROXY_TO_REALM:
                {
-                       REALM *realm = realm_find(vp->vp_strvalue, 0);
-                       if (realm &&
-                           (realm->ipaddr.af == AF_INET) &&
-                           (realm->ipaddr.ipaddr.ip4addr.s_addr != htonl(INADDR_NONE))) {
+                       REALM *realm = realm_find(vp->vp_strvalue);
+                       if (realm && !realm->auth_pool) {
                                return RLM_MODULE_NOOP;
                        }
                        break;
@@ -408,6 +406,14 @@ static int pap_authorize(void *instance, REQUEST *request)
         *      Print helpful warnings if there was no password.
         */
        if (!found_pw) {
+               /*
+                *      Likely going to be proxied.  Avoid printing
+                *      warning message.
+                */
+               if (pairfind(request->config_items, PW_REALM) ||
+                   (pairfind(request->config_items, PW_PROXY_TO_REALM))) {
+                       return RLM_MODULE_NOOP;
+               }
                DEBUG("rlm_pap: WARNING! No \"known good\" password found for the user.  Authentication may fail because of this.");
                return RLM_MODULE_NOOP;
        }
index baffe84..34598cd 100644 (file)
@@ -174,20 +174,20 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
        /*
         *      Allow DEFAULT realms unless told not to.
         */
-       realm = realm_find(realmname, (request->packet->code == PW_ACCOUNTING_REQUEST));
+       realm = realm_find(realmname);
        if (!realm) {
                DEBUG2("    rlm_realm: No such realm \"%s\"",
                       (realmname == NULL) ? "NULL" : realmname);
                return 0;
        }
        if( inst->ignore_default &&
-           (strcmp(realm->realm, "DEFAULT")) == 0) {
+           (strcmp(realm->name, "DEFAULT")) == 0) {
                DEBUG2("    rlm_realm: Found DEFAULT, but skipping due to config.");
                return 0;
        }
 
 
-       DEBUG2("    rlm_realm: Found realm \"%s\"", realm->realm);
+       DEBUG2("    rlm_realm: Found realm \"%s\"", realm->name);
 
        /*
         *      If we've been told to strip the realm off, then do so.
@@ -217,14 +217,14 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
        }
 
        DEBUG2("    rlm_realm: Proxying request from user %s to realm %s",
-              username, realm->realm);
+              username, realm->name);
 
        /*
         *      Add the realm name to the request.
         */
-       pairadd(&request->packet->vps, pairmake("Realm", realm->realm,
+       pairadd(&request->packet->vps, pairmake("Realm", realm->name,
                                                T_OP_EQ));
-       DEBUG2("    rlm_realm: Adding Realm = \"%s\"", realm->realm);
+       DEBUG2("    rlm_realm: Adding Realm = \"%s\"", realm->name);
 
        /*
         *      Figure out what to do with the request.
@@ -239,30 +239,20 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
                 *      Perhaps accounting proxying was turned off.
                 */
        case PW_ACCOUNTING_REQUEST:
-               if (realm->acct_ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) {
+               if (!realm->acct_pool) {
                        DEBUG2("    rlm_realm: Accounting realm is LOCAL.");
                        return 0;
                }
-
-               if (realm->acct_port == 0) {
-                       DEBUG2("    rlm_realm: acct_port is not set.  Proxy cancelled.");
-                       return 0;
-               }
                break;
 
                /*
                 *      Perhaps authentication proxying was turned off.
                 */
        case PW_AUTHENTICATION_REQUEST:
-               if (realm->ipaddr.ipaddr.ip4addr.s_addr == htonl(INADDR_NONE)) {
+               if (!realm->auth_pool) {
                        DEBUG2("    rlm_realm: Authentication realm is LOCAL.");
                        return 0;
                }
-
-               if (realm->auth_port == 0) {
-                       DEBUG2("    rlm_realm: auth_port is not set.  Proxy cancelled.");
-                       return 0;
-               }
                break;
        }
 
@@ -271,19 +261,26 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         *      that has already proxied the request, we don't need to do
         *      it again.
         */
-       for (vp = request->packet->vps; vp; vp = vp->next) {
-               if (vp->attribute == PW_FREERADIUS_PROXIED_TO) {
-                       if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
-                           vp->lvalue == realm->ipaddr.ipaddr.ip4addr.s_addr) {
-                               DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
-                               return 0;
-                       }
-                       if (request->packet->code == PW_ACCOUNTING_REQUEST &&
-                           vp->lvalue == realm->acct_ipaddr.ipaddr.ip4addr.s_addr) {
-                               DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
-                               return 0;
-                       }
+       vp = pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO);
+       if (vp) {
+#if 0
+               /*
+                *      FIXME: HOME SERVER
+                *
+                *      What the heck is this code doing, and why?
+                */
+
+               if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
+                   vp->lvalue == realm->home_auth->ipaddr.ipaddr.ip4addr.s_addr) {
+                       DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
+                       return 0;
+               }
+               if (request->packet->code == PW_ACCOUNTING_REQUEST &&
+                   vp->lvalue == realm->home_acct->ipaddr.ipaddr.ip4addr.s_addr) {
+                       DEBUG2("    rlm_realm: Request not proxied due to Freeradius-Proxied-To");
+                       return 0;
                }
+#endif
         }
 
        /*
@@ -304,7 +301,7 @@ static void add_proxy_to_realm(VALUE_PAIR **vps, REALM *realm)
         *      Tell the server to proxy this request to another
         *      realm.
         */
-       vp = pairmake("Proxy-To-Realm", realm->realm, T_OP_EQ);
+       vp = pairmake("Proxy-To-Realm", realm->name, T_OP_EQ);
        if (!vp) {
                radlog(L_ERR|L_CONS, "no memory");
                exit(1);
@@ -388,7 +385,7 @@ static int realm_authorize(void *instance, REQUEST *request)
         *      Maybe add a Proxy-To-Realm attribute to the request.
         */
        DEBUG2("    rlm_realm: Preparing to proxy authentication request to realm \"%s\"\n",
-              realm->realm);
+              realm->name);
        add_proxy_to_realm(&request->config_items, realm);
 
        return RLM_MODULE_UPDATED; /* try the next module */
@@ -424,7 +421,7 @@ static int realm_preacct(void *instance, REQUEST *request)
         *      Maybe add a Proxy-To-Realm attribute to the request.
         */
        DEBUG2("    rlm_realm: Preparing to proxy accounting request to realm \"%s\"\n",
-              realm->realm);
+              realm->name);
        add_proxy_to_realm(&request->config_items, realm);
 
        return RLM_MODULE_UPDATED; /* try the next module */