Merge tag 'release_3_0_4_rc1' into tr-upgrade
authorSam Hartman <hartmans@debian.org>
Wed, 18 Jun 2014 19:36:13 +0000 (15:36 -0400)
committerSam Hartman <hartmans@debian.org>
Wed, 18 Jun 2014 19:36:13 +0000 (15:36 -0400)
Conflicts:
share/dictionary.freeradius.internal
src/main/listen.c
src/main/realms.c
src/main/tls.c
src/main/tls_listen.c
src/modules/rlm_realm/rlm_realm.c

571 files changed:
.travis.yml
Make.inc.in
Makefile
README.rst
VERSION
acinclude.m4
configure
configure.ac
debian/.gitignore
debian/changelog
debian/compat
debian/control
debian/copyright
debian/freeradius-config.postrm
debian/freeradius-mysql.postinst
debian/freeradius-postgresql.postinst
debian/freeradius-rest.install [new file with mode: 0644]
debian/freeradius-rest.postinst [new file with mode: 0755]
debian/freeradius.init
debian/patches/radiusd-to-freeradius.diff
doc/ChangeLog
doc/concepts/proxy.rst
doc/configuration/variables.rst
doc/developer/DIFFS.rst [deleted file]
doc/developer/contributing.rst [new file with mode: 0644]
doc/modules/ldap_howto.rst
doc/modules/rlm_soh
doc/source/Doxyfile
man/man1/radclient.1
man/man5/rlm_files.5
man/man5/rlm_pap.5
man/man5/unlang.5
man/man8/radconf2xml.8 [deleted file]
man/man8/radiusd.8
raddb/README.rst
raddb/all.mk
raddb/certs/Makefile
raddb/certs/bootstrap
raddb/clients.conf
raddb/dictionary [moved from raddb/dictionary.in with 51% similarity]
raddb/mods-available/always
raddb/mods-available/attr_filter
raddb/mods-available/couchbase [new file with mode: 0644]
raddb/mods-available/detail
raddb/mods-available/dhcp_sqlippool
raddb/mods-available/eap
raddb/mods-available/files
raddb/mods-available/idn
raddb/mods-available/ippool
raddb/mods-available/krb5
raddb/mods-available/ldap
raddb/mods-available/linelog
raddb/mods-available/pap
raddb/mods-available/passwd
raddb/mods-available/perl
raddb/mods-available/radutmp
raddb/mods-available/redis
raddb/mods-available/rest
raddb/mods-available/sql
raddb/mods-available/sqlcounter
raddb/mods-available/unbound [new file with mode: 0644]
raddb/mods-available/unix
raddb/mods-available/unpack [new file with mode: 0644]
raddb/mods-available/yubikey
raddb/mods-config/files/accounting
raddb/mods-config/files/pre-proxy
raddb/mods-config/perl/example.pl [moved from src/modules/rlm_perl/example.pl with 96% similarity]
raddb/mods-config/sql/counter/mysql/dailycounter.conf
raddb/mods-config/sql/counter/mysql/expire_on_login.conf
raddb/mods-config/sql/counter/mysql/monthlycounter.conf
raddb/mods-config/sql/counter/mysql/noresetcounter.conf
raddb/mods-config/sql/counter/postgresql/dailycounter.conf
raddb/mods-config/sql/counter/postgresql/expire_on_login.conf
raddb/mods-config/sql/counter/postgresql/monthlycounter.conf
raddb/mods-config/sql/counter/postgresql/noresetcounter.conf
raddb/mods-config/sql/counter/sqlite/dailycounter.conf
raddb/mods-config/sql/counter/sqlite/expire_on_login.conf
raddb/mods-config/sql/counter/sqlite/monthlycounter.conf
raddb/mods-config/sql/counter/sqlite/noresetcounter.conf
raddb/mods-config/sql/cui/mysql/queries.conf
raddb/mods-config/sql/cui/postgresql/queries.conf
raddb/mods-config/sql/cui/postgresql/schema.sql
raddb/mods-config/sql/cui/sqlite/queries.conf
raddb/mods-config/sql/ippool-dhcp/mysql/queries.conf
raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf [new file with mode: 0644]
raddb/mods-config/sql/ippool-dhcp/oracle/schema.sql [new file with mode: 0644]
raddb/mods-config/sql/ippool-dhcp/sqlite/queries.conf
raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql [new file with mode: 0644]
raddb/mods-config/sql/ippool/mysql/queries.conf
raddb/mods-config/sql/ippool/oracle/procedures.sql [moved from raddb/mods-config/sql/ippool/oracle/msqlippool.txt with 100% similarity]
raddb/mods-config/sql/ippool/oracle/queries.conf
raddb/mods-config/sql/ippool/oracle/schema.sql
raddb/mods-config/sql/ippool/postgresql/queries.conf
raddb/mods-config/sql/ippool/sqlite/queries.conf
raddb/mods-config/sql/main/mssql/queries.conf
raddb/mods-config/sql/main/mssql/schema.sql
raddb/mods-config/sql/main/mysql/queries.conf
raddb/mods-config/sql/main/mysql/schema.sql
raddb/mods-config/sql/main/oracle/queries.conf
raddb/mods-config/sql/main/oracle/schema.sql
raddb/mods-config/sql/main/postgresql/extras/cisco_h323_db_schema.sql
raddb/mods-config/sql/main/postgresql/extras/update_radacct_group.sql
raddb/mods-config/sql/main/postgresql/extras/voip-postpaid.conf
raddb/mods-config/sql/main/postgresql/queries.conf
raddb/mods-config/sql/main/postgresql/schema.sql
raddb/mods-config/sql/main/sqlite/queries.conf
raddb/mods-config/unbound/default.conf [new file with mode: 0644]
raddb/panic.gdb [new file with mode: 0644]
raddb/policy.d/accounting
raddb/policy.d/control
raddb/policy.d/cui
raddb/policy.d/filter
raddb/policy.d/operator-name
raddb/proxy.conf
raddb/radiusd.conf.in
raddb/sites-available/dhcp
raddb/sites-available/dynamic-clients
raddb/sites-available/inner-tunnel
raddb/sites-available/status
raddb/sites-available/virtual.example.com
redhat/freeradius.spec
scripts/boiler.mk
scripts/jlibtool.c
scripts/libtool.mk
scripts/raddebug
share/Makefile
share/dictionary
share/dictionary.actelis [new file with mode: 0644]
share/dictionary.altiga
share/dictionary.bluecoat [new file with mode: 0644]
share/dictionary.brocade [new file with mode: 0644]
share/dictionary.cisco
share/dictionary.cisco.asa [new file with mode: 0644]
share/dictionary.cisco.vpn3000
share/dictionary.dhcp
share/dictionary.equallogic [new file with mode: 0644]
share/dictionary.ericsson.packet.core.networks [new file with mode: 0644]
share/dictionary.freeradius.internal
share/dictionary.h3c
share/dictionary.packeteer
share/dictionary.rfc4072
share/dictionary.rfc4372
share/dictionary.rfc4679
share/dictionary.rfc7055
share/dictionary.symbol
share/dictionary.terena
share/dictionary.wimax.wichorus
share/dictionary.xylan
share/dictionary.zte [new file with mode: 0644]
src/include/.gitignore
src/include/all.mk
src/include/autoconf.h.in
src/include/base64.h
src/include/build-radpaths-h.in
src/include/build.h
src/include/conffile.h
src/include/connection.h
src/include/detail.h
src/include/dhcp.h
src/include/event.h
src/include/features-h
src/include/hash.h
src/include/libradius.h
src/include/log.h
src/include/map.h
src/include/md4.h
src/include/md5.h
src/include/missing-h
src/include/modcall.h
src/include/modpriv.h
src/include/modules.h
src/include/packet.h
src/include/parser.h
src/include/pcap.h [new file with mode: 0644]
src/include/protocol.h
src/include/radclient.h [new file with mode: 0644]
src/include/radius.h
src/include/radiusd.h
src/include/radsniff.h
src/include/realms.h
src/include/soh.h
src/include/stats.h
src/include/sysutmp.h
src/include/tcp.h
src/include/threads.h
src/include/tls-h
src/include/token.h
src/lib/LICENSE
src/lib/all.mk
src/lib/base64.c
src/lib/cbuff.c
src/lib/cursor.c [new file with mode: 0644]
src/lib/debug.c [new file with mode: 0644]
src/lib/dict.c
src/lib/event.c
src/lib/filters.c
src/lib/getaddrinfo.c
src/lib/hash.c
src/lib/heap.c
src/lib/log.c
src/lib/md4.c
src/lib/md5.c
src/lib/misc.c
src/lib/missing.c
src/lib/packet.c
src/lib/pcap.c [new file with mode: 0644]
src/lib/print.c
src/lib/radius.c
src/lib/rbtree.c
src/lib/sha1.c
src/lib/tcp.c
src/lib/token.c
src/lib/udpfromto.c
src/lib/valuepair.c
src/lib/version.c [new file with mode: 0644]
src/main/acct.c
src/main/all.mk
src/main/auth.c
src/main/cb.c
src/main/checkrad.in
src/main/client.c
src/main/collectd.c [new file with mode: 0644]
src/main/command.c
src/main/conffile.c
src/main/connection.c
src/main/crypt.c
src/main/detail.c
src/main/evaluate.c
src/main/exec.c
src/main/files.c
src/main/listen.c
src/main/log.c
src/main/mainconfig.c
src/main/map.c
src/main/modcall.c
src/main/modules.c
src/main/parser.c
src/main/process.c
src/main/radattr.c
src/main/radclient.c
src/main/radconf2xml.c [deleted file]
src/main/radconf2xml.mk [deleted file]
src/main/radiusd.c
src/main/radmin.c
src/main/radsniff.c
src/main/radsniff.mk.in
src/main/radtest.in
src/main/radwho.c
src/main/radzap
src/main/realms.c
src/main/session.c
src/main/soh.c
src/main/stats.c
src/main/threads.c
src/main/tls.c
src/main/tls_listen.c
src/main/unittest.c
src/main/util.c
src/main/valuepair.c
src/main/version.c
src/main/xlat.c
src/mkinstalldirs
src/modules/proto_dhcp/dhcp.c
src/modules/proto_dhcp/dhcpclient.c
src/modules/proto_dhcp/dhcpd.c
src/modules/proto_dhcp/rlm_dhcp.c
src/modules/proto_vmps/vmps.c
src/modules/proto_vmps/vqp.c
src/modules/rlm_always/rlm_always.c
src/modules/rlm_attr_filter/rlm_attr_filter.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_chap/rlm_chap.c
src/modules/rlm_couchbase/.gitignore [new file with mode: 0644]
src/modules/rlm_couchbase/README.md [new file with mode: 0644]
src/modules/rlm_couchbase/all.mk.in [new file with mode: 0644]
src/modules/rlm_couchbase/config.h.in [new file with mode: 0644]
src/modules/rlm_couchbase/configure [new file with mode: 0755]
src/modules/rlm_couchbase/configure.ac [new file with mode: 0644]
src/modules/rlm_couchbase/couchbase.c [new file with mode: 0644]
src/modules/rlm_couchbase/couchbase.h [new file with mode: 0644]
src/modules/rlm_couchbase/jsonc_missing.c [new file with mode: 0644]
src/modules/rlm_couchbase/jsonc_missing.h [new file with mode: 0644]
src/modules/rlm_couchbase/mod.c [new file with mode: 0644]
src/modules/rlm_couchbase/mod.h [new file with mode: 0644]
src/modules/rlm_couchbase/rlm_couchbase.c [new file with mode: 0644]
src/modules/rlm_counter/configure
src/modules/rlm_counter/rad_counter
src/modules/rlm_counter/rlm_counter.c
src/modules/rlm_cram/Standard.draft
src/modules/rlm_cram/rlm_cram.c
src/modules/rlm_date/rlm_date.c
src/modules/rlm_detail/rlm_detail.c
src/modules/rlm_digest/rlm_digest.c
src/modules/rlm_dynamic_clients/rlm_dynamic_clients.c
src/modules/rlm_eap/configure
src/modules/rlm_eap/eap.c
src/modules/rlm_eap/libeap/comp128.c
src/modules/rlm_eap/libeap/eap_chbind.c
src/modules/rlm_eap/libeap/eap_sim.h
src/modules/rlm_eap/libeap/eap_tls.c
src/modules/rlm_eap/libeap/eap_tls.h
src/modules/rlm_eap/libeap/eapcommon.c
src/modules/rlm_eap/libeap/eapcrypto.c
src/modules/rlm_eap/libeap/eapsimlib.c
src/modules/rlm_eap/libeap/fips186prf.c
src/modules/rlm_eap/libeap/mppe_keys.c
src/modules/rlm_eap/mem.c
src/modules/rlm_eap/radeapclient.c
src/modules/rlm_eap/radeapclient.mk
src/modules/rlm_eap/rlm_eap.c
src/modules/rlm_eap/rlm_eap.h
src/modules/rlm_eap/types/rlm_eap_gtc/rlm_eap_gtc.c
src/modules/rlm_eap/types/rlm_eap_ikev2/configure
src/modules/rlm_eap/types/rlm_eap_ikev2/configure.ac
src/modules/rlm_eap/types/rlm_eap_ikev2/ike_conf.c
src/modules/rlm_eap/types/rlm_eap_ikev2/rlm_eap_ikev2.c
src/modules/rlm_eap/types/rlm_eap_leap/eap_leap.h
src/modules/rlm_eap/types/rlm_eap_leap/rlm_eap_leap.c
src/modules/rlm_eap/types/rlm_eap_leap/smbdes.c
src/modules/rlm_eap/types/rlm_eap_md5/rlm_eap_md5.c
src/modules/rlm_eap/types/rlm_eap_mschapv2/rlm_eap_mschapv2.c
src/modules/rlm_eap/types/rlm_eap_peap/eap_peap.h
src/modules/rlm_eap/types/rlm_eap_peap/peap.c
src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c
src/modules/rlm_eap/types/rlm_eap_pwd/configure
src/modules/rlm_eap/types/rlm_eap_pwd/configure.ac
src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.c
src/modules/rlm_eap/types/rlm_eap_pwd/eap_pwd.h
src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.c
src/modules/rlm_eap/types/rlm_eap_pwd/rlm_eap_pwd.h
src/modules/rlm_eap/types/rlm_eap_sim/configure
src/modules/rlm_eap/types/rlm_eap_sim/rlm_eap_sim.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c
src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.h
src/modules/rlm_eap/types/rlm_eap_tnc/configure
src/modules/rlm_eap/types/rlm_eap_tnc/configure.ac
src/modules/rlm_eap/types/rlm_eap_tnc/rlm_eap_tnc.c
src/modules/rlm_eap/types/rlm_eap_ttls/eap_ttls.h
src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c
src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c
src/modules/rlm_example/configure
src/modules/rlm_example/configure.ac
src/modules/rlm_example/rlm_example.c
src/modules/rlm_exec/rlm_exec.c
src/modules/rlm_expiration/rlm_expiration.c
src/modules/rlm_expr/rlm_expr.c
src/modules/rlm_files/rlm_files.c
src/modules/rlm_idn/configure
src/modules/rlm_idn/configure.ac
src/modules/rlm_idn/rlm_idn.c
src/modules/rlm_ippool/configure
src/modules/rlm_ippool/rlm_ippool.c
src/modules/rlm_ippool/rlm_ippool_tool.c
src/modules/rlm_krb5/configure
src/modules/rlm_krb5/configure.ac
src/modules/rlm_krb5/krb5.c
src/modules/rlm_krb5/krb5.h
src/modules/rlm_krb5/rlm_krb5.c
src/modules/rlm_ldap/attrmap.c
src/modules/rlm_ldap/configure
src/modules/rlm_ldap/configure.ac
src/modules/rlm_ldap/groups.c
src/modules/rlm_ldap/ldap.c
src/modules/rlm_ldap/ldap.h
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_logintime/rlm_logintime.c
src/modules/rlm_mschap/README
src/modules/rlm_mschap/configure
src/modules/rlm_mschap/mschap.c
src/modules/rlm_mschap/mschap.h
src/modules/rlm_mschap/opendir.c
src/modules/rlm_mschap/rlm_mschap.c
src/modules/rlm_opendirectory/configure
src/modules/rlm_opendirectory/rlm_opendirectory.c
src/modules/rlm_otp/configure
src/modules/rlm_otp/extern.h
src/modules/rlm_otp/otp_pw_valid.c
src/modules/rlm_otp/otp_pwe.c
src/modules/rlm_otp/otp_util.c
src/modules/rlm_otp/rlm_otp.c
src/modules/rlm_pam/configure
src/modules/rlm_pam/rlm_pam.c
src/modules/rlm_pap/rlm_pap.c
src/modules/rlm_passwd/rlm_passwd.c
src/modules/rlm_perl/all.mk.in
src/modules/rlm_perl/configure
src/modules/rlm_perl/rlm_perl.c
src/modules/rlm_preprocess/rlm_preprocess.c
src/modules/rlm_python/all.mk.in
src/modules/rlm_python/configure
src/modules/rlm_python/configure.ac
src/modules/rlm_python/example.py
src/modules/rlm_python/prepaid.py
src/modules/rlm_python/radiusd.py
src/modules/rlm_python/rlm_python.c
src/modules/rlm_radutmp/configure
src/modules/rlm_radutmp/rlm_radutmp.c
src/modules/rlm_realm/rlm_realm.c
src/modules/rlm_realm/trustrouter_integ.c
src/modules/rlm_redis/configure
src/modules/rlm_redis/rlm_redis.c
src/modules/rlm_redis/rlm_redis.h
src/modules/rlm_rediswho/configure
src/modules/rlm_rediswho/rlm_rediswho.c
src/modules/rlm_replicate/rlm_replicate.c
src/modules/rlm_rest/config.h.in
src/modules/rlm_rest/configure
src/modules/rlm_rest/configure.ac
src/modules/rlm_rest/demo.pl
src/modules/rlm_rest/m4/libcurl_check_config.m4
src/modules/rlm_rest/rest.c
src/modules/rlm_rest/rest.h
src/modules/rlm_rest/rlm_rest.c
src/modules/rlm_ruby/configure
src/modules/rlm_ruby/configure.ac
src/modules/rlm_ruby/example.rb
src/modules/rlm_ruby/m4/ax_prog_ruby_version.m4
src/modules/rlm_ruby/m4/ax_ruby_devel.m4
src/modules/rlm_ruby/rlm_ruby.c
src/modules/rlm_securid/configure
src/modules/rlm_securid/mem.c
src/modules/rlm_securid/rlm_securid.c
src/modules/rlm_securid/rlm_securid.h
src/modules/rlm_smsotp/configure
src/modules/rlm_smsotp/rlm_smsotp.c
src/modules/rlm_soh/rlm_soh.c
src/modules/rlm_sometimes/rlm_sometimes.c
src/modules/rlm_sql/configure
src/modules/rlm_sql/drivers/rlm_sql_db2/configure
src/modules/rlm_sql/drivers/rlm_sql_db2/rlm_sql_db2.c
src/modules/rlm_sql/drivers/rlm_sql_firebird/configure
src/modules/rlm_sql/drivers/rlm_sql_firebird/rlm_sql_firebird.c
src/modules/rlm_sql/drivers/rlm_sql_firebird/sql_fbapi.c
src/modules/rlm_sql/drivers/rlm_sql_freetds/configure
src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c
src/modules/rlm_sql/drivers/rlm_sql_iodbc/configure
src/modules/rlm_sql/drivers/rlm_sql_mysql/configure
src/modules/rlm_sql/drivers/rlm_sql_mysql/configure.ac
src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c
src/modules/rlm_sql/drivers/rlm_sql_oracle/configure
src/modules/rlm_sql/drivers/rlm_sql_oracle/configure.ac
src/modules/rlm_sql/drivers/rlm_sql_oracle/rlm_sql_oracle.c
src/modules/rlm_sql/drivers/rlm_sql_postgresql/config.h.in [new file with mode: 0644]
src/modules/rlm_sql/drivers/rlm_sql_postgresql/configure
src/modules/rlm_sql/drivers/rlm_sql_postgresql/configure.ac
src/modules/rlm_sql/drivers/rlm_sql_postgresql/rlm_sql_postgresql.c
src/modules/rlm_sql/drivers/rlm_sql_postgresql/sql_postgresql.h
src/modules/rlm_sql/drivers/rlm_sql_sqlite/config.h.in
src/modules/rlm_sql/drivers/rlm_sql_sqlite/configure
src/modules/rlm_sql/drivers/rlm_sql_sqlite/configure.ac
src/modules/rlm_sql/drivers/rlm_sql_sqlite/rlm_sql_sqlite.c
src/modules/rlm_sql/drivers/rlm_sql_unixodbc/configure
src/modules/rlm_sql/drivers/rlm_sql_unixodbc/rlm_sql_unixodbc.c
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_sql/rlm_sql.h
src/modules/rlm_sql/sql.c
src/modules/rlm_sqlcounter/configure
src/modules/rlm_sqlcounter/rlm_sqlcounter.c
src/modules/rlm_sqlhpwippool/configure
src/modules/rlm_sqlhpwippool/rlm_sqlhpwippool.c
src/modules/rlm_sqlippool/configure
src/modules/rlm_sqlippool/rlm_sqlippool.c
src/modules/rlm_unbound/.gitignore [new file with mode: 0644]
src/modules/rlm_unbound/all.mk.in [new file with mode: 0644]
src/modules/rlm_unbound/config.h.in [new file with mode: 0644]
src/modules/rlm_unbound/configure [new file with mode: 0755]
src/modules/rlm_unbound/configure.ac [new file with mode: 0644]
src/modules/rlm_unbound/rlm_unbound.5 [new file with mode: 0644]
src/modules/rlm_unbound/rlm_unbound.c [new file with mode: 0644]
src/modules/rlm_unix/configure
src/modules/rlm_unix/configure.ac
src/modules/rlm_unix/rlm_unix.c
src/modules/rlm_unpack/all.mk [new file with mode: 0644]
src/modules/rlm_unpack/rlm_unpack.c [new file with mode: 0644]
src/modules/rlm_utf8/rlm_utf8.c
src/modules/rlm_wimax/configure
src/modules/rlm_wimax/rlm_wimax.c
src/modules/rlm_yubikey/configure
src/modules/rlm_yubikey/configure.ac
src/modules/rlm_yubikey/decrypt.c
src/modules/rlm_yubikey/rlm_yubikey.c
src/modules/rlm_yubikey/rlm_yubikey.h
src/modules/rlm_yubikey/validate.c
src/modules/stable
src/tests/.gitignore
src/tests/Makefile
src/tests/auth/all.mk
src/tests/auth/chap_header [new file with mode: 0644]
src/tests/auth/chap_header.attrs [new file with mode: 0644]
src/tests/auth/md5_password [new file with mode: 0644]
src/tests/auth/md5_password.attrs [new file with mode: 0644]
src/tests/auth/password_with_header [new file with mode: 0644]
src/tests/auth/password_with_header.attrs [new file with mode: 0644]
src/tests/auth/password_without_header [new file with mode: 0644]
src/tests/auth/password_without_header.attrs [new file with mode: 0644]
src/tests/auth/radiusd.conf
src/tests/config/test.conf
src/tests/eap-md5.conf
src/tests/eap-mschapv2.conf
src/tests/eap-ttls-eap-mschapv2.conf
src/tests/eap-ttls-mschapv2.conf
src/tests/eap-ttls-pap.conf
src/tests/eapcrypto-01/eapcrypto-out.txt
src/tests/eapsim-03/eapsim-cooked.txt
src/tests/eapsim-03/eapsim-out.txt
src/tests/eapsim-03/radiusd-example.txt
src/tests/eapsim-03/users-example.txt
src/tests/eapsim-04/eapsim-cooked.txt
src/tests/eapsim-06/description.txt
src/tests/eapsim-06/eapsim-cooked.txt
src/tests/eapsim-06/eapsim-out.txt
src/tests/eapsim-06/eapsim-raw.txt
src/tests/fips186-02/fips186-2.txt
src/tests/keywords/3gpp [new file with mode: 0644]
src/tests/keywords/all.mk
src/tests/keywords/array [new file with mode: 0644]
src/tests/keywords/base64 [new file with mode: 0644]
src/tests/keywords/cast-ipaddr [new file with mode: 0644]
src/tests/keywords/escape [new file with mode: 0644]
src/tests/keywords/foreach-2
src/tests/keywords/foreach-2.attrs
src/tests/keywords/foreach-break
src/tests/keywords/foreach-error [new file with mode: 0644]
src/tests/keywords/foreach-regex [new file with mode: 0644]
src/tests/keywords/foreach-regex.attrs [new file with mode: 0644]
src/tests/keywords/hex [new file with mode: 0644]
src/tests/keywords/if-regex-match
src/tests/keywords/if-regex-match-comp [new file with mode: 0644]
src/tests/keywords/if-regex-match-comp.attrs [new file with mode: 0644]
src/tests/keywords/if-regex-match.attrs
src/tests/keywords/if-skip
src/tests/keywords/integer [new file with mode: 0644]
src/tests/keywords/ipprefix [new file with mode: 0644]
src/tests/keywords/length [new file with mode: 0644]
src/tests/keywords/md5
src/tests/keywords/radiusd.conf
src/tests/keywords/regex-error [new file with mode: 0644]
src/tests/keywords/sha1
src/tests/keywords/sha2 [new file with mode: 0644]
src/tests/keywords/switch
src/tests/keywords/switch-attr-cast [new file with mode: 0644]
src/tests/keywords/switch-attr-cmp [new file with mode: 0644]
src/tests/keywords/switch-value-error [new file with mode: 0644]
src/tests/keywords/switch-value-error2 [new file with mode: 0644]
src/tests/keywords/switch-xlat-error [new file with mode: 0644]
src/tests/keywords/truncation [new file with mode: 0644]
src/tests/keywords/update-array [new file with mode: 0644]
src/tests/keywords/update-exec [new file with mode: 0644]
src/tests/keywords/update-list-error [new file with mode: 0644]
src/tests/keywords/update-operator [new file with mode: 0644]
src/tests/keywords/update-remove-any [new file with mode: 0644]
src/tests/keywords/update-remove-index [new file with mode: 0644]
src/tests/keywords/update-remove-list [new file with mode: 0644]
src/tests/keywords/update-remove-tag [new file with mode: 0644]
src/tests/keywords/update-remove-value [new file with mode: 0644]
src/tests/keywords/update-tag [new file with mode: 0644]
src/tests/keywords/xlat-attr-index [new file with mode: 0644]
src/tests/keywords/xlat-attr-tag [new file with mode: 0644]
src/tests/keywords/xlat-octets [new file with mode: 0644]
src/tests/keywords/xlat-virtual-attr [new file with mode: 0644]
src/tests/panic.gdb [new file with mode: 0644]
src/tests/peap-mschapv2.conf
src/tests/rbmonkey.c
src/tests/runtests.sh
src/tests/tests.gdb [moved from src/tests/gdb.conf with 100% similarity]
src/tests/unit/all.mk
src/tests/unit/condition.txt
src/tests/unit/rfc.txt
src/tests/unit/xlat.txt
suse/freeradius.spec

index 56efb2a..fc0a677 100644 (file)
@@ -2,21 +2,73 @@ language: c
 compiler:
   - clang
   - gcc
+env:
+  global:
+    - PANIC_ACTION="gdb -batch -x raddb/panic.gdb %e %p 1>&0 2>&0"
+    - secure: "H+uQeyOgsIyXtIPPG2VzAG8S/8KYGHlHaWhdiNuz1LM3SMcEKoPqG6o/P+HO8HVvYnA6nelyGuEryV90UfuwGY9YC6A/pqPQvx/gXSso63Zt66XSaiZjulCSm9OV8EB3wyWF7VSQ/ZHcn+L01hIlsQXTqLprMaC33cM0FYPr9fY="
+  matrix:
+    - LIBS_OPTIONAL=no LIBS_SHARED=yes
+    - LIBS_OPTIONAL=yes LIBS_SHARED=yes
+    - LIBS_OPTIONAL=yes LIBS_SHARED=yes BUILD_CFLAGS="-O2 -g3"
+addons:
+  coverity_scan:
+    project:
+      name: FreeRADIUS/freeradius-server
+      version: v3.0.x
+      description: The FreeRADIUS server project
+    notification_email: freeradius-devel@lists.freeradius.org
+    build_command_prepend: ./configure
+    build_command: make
+    branch_pattern: coverity_scan
 before_install:
+  - $CC --version
+  - wget -O - http://packages.couchbase.com/ubuntu/couchbase.key | sudo apt-key add -
+  - sudo sudo wget -O /etc/apt/sources.list.d/couchbase.list http://packages.couchbase.com/ubuntu/couchbase-ubuntu1204.list
   - sudo apt-get update -qq
-  - sudo apt-get install -qq libtalloc-dev libssl-dev
-  - sudo apt-get install -qq gdb
-  - sudo apt-get install -qq build-essential autoconf dh-make debhelper devscripts fakeroot lintian pbuilder quilt
+  - >
+      sudo apt-get install --no-install-recommends -qq
+      autoconf
+      build-essential
+      debhelper
+      devscripts
+      dh-make
+      fakeroot
+      gdb
+      lintian
+      pbuilder
+      python-dev
+      quilt
+      libruby
+      ruby-dev
+      libcollectdclient-dev
+      firebird-dev
+      freetds-dev
+      libcouchbase2-libevent
+      libcouchbase-dev
+      libcurl4-openssl-dev
+      libgdbm-dev
+      libhiredis-dev
+      libidn11-dev
+      libiodbc2-dev libiodbc2
+      libjson0
+      libjson0-dev
+      libkrb5-dev
+      libldap2-dev
+      libpam0g-dev
+      libpcap-dev
+      libpcre3-dev
+      libperl-dev
+      libpq-dev
+      libreadline-dev
+      libsnmp-dev
+      libssl-dev
+      libtalloc-dev
+      libtalloc2-dbg
+      libunbound-dev
+      libykclient-dev
+      libyubikey-dev
   - sudo apt-get install -qq -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" libmysqlclient-dev
-  - sudo apt-get install -qq libkrb5-dev libgdbm-dev libhiredis-dev libldap2-dev libpam0g-dev libpcap-dev libpcre3-dev libperl-dev
-  - sudo apt-get install -qq libpq-dev libreadline-dev libsnmp-dev 
-  - sudo apt-get install -qq libiodbc2 libiodbc2-dev
-  - sudo apt-get install -qq libidn11-dev libyubikey-dev libykclient-dev
-  - sudo apt-get install -qq firebird-dev freetds-dev python-dev ruby-dev
-env:
-  - LIBS_OPTIONAL=no LIBS_SHARED=yes
-  - LIBS_OPTIONAL=yes LIBS_SHARED=yes
 before_script:
-  - ./configure -C --enable-werror --prefix=$HOME/travis --with-shared-libs=$LIBS_SHARED --with-threads=$LIBS_OPTIONAL --with-udpfromto=$LIBS_OPTIONAL --with-openssl=$LIBS_OPTIONAL
+  - CFLAGS="${BUILD_CFLAGS}" ./configure -C --enable-werror --prefix=$HOME/freeradius --with-shared-libs=$LIBS_SHARED --with-threads=$LIBS_OPTIONAL --with-udpfromto=$LIBS_OPTIONAL --with-openssl=$LIBS_OPTIONAL
   - make
-script: make travis-test
+script: if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then make travis-test; fi
index d23efec..7c2c351 100644 (file)
@@ -15,7 +15,7 @@ sbindir               = @sbindir@
 docdir         = @docdir@
 mandir         = @mandir@
 datadir                = @datadir@
-dictdir                = $(datadir)/freeradius
+dictdir                = @dictdir@
 logdir         = @logdir@
 includedir     = @includedir@
 
@@ -47,9 +47,12 @@ MAKEFLAGS    = @FR_MAKEFLAGS@
 
 CC             = @CC@
 RANLIB         = @RANLIB@
-IMACROS                = -imacros ${top_srcdir}/src/freeradius-devel/build.h -imacros ${top_srcdir}/src/freeradius-devel/autoconf.h -imacros ${top_srcdir}/src/freeradius-devel/features.h
-INCLUDE                = -I${top_srcdir} -I${top_srcdir}/src
-CFLAGS         = $(IMACROS) $(INCLUDE) -std=c99 -fno-strict-aliasing @CFLAGS@
+INCLUDE                = -I${top_srcdir} -I${top_srcdir}/src \
+                 -include ${top_srcdir}/src/freeradius-devel/autoconf.h \
+                 -include ${top_srcdir}/src/freeradius-devel/build.h \
+                 -include ${top_srcdir}/src/freeradius-devel/features.h \
+                 -include ${top_srcdir}/src/freeradius-devel/radpaths.h
+CFLAGS         = $(INCLUDE) -std=c99 -fno-strict-aliasing @CFLAGS@
 CPPFLAGS       = @CPPFLAGS@
 LIBPREFIX      = @LIBPREFIX@
 EXEEXT         = @EXEEXT@
@@ -63,18 +66,38 @@ INSTALL_PROGRAM     = ${INSTALL}
 INSTALL_DATA   = ${INSTALL} -m 644
 INSTALL_SCRIPT = ${INSTALL_PROGRAM}
 INSTALLSTRIP   = @INSTALLSTRIP@
-DARWIN_CFLAGS  = @DARWIN_CFLAGS@
+
+#
+#  Linker arguments for libraries searched for by the main
+#  configure script.
+#
+TALLOC_LIBS     = @TALLOC_LIBS@
+TALLOC_LDFLAGS  = @TALLOC_LDFLAGS@
+
+OPENSSL_LIBS    = @OPENSSL_LIBS@
+OPENSSL_LDFLAGS = @OPENSSL_LDFLAGS@
+
+PCAP_LIBS      = @PCAP_LIBS@
+PCAP_LDFLAGS    = @PCAP_LDFLAGS@
+
+COLLECTDC_LIBS = @COLLECTDC_LIBS@
+COLLECTDC_LDFLAGS = @COLLECTDC_LDFLAGS@
 
 LCRYPT         = @CRYPTLIB@
-LIBS           = @LIBS@
-LDFLAGS                = @LDFLAGS@
+
+#
+#  OpenSSL libs (if used) must be linked everywhere in order for
+#  the server to work properly on on all platforms.
+#
+LIBS           = $(OPENSSL_LIBS) $(TALLOC_LIBS) @LIBS@
+LDFLAGS                = $(OPENSSL_LDFLAGS) $(TALLOC_LDFLAGS) @LDFLAGS@
 
 LOGDIR         = ${logdir}
 RADDBDIR       = ${raddbdir}
 RUNDIR         = ${localstatedir}/run/radiusd
 SBINDIR                = ${sbindir}
 RADIR          = ${radacctdir}
-LIBRADIUS      = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la -ltalloc
+LIBRADIUS      = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la $(TALLOC_LIBS)
 
 USE_SHARED_LIBS = @USE_SHARED_LIBS@
 bm_shared_libs  = @USE_SHARED_LIBS@
@@ -82,18 +105,6 @@ USE_STATIC_LIBS = @USE_STATIC_LIBS@
 bm_static_libs  = @USE_STATIC_LIBS@
 
 STATIC_MODULES = @STATIC_MODULES@
-
-OPENSSL_LIBS   = @OPENSSL_LIBS@
-OPENSSL_INCLUDE = @OPENSSL_INCLUDE@
-
-#
-#  If the system has OpenSSL, use it's version of MD4/MD5/SHA1, instead of
-#  using ours.
-#
-ifneq "$(OPENSSL_INCLUDE)" ""
-CFLAGS         +=  -DWITH_OPENSSL_MD4 -DWITH_OPENSSL_MD5
-endif
-
 LIBREADLINE    = @LIBREADLINE@
 
 #
@@ -102,15 +113,23 @@ LIBREADLINE       = @LIBREADLINE@
 RADIUSD_VERSION_STRING = @RADIUSD_VERSION_STRING@
 
 #
-#  This allows dlopen to do runtime checks for version mistmatches
+#  This allows dlopen to do runtime checks for version mismatches
 #  between what it was originally linked with, and the library it's
 #  actually loading.
 #
-#LDFLAGS += -release=$(RADIUSD_VERSION_STRING)
-
 MODULES                = @MODULES@
 HOSTINFO       = @HOSTINFO@
 
+#
+#  If the system has OpenSSL, use it's version of MD4/MD5/SHA1, instead of
+#  using ours.
+#
+ifeq "$(WITH_OPENSSL)" "yes"
+CFLAGS         +=  -DWITH_OPENSSL_MD4 -DWITH_OPENSSL_MD5
+endif
+
+OPENSSL_LIBS   = @OPENSSL_LIBS@
+
 ifneq ($(WITH_OPENSSL_MD5),)
 LIBRADIUS_WITH_OPENSSL = 1
 CFLAGS += -DWITH_OPENSSL_MD5
@@ -131,11 +150,11 @@ endif
 
 #  http://clang.llvm.org/StaticAnalysis.html
 #
-#  $ make SCAN=/path/to/checker/ 
+#  $ make SCAN=/path/to/checker/
 #
 ifneq ($(SCAN),)
 CC             := $(SCAN)/scan-build gcc -DFR_SCAN_BUILD
-LIBTOOL                := 
+LIBTOOL                :=
 endif
 
 #
@@ -150,7 +169,7 @@ LINK_MODE.exe       = -static
 endif
 
 ifneq "$(LIBTOOL)" ""
-COMPILE.c      := $(LIBTOOL) --quiet --mode=compile $(CC) 
+COMPILE.c      := $(LIBTOOL) --quiet --mode=compile $(CC)
 LINK.lib       := $(LIBTOOL) --quiet --mode=link $(CC) -rpath $(libdir) -o
 LO             := lo
 LA             := la
index 05f02bb..7689102 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -25,16 +25,31 @@ export DESTDIR := $(R)
 include scripts/boiler.mk
 
 #
+#  To work around OpenSSL issues with travis.
+#
+.PHONY:
+raddb/test.conf:
+       @echo 'security {' >> $@
+       @echo '        allow_vulnerable_openssl = yes' >> $@
+       @echo '}' >> $@
+       @echo '$$INCLUDE radiusd.conf' >> $@
+
+#
 #  Run "radiusd -C", looking for errors.
 #
-$(BUILD_DIR)/tests/radiusd-c: ${BUILD_DIR}/bin/radiusd | build.raddb
+# Only redirect STDOUT, which should contain details of why the test failed.
+# Don't molest STDERR as this may be used to receive output from a debugger.
+$(BUILD_DIR)/tests/radiusd-c: raddb/test.conf ${BUILD_DIR}/bin/radiusd | build.raddb
        @$(MAKE) -C raddb/certs
-       @if ! ./build/make/jlibtool --mode=execute ./build/bin/radiusd -XCMd ./raddb -n debug -D ./share > $(BUILD_DIR)/tests/radiusd.config.log 2>&1; then \
+       @printf "radiusd -C... "
+       @if ! ./build/make/jlibtool --mode=execute ./build/bin/radiusd -XCMd ./raddb -D ./share -n test > $(BUILD_DIR)/tests/radiusd.config.log; then \
+               @rm -f raddb/test.conf; \
                cat $(BUILD_DIR)/tests/radiusd.config.log; \
-               echo "FAIL: radiusd -C"; \
+               echo "fail"; \
                exit 1; \
        fi
-       @echo "OK: radiusd -C"
+       @rm -f raddb/test.conf
+       @echo "ok"
        @touch $@
 
 test: ${BUILD_DIR}/bin/radiusd ${BUILD_DIR}/bin/radclient tests.unit tests.keywords $(BUILD_DIR)/tests/radiusd-c | build.raddb
@@ -43,8 +58,12 @@ test: ${BUILD_DIR}/bin/radiusd ${BUILD_DIR}/bin/radclient tests.unit tests.keywo
 # Â Tests specifically for Travis. Â We do a LOT more than just
 # Â the above tests
 ifneq "$(findstring travis,${prefix})" ""
-travis-test: test
+travis-test: raddb/test.conf test
+       @./build/make/jlibtool --mode=execute ./build/bin/radiusd -xxxv -n test
+       @rm -f raddb/test.conf
        @$(MAKE) install
+       @perl -p -i -e 's/allow_vulnerable_openssl = no/allow_vulnerable_openssl = yes/' ${raddbdir}/radiusd.conf
+       @${sbindir}/radiusd -XC
        @$(MAKE) deb
 endif
 
@@ -195,7 +214,7 @@ CONFIGURE_ARGS         := $(shell head -10 config.log | grep '^  \$$' | sed 's/^..../
 src/%all.mk: src/%all.mk.in src/%configure
        @echo CONFIGURE $(dir $@)
        @rm -f ./config.cache $(dir $<)/config.cache
-       @cd $(dir $<) && CPPFLAGS=$(DARWIN_CFLAGS) CFLAGS=$(DARWIN_CFLAGS) ./configure $(CONFIGURE_ARGS)
+       @cd $(dir $<) && ./configure $(CONFIGURE_ARGS)
 endif
 
 .PHONY: check-includes
@@ -211,7 +230,7 @@ TAGS:
 #
 .PHONY: certs
 certs:
-       @cd raddb/certs && $(MAKE)
+       @$(MAKE) -C raddb/certs
 
 ######################################################################
 #
@@ -279,3 +298,12 @@ deb:
 warnings:
        @(make clean all 2>&1) | egrep -v '^/|deprecated|^In file included|: In function|   from |^HEADER|^CC|^LINK' > warnings.txt
        @wc -l warnings.txt
+
+#
+#  Ensure we're using tabs in the configuration files,
+#  and remove trailing whitespace in source files.
+#
+.PHONY: whitespace
+whitespace:
+       @for x in $$(git ls-files raddb/ src/); do unexpand $$x > $$x.bak; cp $$x.bak $$x; rm -f $$x.bak;done
+       @perl -p -i -e 'trim' $$(git ls-files src/)
index 4cc1f5c..97d2092 100644 (file)
@@ -1,15 +1,13 @@
 The FreeRADIUS server
 =====================
 
-0. BRANCH STATE
----------------
-|BuildStatus|_
+|BuildStatus|_ |CoverityStatus|_
 
-.. |BuildStatus| image:: https://travis-ci.org/FreeRADIUS/freeradius-server.png?branch=v3.0.x
-.. _BuildStatus: https://travis-ci.org/FreeRADIUS/freeradius-server
+.. contents::
+   :local:
 
-1. INTRODUCTION
----------------
+Introduction
+------------
 
 The FreeRADIUS Server Project is a high performance and highly
 configurable RADIUS server that is available under the terms of the
@@ -43,14 +41,14 @@ Please see http://freeradius.org and http://wiki.freeradius.org for
 more information.
 
 
-2. INSTALLATION
----------------
+Installation
+------------
 
 To install the server, please see the INSTALL file in this directory.
 
 
-3. DEBUGGING THE SERVER
------------------------
+Debugging the Server
+--------------------
 
 Run the server in debugging mode, (``radiusd -X``) and READ the output.
 We cannot emphasize this point strongly enough.  The vast majority of
@@ -74,8 +72,8 @@ Type some key words into the search box, and you should find
 discussions about common problems and solution.
 
 
-4. ADDITIONAL INFORMATION
--------------------------
+Additional Information
+----------------------
 
 See ``doc/README`` for more information about FreeRADIUS.
 
@@ -85,20 +83,8 @@ old, and is not much more than a basic introduction to the subject.
 
 http://www.amazon.com/exec/obidos/ASIN/0596003226/freeradiusorg-20/
 
-For other RADIUS information, the Livington internet site had a lot
-of information about radius online.  Unfortunately Livingston, and the
-site, don't exist anymore but there is a copy of the site still at:
-
-http://portmasters.com/www.livingston.com/
-
-Especially worth reading is the "RADIUS for Unix administrators guide"
-
-* HTML:  http://portmasters.com/tech/docs/radius/1185title.html
-* PDF:   http://portmasters.com/tech/docs/pdf/radius.pdf
-
-
-5. PROBLEMS AND CONCERNS
-------------------------
+Problems and Concerns
+---------------------
 
 We understand that the server may be difficult to configure,
 install, or administer.  It is, after all, a complex system with many
@@ -113,10 +99,10 @@ following:
 2. Save a copy of the default configuration: It WORKS.  Don't change it!
 3. Verify that the server starts.  (You ARE using debugging mode, right?)
 4. Send it test packets using "radclient", or a NAS or AP.
-5. Verify that the server does what you expect.
-      - If it does not work, change the configuration, and go to step (3) 
-        If you're stuck, revert to using the "last working" configuration.
-      - If it works, proceed to step (6).
+5. Verify that the server does what you expect
+    - If it does not work, change the configuration, and go to step (3) 
+    - If you're stuck, revert to using the "last working" configuration.
+    - If it works, proceed to step (6).
 6. Save a copy of the working configuration, along with a note of what 
    you changed, and why.
 7. Make a SMALL change to the configuration.
@@ -129,8 +115,8 @@ will always take more time.  The "fast and loose" way will be MORE
 frustrating than quickly making forward progress!
 
 
-6. FEEDBACK
------------
+Feedback
+--------
 
 If you have any comments, bug reports, problems, or concerns, please
 send them to the 'freeradius-users' list (see the URL above).  We will
@@ -166,3 +152,9 @@ http://freeradius.org
 Please submit bug reports, suggestions, or patches.  That feedback
 gives the developers a guide as to where they should focus their work.
 If you like the server, feel free to mail the list and say so.
+
+.. |CoverityStatus| image:: https://scan.coverity.com/projects/58/badge.svg?
+.. _CoverityStatus: https://scan.coverity.com/projects/58
+
+.. |BuildStatus| image:: https://travis-ci.org/FreeRADIUS/freeradius-server.png
+.. _BuildStatus: https://travis-ci.org/FreeRADIUS/freeradius-server
diff --git a/VERSION b/VERSION
index d4c4b54..6e94a52 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-3.0.1
+3.0.4
 
index 17ec164..592b584 100644 (file)
@@ -115,8 +115,21 @@ AC_DEFUN([FR_SMART_CHECK_LIB], [
 sm_lib_safe=`echo "$1" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "$2" | sed 'y%./+-%__p_%'`
 
+dnl #
+dnl #  We pass all arguments for linker testing in CCPFLAGS as these
+dnl #  will be passed to the compiler (then linker) first.
+dnl #
+dnl #  The linker will search through -L directories in the order they
+dnl #  appear on the command line.  Unfortunately the same rules appear
+dnl #  to apply to directories specified with --sysroot, so we must
+dnl #  pass the user specified directory first.
+dnl #
+dnl #  Really we should be using LDFLAGS (-L<dir>) for this.
+dnl #
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 dnl #
@@ -126,17 +139,20 @@ dnl #
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     AC_MSG_CHECKING([for $2 in -l$1 in $try])
-    LIBS="-L$try -l$1 $old_LIBS -Wl,-rpath,$try"
+    LIBS="-l$1 $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     AC_TRY_LINK([extern char $2();],
                [$2()],
                [
-                smart_lib="-L$try -l$1 -Wl,-rpath,$try"
+                smart_lib="-l$1"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 AC_MSG_RESULT(yes)
                 break
                ],
                [AC_MSG_RESULT(no)])
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 dnl #
@@ -164,17 +180,20 @@ if test "x$smart_lib" = "x"; then
 
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     AC_MSG_CHECKING([for $2 in -l$1 in $try])
-    LIBS="-L$try -l$1 $old_LIBS -Wl,-rpath,$try"
+    LIBS="-l$1 $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     AC_TRY_LINK([extern char $2();],
                [$2()],
                [
-                 smart_lib="-L$try -l$1 -Wl,-rpath,$try"
+                 smart_lib="-l$1"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  AC_MSG_RESULT(yes)
                  break
                ],
                [AC_MSG_RESULT(no)])
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 dnl #
@@ -182,8 +201,8 @@ dnl #  Found it, set the appropriate variable.
 dnl #
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 ])
 
@@ -196,7 +215,7 @@ dnl #
 AC_DEFUN([FR_SMART_CHECK_INCLUDE], [
 
 ac_safe=`echo "$1" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -207,7 +226,7 @@ dnl #
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     AC_MSG_CHECKING([for $1 in $try])
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     AC_TRY_COMPILE([$2
                    #include <$1>],
                   [int a = 1;],
@@ -221,7 +240,7 @@ if test "x$smart_try_dir" != "x"; then
                     AC_MSG_RESULT(no)
                   ])
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 dnl #
@@ -250,7 +269,7 @@ if test "x$smart_include" = "x"; then
   FR_LOCATE_DIR(smart_include_dir,$1)
   for try in $smart_include_dir /usr/local/include /opt/include; do
     AC_MSG_CHECKING([for $1 in $try])
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     AC_TRY_COMPILE([$2
                    #include <$1>],
                   [int a = 1;],
@@ -264,7 +283,7 @@ if test "x$smart_include" = "x"; then
                     AC_MSG_RESULT(no)
                   ])
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 dnl #
@@ -272,8 +291,8 @@ dnl #  Found it, set the appropriate variable.
 dnl #
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 ])
 
@@ -325,11 +344,14 @@ m4_pushdef([AC_OUTPUT],
   AC_OUTPUT([$1],[$2],[$3])
 ])
 
-
-#  See if the compilation works with __thread, for thread-local storage
-#
+dnl #
+dnl #  Figure out which storage class specifier for Thread Local Storage is supported by the compiler
+dnl #
 AC_DEFUN([FR_TLS],
 [
+dnl #
+dnl #  See if the compilation works with __thread, for thread-local storage
+dnl #
   AC_MSG_CHECKING(for __thread support in compiler)
   AC_RUN_IFELSE(
     [AC_LANG_SOURCE(
@@ -343,10 +365,52 @@ AC_DEFUN([FR_TLS],
     ],[have_tls=yes],[have_tls=no],[have_tls=no])
   AC_MSG_RESULT($have_tls)
   if test "x$have_tls" = "xyes"; then
-    AC_DEFINE([HAVE_THREAD_TLS],[1],[Define if the compiler supports __thread])
+    AC_DEFINE([TLS_STORAGE_CLASS],[__thread],[Define if the compiler supports a thread local storage class])
   fi
-])
 
+dnl #
+dnl #  __declspec(thread) does exactly the same thing as __thread, but is supported by MSVS
+dnl #
+  if test "x$have_tls" = "xno"; then
+    AC_MSG_CHECKING(for __declspec(thread) support in compiler)
+    AC_RUN_IFELSE(
+      [AC_LANG_SOURCE(
+        [[
+          static _Thread_local int val;
+          int main(int argc, char **argv) {
+            val = 0;
+            return val;
+          }
+        ]])
+      ],[have_tls=yes],[have_tls=no],[have_tls=no])
+    AC_MSG_RESULT($have_tls)
+    if test "x$have_tls" = "xyes"; then
+      AC_DEFINE([TLS_STORAGE_CLASS],[__declspec(thread)],[Define if the compiler supports a thread local storage class])
+    fi
+  fi
+dnl #
+dnl #  _Thread_local does exactly the same thing as __thread, but it's standards compliant with C11.
+dnl #  we, however, state we are only compliant with C99, so the compiler will probably emit warnings
+dnl #  if we use it.  So save it as a last resort.
+dnl #
+  if test "x$have_tls" = "xno"; then
+    AC_MSG_CHECKING(for _Thread_local support in compiler)
+    AC_RUN_IFELSE(
+      [AC_LANG_SOURCE(
+        [[
+          static _Thread_local int val;
+          int main(int argc, char **argv) {
+            val = 0;
+            return val;
+          }
+        ]])
+      ],[have_tls=yes],[have_tls=no],[have_tls=no])
+    AC_MSG_RESULT($have_tls)
+    if test "x$have_tls" = "xyes"; then
+      AC_DEFINE([TLS_STORAGE_CLASS],[_Thread_local],[Define if the compiler supports a thread local storage class])
+    fi
+  fi
+])
 
 AC_DEFUN([VL_LIB_READLINE], [
   AC_CACHE_CHECK([for a readline compatible library],
@@ -394,4 +458,50 @@ AC_DEFUN([VL_LIB_READLINE], [
   AC_SUBST(LIBREADLINE)
 ])dnl
 
+dnl #
+dnl #  Figure out which storage class specifier for Thread Local Storage is supported by the compiler
+dnl #
+AC_DEFUN([FR_HAVE_BUILTIN_CHOOSE_EXPR],
+[
+dnl #
+dnl #  See if the compilation works with __thread, for thread-local storage
+dnl #
+  AC_MSG_CHECKING(for __builtin_choose_expr support in compiler)
+  AC_RUN_IFELSE(
+    [AC_LANG_SOURCE(
+      [[
+        int main(int argc, char **argv) {
+               return __builtin_choose_expr(0, 1, 0);
+        }
+      ]])
+    ],[have_builtin=yes],[have_builtin=no],[have_builtin=no])
+  AC_MSG_RESULT($have_builtin)
+  if test "x$have_builtin" = "xyes"; then
+    AC_DEFINE([HAVE_BUILTIN_CHOOSE_EXPR],1,[Define if the compiler supports __builtin_choose_expr])
+  fi
+])
+
+dnl #
+dnl #  Figure out which storage class specifier for Thread Local Storage is supported by the compiler
+dnl #
+AC_DEFUN([FR_HAVE_BUILTIN_TYPES_COMPATIBLE_P],
+[
+dnl #
+dnl #  See if the compilation works with __thread, for thread-local storage
+dnl #
+  AC_MSG_CHECKING(for __builtin_types_compatible_p support in compiler)
+  AC_RUN_IFELSE(
+    [AC_LANG_SOURCE(
+      [[
+        int main(int argc, char **argv) {
+               return !(__builtin_types_compatible_p(char *, char *));
+        }
+      ]])
+    ],[have_builtin=yes],[have_builtin=no],[have_builtin=no])
+  AC_MSG_RESULT($have_builtin)
+  if test "x$have_builtin" = "xyes"; then
+    AC_DEFINE([HAVE_BUILTIN_TYPES_COMPATIBLE_P],1,[Define if the compiler supports __builtin_types_compatible_p])
+  fi
+])
+
 AC_INCLUDE(aclocal.m4)
index c85aa48..4528614 100755 (executable)
--- a/configure
+++ b/configure
@@ -637,10 +637,15 @@ REGEX_PCRE
 REGEX
 CRYPTLIB
 LIBPREFIX
+COLLECTDC_LDFLAGS
+COLLECTDC_LIBS
+PCAP_LDFLAGS
 PCAP_LIBS
+OPENSSL_LDFLAGS
 OPENSSL_LIBS
-OPENSSL_INCLUDE
 LIBREADLINE
+TALLOC_LDFLAGS
+TALLOC_LIBS
 DIRNAME
 LOCATE
 AUTOHEADER
@@ -651,6 +656,7 @@ SNMPWALK
 SNMPGET
 PERL
 modconfdir
+dictdir
 raddbdir
 radacctdir
 logdir
@@ -673,9 +679,6 @@ CPPFLAGS
 LDFLAGS
 CFLAGS
 CC
-DARWIN_CFLAGS
-XCODEBUILD
-SW_VERS
 target_os
 target_vendor
 target_cpu
@@ -738,6 +741,7 @@ with_docdir
 with_logdir
 with_radacctdir
 with_raddbdir
+with_dictdir
 with_ascend_binary
 with_threads
 with_tcp
@@ -748,17 +752,21 @@ with_shared_libs
 with_modules
 with_experimental_modules
 with_udpfromto
-with_openssl
-with_openssl_includes
-with_openssl_libraries
 with_rlm_FOO_lib_dir
 with_rlm_FOO_include_dir
-with_talloc_include_dir
+with_openssl
+with_openssl_lib_dir
+with_openssl_include_dir
 with_talloc_lib_dir
-with_pcap_include_dir
+with_talloc_include_dir
 with_pcap_lib_dir
-with_rlm_pcre_lib_dir
-with_rlm_pcre_include_dir
+with_pcap_include_dir
+with_collectdclient_lib_dir
+with_collectdclient_include_dir
+with_execinfo_lib_dir
+with_execinfo_include_dir
+with_pcre_lib_dir
+with_pcre_include_dir
 '
       ac_precious_vars='build_alias
 host_alias
@@ -1398,27 +1406,53 @@ Optional Packages:
   --with-logdir=DIR       directory for logfiles LOCALSTATEDIR/log/radius
   --with-radacctdir=DIR   directory for detail files LOGDIR/radacct
   --with-raddbdir=DIR     directory for config files SYSCONFDIR/raddb
+  --with-dictdir=DIR      directory for dictionary files DATAROOTDIR/freeradius
   --with-ascend-binary    include support for Ascend binary filter attributes (default=yes)
   --with-threads          use threads, if available.  (default=yes)
   --with-tcp              compile in TCP support. (default=yes)
   --with-vmps             compile in VMPS support. (default=yes)
   --with-dhcp             compile in DHCP support. (default=yes)
   --with-static-modules=QUOTED-MODULE-LIST
-  --with-shared-libs               build dynamic libraries and link against them. (default=yes)
+  --with-shared-libs      build dynamic libraries and link against them.
+                          (default=yes)
   --with-modules=QUOTED-MODULE-LIST
-  --with-experimental-modules      use experimental and unstable modules. (default=no, unless --enable-developer=yes)
-  --with-udpfromto                 compile in UDPFROMTO support. (default=yes)
-  --with-openssl                   use OpenSSL. (default=yes)
-  --with-openssl-includes=DIR      directory to look for OpenSSL include files in
-  --with-openssl-libraries=DIR     directory to look for OpenSSL library files in
-  --with-rlm-FOO-lib-dir=DIR       directory to look for library files used by module FOO in
-  --with-rlm-FOO-include-dir=DIR   directory to look for include files used by module FOO in
-  --with-talloc-include-dir=DIR    directory to look for talloc include files in
-  --with-talloc-lib-dir=DIR        directory to look for talloc library files in
-  --with-pcap-include-dir=DIR    directory to look for pcap include files in
-  --with-pcap-lib-dir=DIR        directory to look for pcap library files in
-  --with-pcre-lib-dir=DIR          directory to look for PCRE library files in
-  --with-pcre-include-dir=DIR      directory to look for PCRE include files in
+  --with-experimental-modules
+                          use experimental and unstable modules. (default=no,
+                          unless --enable-developer=yes)
+  --with-udpfromto        compile in UDPFROMTO support. (default=yes)
+  --with-rlm-FOO-lib-dir=DIR
+                          directory in which to look for library files used by
+                          module FOO
+  --with-rlm-FOO-include-dir=DIR
+                          directory in which to look for include files used by
+                          module FOO
+  --with-openssl          use OpenSSL. (default=yes)
+  --with-openssl-lib-dir=DIR
+                          directory to look for OpenSSL library files
+  --with-openssl-include-dir=DIR
+                          directory to look for OpenSSL include files
+  --with-talloc-lib-dir=DIR
+                          directory in which to look for talloc library files
+  --with-talloc-include-dir=DIR
+                          directory in which to look for talloc include files
+  --with-pcap-lib-dir=DIR directory in which to look for pcap library files
+  --with-pcap-include-dir=DIR
+                          directory in which to look for pcap include files
+  --with-collectdclient-lib-dir=DIR
+                          directory in which to look for collectdclient
+                          library files
+  --with-collectdclient-include-dir=DIR
+                          directory in which to look for collectdclient
+                          include files
+  --with-execinfo-lib-dir=DIR
+                          directory in which to look for execinfo library
+                          files
+  --with-execinfo-include-dir=DIR
+                          directory in which to look for execinfo include
+                          files
+  --with-pcre-lib-dir=DIR directory in which to look for pcre library files
+  --with-pcre-include-dir=DIR
+                          directory in which to look for pcre include files
 
 Some influential environment variables:
   CC          C compiler command
@@ -1836,60 +1870,6 @@ fi
 
 } # ac_fn_c_try_link
 
-# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
-# -------------------------------------------
-# Tests whether TYPE exists after having included INCLUDES, setting cache
-# variable VAR accordingly.
-ac_fn_c_check_type ()
-{
-  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  eval "$3=no"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-if (sizeof ($2))
-        return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-$4
-int
-main ()
-{
-if (sizeof (($2)))
-           return 0;
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-
-else
-  eval "$3=yes"
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-eval ac_res=\$$3
-              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_type
-
 # ac_fn_c_check_func LINENO FUNC VAR
 # ----------------------------------
 # Tests whether FUNC exists, setting the cache variable VAR accordingly
@@ -1957,6 +1937,60 @@ $as_echo "$ac_res" >&6; }
 
 } # ac_fn_c_check_func
 
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
+
 # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES
 # ---------------------------------------------
 # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
@@ -2560,110 +2594,6 @@ test -n "$target_alias" &&
   program_prefix=${target_alias}-
 
 
-case "$host" in
-  *-darwin*)
-                    { $as_echo "$as_me:${as_lineno-$LINENO}: checking if cc is apple llvm" >&5
-$as_echo_n "checking if cc is apple llvm... " >&6; }
-    if ! $CC --version 2>&1 | grep -I 'Apple LLVM' > /dev/null; then
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-      # Extract the first word of "sw_vers", so it can be a program name with args.
-set dummy sw_vers; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_SW_VERS+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$SW_VERS"; then
-  ac_cv_prog_SW_VERS="$SW_VERS" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_SW_VERS="yes"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-  test -z "$ac_cv_prog_SW_VERS" && ac_cv_prog_SW_VERS="no"
-fi
-fi
-SW_VERS=$ac_cv_prog_SW_VERS
-if test -n "$SW_VERS"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $SW_VERS" >&5
-$as_echo "$SW_VERS" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-      # Extract the first word of "xcodebuild", so it can be a program name with args.
-set dummy xcodebuild; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_XCODEBUILD+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  if test -n "$XCODEBUILD"; then
-  ac_cv_prog_XCODEBUILD="$XCODEBUILD" # Let the user override the test.
-else
-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
-for as_dir in $PATH
-do
-  IFS=$as_save_IFS
-  test -z "$as_dir" && as_dir=.
-    for ac_exec_ext in '' $ac_executable_extensions; do
-  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
-    ac_cv_prog_XCODEBUILD="yes"
-    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
-    break 2
-  fi
-done
-  done
-IFS=$as_save_IFS
-
-  test -z "$ac_cv_prog_XCODEBUILD" && ac_cv_prog_XCODEBUILD="no"
-fi
-fi
-XCODEBUILD=$ac_cv_prog_XCODEBUILD
-if test -n "$XCODEBUILD"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XCODEBUILD" >&5
-$as_echo "$XCODEBUILD" >&6; }
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-
-
-
-      if test "x$SW_VERS" = "xyes" && test "x$XCODEBUILD" = "xyes"; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: determining OSX SDK path" >&5
-$as_echo "$as_me: determining OSX SDK path" >&6;}
-        osx_sdk_path=$(xcodebuild -version -sdk macosx$(sw_vers -productVersion | egrep -o '^[0-9]+\.[0-9]+') Path)
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $osx_sdk_path" >&5
-$as_echo "$osx_sdk_path" >&6; }
-
-                                        export CFLAGS="$CFLAGS --sysroot=$osx_sdk_path"
-        export CPPFLAGS="$CPPFLAGS --sysroot=$osx_sdk_path"
-        export LDFLAGS="$LDFLAGS -L$osx_sdk_path/usr/lib/"
-        DARWIN_CFLAGS="--sysroot=$osx_sdk_path"
-
-      fi
-    else
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-    fi
-    ;;
-esac
-
 ac_ext=c
 ac_cpp='$CPP $CPPFLAGS'
 ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -4840,10 +4770,14 @@ fi
 $as_echo "$ac_cv_c_bigendian" >&6; }
  case $ac_cv_c_bigendian in #(
    yes)
-     $as_echo "#define WORDS_BIGENDIAN 1" >>confdefs.h
+
+$as_echo "#define BIG_ENDIAN 1" >>confdefs.h
 ;; #(
    no)
-      ;; #(
+
+$as_echo "#define LITTLE_ENDIAN 1" >>confdefs.h
+
+ ;; #(
    universal)
 
 $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
@@ -5238,6 +5172,32 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $raddbdir" >&5
 $as_echo "$raddbdir" >&6; }
 
+dictdir='${datarootdir}/freeradius'
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dictdir" >&5
+$as_echo_n "checking dictdir... " >&6; }
+
+# Check whether --with-dictdir was given.
+if test "${with_dictdir+set}" = set; then :
+  withval=$with_dictdir;  case "$withval" in
+  no)
+    as_fn_error $? "Need dictdir" "$LINENO" 5
+    ;;
+  yes)
+    ;;
+  [\\/$]* | ?:[\\/]* )
+    dictdir="$withval"
+    ;;
+  *)
+    as_fn_error $? "expected an absolute directory name for --with-dictdir: $withval" "$LINENO" 5
+    ;;
+  esac
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dictdir" >&5
+$as_echo "$dictdir" >&6; }
+
 modconfdir='${raddbdir}/mods-config'
 
 
@@ -5407,63 +5367,63 @@ $as_echo "#define WITH_UDPFROMTO /**/" >>confdefs.h
 
 fi
 
-WITH_OPENSSL=yes
 
-# Check whether --with-openssl was given.
-if test "${with_openssl+set}" = set; then :
-  withval=$with_openssl;  case "$withval" in
-  no)
-    WITH_OPENSSL=no
-    ;;
+
+# Check whether --with-rlm-FOO-lib-dir was given.
+if test "${with_rlm_FOO_lib_dir+set}" = set; then :
+  withval=$with_rlm_FOO_lib_dir;  case "$withval" in
   *)
-    WITH_OPENSSL=yes
     ;;
   esac
 
 fi
 
 
-OPENSSL_INCLUDE_DIR=
 
-# Check whether --with-openssl-includes was given.
-if test "${with_openssl_includes+set}" = set; then :
-  withval=$with_openssl_includes;  case "$withval" in
-  *) OPENSSL_INCLUDE_DIR="$withval"
+# Check whether --with-rlm-FOO-include-dir was given.
+if test "${with_rlm_FOO_include_dir+set}" = set; then :
+  withval=$with_rlm_FOO_include_dir;  case "$withval" in
+  *)
     ;;
   esac
 
 fi
 
 
-OPENSSL_LIB_DIR=
+WITH_OPENSSL=yes
 
-# Check whether --with-openssl-libraries was given.
-if test "${with_openssl_libraries+set}" = set; then :
-  withval=$with_openssl_libraries;  case "$withval" in
-  *) OPENSSL_LIB_DIR="$withval"
+# Check whether --with-openssl was given.
+if test "${with_openssl+set}" = set; then :
+  withval=$with_openssl;  case "$withval" in
+  no)
+    WITH_OPENSSL=no
+    ;;
+  *)
+    WITH_OPENSSL=yes
     ;;
   esac
 
 fi
 
 
+openssl_lib_dir=
 
-
-# Check whether --with-rlm-FOO-lib-dir was given.
-if test "${with_rlm_FOO_lib_dir+set}" = set; then :
-  withval=$with_rlm_FOO_lib_dir;  case "$withval" in
-  *)
+# Check whether --with-openssl-lib-dir was given.
+if test "${with_openssl_lib_dir+set}" = set; then :
+  withval=$with_openssl_lib_dir;  case "$withval" in
+  *) openssl_lib_dir="$withval"
     ;;
   esac
 
 fi
 
 
+openssl_include_dir=
 
-# Check whether --with-rlm-FOO-include-dir was given.
-if test "${with_rlm_FOO_include_dir+set}" = set; then :
-  withval=$with_rlm_FOO_include_dir;  case "$withval" in
-  *)
+# Check whether --with-openssl-include-dir was given.
+if test "${with_openssl_include_dir+set}" = set; then :
+  withval=$with_openssl_include_dir;  case "$withval" in
+  *) openssl_include_dir="$withval"
     ;;
   esac
 
 
 
 
-old_CFLAGS=$CFLAGS
-if test "x$WITH_THREADS" = "xyes"; then
-  if test $ac_cv_prog_suncc = "yes"; then
-    CFLAGS="$CFLAGS -mt"
-  fi
-
-  for ac_header in pthread.h
-do :
-  ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
-if test "x$ac_cv_header_pthread_h" = xyes; then :
-  cat >>confdefs.h <<_ACEOF
-#define HAVE_PTHREAD_H 1
-_ACEOF
-
-else
-   WITH_THREADS="no"
-fi
-
-done
-
+talloc_lib_dir=
 
-                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
-$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
+# Check whether --with-talloc-lib-dir was given.
+if test "${with_talloc_lib_dir+set}" = set; then :
+  withval=$with_talloc_lib_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need talloc-lib-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_lib_dir="$withval"
+      ;;
+  esac
+fi
+
+
+talloc_include_dir=
+
+# Check whether --with-talloc-include-dir was given.
+if test "${with_talloc_include_dir+set}" = set; then :
+  withval=$with_talloc_include_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need talloc-include-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_include_dir="$withval"
+      ;;
+  esac
+fi
+
+
+smart_try_dir="$talloc_lib_dir"
+
+
+sm_lib_safe=`echo "talloc" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "_talloc" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
+$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
+    LIBS="-ltalloc $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char _talloc();
+int
+main ()
+{
+_talloc()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-ltalloc"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc" >&5
+$as_echo_n "checking for _talloc in -ltalloc... " >&6; }
+  LIBS="-ltalloc $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char _talloc();
+int
+main ()
+{
+_talloc()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-ltalloc"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libtalloc${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libtalloc.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
+$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
+    LIBS="-ltalloc $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char _talloc();
+int
+main ()
+{
+_talloc()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-ltalloc"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&2;}
+  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
+fi
+
+TALLOC_LIBS="${smart_lib}"
+TALLOC_LDFLAGS="${smart_ldflags}"
+
+
+LIBS="$old_LIBS"
+
+old_CFLAGS=$CFLAGS
+if test "x$WITH_THREADS" = "xyes"; then
+  if test $ac_cv_prog_suncc = "yes"; then
+    CFLAGS="$CFLAGS -mt"
+  fi
+
+  for ac_header in pthread.h
+do :
+  ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
+if test "x$ac_cv_header_pthread_h" = xyes; then :
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_PTHREAD_H 1
+_ACEOF
+
+else
+
+      WITH_THREADS="no"
+      fail=pthread.h
+
+fi
+
+done
+
+
+                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
 if ${ac_cv_lib_pthread_pthread_create+:} false; then :
   $as_echo_n "(cached) " >&6
 else
@@ -5866,7 +6050,7 @@ if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
 
 else
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5
+                              { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lc_r" >&5
 $as_echo_n "checking for pthread_create in -lc_r... " >&6; }
 if ${ac_cv_lib_c_r_pthread_create+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -5905,7 +6089,10 @@ $as_echo "$ac_cv_lib_c_r_pthread_create" >&6; }
 if test "x$ac_cv_lib_c_r_pthread_create" = xyes; then :
    CFLAGS="$CFLAGS -pthread -D_THREAD_SAFE"
 else
-   WITH_THREADS="no"
+
+          WITH_THREADS="no"
+          fail=-lpthread
+
 
 fi
 
@@ -5913,6 +6100,17 @@ fi
 
 fi
 
+
+  if test "x$WITH_THREADS" != "xyes"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building with thread support." >&5
+$as_echo "$as_me: WARNING: silently not building with thread support." >&2;}
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: thread support requires: $fail." >&5
+$as_echo "$as_me: WARNING: FAILURE: thread support requires: $fail." >&2;}
+  else
+
+$as_echo "#define WITH_THREADS 1" >>confdefs.h
+
+  fi
 fi
 
 if test "x$WITH_THREADS" != "xyes"; then
@@ -5982,12 +6180,6 @@ fi
 
 fi
 
-if test "x$WITH_THREADS" = "xyes"; then
-
-$as_echo "#define WITH_THREADS 1" >>confdefs.h
-
-fi
-
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
 $as_echo_n "checking for dlopen in -ldl... " >&6; }
 if ${ac_cv_lib_dl_dlopen+:} false; then :
@@ -6217,21 +6409,58 @@ _ACEOF
 fi
 
 
-smart_try_dir="$pcap_lib_dir"
+pcap_lib_dir=
 
+# Check whether --with-pcap-lib-dir was given.
+if test "${with_pcap_lib_dir+set}" = set; then :
+  withval=$with_pcap_lib_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need pcap-lib-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_lib_dir="$withval"
+      ;;
+  esac
+fi
 
-sm_lib_safe=`echo "pcap" | sed 'y%./+-%__p_%'`
+
+pcap_include_dir=
+
+# Check whether --with-pcap-include-dir was given.
+if test "${with_pcap_include_dir+set}" = set; then :
+  withval=$with_pcap_include_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need pcap-include-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_include_dir="$withval"
+      ;;
+  esac
+fi
+
+
+smart_try_dir="$pcap_lib_dir"
+
+
+sm_lib_safe=`echo "pcap" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "pcap_open_live" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_open_live in -lpcap in $try" >&5
 $as_echo_n "checking for pcap_open_live in -lpcap in $try... " >&6; }
-    LIBS="-L$try -lpcap $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpcap $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char pcap_open_live();
@@ -6245,7 +6474,8 @@ pcap_open_live()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lpcap -Wl,-rpath,$try"
+                smart_lib="-lpcap"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -6258,6 +6488,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -6349,7 +6580,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_open_live in -lpcap in $try" >&5
 $as_echo_n "checking for pcap_open_live in -lpcap in $try... " >&6; }
-    LIBS="-L$try -lpcap $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpcap $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char pcap_open_live();
@@ -6363,7 +6595,8 @@ pcap_open_live()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lpcap -Wl,-rpath,$try"
+                 smart_lib="-lpcap"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -6376,26 +6609,263 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
 if test "x$ac_cv_lib_pcap_pcap_open_live" != "xyes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap library not found. Use --with-pcap-lib-dir=<path>." >&5
-$as_echo "$as_me: WARNING: pcap library not found. Use --with-pcap-lib-dir=<path>." >&2;}
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap library not found, silently disabling the RADIUS sniffer and ARP listener." >&5
-$as_echo "$as_me: WARNING: pcap library not found, silently disabling the RADIUS sniffer and ARP listener." >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap library not found, silently disabling the RADIUS sniffer, and ARP listener.  Use --with-pcap-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: pcap library not found, silently disabling the RADIUS sniffer, and ARP listener.  Use --with-pcap-lib-dir=<path>." >&2;}
 else
-  PCAP_LIBS="${smart_lib}"
-  LIBS=$old_LIBS
 
 $as_echo "#define HAVE_LIBPCAP 1" >>confdefs.h
 
+
+  for ac_func in \
+    pcap_fopen_offline \
+    pcap_dump_fopen \
+    pcap_create \
+    pcap_activate
+
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+  PCAP_LIBS="${smart_lib}"
+  PCAP_LDFLAGS="${smart_ldflags}"
+fi
+LIBS="${old_LIBS}"
+
+collectdclient_lib_dir=
+
+# Check whether --with-collectdclient-lib-dir was given.
+if test "${with_collectdclient_lib_dir+set}" = set; then :
+  withval=$with_collectdclient_lib_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need collectdclient-lib-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_lib_dir="$withval"
+      ;;
+  esac
+fi
+
+
+collectdclient_include_dir=
+
+# Check whether --with-collectdclient-include-dir was given.
+if test "${with_collectdclient_include_dir+set}" = set; then :
+  withval=$with_collectdclient_include_dir; case "$withval" in
+    no)
+      as_fn_error $? "Need collectdclient-include-dir" "$LINENO" 5
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_include_dir="$withval"
+      ;;
+  esac
+fi
+
+
+smart_try_dir="$collectdclient_lib_dir"
+
+
+sm_lib_safe=`echo "collectdclient" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "lcc_connect" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcc_connect in -lcollectdclient in $try" >&5
+$as_echo_n "checking for lcc_connect in -lcollectdclient in $try... " >&6; }
+    LIBS="-lcollectdclient $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcc_connect();
+int
+main ()
+{
+lcc_connect()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-lcollectdclient"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcc_connect in -lcollectdclient" >&5
+$as_echo_n "checking for lcc_connect in -lcollectdclient... " >&6; }
+  LIBS="-lcollectdclient $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcc_connect();
+int
+main ()
+{
+lcc_connect()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lcollectdclient"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcollectdclient${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcollectdclient.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcc_connect in -lcollectdclient in $try" >&5
+$as_echo_n "checking for lcc_connect in -lcollectdclient in $try... " >&6; }
+    LIBS="-lcollectdclient $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcc_connect();
+int
+main ()
+{
+lcc_connect()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lcollectdclient"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+if test "x$ac_cv_lib_collectdclient_lcc_connect" != "xyes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: collectdclient library not found. Use --with-collectdclient-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: collectdclient library not found. Use --with-collectdclient-lib-dir=<path>." >&2;}
+else
+  COLLECTDC_LIBS="${smart_lib}"
+  COLLECTDC_LDFLAGS="${smart_ldflags}"
 fi
+LIBS="${old_LIBS}"
 
 
   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a readline compatible library" >&5
@@ -6528,47 +6998,198 @@ done
 
 
 
-case "$host" in
-  *-interix*)
-    CFLAGS="$CFLAGS -D_ALL_SOURCE"
-    ;;
-  *-darwin*)
-    CFLAGS="$CFLAGS -DDARWIN"
-    LIBS="-framework DirectoryService $LIBS"
-    ;;
-esac
+smart_try_dir="$talloc_include_dir"
 
-ac_header_dirent=no
-for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
-  as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
-$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
-if eval \${$as_ac_Header+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+ac_safe=`echo "talloc.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
+$as_echo_n "checking for talloc.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#include <sys/types.h>
-#include <$ac_hdr>
 
+                   #include <talloc.h>
 int
 main ()
 {
-if ((DIR *) 0)
-return 0;
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  eval "$as_ac_Header=yes"
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
 else
-  eval "$as_ac_Header=no"
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-eval ac_res=\$$as_ac_Header
-              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h" >&5
+$as_echo_n "checking for talloc.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <talloc.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=talloc.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
+$as_echo_n "checking for talloc.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <talloc.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
+fi
+
+if test "x$ac_cv_header_talloc_h" != "xyes"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&2;}
+  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
+fi
+
+case "$host" in
+  *-interix*)
+    CFLAGS="$CFLAGS -D_ALL_SOURCE"
+    ;;
+  *-darwin*)
+    CFLAGS="$CFLAGS -DDARWIN"
+    LIBS="-framework DirectoryService $LIBS"
+    ;;
+esac
+
+ac_header_dirent=no
+for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
+  as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
+$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
+if eval \${$as_ac_Header+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <sys/types.h>
+#include <$ac_hdr>
+
+int
+main ()
+{
+if ((DIR *) 0)
+return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  eval "$as_ac_Header=yes"
+else
+  eval "$as_ac_Header=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$as_ac_Header
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
 $as_echo "$ac_res" >&6; }
 if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
   cat >>confdefs.h <<_ACEOF
@@ -6924,7 +7545,9 @@ for ac_header in \
   stddef.h \
   fnmatch.h \
   sia.h \
-  siad.h
+  siad.h \
+  features.h \
+  limits.h
 
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
@@ -6942,9 +7565,10 @@ done
 for ac_header in net/if.h
 do :
   ac_fn_c_check_header_compile "$LINENO" "net/if.h" "ac_cv_header_net_if_h" "
-  #ifdef HAVE_SYS_SOCKET_H
-  #  include <sys/socket.h>
-  #endif
+    #ifdef HAVE_SYS_SOCKET_H
+    #  include <sys/socket.h>
+    #endif
+
 
 "
 if test "x$ac_cv_header_net_if_h" = xyes; then :
@@ -6972,135 +7596,550 @@ $as_echo "#define OSFSIA /**/" >>confdefs.h
 fi
 
 if test "x$WITH_OPENSSL" = xyes; then
-  old_LIBS=$LIBS
-  old_LDFLAGS="$LDFLAGS"
+  OLD_LIBS="$LIBS"
 
-  OPENSSL_INCLUDE="-DNO_OPENSSL"
-  OPENSSL_LIBS=
-  if test "x$OPENSSL_LIB_DIR" != "x"; then
-    LDFLAGS="-L$OPENSSL_LIB_DIR $LDFLAGS"
-  fi
+        CFLAGS="$CFLAGS -DOPENSSL_NO_KRB5"
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto" >&5
-$as_echo_n "checking for DH_new in -lcrypto... " >&6; }
-if ${ac_cv_lib_crypto_DH_new+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcrypto  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
+        smart_try_dir="$openssl_lib_dir"
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char DH_new ();
+
+sm_lib_safe=`echo "crypto" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "DH_new" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto in $try" >&5
+$as_echo_n "checking for DH_new in -lcrypto in $try... " >&6; }
+    LIBS="-lcrypto $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char DH_new();
 int
 main ()
 {
-return DH_new ();
+DH_new()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_crypto_DH_new=yes
+
+                smart_lib="-lcrypto"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
 else
-  ac_cv_lib_crypto_DH_new=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_DH_new" >&5
-$as_echo "$ac_cv_lib_crypto_DH_new" >&6; }
-if test "x$ac_cv_lib_crypto_DH_new" = xyes; then :
 
-      LIBS="-lcrypto $LIBS"
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto" >&5
+$as_echo_n "checking for DH_new in -lcrypto... " >&6; }
+  LIBS="-lcrypto $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char DH_new();
+int
+main ()
+{
+DH_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
 
-$as_echo "#define HAVE_LIBCRYPTO 1" >>confdefs.h
+               smart_lib="-lcrypto"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl" >&5
-$as_echo_n "checking for SSL_new in -lssl... " >&6; }
-if ${ac_cv_lib_ssl_SSL_new+:} false; then :
-  $as_echo_n "(cached) " >&6
 else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lssl  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char SSL_new ();
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcrypto${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libcrypto.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for DH_new in -lcrypto in $try" >&5
+$as_echo_n "checking for DH_new in -lcrypto in $try... " >&6; }
+    LIBS="-lcrypto $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char DH_new();
+int
+main ()
+{
+DH_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lcrypto"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+  if test "x$ac_cv_lib_crypto_DH_new" = "xyes"; then
+
+$as_echo "#define HAVE_LIBCRYPTO 1" >>confdefs.h
+
+    OPENSSL_LIBS="$smart_lib"
+    OPENSSL_LDFLAGS="$smart_ldflags"
+
+
+
+sm_lib_safe=`echo "ssl" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "SSL_new" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl in $try" >&5
+$as_echo_n "checking for SSL_new in -lssl in $try... " >&6; }
+    LIBS="-lssl $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char SSL_new();
+int
+main ()
+{
+SSL_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-lssl"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl" >&5
+$as_echo_n "checking for SSL_new in -lssl... " >&6; }
+  LIBS="-lssl $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char SSL_new();
+int
+main ()
+{
+SSL_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lssl"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libssl${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=libssl.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SSL_new in -lssl in $try" >&5
+$as_echo_n "checking for SSL_new in -lssl in $try... " >&6; }
+    LIBS="-lssl $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char SSL_new();
+int
+main ()
+{
+SSL_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lssl"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+    if test "x$ac_cv_lib_ssl_SSL_new" != "xyes"; then
+      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed linking to libssl. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)
+See \`config.log' for more details" "$LINENO" 5; }
+    else
+
+$as_echo "#define HAVE_LIBSSL 1" >>confdefs.h
+
+      OPENSSL_LIBS="$OPENSSL_LIBS $smart_lib"
+
+      if test "$OPENSSL_LDFLAGS" != "$smart_ldflags"; then
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "\"inconsistent LDFLAGS between -lssl '$smart_ldflags' and -lcrypto '$OPENSSL_LDFLAGS'\"
+See \`config.log' for more details" "$LINENO" 5; }
+      fi
+    fi
+  else
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed linking to libcrypto. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+
+  smart_try_dir="$openssl_include_dir"
+
+
+ac_safe=`echo "openssl/ssl.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $try" >&5
+$as_echo_n "checking for openssl/ssl.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <openssl/ssl.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h" >&5
+$as_echo_n "checking for openssl/ssl.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <openssl/ssl.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=openssl/ssl.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ssl.h in $try" >&5
+$as_echo_n "checking for openssl/ssl.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <openssl/ssl.h>
 int
 main ()
 {
-return SSL_new ();
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_ssl_SSL_new=yes
-else
-  ac_cv_lib_ssl_SSL_new=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ssl_SSL_new" >&5
-$as_echo "$ac_cv_lib_ssl_SSL_new" >&6; }
-if test "x$ac_cv_lib_ssl_SSL_new" = xyes; then :
-
-
-$as_echo "#define HAVE_LIBSSL 1" >>confdefs.h
+if ac_fn_c_try_compile "$LINENO"; then :
 
-          if test "x$OPENSSL_LIB_DIR" != "x"; then
-            OPENSSL_LIBS="-L$OPENSSL_LIB_DIR"
-          fi
-          OPENSSL_LIBS="$OPENSSL_LIBS -lcrypto -lssl -lcrypto"
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
 
 else
 
-          { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "failed linking to libssl
-See \`config.log' for more details" "$LINENO" 5; }
-
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
 fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
 
-
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
+  if test "x$ac_cv_header_openssl_ssl_h" = "xyes"; then
+
+$as_echo "#define HAVE_OPENSSL_SSL_H 1" >>confdefs.h
 
-        old_CPPFLAGS=$CPPFLAGS
-  old_CFLAGS=$CFLAGS
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    CPPFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CPPFLAGS"
-    CFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CFLAGS"
-  fi
 
-        CPPFLAGS="$CPPFLAGS -DOPENSSL_NO_KRB5"
-  for ac_header in \
-    openssl/ssl.h \
-    openssl/crypto.h \
-    openssl/err.h \
-    openssl/evp.h \
-    openssl/md5.h \
-    openssl/md4.h \
-    openssl/sha.h \
-    openssl/ocsp.h \
-    openssl/engine.h
+    for ac_header in \
+      openssl/crypto.h \
+      openssl/err.h \
+      openssl/evp.h \
+      openssl/md5.h \
+      openssl/md4.h \
+      openssl/sha.h \
+      openssl/ocsp.h \
+      openssl/engine.h
 do :
   as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
 ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@@ -7111,9 +8150,9 @@ _ACEOF
 
 else
 
-      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "failed locating OpenSSL headers
+as_fn_error $? "failed locating OpenSSL headers. Use --with-openssl-include-dir=<path>, or --with-openssl=no (builds without OpenSSL)
 See \`config.log' for more details" "$LINENO" 5; }
 
 
@@ -7122,27 +8161,27 @@ fi
 done
 
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL version >= 0.9.7" >&5
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL version >= 0.9.7" >&5
 $as_echo_n "checking for OpenSSL version >= 0.9.7... " >&6; }
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <openssl/crypto.h>
-     #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
-     yes
-     #endif
+       #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+       yes
+       #endif
 
 _ACEOF
 if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
   $EGREP "yes" >/dev/null 2>&1; then :
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 as_fn_error $? "OpenSSL version too old
 See \`config.log' for more details" "$LINENO" 5; }
@@ -7152,15 +8191,12 @@ fi
 rm -f conftest*
 
 
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    OPENSSL_INCLUDE="-isystem $OPENSSL_INCLUDE_DIR -DOPENSSL_NO_KRB5"
-  else
-    OPENSSL_INCLUDE="-DOPENSSL_NO_KRB5"
-  fi
+                        old_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$OPENSSL_LDFLAGS $CPPFLAGS"
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking OpenSSL library and header version consistency" >&5
+                { $as_echo "$as_me:${as_lineno-$LINENO}: checking OpenSSL library and header version consistency" >&5
 $as_echo_n "checking OpenSSL library and header version consistency... " >&6; }
-  if test "$cross_compiling" = yes; then :
+    if test "$cross_compiling" = yes; then :
   { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 as_fn_error $? "cannot run test program while cross compiling
@@ -7169,20 +8205,20 @@ else
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-        #include <stdio.h>
-        #include <openssl/opensslv.h>
-        #include <openssl/crypto.h>
+          #include <stdio.h>
+          #include <openssl/opensslv.h>
+          #include <openssl/crypto.h>
 
 int
 main ()
 {
 
-        if (SSLeay() == OPENSSL_VERSION_NUMBER) {
-          return 0;
-        } else {
           printf("library: %lx header: %lx... ", (unsigned long) SSLeay(), (unsigned long) OPENSSL_VERSION_NUMBER);
-          return 1;
-        }
+          if (SSLeay() == OPENSSL_VERSION_NUMBER) {
+            return 0;
+          } else {
+            return 1;
+          }
 
 
   ;
@@ -7191,14 +8227,14 @@ main ()
 _ACEOF
 if ac_fn_c_try_run "$LINENO"; then :
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
 
-      { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-      { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+        { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
 as_fn_error $? "OpenSSL library version does not match header version
 See \`config.log' for more details" "$LINENO" 5; }
@@ -7209,30 +8245,24 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
   conftest.$ac_objext conftest.beam conftest.$ac_ext
 fi
 
-
-  if test "x$OPENSSL_LIBS" = x; then
-    LIBS=$old_LIBS
-    LDFLAGS="$old_LDFLAGS"
+    CPPFLAGS="$old_CPPFLAGS"
   fi
-  if test "x$OPENSSL_INCLUDE" = x; then
-    CPPFLAGS=$old_CPPFLAGS
-    CFLAGS=$old_CFLAGS
-  fi
-fi
 
+  LIBS="$OLD_LIBS"
 
 
-export OPENSSL_LIBS
+  export OPENSSL_LIBS OPENSSL_LDFLAGS
+fi
 
 if test "x$PCAP_LIBS" = x; then
   { $as_echo "$as_me:${as_lineno-$LINENO}: skipping test for pcap.h." >&5
 $as_echo "$as_me: skipping test for pcap.h." >&6;}
 else
-  smart_try_dir="$pcap_include_dir"
+        smart_try_dir="$pcap_include_dir"
 
 
 ac_safe=`echo "pcap.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -7240,7 +8270,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h in $try" >&5
 $as_echo_n "checking for pcap.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -7269,7 +8299,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -7335,7 +8365,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap.h in $try" >&5
 $as_echo_n "checking for pcap.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -7364,121 +8394,189 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
-  if test "x$ac_cv_header_pcap_h" != "xyes"; then
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap headers not found. Use --with-pcap-include-dir=<path>." >&5
-$as_echo "$as_me: WARNING: pcap headers not found. Use --with-pcap-include-dir=<path>." >&2;}
-    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap.h not found, silently disabling the RADIUS sniffer, and ARP listener." >&5
-$as_echo "$as_me: WARNING: pcap.h not found, silently disabling the RADIUS sniffer, and ARP listener." >&2;}
-  else
+  if test "x$ac_cv_header_pcap_h" == "xyes"; then
 
 $as_echo "#define HAVE_PCAP_H 1" >>confdefs.h
 
 
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_fopen_offline in -lpcap" >&5
-$as_echo_n "checking for pcap_fopen_offline in -lpcap... " >&6; }
-if ${ac_cv_lib_pcap_pcap_fopen_offline+:} false; then :
-  $as_echo_n "(cached) " >&6
+
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: pcap headers not found, silently disabling the RADIUS sniffer, and ARP listener. Use --with-pcap-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: pcap headers not found, silently disabling the RADIUS sniffer, and ARP listener. Use --with-pcap-include-dir=<path>." >&2;}
+  fi
+fi
+
+if test "x$COLLECTDC_LIBS" = x; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: skipping test for collectd/client.h." >&5
+$as_echo "$as_me: skipping test for collectd/client.h." >&6;}
 else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpcap  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+        smart_try_dir="$collectdclient_include_dir"
+
+
+ac_safe=`echo "collectd/client.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for collectd/client.h in $try" >&5
+$as_echo_n "checking for collectd/client.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pcap_fopen_offline ();
+                   #include <collectd/client.h>
 int
 main ()
 {
-return pcap_fopen_offline ();
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_pcap_pcap_fopen_offline=yes
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
 else
-  ac_cv_lib_pcap_pcap_fopen_offline=no
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_fopen_offline" >&5
-$as_echo "$ac_cv_lib_pcap_pcap_fopen_offline" >&6; }
-if test "x$ac_cv_lib_pcap_pcap_fopen_offline" = xyes; then :
 
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for collectd/client.h" >&5
+$as_echo_n "checking for collectd/client.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <collectd/client.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
 
-$as_echo "#define HAVE_PCAP_FOPEN_OFFLINE 1" >>confdefs.h
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
 
+else
 
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
 fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
 
+if test "x$smart_include" = "x"; then
 
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcap_dump_fopen in -lpcap" >&5
-$as_echo_n "checking for pcap_dump_fopen in -lpcap... " >&6; }
-if ${ac_cv_lib_pcap_pcap_dump_fopen+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lpcap  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+if test "x$LOCATE" != "x"; then
+        DIRS=
+  file=collectd/client.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                        base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+                exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for collectd/client.h in $try" >&5
+$as_echo_n "checking for collectd/client.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char pcap_dump_fopen ();
+                   #include <collectd/client.h>
 int
 main ()
 {
-return pcap_dump_fopen ();
+int a = 1;
   ;
   return 0;
 }
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_pcap_pcap_dump_fopen=yes
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
 else
-  ac_cv_lib_pcap_pcap_dump_fopen=no
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
 fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pcap_pcap_dump_fopen" >&5
-$as_echo "$ac_cv_lib_pcap_pcap_dump_fopen" >&6; }
-if test "x$ac_cv_lib_pcap_pcap_dump_fopen" = xyes; then :
 
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
+fi
 
-$as_echo "#define HAVE_PCAP_DUMP_FOPEN 1" >>confdefs.h
+  if test "x$ac_cv_header_collectd_client_h" == "xyes"; then
 
+$as_echo "#define HAVE_COLLECTDC_H 1" >>confdefs.h
 
 
-fi
 
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: collectdclient headers not found. Use --with-collectdclient-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: collectdclient headers not found. Use --with-collectdclient-include-dir=<path>." >&2;}
   fi
 fi
 
 
-
 ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
 if test "x$ac_cv_type_off_t" = xyes; then :
 
@@ -7633,7 +8731,91 @@ $as_echo_n "checking for uint16_t... " >&6; }
 if ${ac_cv_type_uint16_t+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-   ac_cv_type_uint16_t=no
+   ac_cv_type_uint16_t=no
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+
+int
+main ()
+{
+uint16_t foo
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_type_uint16_t=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint16_t" >&5
+$as_echo "$ac_cv_type_uint16_t" >&6; }
+
+  if test "$ac_cv_type_uint16_t" != "yes"; then
+
+$as_echo "#define uint16_t unsigned short" >>confdefs.h
+
+  fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint32_t" >&5
+$as_echo_n "checking for uint32_t... " >&6; }
+if ${ac_cv_type_uint32_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_uint32_t=no
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+
+int
+main ()
+{
+uint32_t foo
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_type_uint32_t=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint32_t" >&5
+$as_echo "$ac_cv_type_uint32_t" >&6; }
+
+  if test "$ac_cv_type_uint32_t" != "yes"; then
+
+$as_echo "#define uint32_t unsigned int" >>confdefs.h
+
+  fi
+
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint64_t" >&5
+$as_echo_n "checking for uint64_t... " >&6; }
+if ${ac_cv_type_uint64_t+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+   ac_cv_type_uint64_t=no
       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #ifdef HAVE_INTTYPES_H
@@ -7647,70 +8829,92 @@ else
 int
 main ()
 {
-uint16_t foo
+uint64_t foo
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_uint16_t=yes
+  ac_cv_type_uint64_t=yes
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint16_t" >&5
-$as_echo "$ac_cv_type_uint16_t" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint64_t" >&5
+$as_echo "$ac_cv_type_uint64_t" >&6; }
 
-  if test "$ac_cv_type_uint16_t" != "yes"; then
+  if test "$ac_cv_type_uint64_t" != "yes"; then
 
-$as_echo "#define uint16_t unsigned short" >>confdefs.h
+$as_echo "#define uint64_t unsigned long long" >>confdefs.h
 
   fi
 
 
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint32_t" >&5
-$as_echo_n "checking for uint32_t... " >&6; }
-if ${ac_cv_type_uint32_t+:} false; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sig_t" >&5
+$as_echo_n "checking for sig_t... " >&6; }
+if ${ac_cv_type_sig_t+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-   ac_cv_type_uint32_t=no
+   ac_cv_type_sig_t=no
       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-#ifdef HAVE_INTTYPES_H
-    #  include <inttypes.h>
-    #endif
-
-    #ifdef HAVE_STDINT_H
-    #  include <stdint.h>
+#ifdef HAVE_SIGNAL_H
+    #  include <signal.h>
     #endif
 
 int
 main ()
 {
-uint32_t foo
+sig_t foo
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_compile "$LINENO"; then :
-  ac_cv_type_uint32_t=yes
+  ac_cv_type_sig_t=yes
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
 
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uint32_t" >&5
-$as_echo "$ac_cv_type_uint32_t" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_sig_t" >&5
+$as_echo "$ac_cv_type_sig_t" >&6; }
 
-  if test "$ac_cv_type_uint32_t" != "yes"; then
+  if test "$ac_cv_type_sig_t" != "yes"; then
 
-$as_echo "#define uint32_t unsigned int" >>confdefs.h
+$as_echo "#define sig_t void(*sig_t)(int)" >>confdefs.h
 
   fi
 
 
+ac_fn_c_check_type "$LINENO" "__uint128_t" "ac_cv_type___uint128_t" "$ac_includes_default"
+if test "x$ac_cv_type___uint128_t" = xyes; then :
+
+$as_echo "#define HAVE___UINT128_T 1" >>confdefs.h
+
+fi
+
+
+ac_fn_c_check_type "$LINENO" "uint128_t" "ac_cv_type_uint128_t" "
+    #ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+
+
+"
+if test "x$ac_cv_type_uint128_t" = xyes; then :
+
+$as_echo "#define HAVE_UINT128_T 1" >>confdefs.h
+
+fi
+
+
 ac_fn_c_check_type "$LINENO" "struct in6_addr" "ac_cv_type_struct_in6_addr" "
     #ifdef HAVE_NETINET_IN_H
     #  include <netinet/in.h>
@@ -7796,6 +9000,7 @@ for ac_func in \
   inet_aton \
   inet_pton \
   inet_ntop \
+  mallopt \
   setlinebuf \
   setvbuf \
   getusershell \
@@ -8181,143 +9386,389 @@ $as_echo "yes" >&6; }
 
 $as_echo "#define WITH_NDEBUG 1" >>confdefs.h
 
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+export EXPERIMENTAL
+
+if test -d $srcdir/.git -a "x$GIT" = "xyes"; then
+  RADIUSD_VERSION_COMMIT=`git log --pretty=format:'%h' -n 1`
+
+cat >>confdefs.h <<_ACEOF
+#define RADIUSD_VERSION_COMMIT ${RADIUSD_VERSION_COMMIT}
+_ACEOF
+
+fi
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __thread support in compiler" >&5
+$as_echo_n "checking for __thread support in compiler... " >&6; }
+  if test "$cross_compiling" = yes; then :
+  have_tls=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+        static __thread int val;
+        int main(int argc, char **argv) {
+          val = 0;
+          return val;
+        }
+
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_tls=yes
+else
+  have_tls=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
+$as_echo "$have_tls" >&6; }
+  if test "x$have_tls" = "xyes"; then
+
+$as_echo "#define TLS_STORAGE_CLASS __thread" >>confdefs.h
+
+  fi
+
+  if test "x$have_tls" = "xno"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __declspec(thread) support in compiler" >&5
+$as_echo_n "checking for __declspec(thread) support in compiler... " >&6; }
+    if test "$cross_compiling" = yes; then :
+  have_tls=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+          static _Thread_local int val;
+          int main(int argc, char **argv) {
+            val = 0;
+            return val;
+          }
+
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_tls=yes
+else
+  have_tls=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
+$as_echo "$have_tls" >&6; }
+    if test "x$have_tls" = "xyes"; then
+
+$as_echo "#define TLS_STORAGE_CLASS __declspec(thread)" >>confdefs.h
+
+    fi
+  fi
+  if test "x$have_tls" = "xno"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Thread_local support in compiler" >&5
+$as_echo_n "checking for _Thread_local support in compiler... " >&6; }
+    if test "$cross_compiling" = yes; then :
+  have_tls=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+          static _Thread_local int val;
+          int main(int argc, char **argv) {
+            val = 0;
+            return val;
+          }
+
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_tls=yes
+else
+  have_tls=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
+$as_echo "$have_tls" >&6; }
+    if test "x$have_tls" = "xyes"; then
+
+$as_echo "#define TLS_STORAGE_CLASS _Thread_local" >>confdefs.h
+
+    fi
+  fi
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_choose_expr support in compiler" >&5
+$as_echo_n "checking for __builtin_choose_expr support in compiler... " >&6; }
+  if test "$cross_compiling" = yes; then :
+  have_builtin=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+        int main(int argc, char **argv) {
+               return __builtin_choose_expr(0, 1, 0);
+        }
+
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_builtin=yes
+else
+  have_builtin=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_builtin" >&5
+$as_echo "$have_builtin" >&6; }
+  if test "x$have_builtin" = "xyes"; then
+
+$as_echo "#define HAVE_BUILTIN_CHOOSE_EXPR 1" >>confdefs.h
+
+  fi
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_types_compatible_p support in compiler" >&5
+$as_echo_n "checking for __builtin_types_compatible_p support in compiler... " >&6; }
+  if test "$cross_compiling" = yes; then :
+  have_builtin=no
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+        int main(int argc, char **argv) {
+               return !(__builtin_types_compatible_p(char *, char *));
+        }
+
+
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+  have_builtin=yes
+else
+  have_builtin=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+  conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_builtin" >&5
+$as_echo "$have_builtin" >&6; }
+  if test "x$have_builtin" = "xyes"; then
+
+$as_echo "#define HAVE_BUILTIN_TYPES_COMPATIBLE_P 1" >>confdefs.h
+
+  fi
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc_set_memlimit in -ltalloc" >&5
+$as_echo_n "checking for talloc_set_memlimit in -ltalloc... " >&6; }
+if ${ac_cv_lib_talloc_talloc_set_memlimit+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-ltalloc  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char talloc_set_memlimit ();
+int
+main ()
+{
+return talloc_set_memlimit ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_talloc_talloc_set_memlimit=yes
+else
+  ac_cv_lib_talloc_talloc_set_memlimit=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_talloc_talloc_set_memlimit" >&5
+$as_echo "$ac_cv_lib_talloc_talloc_set_memlimit" >&6; }
+if test "x$ac_cv_lib_talloc_talloc_set_memlimit" = xyes; then :
+
+
+$as_echo "#define HAVE_TALLOC_SET_MEMLIMIT 1" >>confdefs.h
+
+
+
+fi
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
+$as_echo_n "checking for crypt in -lcrypt... " >&6; }
+if ${ac_cv_lib_crypt_crypt+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcrypt  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char crypt ();
+int
+main ()
+{
+return crypt ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_crypt_crypt=yes
+else
+  ac_cv_lib_crypt_crypt=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypt_crypt" >&5
+$as_echo "$ac_cv_lib_crypt_crypt" >&6; }
+if test "x$ac_cv_lib_crypt_crypt" = xyes; then :
+  CRYPTLIB="-lcrypt"
+
 fi
 
-export EXPERIMENTAL
 
-if test -d $srcdir/.git -a "x$GIT" = "xyes"; then
-  RADIUSD_VERSION_COMMIT=`git log --pretty=format:'%h' -n 1`
+if test "$CRYPTLIB" != ""; then
 
-cat >>confdefs.h <<_ACEOF
-#define RADIUSD_VERSION_COMMIT "${RADIUSD_VERSION_COMMIT}"
-_ACEOF
+$as_echo "#define HAVE_CRYPT /**/" >>confdefs.h
+
+else
+  ac_fn_c_check_func "$LINENO" "crypt" "ac_cv_func_crypt"
+if test "x$ac_cv_func_crypt" = xyes; then :
+
+$as_echo "#define HAVE_CRYPT /**/" >>confdefs.h
 
 fi
 
+fi
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for __thread support in compiler" >&5
-$as_echo_n "checking for __thread support in compiler... " >&6; }
-  if test "$cross_compiling" = yes; then :
-  have_tls=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setkey in -lcipher" >&5
+$as_echo_n "checking for setkey in -lcipher... " >&6; }
+if ${ac_cv_lib_cipher_setkey+:} false; then :
+  $as_echo_n "(cached) " >&6
 else
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lcipher  $LIBS"
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-        static __thread int val;
-        int main(int argc, char **argv) {
-          val = 0;
-          return val;
-        }
-
-
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setkey ();
+int
+main ()
+{
+return setkey ();
+  ;
+  return 0;
+}
 _ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-  have_tls=yes
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_lib_cipher_setkey=yes
 else
-  have_tls=no
+  ac_cv_lib_cipher_setkey=no
 fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
-  conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
 fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cipher_setkey" >&5
+$as_echo "$ac_cv_lib_cipher_setkey" >&6; }
+if test "x$ac_cv_lib_cipher_setkey" = xyes; then :
+  CRYPTLIB="${CRYPTLIB} -lcipher"
 
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_tls" >&5
-$as_echo "$have_tls" >&6; }
-  if test "x$have_tls" = "xyes"; then
-
-$as_echo "#define HAVE_THREAD_TLS 1" >>confdefs.h
-
-  fi
+fi
 
 
 
-talloc_include_dir=
+execinfo_lib_dir=
 
-# Check whether --with-talloc-include-dir was given.
-if test "${with_talloc_include_dir+set}" = set; then :
-  withval=$with_talloc_include_dir; case "$withval" in
+# Check whether --with-execinfo-lib-dir was given.
+if test "${with_execinfo_lib_dir+set}" = set; then :
+  withval=$with_execinfo_lib_dir;  case "$withval" in
     no)
-      as_fn_error $? "Need talloc-include-dir" "$LINENO" 5
-      ;;
+        as_fn_error $? "Need execinfo-lib-dir" "$LINENO" 5
+       ;;
     yes)
-      ;;
+       ;;
     *)
-      talloc_include_dir="$withval"
-      ;;
+       execinfo_lib_dir="$withval"
+       ;;
   esac
-fi
-
-
-talloc_lib_dir=
 
-# Check whether --with-talloc-lib-dir was given.
-if test "${with_talloc_lib_dir+set}" = set; then :
-  withval=$with_talloc_lib_dir; case "$withval" in
-    no)
-      as_fn_error $? "Need talloc-lib-dir" "$LINENO" 5
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_lib_dir="$withval"
-      ;;
-  esac
 fi
 
 
-pcap_include_dir=
+execinfo_include_dir=
 
-# Check whether --with-pcap-include-dir was given.
-if test "${with_pcap_include_dir+set}" = set; then :
-  withval=$with_pcap_include_dir; case "$withval" in
+# Check whether --with-execinfo-include-dir was given.
+if test "${with_execinfo_include_dir+set}" = set; then :
+  withval=$with_execinfo_include_dir;  case "$withval" in
     no)
-      as_fn_error $? "Need pcap-include-dir" "$LINENO" 5
-      ;;
+        as_fn_error $? "Need execinfo-include-dir" "$LINENO" 5
+       ;;
     yes)
-      ;;
+       ;;
     *)
-      pcap_include_dir="$withval"
-      ;;
+       execinfo_include_dir="$withval"
+       ;;
   esac
-fi
-
-
-pcap_lib_dir=
 
-# Check whether --with-pcap-lib-dir was given.
-if test "${with_pcap_lib_dir+set}" = set; then :
-  withval=$with_pcap_lib_dir; case "$withval" in
-    no)
-      as_fn_error $? "Need pcap-lib-dir" "$LINENO" 5
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_lib_dir="$withval"
-      ;;
-  esac
 fi
 
 
-smart_try_dir="$talloc_include_dir"
+smart_try_dir=$execinfo_include_dir
 
 
-ac_safe=`echo "talloc.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+ac_safe=`echo "execinfo.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
-$as_echo_n "checking for talloc.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for execinfo.h in $try" >&5
+$as_echo_n "checking for execinfo.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <talloc.h>
+                   #include <execinfo.h>
 int
 main ()
 {
@@ -8342,16 +9793,16 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h" >&5
-$as_echo_n "checking for talloc.h... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for execinfo.h" >&5
+$as_echo_n "checking for execinfo.h... " >&6; }
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                 #include <talloc.h>
+                 #include <execinfo.h>
 int
 main ()
 {
@@ -8382,7 +9833,7 @@ if test "x$smart_include" = "x"; then
 
 if test "x$LOCATE" != "x"; then
         DIRS=
-  file=talloc.h
+  file=execinfo.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
                                         base=`echo $x | sed "s%/${file}%%"`
@@ -8406,13 +9857,13 @@ fi
 eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
 
   for try in $smart_include_dir /usr/local/include /opt/include; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for talloc.h in $try" >&5
-$as_echo_n "checking for talloc.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for execinfo.h in $try" >&5
+$as_echo_n "checking for execinfo.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-                   #include <talloc.h>
+                   #include <execinfo.h>
 int
 main ()
 {
@@ -8437,50 +9888,49 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
-fi
-
-if test "x$ac_cv_header_talloc_h" != "xyes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&5
-$as_echo "$as_me: WARNING: talloc headers not found. Use --with-talloc-include-dir=<path>." >&2;}
-  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
-smart_try_dir="$talloc_lib_dir"
+if test "x$ac_cv_header_execinfo_h" = "xyes"; then
+  smart_try_dir=$execinfo_lib_dir
 
 
-sm_lib_safe=`echo "talloc" | sed 'y%./+-%__p_%'`
-sm_func_safe=`echo "_talloc" | sed 'y%./+-%__p_%'`
+sm_lib_safe=`echo "execinfo" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "backtrace_symbols" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
-$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
-    LIBS="-L$try -ltalloc $old_LIBS -Wl,-rpath,$try"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for backtrace_symbols in -lexecinfo in $try" >&5
+$as_echo_n "checking for backtrace_symbols in -lexecinfo in $try... " >&6; }
+    LIBS="-lexecinfo $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-extern char _talloc();
+extern char backtrace_symbols();
 int
 main ()
 {
-_talloc()
+backtrace_symbols()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -ltalloc -Wl,-rpath,$try"
+                smart_lib="-lexecinfo"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -8493,26 +9943,27 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc" >&5
-$as_echo_n "checking for _talloc in -ltalloc... " >&6; }
-  LIBS="-ltalloc $old_LIBS"
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for backtrace_symbols in -lexecinfo" >&5
+$as_echo_n "checking for backtrace_symbols in -lexecinfo... " >&6; }
+  LIBS="-lexecinfo $old_LIBS"
   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-extern char _talloc();
+extern char backtrace_symbols();
 int
 main ()
 {
-_talloc()
+backtrace_symbols()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-ltalloc"
+               smart_lib="-lexecinfo"
                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
@@ -8530,7 +9981,7 @@ if test "x$smart_lib" = "x"; then
 
 if test "x$LOCATE" != "x"; then
         DIRS=
-  file=libtalloc${libltdl_cv_shlibext}
+  file=libexecinfo${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
                                         base=`echo $x | sed "s%/${file}%%"`
@@ -8557,7 +10008,7 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 if test "x$LOCATE" != "x"; then
         DIRS=
-  file=libtalloc.a
+  file=libexecinfo.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
                                         base=`echo $x | sed "s%/${file}%%"`
@@ -8582,23 +10033,25 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _talloc in -ltalloc in $try" >&5
-$as_echo_n "checking for _talloc in -ltalloc in $try... " >&6; }
-    LIBS="-L$try -ltalloc $old_LIBS -Wl,-rpath,$try"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for backtrace_symbols in -lexecinfo in $try" >&5
+$as_echo_n "checking for backtrace_symbols in -lexecinfo in $try... " >&6; }
+    LIBS="-lexecinfo $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
-extern char _talloc();
+extern char backtrace_symbols();
 int
 main ()
 {
-_talloc()
+backtrace_symbols()
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -ltalloc -Wl,-rpath,$try"
+                 smart_lib="-lexecinfo"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -8611,127 +10064,64 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
-fi
-
-if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&5
-$as_echo "$as_me: WARNING: talloc library not found. Use --with-talloc-lib-dir=<path>." >&2;}
-  as_fn_error $? "FreeRADIUS requires libtalloc" "$LINENO" 5
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypt in -lcrypt" >&5
-$as_echo_n "checking for crypt in -lcrypt... " >&6; }
-if ${ac_cv_lib_crypt_crypt+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcrypt  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+  if test "x$ac_cv_lib_execinfo_backtrace_symbols" != "xyes"; then
+        { $as_echo "$as_me:${as_lineno-$LINENO}: checking if execinfo provided as part of libc" >&5
+$as_echo_n "checking if execinfo provided as part of libc... " >&6; }
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char crypt ();
+        #include <execinfo.h>
+
 int
 main ()
 {
-return crypt ();
+
+        void *sym[1];
+        backtrace_symbols(&sym, sizeof(sym))
   ;
   return 0;
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_crypt_crypt=yes
-else
-  ac_cv_lib_crypt_crypt=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypt_crypt" >&5
-$as_echo "$ac_cv_lib_crypt_crypt" >&6; }
-if test "x$ac_cv_lib_crypt_crypt" = xyes; then :
-  CRYPTLIB="-lcrypt"
-
-fi
 
-
-if test "$CRYPTLIB" != ""; then
-
-$as_echo "#define HAVE_CRYPT /**/" >>confdefs.h
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+        ac_cv_lib_execinfo_backtrace_symbols="yes"
 
 else
-  ac_fn_c_check_func "$LINENO" "crypt" "ac_cv_func_crypt"
-if test "x$ac_cv_func_crypt" = xyes; then :
-
-$as_echo "#define HAVE_CRYPT /**/" >>confdefs.h
-
-fi
 
-fi
+        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for setkey in -lcipher" >&5
-$as_echo_n "checking for setkey in -lcipher... " >&6; }
-if ${ac_cv_lib_cipher_setkey+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-lcipher  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
 
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char setkey ();
-int
-main ()
-{
-return setkey ();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_cipher_setkey=yes
-else
-  ac_cv_lib_cipher_setkey=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cipher_setkey" >&5
-$as_echo "$ac_cv_lib_cipher_setkey" >&6; }
-if test "x$ac_cv_lib_cipher_setkey" = xyes; then :
-  CRYPTLIB="${CRYPTLIB} -lcipher"
-
-fi
+  fi
 
+  if test "x$ac_cv_lib_execinfo_backtrace_symbols" = "xyes"; then
 
+$as_echo "#define HAVE_EXECINFO 1" >>confdefs.h
 
+  fi
+fi
 
 pcre_lib_dir=
 
-# Check whether --with-rlm-pcre-lib-dir was given.
-if test "${with_rlm_pcre_lib_dir+set}" = set; then :
-  withval=$with_rlm_pcre_lib_dir;  case "$withval" in
+# Check whether --with-pcre-lib-dir was given.
+if test "${with_pcre_lib_dir+set}" = set; then :
+  withval=$with_pcre_lib_dir;  case "$withval" in
     no)
-       as_fn_error $? "Need rlm-pcre-lib-dir" "$LINENO" 5
+       as_fn_error $? "Need pcre-lib-dir" "$LINENO" 5
        ;;
     yes)
        ;;
 
 pcre_include_dir=
 
-# Check whether --with-rlm-pcre-include-dir was given.
-if test "${with_rlm_pcre_include_dir+set}" = set; then :
-  withval=$with_rlm_pcre_include_dir;  case "$withval" in
+# Check whether --with-pcre-include-dir was given.
+if test "${with_pcre_include_dir+set}" = set; then :
+  withval=$with_pcre_include_dir;  case "$withval" in
     no)
-       as_fn_error $? "Need rlm-pcre-include-dir" "$LINENO" 5
+       as_fn_error $? "Need pcre-include-dir" "$LINENO" 5
        ;;
     yes)
        ;;
@@ -8769,7 +10159,7 @@ smart_try_dir=$pcre_include_dir
 
 
 ac_safe=`echo "pcreposix.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -8777,7 +10167,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcreposix.h in $try" >&5
 $as_echo_n "checking for pcreposix.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -8806,7 +10196,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -8872,7 +10262,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pcreposix.h in $try" >&5
 $as_echo_n "checking for pcreposix.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -8901,13 +10291,13 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
 if test "x$ac_cv_header_pcreposix_h" = "xyes"; then
@@ -8927,7 +10317,7 @@ else
 
 
 ac_safe=`echo "regex.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -8935,7 +10325,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for regex.h in $try" >&5
 $as_echo_n "checking for regex.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -8964,7 +10354,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -9030,7 +10420,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for regex.h in $try" >&5
 $as_echo_n "checking for regex.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -9059,13 +10449,13 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
   if test "x$ac_cv_header_regex_h" = "xyes"; then
@@ -9471,7 +10861,7 @@ USE_STATIC_LIBS="yes"
   unset ac_cv_env_LIBS_set
   unset ac_cv_env_LIBS_value
 
-  ac_config_files="$ac_config_files ./Make.inc ./src/include/build-radpaths-h ./src/main/radsniff.mk ./src/main/checkrad ./src/main/radlast ./src/main/radtest ./scripts/rc.radiusd ./scripts/cron/radiusd.cron.daily ./scripts/cron/radiusd.cron.monthly ./scripts/cryptpasswd ./raddb/dictionary ./raddb/radrelay.conf ./raddb/radiusd.conf"
+  ac_config_files="$ac_config_files ./Make.inc ./src/include/build-radpaths-h ./src/main/radsniff.mk ./src/main/checkrad ./src/main/radlast ./src/main/radtest ./scripts/rc.radiusd ./scripts/cron/radiusd.cron.daily ./scripts/cron/radiusd.cron.monthly ./scripts/cryptpasswd ./raddb/radrelay.conf ./raddb/radiusd.conf"
 
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
@@ -10183,7 +11573,6 @@ do
     "./scripts/cron/radiusd.cron.daily") CONFIG_FILES="$CONFIG_FILES ./scripts/cron/radiusd.cron.daily" ;;
     "./scripts/cron/radiusd.cron.monthly") CONFIG_FILES="$CONFIG_FILES ./scripts/cron/radiusd.cron.monthly" ;;
     "./scripts/cryptpasswd") CONFIG_FILES="$CONFIG_FILES ./scripts/cryptpasswd" ;;
-    "./raddb/dictionary") CONFIG_FILES="$CONFIG_FILES ./raddb/dictionary" ;;
     "./raddb/radrelay.conf") CONFIG_FILES="$CONFIG_FILES ./raddb/radrelay.conf" ;;
     "./raddb/radiusd.conf") CONFIG_FILES="$CONFIG_FILES ./raddb/radiusd.conf" ;;
 
index f0e1e3e..06920cf 100644 (file)
@@ -115,45 +115,6 @@ dnl #
 AC_CANONICAL_SYSTEM
 
 dnl #
-dnl #  As of OSX 10.9 (Mavericks), /usr is no longer populated with the
-dnl #  standard set of headers and libraries, instead were meant to use
-dnl #  one of the SDKs which contains system headers and libraries for
-dnl #  different versions of OSX and iOS.
-dnl #
-case "$host" in
-  *-darwin*)
-    dnl #
-    dnl #  The version of GCC apple ships with Mavericks works out of the
-    dnl #  box, and presumably selects the highest version SDK for OSX.
-    dnl #
-    AC_MSG_CHECKING([if cc is apple llvm])
-    if ! $CC --version 2>&1 | grep -I 'Apple LLVM' > /dev/null; then
-      AC_MSG_RESULT(no)
-      AC_CHECK_PROG(SW_VERS, sw_vers, yes, no)
-      AC_CHECK_PROG(XCODEBUILD, xcodebuild, yes, no)
-
-      if test "x$SW_VERS" = "xyes" && test "x$XCODEBUILD" = "xyes"; then
-        AC_MSG_NOTICE([determining OSX SDK path])
-        osx_sdk_path=$(xcodebuild -version -sdk macosx$(sw_vers -productVersion | egrep -o '^[[0-9]]+\.[[0-9]]+') Path)
-        AC_MSG_RESULT([$osx_sdk_path])
-
-        dnl #
-        dnl #  We need to export these, else the child configure scripts all fail
-        dnl #  their compiler checks.
-        dnl #
-        export CFLAGS="$CFLAGS --sysroot=$osx_sdk_path"
-        export CPPFLAGS="$CPPFLAGS --sysroot=$osx_sdk_path"
-        export LDFLAGS="$LDFLAGS -L$osx_sdk_path/usr/lib/"
-        DARWIN_CFLAGS="--sysroot=$osx_sdk_path"
-       AC_SUBST(DARWIN_CFLAGS)
-      fi
-    else
-      AC_MSG_RESULT(yes)
-    fi
-    ;;
-esac
-
-dnl #
 dnl #  Check for GNU cc
 dnl #
 AC_PROG_CC
@@ -205,7 +166,10 @@ dnl #
 dnl #  check for system bytesex
 dnl #  AC_DEFINES WORDS_BIGENDIAN
 dnl #
-AC_C_BIGENDIAN
+AC_C_BIGENDIAN(
+  [AC_DEFINE(BIG_ENDIAN, 1, [Define if your processor stores words with the most significant byte first])],
+  [AC_DEFINE(LITTLE_ENDIAN, 1, [Define if your processor stores words with the least significant byte first])]
+)
 
 dnl #
 dnl #  Find GNU Make.
@@ -362,6 +326,30 @@ AC_ARG_WITH(raddbdir,
 AC_SUBST(raddbdir)
 AC_MSG_RESULT($raddbdir)
 
+dnl #
+dnl #  extra argument: --with-dictdir
+dnl #
+dictdir='${datarootdir}/freeradius'
+AC_MSG_CHECKING(dictdir)
+AC_ARG_WITH(dictdir,
+[  --with-dictdir=DIR      directory for dictionary files [DATAROOTDIR/freeradius] ],
+[ case "$withval" in
+  no)
+    AC_MSG_ERROR([Need dictdir])
+    ;;
+  yes)
+    ;;
+  [[\\/$]]* | ?:[[\\/]]* )
+    dictdir="$withval"
+    ;;
+  *)
+    AC_MSG_ERROR([expected an absolute directory name for --with-dictdir: $withval])
+    ;;
+  esac ]
+)
+AC_SUBST(dictdir)
+AC_MSG_RESULT($dictdir)
+
 modconfdir='${raddbdir}/mods-config'
 AC_SUBST(modconfdir)
 
@@ -461,7 +449,8 @@ AC_ARG_WITH(static_modules,
 
 USE_SHARED_LIBS=yes
 AC_ARG_WITH(shared-libs,
-[  --with-shared-libs               build dynamic libraries and link against them. (default=yes)],
+[AS_HELP_STRING([--with-shared-libs ],
+[build dynamic libraries and link against them. (default=yes)])],
 [ case "$withval" in
   no)
     USE_SHARED_LIBS=no
@@ -483,7 +472,8 @@ dnl #  extra argument: --with-experimental-modules
 dnl #
 EXPERIMENTAL=
 AC_ARG_WITH(experimental-modules,
-[  --with-experimental-modules      use experimental and unstable modules. (default=no, unless --enable-developer=yes) ],
+[AS_HELP_STRING([--with-experimental-modules],
+[use experimental and unstable modules. (default=no, unless --enable-developer=yes)])],
 [ case "$withval" in
   yes)
     EXPERIMENTAL=yes
@@ -500,7 +490,7 @@ dnl #  extra argument: --with-udpfromto
 dnl #
 WITH_UDPFROMTO=yes
 AC_ARG_WITH(udpfromto,
-[  --with-udpfromto                 compile in UDPFROMTO support. (default=yes)],
+[  --with-udpfromto        compile in UDPFROMTO support. (default=yes)],
 [ case "$withval" in
   yes)
     WITH_UDPFROMTO=yes
@@ -515,69 +505,73 @@ if test "x$WITH_UDPFROMTO" = "xyes"; then
 fi
 
 dnl #
-dnl #  extra argument: --with-openssl
+dnl #  These next two arguments don't actually do anything.  They're
+dnl #  place holders so that the top-level configure script can tell
+dnl #  the user how to configure lower-level modules
 dnl #
-WITH_OPENSSL=yes
-AC_ARG_WITH(openssl,
-[  --with-openssl                   use OpenSSL. (default=yes)],
+
+dnl #
+dnl #  extra argument: --with-rlm-FOO-lib-dir
+dnl #
+AC_ARG_WITH(rlm-FOO-lib-dir,
+[AS_HELP_STRING([--with-rlm-FOO-lib-dir=DIR],
+[directory in which to look for library files used by module FOO])],
 [ case "$withval" in
-  no)
-    WITH_OPENSSL=no
-    ;;
   *)
-    WITH_OPENSSL=yes
     ;;
   esac ]
 )
 
 dnl #
-dnl #  extra argument: --with-openssl-includes=dir
+dnl #  extra argument: --with-rlm-FOO-include-dir
 dnl #
-OPENSSL_INCLUDE_DIR=
-AC_ARG_WITH(openssl-includes,
-[  --with-openssl-includes=DIR      directory to look for OpenSSL include files in],
+AC_ARG_WITH(rlm-FOO-include-dir,
+[AS_HELP_STRING([--with-rlm-FOO-include-dir=DIR],
+[directory in which to look for include files used by module FOO])],
 [ case "$withval" in
-  *) OPENSSL_INCLUDE_DIR="$withval"
+  *)
     ;;
   esac ]
 )
 
 dnl #
-dnl #  extra argument: --with-openssl-libraries=dir
+dnl #  extra argument: --with-openssl
 dnl #
-OPENSSL_LIB_DIR=
-AC_ARG_WITH(openssl-libraries,
-[  --with-openssl-libraries=DIR     directory to look for OpenSSL library files in],
+WITH_OPENSSL=yes
+AC_ARG_WITH(openssl,
+[  --with-openssl          use OpenSSL. (default=yes)],
 [ case "$withval" in
-  *) OPENSSL_LIB_DIR="$withval"
+  no)
+    WITH_OPENSSL=no
+    ;;
+  *)
+    WITH_OPENSSL=yes
     ;;
   esac ]
 )
 
 dnl #
-dnl #  These next two arguments don't actually do anything.  They're
-dnl #  place holders so that the top-level configure script can tell
-dnl #  the user how to configure lower-level modules
+dnl #  extra argument: --with-openssl-lib-dir=dir
 dnl #
-
-dnl #
-dnl #  extra argument: --with-rlm-FOO-lib-dir
-dnl #
-AC_ARG_WITH(rlm-FOO-lib-dir,
-[  --with-rlm-FOO-lib-dir=DIR       directory to look for library files used by module FOO in],
+openssl_lib_dir=
+AC_ARG_WITH(openssl-lib-dir,
+[AS_HELP_STRING([--with-openssl-lib-dir=DIR],
+[directory to look for OpenSSL library files])],
 [ case "$withval" in
-  *)
+  *) openssl_lib_dir="$withval"
     ;;
   esac ]
 )
 
 dnl #
-dnl #  extra argument: --with-rlm-FOO-include-dir
+dnl #  extra argument: --with-openssl-includes=dir
 dnl #
-AC_ARG_WITH(rlm-FOO-include-dir,
-[  --with-rlm-FOO-include-dir=DIR   directory to look for include files used by module FOO in],
+openssl_include_dir=
+AC_ARG_WITH(openssl-include-dir,
+[AS_HELP_STRING([--with-openssl-include-dir=DIR],
+[directory to look for OpenSSL include files])],
 [ case "$withval" in
-  *)
+  *) openssl_include_dir="$withval"
     ;;
   esac ]
 )
@@ -623,6 +617,52 @@ dnl #  2. Checks for libraries
 dnl #
 dnl #############################################################
 
+dnl Check for talloc
+dnl extra argument: --with-talloc-lib-dir=DIR
+talloc_lib_dir=
+AC_ARG_WITH(talloc-lib-dir,
+  [AS_HELP_STRING([--with-talloc-lib-dir=DIR],
+  [directory in which to look for talloc library files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need talloc-lib-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_lib_dir="$withval"
+      ;;
+  esac])
+
+dnl extra argument: --with-talloc-include-dir=DIR
+talloc_include_dir=
+AC_ARG_WITH(talloc-include-dir,
+  [AS_HELP_STRING([--with-talloc-include-dir=DIR],
+  [directory in which to look for talloc include files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need talloc-include-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      talloc_include_dir="$withval"
+      ;;
+  esac])
+
+smart_try_dir="$talloc_lib_dir"
+FR_SMART_CHECK_LIB(talloc, _talloc)
+if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
+  AC_MSG_WARN([talloc library not found. Use --with-talloc-lib-dir=<path>.])
+  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
+fi
+
+TALLOC_LIBS="${smart_lib}"
+TALLOC_LDFLAGS="${smart_ldflags}"
+AC_SUBST(TALLOC_LIBS)
+AC_SUBST(TALLOC_LDFLAGS)
+LIBS="$old_LIBS"
+
 dnl #
 dnl #  If using pthreads, check for -lpthread (posix) or -lc_r (*BSD)
 dnl #
@@ -632,7 +672,11 @@ if test "x$WITH_THREADS" = "xyes"; then
     CFLAGS="$CFLAGS -mt"
   fi
 
-  AC_CHECK_HEADERS(pthread.h, [], [ WITH_THREADS="no" ])
+  AC_CHECK_HEADERS(pthread.h, [],
+    [
+      WITH_THREADS="no"
+      fail=[pthread.h]
+    ])
 
   dnl #
   dnl #  pthread stuff is usually in -lpthread
@@ -647,12 +691,26 @@ if test "x$WITH_THREADS" = "xyes"; then
       LIBS="-lpthread $LIBS"
     ],
     [
+      dnl #
+      dnl # -pthread is not a typo, it's a GCC option which sets additional flags required
+      dnl # for multithreading with the pthreads library.
+      dnl #
       AC_CHECK_LIB(c_r, pthread_create,
         [ CFLAGS="$CFLAGS -pthread -D_THREAD_SAFE" ],
-        [ WITH_THREADS="no" ]
+        [
+          WITH_THREADS="no"
+          fail=[-lpthread]
+        ]
       )
     ]
   )
+
+  if test "x$WITH_THREADS" != "xyes"; then
+    AC_MSG_WARN([silently not building with thread support.])
+    AC_MSG_WARN([FAILURE: thread support requires: $fail.])
+  else
+    AC_DEFINE(WITH_THREADS, [1], [define if you want thread support])
+  fi
 fi
 
 dnl #
@@ -679,10 +737,6 @@ else
   )
 fi
 
-if test "x$WITH_THREADS" = "xyes"; then
-  AC_DEFINE(WITH_THREADS, [1], [define if you want thread support])
-fi
-
 dnl #
 dnl #  Check if we need -lsocket
 dnl #
@@ -709,18 +763,103 @@ AC_CHECK_LIB(ws2_32, htonl)
 dnl #
 dnl #  Check the pcap library for the RADIUS sniffer.
 dnl #
+dnl extra argument: --with-pcap-lib-dir=DIR
+pcap_lib_dir=
+AC_ARG_WITH(pcap-lib-dir,
+  [AS_HELP_STRING([--with-pcap-lib-dir=DIR],
+  [directory in which to look for pcap library files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need pcap-lib-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_lib_dir="$withval"
+      ;;
+  esac])
+
+dnl extra argument: --with-pcap-include-dir=DIR
+pcap_include_dir=
+AC_ARG_WITH(pcap-include-dir,
+  [AS_HELP_STRING([--with-pcap-include-dir=DIR],
+  [directory in which to look for pcap include files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need pcap-include-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      pcap_include_dir="$withval"
+      ;;
+  esac])
+
 smart_try_dir="$pcap_lib_dir"
 FR_SMART_CHECK_LIB(pcap, pcap_open_live)
 if test "x$ac_cv_lib_pcap_pcap_open_live" != "xyes"; then
-  AC_MSG_WARN([pcap library not found. Use --with-pcap-lib-dir=<path>.])
-  AC_MSG_WARN([pcap library not found, silently disabling the RADIUS sniffer and ARP listener.])
+  AC_MSG_WARN([pcap library not found, silently disabling the RADIUS sniffer, and ARP listener.  Use --with-pcap-lib-dir=<path>.])
 else
-  PCAP_LIBS="${smart_lib}"
-  LIBS=$old_LIBS
   AC_DEFINE(HAVE_LIBPCAP, 1,
     [Define to 1 if you have the `pcap' library (-lpcap).]
   )
+
+  AC_CHECK_FUNCS(\
+    pcap_fopen_offline \
+    pcap_dump_fopen \
+    pcap_create \
+    pcap_activate
+  )
+
+  PCAP_LIBS="${smart_lib}"
+  PCAP_LDFLAGS="${smart_ldflags}"
+fi
+dnl Set by FR_SMART_CHECK_LIB
+LIBS="${old_LIBS}"
+
+dnl Check for collectdclient
+dnl extra argument: --with-collectdclient-lib-dir=DIR
+collectdclient_lib_dir=
+AC_ARG_WITH(collectdclient-lib-dir,
+  [AS_HELP_STRING([--with-collectdclient-lib-dir=DIR],
+  [directory in which to look for collectdclient library files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need collectdclient-lib-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_lib_dir="$withval"
+      ;;
+  esac])
+
+dnl extra argument: --with-collectdclient-include-dir=DIR
+collectdclient_include_dir=
+AC_ARG_WITH(collectdclient-include-dir,
+  [AS_HELP_STRING([--with-collectdclient-include-dir=DIR],
+  [directory in which to look for collectdclient include files])],
+  [case "$withval" in
+    no)
+      AC_MSG_ERROR([Need collectdclient-include-dir])
+      ;;
+    yes)
+      ;;
+    *)
+      collectdclient_include_dir="$withval"
+      ;;
+  esac])
+
+smart_try_dir="$collectdclient_lib_dir"
+FR_SMART_CHECK_LIB(collectdclient, lcc_connect)
+if test "x$ac_cv_lib_collectdclient_lcc_connect" != "xyes"; then
+  AC_MSG_WARN([collectdclient library not found. Use --with-collectdclient-lib-dir=<path>.])
+else
+  COLLECTDC_LIBS="${smart_lib}"
+  COLLECTDC_LDFLAGS="${smart_ldflags}"
 fi
+dnl Set by FR_SMART_CHECKLIB
+LIBS="${old_LIBS}"
 
 VL_LIB_READLINE
 
@@ -731,10 +870,19 @@ dnl #
 dnl #############################################################
 
 dnl #
+dnl # Check for talloc header files
+dnl #
+smart_try_dir="$talloc_include_dir"
+FR_SMART_CHECK_INCLUDE([talloc.h])
+if test "x$ac_cv_header_talloc_h" != "xyes"; then
+  AC_MSG_WARN([talloc headers not found. Use --with-talloc-include-dir=<path>.])
+  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
+fi
+
+dnl #
 dnl #  Interix requires us to set -D_ALL_SOURCE, otherwise
 dnl #  getopt will be #included, but won't link.  <sigh>
 dnl #
-dnl #
 case "$host" in
   *-interix*)
     CFLAGS="$CFLAGS -D_ALL_SOURCE"
@@ -789,18 +937,21 @@ AC_CHECK_HEADERS( \
   stddef.h \
   fnmatch.h \
   sia.h \
-  siad.h
+  siad.h \
+  features.h \
+  limits.h
 )
 
 dnl #
 dnl #  FreeBSD requires sys/socket.h before net/if.h
 dnl #
 AC_CHECK_HEADERS(net/if.h, [], [],
-[
-  #ifdef HAVE_SYS_SOCKET_H
-  #  include <sys/socket.h>
-  #endif
-])
+  [
+    #ifdef HAVE_SYS_SOCKET_H
+    #  include <sys/socket.h>
+    #endif
+  ]
+)
 
 dnl #
 dnl #  other checks which require headers
@@ -819,132 +970,118 @@ dnl #
 dnl #  Were we told to use OpenSSL, if we were and we find an error, call AC_MSG_FAILURE and exit
 dnl #
 if test "x$WITH_OPENSSL" = xyes; then
-  old_LIBS=$LIBS
-  old_LDFLAGS="$LDFLAGS"
-
-  OPENSSL_INCLUDE="-DNO_OPENSSL"
-  OPENSSL_LIBS=
-  if test "x$OPENSSL_LIB_DIR" != "x"; then
-    LDFLAGS="-L$OPENSSL_LIB_DIR $LDFLAGS"
-  fi
+  OLD_LIBS="$LIBS"
 
   dnl #
-  dnl #  Check we can link to libssl
+  dnl #  Apparently OpenSSL will attempt to build with kerberos if we don't pass this?!
   dnl #
-  AC_CHECK_LIB(crypto, DH_new,
-    [
-      LIBS="-lcrypto $LIBS"
-      AC_DEFINE(HAVE_LIBCRYPTO, 1, [Define to 1 if you have the `crypto' library (-lcrypto).])
-      AC_CHECK_LIB(ssl, SSL_new,
-        [
-          AC_DEFINE(HAVE_LIBSSL, 1, [Define to 1 if you have the `ssl' library (-lssl).])
-          if test "x$OPENSSL_LIB_DIR" != "x"; then
-            OPENSSL_LIBS="-L$OPENSSL_LIB_DIR"
-          fi
-          OPENSSL_LIBS="$OPENSSL_LIBS -lcrypto -lssl -lcrypto"
-        ],
-        [
-          AC_MSG_FAILURE([failed linking to libssl])
-        ]
-      )
-    ],
-    []
-  )
+  CFLAGS="$CFLAGS -DOPENSSL_NO_KRB5"
 
   dnl #
-  dnl #  Check we can find required headers
+  dnl #  Check we can link to libcrypto and libssl
   dnl #
-  old_CPPFLAGS=$CPPFLAGS
-  old_CFLAGS=$CFLAGS
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    CPPFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CPPFLAGS"
-    CFLAGS="-isystem $OPENSSL_INCLUDE_DIR $CFLAGS"
+  smart_try_dir="$openssl_lib_dir"
+  FR_SMART_CHECK_LIB(crypto, DH_new)
+  if test "x$ac_cv_lib_crypto_DH_new" = "xyes"; then
+    AC_DEFINE(HAVE_LIBCRYPTO, 1, [Define to 1 if you have the `crypto' library (-lcrypto).])
+    OPENSSL_LIBS="$smart_lib"
+    OPENSSL_LDFLAGS="$smart_ldflags"
+
+    FR_SMART_CHECK_LIB(ssl, SSL_new)
+    if test "x$ac_cv_lib_ssl_SSL_new" != "xyes"; then
+      AC_MSG_FAILURE([failed linking to libssl. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)])
+    else
+      AC_DEFINE(HAVE_LIBSSL, 1, [Define to 1 if you have the `ssl' library (-lssl).])
+      OPENSSL_LIBS="$OPENSSL_LIBS $smart_lib"
+
+      if test "$OPENSSL_LDFLAGS" != "$smart_ldflags"; then
+        AC_MSG_FAILURE(["inconsistent LDFLAGS between -lssl '$smart_ldflags' and -lcrypto '$OPENSSL_LDFLAGS'"])
+      fi
+    fi
+  else
+    AC_MSG_FAILURE([failed linking to libcrypto. Use --with-openssl-lib-dir=<path>, or --with-openssl=no (builds without OpenSSL)])
   fi
 
-  dnl #
-  dnl #  Stupid RedHat shit
-  dnl #
-  CPPFLAGS="$CPPFLAGS -DOPENSSL_NO_KRB5"
-  AC_CHECK_HEADERS( \
-    openssl/ssl.h \
-    openssl/crypto.h \
-    openssl/err.h \
-    openssl/evp.h \
-    openssl/md5.h \
-    openssl/md4.h \
-    openssl/sha.h \
-    openssl/ocsp.h \
-    openssl/engine.h,
-    [],
-    [
-      AC_MSG_FAILURE([failed locating OpenSSL headers])
-    ]
-  )
+  smart_try_dir="$openssl_include_dir"
+  FR_SMART_CHECK_INCLUDE(openssl/ssl.h)
+  if test "x$ac_cv_header_openssl_ssl_h" = "xyes"; then
+    AC_DEFINE(HAVE_OPENSSL_SSL_H, 1, [Define to 1 if you have the <openssl/ssl.h> header file.])
+
+    AC_CHECK_HEADERS( \
+      openssl/crypto.h \
+      openssl/err.h \
+      openssl/evp.h \
+      openssl/md5.h \
+      openssl/md4.h \
+      openssl/sha.h \
+      openssl/ocsp.h \
+      openssl/engine.h,
+      [],
+      [
+        AC_MSG_FAILURE([failed locating OpenSSL headers. Use --with-openssl-include-dir=<path>, or --with-openssl=no (builds without OpenSSL)])
+      ]
+    )
 
-  AC_MSG_CHECKING([for OpenSSL version >= 0.9.7])
-  AC_EGREP_CPP(yes,
-    [#include <openssl/crypto.h>
-     #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
-     yes
-     #endif
-    ],
-    [
-      AC_MSG_RESULT(yes)
-    ],
-    [
-      AC_MSG_RESULT(no)
-      AC_MSG_FAILURE([OpenSSL version too old])
-    ]
-  )
+    AC_MSG_CHECKING([for OpenSSL version >= 0.9.7])
+    AC_EGREP_CPP(yes,
+      [#include <openssl/crypto.h>
+       #if (OPENSSL_VERSION_NUMBER >= 0x00907000L)
+       yes
+       #endif
+      ],
+      [
+        AC_MSG_RESULT(yes)
+      ],
+      [
+        AC_MSG_RESULT(no)
+        AC_MSG_FAILURE([OpenSSL version too old])
+      ]
+    )
 
-  if test "x$OPENSSL_INCLUDE_DIR" != "x"; then
-    OPENSSL_INCLUDE="-isystem $OPENSSL_INCLUDE_DIR -DOPENSSL_NO_KRB5"
-  else
-    OPENSSL_INCLUDE="-DOPENSSL_NO_KRB5"
-  fi
+    dnl #
+    dnl #  CPPFLAGS are passed to the compiler first, so we use
+    dnl #  them to ensure things like --sysroot don't override the
+    dnl #  library location we discovered previously.
+    dnl #
+    old_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$OPENSSL_LDFLAGS $CPPFLAGS"
 
-  dnl #
-  dnl #  Now check that the header versions match the library
-  dnl #
-  AC_MSG_CHECKING([OpenSSL library and header version consistency])
-  AC_RUN_IFELSE(
-    [AC_LANG_PROGRAM(
-      [[
-        #include <stdio.h>
-        #include <openssl/opensslv.h>
-        #include <openssl/crypto.h>
-      ]],
-      [[
-        if (SSLeay() == OPENSSL_VERSION_NUMBER) {
-          return 0;
-        } else {
+    dnl #
+    dnl #  Now check that the header versions match the library
+    dnl #
+    AC_MSG_CHECKING([OpenSSL library and header version consistency])
+    AC_RUN_IFELSE(
+      [AC_LANG_PROGRAM(
+        [[
+          #include <stdio.h>
+          #include <openssl/opensslv.h>
+          #include <openssl/crypto.h>
+        ]],
+        [[
           printf("library: %lx header: %lx... ", (unsigned long) SSLeay(), (unsigned long) OPENSSL_VERSION_NUMBER);
-          return 1;
-        }
-      ]]
-    )],
-    [
-      AC_MSG_RESULT(yes)
-    ],
-    [
-      AC_MSG_RESULT(no)
-      AC_MSG_FAILURE([OpenSSL library version does not match header version])
-    ]
-  )
-
-  if test "x$OPENSSL_LIBS" = x; then
-    LIBS=$old_LIBS
-    LDFLAGS="$old_LDFLAGS"
-  fi
-  if test "x$OPENSSL_INCLUDE" = x; then
-    CPPFLAGS=$old_CPPFLAGS
-    CFLAGS=$old_CFLAGS
+          if (SSLeay() == OPENSSL_VERSION_NUMBER) {
+            return 0;
+          } else {
+            return 1;
+          }
+        ]]
+      )],
+      [
+        AC_MSG_RESULT(yes)
+      ],
+      [
+        AC_MSG_RESULT(no)
+        AC_MSG_FAILURE([OpenSSL library version does not match header version])
+      ]
+    )
+    CPPFLAGS="$old_CPPFLAGS"
   fi
-fi
 
-AC_SUBST(OPENSSL_INCLUDE)
-AC_SUBST(OPENSSL_LIBS)
-export OPENSSL_LIBS
+  LIBS="$OLD_LIBS"
+  AC_SUBST(OPENSSL_LIBS)
+  AC_SUBST(OPENSSL_LDFLAGS)
+  export OPENSSL_LIBS OPENSSL_LDFLAGS
+fi
 
 dnl #
 dnl #  Check the pcap includes for the RADIUS sniffer.
@@ -952,28 +1089,37 @@ dnl #
 if test "x$PCAP_LIBS" = x; then
   AC_MSG_NOTICE([skipping test for pcap.h.])
 else
+  dnl #
+  dnl # Check for pcap header files
+  dnl #
   smart_try_dir="$pcap_include_dir"
   FR_SMART_CHECK_INCLUDE([pcap.h])
-  if test "x$ac_cv_header_pcap_h" != "xyes"; then
-    AC_MSG_WARN([pcap headers not found. Use --with-pcap-include-dir=<path>.])
-    AC_MSG_WARN([pcap.h not found, silently disabling the RADIUS sniffer, and ARP listener.])
-  else
+  if test "x$ac_cv_header_pcap_h" == "xyes"; then
     AC_DEFINE(HAVE_PCAP_H, 1, [Define to 1 if you have the <pcap.h> header file.])
+    AC_SUBST(PCAP_LIBS)
+    AC_SUBST(PCAP_LDFLAGS)
+  else
+    AC_MSG_WARN([pcap headers not found, silently disabling the RADIUS sniffer, and ARP listener. Use --with-pcap-include-dir=<path>.])
+  fi
+fi
 
-    AC_CHECK_LIB(pcap, pcap_fopen_offline,
-      [
-        AC_DEFINE(HAVE_PCAP_FOPEN_OFFLINE, 1, [Define to 1 if you have the function pcap_fopen_offline.])
-      ]
-    )
-
-    AC_CHECK_LIB(pcap, pcap_dump_fopen,
-      [
-        AC_DEFINE(HAVE_PCAP_DUMP_FOPEN, 1, [Define to 1 if you have the function pcap_dump_fopen.])
-      ]
-    )
+dnl Check for collectd-client
+if test "x$COLLECTDC_LIBS" = x; then
+  AC_MSG_NOTICE([skipping test for collectd/client.h.])
+else
+  dnl #
+  dnl # Check for collectd-client header files
+  dnl #
+  smart_try_dir="$collectdclient_include_dir"
+  FR_SMART_CHECK_INCLUDE([collectd/client.h])
+  if test "x$ac_cv_header_collectd_client_h" == "xyes"; then
+    AC_DEFINE(HAVE_COLLECTDC_H, 1, [Define to 1 if you have the `collectdclient' library (-lcollectdclient).])
+    AC_SUBST(COLLECTDC_LIBS)
+    AC_SUBST(COLLECTDC_LDFLAGS)
+  else
+    AC_MSG_WARN([collectdclient headers not found. Use --with-collectdclient-include-dir=<path>.])
   fi
 fi
-AC_SUBST(PCAP_LIBS)
 
 dnl #############################################################
 dnl #
@@ -1053,6 +1199,51 @@ FR_CHECK_TYPE_INCLUDE(
   uint32_t, unsigned int, [uint32_t should be the canonical 'network integer']
 )
 
+dnl #
+dnl #  Check for uint64_t
+dnl #
+FR_CHECK_TYPE_INCLUDE(
+  [
+    #ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+  ],
+  uint64_t, unsigned long long, [uint64_t is required for larger counters]
+)
+
+FR_CHECK_TYPE_INCLUDE(
+  [
+    #ifdef HAVE_SIGNAL_H
+    #  include <signal.h>
+    #endif
+  ],
+  sig_t, void(*sig_t)(int), [signal action callback function]
+)
+
+dnl #
+dnl #  Check for __uint128_t (compiler builtin)
+dnl #
+AC_CHECK_TYPE(__uint128_t, AC_DEFINE(HAVE___UINT128_T, 1, [compiler specific 128 bit unsigned integer]), [], [])
+
+dnl #
+dnl #  Check for uint128_t (fictitious future data type)
+dnl #
+AC_CHECK_TYPE(uint128_t, AC_DEFINE(HAVE_UINT128_T, 1, [128 bit unsigned integer]), [],
+  [
+    #ifdef HAVE_INTTYPES_H
+    #  include <inttypes.h>
+    #endif
+
+    #ifdef HAVE_STDINT_H
+    #  include <stdint.h>
+    #endif
+  ]
+)
+
 AC_CHECK_TYPE(struct in6_addr, AC_DEFINE(HAVE_STRUCT_IN6_ADDR, 1, [IPv6 address structure]), [],
   [
     #ifdef HAVE_NETINET_IN_H
@@ -1119,6 +1310,7 @@ AC_CHECK_FUNCS( \
   inet_aton \
   inet_pton \
   inet_ntop \
+  mallopt \
   setlinebuf \
   setvbuf \
   getusershell \
@@ -1239,10 +1431,15 @@ dnl #  append the current git hash onto the version string
 dnl #
 if test -d $srcdir/.git -a "x$GIT" = "xyes"; then
   RADIUSD_VERSION_COMMIT=`git log --pretty=format:'%h' -n 1`
-  AC_DEFINE_UNQUOTED([RADIUSD_VERSION_COMMIT],["${RADIUSD_VERSION_COMMIT}"],[Commit HEAD at time of configuring])
+  AC_DEFINE_UNQUOTED([RADIUSD_VERSION_COMMIT],[${RADIUSD_VERSION_COMMIT}],[Commit HEAD at time of configuring])
 fi
 
+dnl #
+dnl #  check for some compiler features
+dnl #
 FR_TLS
+FR_HAVE_BUILTIN_CHOOSE_EXPR
+FR_HAVE_BUILTIN_TYPES_COMPATIBLE_P
 
 dnl #############################################################
 dnl #
@@ -1250,86 +1447,15 @@ dnl #  7. Checks for library functions
 dnl #
 dnl #############################################################
 
-dnl Check for talloc
-dnl extra argument: --with-talloc-include-dir=DIR
-talloc_include_dir=
-AC_ARG_WITH(talloc-include-dir,
-  [  --with-talloc-include-dir=DIR    directory to look for talloc include files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need talloc-include-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_include_dir="$withval"
-      ;;
-  esac])
-
-dnl Check for talloc
-dnl extra argument: --with-talloc-lib-dir=DIR
-talloc_lib_dir=
-AC_ARG_WITH(talloc-lib-dir,
-  [  --with-talloc-lib-dir=DIR        directory to look for talloc library files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need talloc-lib-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      talloc_lib_dir="$withval"
-      ;;
-  esac])
-
-dnl Check for pcap
-dnl extra argument: --with-pcap-include-dir=DIR
-pcap_include_dir=
-AC_ARG_WITH(pcap-include-dir,
-  [  --with-pcap-include-dir=DIR    directory to look for pcap include files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need pcap-include-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_include_dir="$withval"
-      ;;
-  esac])
-
-dnl Check for pcap
-dnl extra argument: --with-pcap-lib-dir=DIR
-pcap_lib_dir=
-AC_ARG_WITH(pcap-lib-dir,
-  [  --with-pcap-lib-dir=DIR        directory to look for pcap library files in],
-  [case "$withval" in
-    no)
-      AC_MSG_ERROR([Need pcap-lib-dir])
-      ;;
-    yes)
-      ;;
-    *)
-      pcap_lib_dir="$withval"
-      ;;
-  esac])
-
 dnl #
-dnl # Check for talloc header files
+dnl # Check for talloc_set_memlimit
+dnl # This was only included in version 2.0.8
 dnl #
-smart_try_dir="$talloc_include_dir"
-FR_SMART_CHECK_INCLUDE([talloc.h])
-if test "x$ac_cv_header_talloc_h" != "xyes"; then
-  AC_MSG_WARN([talloc headers not found. Use --with-talloc-include-dir=<path>.])
-  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
-fi
-
-smart_try_dir="$talloc_lib_dir"
-FR_SMART_CHECK_LIB(talloc, _talloc)
-if test "x$ac_cv_lib_talloc__talloc" != "xyes"; then
-  AC_MSG_WARN([talloc library not found. Use --with-talloc-lib-dir=<path>.])
-  AC_MSG_ERROR([FreeRADIUS requires libtalloc])
-fi
+AC_CHECK_LIB(talloc, talloc_set_memlimit,
+  [
+    AC_DEFINE(HAVE_TALLOC_SET_MEMLIMIT, 1, [Define to 1 if you have the function talloc_set_memlimit.])
+  ]
+)
 
 dnl #
 dnl # Check for libcrypt
@@ -1351,6 +1477,76 @@ AC_CHECK_LIB(cipher, setkey,
 )
 AC_SUBST(CRYPTLIB)
 
+dnl #
+dnl #  Check for libexecinfo support, on some systems this is built into libc
+dnl #  on others it's a separate library.
+dnl #
+dnl extra argument: --with-execinfo-lib-dir
+execinfo_lib_dir=
+AC_ARG_WITH(execinfo-lib-dir,
+[AS_HELP_STRING([--with-execinfo-lib-dir=DIR],
+[directory in which to look for execinfo library files])],
+[ case "$withval" in
+    no)
+        AC_MSG_ERROR([Need execinfo-lib-dir])
+       ;;
+    yes)
+       ;;
+    *)
+       execinfo_lib_dir="$withval"
+       ;;
+  esac ]
+)
+
+dnl extra argument: --with-execinfo-include-dir
+execinfo_include_dir=
+AC_ARG_WITH(execinfo-include-dir,
+[AS_HELP_STRING([--with-execinfo-include-dir=DIR],
+[directory in which to look for execinfo include files])],
+[ case "$withval" in
+    no)
+        AC_MSG_ERROR([Need execinfo-include-dir])
+       ;;
+    yes)
+       ;;
+    *)
+       execinfo_include_dir="$withval"
+       ;;
+  esac ]
+)
+
+dnl #
+dnl #  Look for execinfo.h and symbols
+dnl #
+smart_try_dir=$execinfo_include_dir
+FR_SMART_CHECK_INCLUDE(execinfo.h)
+if test "x$ac_cv_header_execinfo_h" = "xyes"; then
+  smart_try_dir=$execinfo_lib_dir
+  FR_SMART_CHECK_LIB(execinfo, backtrace_symbols)
+  if test "x$ac_cv_lib_execinfo_backtrace_symbols" != "xyes"; then
+    dnl # Might be provided as part of libc
+    AC_MSG_CHECKING([if execinfo provided as part of libc])
+    AC_TRY_LINK(
+      [
+        #include <execinfo.h>
+      ],
+      [
+        void *sym[1];
+        backtrace_symbols(&sym, sizeof(sym)) ],
+      [
+        AC_MSG_RESULT(yes)
+        ac_cv_lib_execinfo_backtrace_symbols="yes"
+      ],
+      [
+        AC_MSG_RESULT(no)
+      ]
+    )
+  fi
+
+  if test "x$ac_cv_lib_execinfo_backtrace_symbols" = "xyes"; then
+    AC_DEFINE(HAVE_EXECINFO, [1], [define this if we have <execinfo.h> and symbols])
+  fi
+fi
 
 dnl #
 dnl #  Check for regular expression support, if were using PCRE it MUST be included
@@ -1360,11 +1556,12 @@ dnl #  functions.
 dnl #
 dnl extra argument: --with-pcre-lib-dir
 pcre_lib_dir=
-AC_ARG_WITH(rlm-pcre-lib-dir,
-[  --with-pcre-lib-dir=DIR          directory to look for PCRE library files in],
+AC_ARG_WITH(pcre-lib-dir,
+[AS_HELP_STRING([--with-pcre-lib-dir=DIR],
+[directory in which to look for pcre library files])],
 [ case "$withval" in
     no)
-       AC_MSG_ERROR(Need rlm-pcre-lib-dir)
+       AC_MSG_ERROR(Need pcre-lib-dir)
        ;;
     yes)
        ;;
@@ -1376,11 +1573,12 @@ AC_ARG_WITH(rlm-pcre-lib-dir,
 
 dnl extra argument: --with-pcre-include--dir
 pcre_include_dir=
-AC_ARG_WITH(rlm-pcre-include-dir,
-[  --with-pcre-include-dir=DIR      directory to look for PCRE include files in],
+AC_ARG_WITH(pcre-include-dir,
+[AS_HELP_STRING([--with-pcre-include-dir=DIR],
+[directory in which to look for pcre include files])],
 [ case "$withval" in
     no)
-       AC_MSG_ERROR(Need rlm-pcre-include-dir)
+       AC_MSG_ERROR(Need pcre-include-dir)
        ;;
     yes)
        ;;
@@ -1741,7 +1939,6 @@ AC_OUTPUT(\
   ./scripts/cron/radiusd.cron.daily \
   ./scripts/cron/radiusd.cron.monthly \
   ./scripts/cryptpasswd \
-  ./raddb/dictionary \
   ./raddb/radrelay.conf \
   ./raddb/radiusd.conf
 )
index 19ba8cd..4ee4442 100644 (file)
@@ -6,6 +6,7 @@ freeradius-krb5
 freeradius-ldap
 freeradius-mysql
 freeradius-postgresql
+freeradius-rest
 freeradius-utils
 freeradius
 libfreeradius-dev
index 962d7db..08e958e 100644 (file)
@@ -1,3 +1,15 @@
+freeradius (3.0.3+git) unstable; urgency=medium
+
+  * New upstream version.
+
+ -- Alan DeKok <aland@freeradius.org>  Fri, 21 Mar 2014 08:30:00 -0400
+
+freeradius (3.0.2+git) unstable; urgency=medium
+
+  * New upstream version.
+
+ -- Alan DeKok <aland@freeradius.org>  Wed, 15 Jan 2014 21:23:14 -0400
+
 freeradius (3.0.1+git) unstable; urgency=medium
 
   * New upstream version.
index ec63514..7f8f011 100644 (file)
@@ -1 +1 @@
-9
+7
index de9403f..bd3a400 100644 (file)
@@ -1,24 +1,27 @@
 Source: freeradius
-Build-Depends: debhelper (>= 9),
+Build-Depends: debhelper (>= 7.4),
  quilt,
  dpkg-dev (>= 1.13.19),
  autotools-dev,
- libpam0g-dev,
- libmysqlclient-dev,
- libsqlite3-dev,
+ libcurl4-openssl-dev,
  libgdbm-dev,
- libldap2-dev,
- libsasl2-dev,
  libiodbc2-dev,
+ libjson0,
+ libjson0-dev,
  libkrb5-dev,
- libperl-dev,
+ libldap2-dev,
+ libpam0g-dev,
  libpcap-dev,
python-dev,
- libreadline-dev,
libperl-dev,
+ libmysqlclient-dev,
  libpq-dev,
+ libreadline-dev,
+ libsasl2-dev,
+ libsqlite3-dev,
  libssl-dev,
  libtalloc-dev,
- libyubikey-dev
+ libyubikey-dev,
+ python-dev
 Section: net
 Priority: optional
 Maintainer: Josip Rodin <joy-packages@debian.org>
@@ -109,6 +112,13 @@ Description: LDAP module for FreeRADIUS server
  The FreeRADIUS server can use LDAP to authenticate users, and this module
  is necessary for that.
 
+Package: freeradius-rest
+Architecture: any
+Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}
+Description: REST module for FreeRADIUS server
+ The FreeRADIUS server can make calls to remote web APIs, and this module
+ is necessary for that.
+
 Package: freeradius-postgresql
 Architecture: any
 Depends: freeradius (= ${binary:Version}), ${shlibs:Depends}
index 77467e4..2dd87c6 100644 (file)
@@ -9,7 +9,7 @@ It was downloaded from http://www.freeradius.org/
 
 Copyright: 
 
-Copyright (C) 2000-2013 The FreeRADIUS Server Project
+Copyright (C) 2000-2014 The FreeRADIUS Server Project
 Copyright (C) 1997-1999 Cistron Internet Services B.V.
 
 License:
index f2d8911..e8ca770 100644 (file)
@@ -3,13 +3,25 @@
 set -e
 
 case "$1" in
-        remove)
-               ;;
-        purge)
-               rmdir --ignore-fail-on-non-empty /etc/freeradius
-               ;;
-         *)
-               ;;
+       remove)
+               ;;
+       purge)
+               if dpkg-statoverride --list | grep -qw /etc/freeradius/dictionary$; then
+                       dpkg-statoverride --remove /etc/freeradius/dictionary
+               fi
+
+               if dpkg-statoverride --list | grep -qw /etc/freeradius/radiusd.conf$; then
+                       dpkg-statoverride --remove /etc/freeradius/radiusd.conf
+               fi
+
+               if dpkg-statoverride --list | grep -qw /etc/freeradius$; then
+                       dpkg-statoverride --remove /etc/freeradius
+               fi
+
+               rmdir --ignore-fail-on-non-empty /etc/freeradius
+               ;;
+       *)
+               ;;
 esac
 
 #DEBHELPER#
index ebdea6e..1b074d9 100755 (executable)
@@ -4,21 +4,6 @@ set -e
 
 case "$1" in
   configure)
-        for file in `find /etc/freeradius/mods-config/sql/main/mysql/ -print`
-        do
-          if ! dpkg-statoverride --list | grep -qw $file$; then
-            dpkg-statoverride --add --update root freerad 0640 $file
-          fi
-        done
-
-        for dir in /etc/freeradius/mods-config/sql/main \
-          /etc/freeradius/mods-config/sql/main/mysql
-        do
-          if ! dpkg-statoverride --list | grep -qw $dir$; then
-            dpkg-statoverride --add --update root freerad 2751 $dir
-          fi
-        done
-
         if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
           invoke-rc.d freeradius force-reload
         else
index aedc5dd..89022bf 100755 (executable)
@@ -4,21 +4,6 @@ set -e
 
 case "$1" in
   configure)
-        for file in `find /etc/freeradius/mods-config/sql/main/postgresql/ -print` 
-        do
-          if ! dpkg-statoverride --list | grep -qw $file$; then
-            dpkg-statoverride --add --update root freerad 0640 $file
-          fi
-        done
-
-        for dir in /etc/freeradius/mods-config/sql/main \
-          /etc/freeradius/mods-config/sql/main/postgresql
-        do
-          if ! dpkg-statoverride --list | grep -qw $dir$; then
-            dpkg-statoverride --add --update root freerad 2751 $dir
-          fi
-        done
-
         if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
           invoke-rc.d freeradius force-reload
         else
diff --git a/debian/freeradius-rest.install b/debian/freeradius-rest.install
new file mode 100644 (file)
index 0000000..a8582fd
--- /dev/null
@@ -0,0 +1 @@
+usr/lib/freeradius/rlm_rest*.so
diff --git a/debian/freeradius-rest.postinst b/debian/freeradius-rest.postinst
new file mode 100755 (executable)
index 0000000..adfb99d
--- /dev/null
@@ -0,0 +1,22 @@
+#! /bin/sh
+
+set -e
+
+case "$1" in
+  configure)
+        if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
+          invoke-rc.d freeradius force-reload
+        else
+          /etc/init.d/freeradius force-reload
+        fi
+        ;;
+  abort-upgrade)
+        ;;
+  abort-remove)
+        ;;
+  abort-deconfigure)
+        ;;
+esac
+
+#DEBHELPER#
+
index b9d0234..828bd83 100755 (executable)
@@ -22,24 +22,24 @@ set -e
 . /lib/lsb/init-functions
 
 test_freeradius_config() {
-  log_action_begin_msg "Checking $DESCR configuration"
+    log_action_begin_msg "Checking $DESCR configuration"
 
-  out=`$PROGRAM -Cxl stdout $FREERADIUS_OPTIONS`; ret=$?
-  out=`echo "${out}" | tail -n 1 | sed 's/^\s*ERROR:\s*\(.*\)\s*$/\1/'`
-  log_action_end_msg $ret "$out"
-  return $ret
+    out=`$PROGRAM -Cxl stdout $FREERADIUS_OPTIONS`; ret=$?
+    out=`echo "${out}" | tail -n 1 | sed 's/^\s*ERROR:\s*\(.*\)\s*$/\1/'`
+    log_action_end_msg $ret "$out"
+    return $ret
 }
 
 if [ -r /etc/default/$PROG ]; then
-  . /etc/default/$PROG
+    . /etc/default/$PROG
 fi
 
 test -f $PROGRAM || exit 0
 
 # /var/run may be a tmpfs
 if [ ! -d /var/run/freeradius ]; then
-  mkdir -p /var/run/freeradius
-  chown freerad:freerad /var/run/freeradius
+    mkdir -p /var/run/freeradius
+    chown freerad:freerad /var/run/freeradius
 fi
 
 export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
@@ -47,54 +47,59 @@ export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"
 ret=0
 
 case "$1" in
-        start)
-                log_daemon_msg "Starting $DESCR" "$PROG"
+    start)
+        log_daemon_msg "Starting $DESCR" "$PROG"
 
-                start_daemon -p "$PIDFILE" "$PROGRAM" $FREERADIUS_OPTIONS || ret=$?
-                log_end_msg $ret
-                ;;
+        # eval allows quoted arguments (config directories for example) to be passed in $FREERADIUS_OPTIONS
+        eval "start_daemon -p '$PIDFILE' '$PROGRAM' $FREERADIUS_OPTIONS" || ret=$?
+        log_end_msg $ret
+        ;;
 
-        stop)
-                log_daemon_msg "Stopping $DESCR" "$PROG"
+    stop)
+        log_daemon_msg "Stopping $DESCR" "$PROG"
 
-                killproc -p "$PIDFILE" || ret=$?
-                log_end_msg $ret
-                ;;
+        killproc -p "$PIDFILE" || ret=$?
+        log_end_msg $ret
+        ;;
 
-        restart|force-reload)
-                test_freeradius_config || exit $?
+    restart|force-reload)
+        test_freeradius_config || exit $?
 
-                $0 stop
-                $0 start
-                ;;
+        $0 stop
+        $0 start
+        ;;
 
-        reload)
-                test_freeradius_config || exit $?
+    reload)
+        test_freeradius_config || exit $?
 
-                if status_of_proc -p "$PIDFILE" "$PROG" "$DESCR"; then
-                  log_daemon_msg "Reloading $DESCR" "$PROG"
+        if status_of_proc -p "$PIDFILE" "$PROG" "$DESCR"; then
+            log_daemon_msg "Reloading $DESCR" "$PROG"
 
-                  start-stop-daemon --stop --signal HUP --quiet --pidfile $PIDFILE || ret=$?
-                  log_end_msg $ret
-                fi
-                ;;
+            start-stop-daemon --stop --signal HUP --quiet --pidfile $PIDFILE || ret=$?
+            log_end_msg $ret
+        fi
+        ;;
 
-        configtest|testconfig)
-                test_freeradius_config || exit $?
-                ;;
+    configtest|testconfig)
+        test_freeradius_config || exit $?
+        ;;
 
-        debug)
-                $PROGRAM -X $FREERADIUS_OPTIONS
-                ;;
+    debug)
+        $PROGRAM -X $FREERADIUS_OPTIONS
+        ;;
 
-        status)
-                status_of_proc -p "$PIDFILE" "$PROGRAM" "$PROG" && exit 0 || exit $?
-                ;;
+    debug-threaded)
+        $PROGRAM -f -xx -l stdout $FREERADIUS_OPTIONS
+        ;;
 
-        *)
-                echo "Usage: $0 start|stop|restart|force-reload|reload|configtest|debug|status"
-                exit 1
-                ;;
+    status)
+        status_of_proc -p "$PIDFILE" "$PROGRAM" "$PROG" && exit 0 || exit $?
+        ;;
+
+    *)
+        echo "Usage: $0 start|stop|restart|force-reload|reload|configtest|debug|debug-threaded|status"
+        exit 1
+        ;;
 esac
 
 exit 0
index 8e70301..9f2754b 100644 (file)
@@ -1,6 +1,6 @@
 --- a/Make.inc.in
 +++ b/Make.inc.in
-@@ -50,7 +50,7 @@ LDFLAGS              = @LDFLAGS@
+@@ -94,7 +94,7 @@
  
  LOGDIR                = ${logdir}
  RADDBDIR      = ${raddbdir}
@@ -8,10 +8,10 @@
 +RUNDIR                = ${localstatedir}/run/freeradius
  SBINDIR               = ${sbindir}
  RADIR         = ${radacctdir}
- LIBRADIUS     = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la
+ LIBRADIUS     = $(top_builddir)/src/lib/$(LIBPREFIX)freeradius-radius.la $(TALLOC_LIBS)
 --- a/raddb/radiusd.conf.in
 +++ b/raddb/radiusd.conf.in
-@@ -62,7 +62,7 @@ radacctdir = @radacctdir@
+@@ -61,7 +61,7 @@
  
  #
  #  name of the running server.  See also the "-n" command-line option.
@@ -20,7 +20,7 @@
  
  #  Location of config and logfiles.
  confdir = ${raddbdir}
-@@ -463,8 +463,8 @@ security {
+@@ -415,8 +415,8 @@
        #  member.  This can allow for some finer-grained access
        #  controls.
        #
@@ -46,4 +46,3 @@
     if failed host 127.0.0.1 port 1812 type udp protocol radius secret testing123 then alert
     if failed host 127.0.0.1 port 1813 type udp protocol radius secret testing123 then alert
     if cpu > 95% for 2 cycles then alert
-
index c139ee1..fcd8efe 100644 (file)
@@ -1,3 +1,301 @@
+FreeRADIUS 3.0.4 Mon 12 May 2014 15:30:00 EDT urgency=medium
+       Feature improvements
+       * Home server "response_window" can now take fractions of a
+         second.  See proxy.conf.
+       * radmin now supports "show module status", as the counterpart
+         to "set module status"
+       * Added dictionary ericsson.packet.ccore.networks
+       * Add %{tag:} expansion to get the tag value of an attribute.
+       * Report 'application_name' in connections to PostgreSQL servers.
+         FreeRADIUS connections will now appear as
+         'FreeRADIUS <version> - <name>' in pg_stat_activity.
+       * All config item fields are now type checked at compile time
+         to prevent issues similar to #634 occuring again.
+       * Modify pairparsevalue to deal with embedded NULLs better,
+         and use the binary versions of attribute values in rlm_ldap.
+       * "ipaddr" will now use v6 if no v4 address is present.  You should
+         use "ipv4addr" or "ipv6addr" to force v4/v6 addresses.
+       * The above applies to "listen", "home_server", and "client" sections.
+       * "client" sections will allow prefixes as "192.192.0/24".  The old
+         "netmask" is still accepted, but the new format is preferred.
+
+       Bug fixes
+       * make case-insensitive regular expressions work again.
+       * Added tests for the above
+       * A few more talloc parenting issues
+       * Fix delayed proxy reply handling.  Closes #637
+       * Fix OpenSSL initialization order when using
+         RADIUS/TLS.  Fixes #646
+       * Don't double-quote strings in debugging messages
+       * Fix foreach / break.  Fixes #639
+       * Chargeable-User-Identifier should be "octets"
+       * Fix typo in mainconfig.  Fixes #634
+       * More rlm_perl fixes.  Fixes #635
+       * Free OpenSSL memory on clean exit.
+       * Fix <attr>[0] !* ANY - Was removing all instances of <attr>
+       * Fix case where multiple attributes were returned from LHS of
+         mapping, as with rlm_ldap. Fixes #652
+       * Fix corner case in cursor where using fr_cursor_next_by_da
+         after calling fr_cursor_remove may of resulted in a read of
+         uninitialised memory.
+       * Don't SEGV if all connections to a database server go away.
+         Fixes #651.
+       * Fix issue where <attr> -= <value> was not removing tagged
+         instances of <attr> equal to <value> (only untagged).
+       * Fix issue where tag values were not being set on attributes
+         created with unlang/ldap update blocks.
+       * Create rlm_sqlcounter attributes as integer64 types instead
+         of integer types, so large counter values can be specified.
+       * Fix issue where specifying a dynamic client IP addresss using
+         FreeRADIUS-Client-IPv6-Prefix or FreeRADIUS-Client-IP-Prefix
+         may have caused a validation error.
+       * Don't print two "&" for messages about attribute references
+
+FreeRADIUS 3.0.3 Mon 12 May 2014 15:30:00 EDT urgency=medium
+       Feature improvements
+       * Everything now builds with no warnings from the C compiler,
+         clang static analyzer, or cppcheck.
+       * rlm_ldap now supports defining the LDAP attribute name via
+         backticked expansion (i.e. shell command) in
+         RADIUS <-> LDAP mappings.
+       * rlm_ldap now supports older style generic attributes.
+       * dynamic expansions (e.g. "%{expr:1 + 2}" are now parsed
+         when the server starts.  Syntax errors in the strings
+         are caught, and a descriptive error is printed.
+       * Static regular expressions (e.g. /a*b/) are now parsed
+         when the server starts.  Syntax errors in the strings
+         are caught, and a descriptive error is printed.
+       * dynamic expansions are cached after being parsed.  They are
+         no longer re-parsed at run-time for every request.
+       * regular expressions are now parsed and cached when the server
+         starts.
+       * Added the %{rest:} expansion to rlm_rest, which will send
+         a GET request to the URL passed as the format string.
+         Any body text will be written to the expansion buffer.
+       * rlm_rest now available as a debian package.
+       * When an 'if' condition statically evaluates to true/false,
+         unlang does more static optimization.  For examples, see
+         src/tests/keywords/if-skip
+       * All modules are marked as safe for '-C', which lets the
+         dynamic expansion checks work in more situations.
+       * Added 'none' and 'custom' rlm_rest body types. 'custom'
+         allows sending of arbitrary expanded text and content-type
+         headers.
+       * Added "config" section to Perl.  See mods-available/perl
+       * Added '%v' which expands to the server version - Patch
+         from Alan Buxey.
+       * more mis-matched casts are caught in "if" conditions,
+         and descriptive errors are printed.
+       * Support basic response validation in radclient. This allows
+         administrators to write local test cases for their
+         site-specific configurations.
+       * Removed radconf2xml and radmin "show client config" and
+         "show home_server config".
+       * Forbid running with vulnerable versions of OpenSSL.
+         See "allow_vulnerable_openssl" in the "security"
+         subsection of "radiusd.conf"
+       * Catch underlying "heartbleed" problem, so that nothing bad
+         happens even when using a vulnerable version of OpenSSL.
+       * Add locking API for sql_null, linelog, and detail modules,
+         which should improve performance and work around issues
+         on platforms with bad file locking.
+       * Allow DHCP NAKs to be delayed, via setting
+         reply:FreeRADIUS-Response-Delay = 1
+       * Allow tag and array references anywhere attributes
+         are allowed in "unlang".
+       * many enhancements to radsniff, including output
+         to collectd, ipv6 support and packet loss statistics.
+       * Many dictionary updates (ZTE, Brocade, Motorola).
+       * rlm_yubikey now automatically splits passwords from OTP
+         strings.
+       * The detail file reader is now threaded by default.
+         This should improve performance reading the files.
+
+       Bug fixes
+       * Fix xlat expression %{attribute[n]} so that it actually
+         returns the n'th attribute instead of the first one.
+       * Don't parse string on RHS of update {} when using unary
+         operators (!*).  The RHS should always be ignored.
+       * Check for more optional functions in json-c so we can
+         Build with libjson0, which is the name of the json-c package
+         on debian/ubuntu.
+       * Fix issue in radmin where the main dictionaries would
+         not be loaded which, depending on the configuration, may
+         have caused validation errors.
+       * Fix handling of "%{reply:3GPP-*}"
+       * Fix rlm_perl garbage attributes
+       * Fix oracle SQL queries, which amongst other things still
+         used the old expansion format, which is no longer
+         supported/parsed.
+       * Truncate long format strings and error markers instead of
+         omitting them.
+       * Fix multiple attribute parsing in rlm_rest JSON.
+       * Don't crash in rlm_rest if connect_uri is commented out
+         in the configuration.
+       * Don't double-escape strings to / from Perl.  You may need
+         to double-check your Perl scripts if they use "\" characters.
+         See mods-available/perl for documentation.
+       * Don't re-run "authorize" if a home server fails to respond.
+       * Don't append "0x" to hex output of octets types, for xlat
+         expansions.  This is the same as v2, and makes it easier
+         to concatenate multiple attributes of type "octets"
+       * FreeBSD fixes for execinfo linking.
+       * Make some of the module configurations more consistent.
+       * Fix corner cases where STDOUT wouldn't be closed in
+         daemon mode.
+       * Re-enable "update coa" and originating CoA requests.
+       * Prevent multiple threads writing to the sql query logs.
+       * Fix zombie period calculation.  Closes #579
+       * Properly parent VPs for talloc, when moving them in map2request.
+       * Various fixes for talloc parent / child relationships
+       * Allow rlm_counter to support VSAs.
+       * Normalize return codes for many modules. "do nothing" is noop,
+         not "ok".
+       * Run Post-Proxy-Type Fail.  Closes #576
+       * Fix DHCP destination port for replies to relays.  Closes #591
+       * Do-Not-Respond policy works again  Closes #593
+       * Proxy-To-Virtual-Server works again.  Closes #596
+       * Build fixes for ancient systems.  Closes #607, #608, #609.
+       * %{Module-Return-Code} works again.  Closes #610.
+       * Don't increment statistics for Status-Server responses.
+         Closes #612.
+       * A duplicate request isn't a duplicate if the original one
+         is marked "done".  This should lower retransmissions from
+         clients.
+       * Fix multiple regular expression and glob memory leaks.
+       * Don't allocate any memory in fr_fault() as it can cause malloc
+         to deadlock.
+       * Temporarily set dumpable flag before calling system in fr_fault()
+         else the debugger may not be able to attach.
+       * Set nonblock on all TCP client sockets.
+       * Fix minor buffer overrun in mschapv2 where some attribute strings
+         were not correctly \0 terminated.
+       * Fix crash on authentication failure with MIT kerberos.
+       * Fix code so that octal escape sequences aren't prematurely unescaped
+         in rlm_sql, radclient, preprocess, and other places. This may
+         require configuration changes, as these sequences will no longer
+         need double escaping (\\) of the backslash.
+       * The connection pools no longer have one connection used twice
+         in certain rare conditions.
+       * Use self pipes for internal signals.  The code was there, but was
+         unused.
+       * Don't crash if there are outstanding EAP sessions and were told to
+         exit gracefully.
+       * Fix typo in dictionary.rfc4072
+
+FreeRADIUS 3.0.2 Fri 21 Mar 2014 08:30:00 EDT urgency=medium
+       Feature improvements
+       * secret keys and LDAP / SQL passwords are now printed as
+         '<<< secret >>>' in debugging mode.  Use -Xx to see the
+         actual passwords.
+       * Print out more information about passwords in -Xx,
+         including hashes, comparisons, etc.
+       * Allow cast (and implicit conversion) of integers to IPv4 addresses
+       * More xlats allow attribute references.  This means they can
+         operate on binary data.  e.g. expr, base64, md5, sha1.
+       * Added more tests.
+       * The dictionaries are now auto-loaded.  raddb/dictionary
+         should no longer have $INCLUDE ${prefix}/share/dictionary
+       * A "panic_action" can be set to have the server dump a gdb
+         log on SEGV or other fatal error.  See radiusd.conf
+       * Add support for SHA-224, SHA-256, SHA-384, SHA-512 to rlm_pap.
+       * Add "%{sha256:}" and "%{sha512:}" xlat functions.
+       * Cache CUI in EAP session resumption.
+       * templates can now have sub-sections, which will be included
+         in the section referencing the template.
+       * Update more dictionaries.
+       * Added more instances of the "always" module, for all return
+         codes.
+       * Suppress broken NASes when proxying.  Retransmits which occur
+         more than once per second are rate-limited to once per second.
+       * Allow '&' in more xlat expansions.
+       * Update PostgreSQL schema and queries to record last updated
+         time, and accounting interim.
+       * Optimize more "if" conditions when the server loads.  This will
+         avoid work at run time.  e.g. ("foo" == "bar") --> FALSE.
+       * Allow removal of all attributes within a list with !* operator.
+       * Allow list to list copies with request qualifiers (outer.).
+       * Add support for ipv4 prefixes and ipv6 addresses and prefixes to
+         %{integer:}.
+       * allow radmin command "set module status <module> <code>"
+         which can be used to forcibly enable/disable modules.
+       * pap module now assumes Cleartext-Password if Password-With-Header
+         doesn't have a {...} header.
+       * Added "unpack" module.  It can unpack binary data from horrible
+         VSA formats.  See raddb/mods-available/unpack
+       * Added example IP Pool for DHCP, using sqlite.  From Matthew Newton
+         See raddb/mods-config/sql/ippool-dhcp/
+
+       Bug fixes
+       * Fix SQL groups.
+       * Fix operation of fr_strerror() with RE*() macros.
+       * Don't assert if the connection we're trying to reconnect
+         is not in_use.
+       * Fix %{mschap:User-Name} xlat.
+       * Allow comparisons of signed integers and of ethernet addresses.
+       * Fix parsing of text-based ascend binary filters.
+       * Fix a few minor Coverity and clang analyzer issues.
+       * Log WARNING and ERROR prefixes only once, not twice.
+       * Fix attribute truncation seen in Perl and other places.
+       * Use correct port when DHCP relaying.
+       * Fix behaviour on FreeBSD where sending packets from an interface
+         bound to an IP address would fail when the server was built with
+         udpfromto.
+       * Don't abort() when freeing home servers on exit.
+       * Fix edge case in pairmove() when some attributes could be over-
+         written.
+       * Do checks for individual sqlite v2 functions so rlm_sqlite builds
+         correctly with more versions of the library.
+       * In heimdal kerberos, create MEMORY ccaches on a per context basis.
+         This prevents issues with the root ccache being used.
+       * Fix corner case with proxying, where home server goes down.
+       * Rate-limit "max_requests" complaint.  We don't want to fill the
+         logs when something goes wrong.
+       * Use /dev/urandom for raddb/certs/random, if it exists.
+       * Issue WARNING that old-style clients should no longer be used.
+       * Auto-set secret to "radsec" for tcp+tls home servers.
+       * Fix double free in home_server_add when there is a parse error
+         on startup.
+       * rlm_unix checks if the dictionaries are broken, instead of crashing
+       * Fix potential memory corruption when normalising salted password
+         hashes from hex, where the combined hash and salt was > 64 bytes.
+       * Register sqlcounter attributes correctly, and other issues with it
+       * treat 127.0.0.1/32 as being identical to 127.0.0.1
+       * Don't mangle error output of SQL drivers like PostgreSQL
+       * Fix usage of "tls = ${tls}".  It could previously cause problems
+         when the reference was used multiple times.
+       * Fix TLS session leak for incoming sockets.
+       * Try harder to clean up memory on exit when using "-mM"
+       * Fix memory leak when home server is down for RadSec connections
+       * rate-limit outgoing connection attempts when the home server
+         is down.  It will retry no more than once per second.
+       * When parsing ipv6 address prefixes, always mask off the host
+         portion.
+       * Fix rlm_counter so that it does not create two reply
+         attributes.
+       * Fix issues with DHCP Sub-TLVs where the value of the first
+         Sub-TLV would appear corrupted, and subsequent TLVs would
+         not appear in debug output.
+       * Initialize scope in IP address parsing
+       * Prevent vendor attributes and RFC space attributes from clashing
+         in rlm_attr_filter.
+       * Set source IP address for DHCP packets from DHCP-Server-IP-Address,
+         or DHCP-DHCP-Server-Identifier, if we're unable to otherwise
+         determine the source IP.
+       * Fix POST attribute parsing in rlm_rest.
+       * Fix JSON attribute parsing in rlm_rest.
+       * Don't append trailing & to POST options in rlm_rest (minor).
+       * Process HTTP 100 Continue messages correctly in rlm_rest
+       * Fix generation of long > 512 byte POST payloads, where attribute
+         values on the chunk boundary may have been omitted in rlm_rest.
+       * Remove duplicate escape sequence parsing in rlm_sqlippool and
+         rlm_sqlcounter which caused issues with escaping %. Escape
+         sequence parsing is now handled purely by the xlat functions.
+       * Ensure %% is treated as a string literal, and so not passed to any
+         xlat escape functions for processing.
+       * Correct calculation of Message-Authenticator
+         for CoA packets.  Closes #556
+
 FreeRADIUS 3.0.1 Mon 13 Jan 2014 14:30:00 EDT urgency=medium
        Feature improvements
        * Add "timeout" to exec, and "ntlm_auth_timeout" to mschap.
index 703898e..1af597a 100644 (file)
@@ -24,7 +24,8 @@ be handled.  Common realm formats are:
   realm\username
 
 The realm parsing syntax ( and search order ) is user definable via the
-realm module config in the ``/etc/raddb/radiusd.conf`` configuration file.
+realm module config in the ``/etc/raddb/mods-available/realm`` configuration
+file.
 
 You can define multiple instances of the realm module to support multiple
 realm syntax's at the same time.  Be sure to pay close attention to the
@@ -44,7 +45,7 @@ The realm ``NULL`` matches any requests WITHOUT a realm.
 If you set the remote server to ``LOCAL``, the request will be handled
 locally as usual, without sending it to a remote radius server.
 
-There are several options you can add in both files:
+There are several options you can add in ``/etc/raddb/proxy.conf``:
 
 - nostrip:
   By default the realm is stripped from the username before sending it
index c795931..4a2f28c 100644 (file)
@@ -138,6 +138,8 @@ and their new equivalents.
 |%V         |Request-Authenticator      |                       |
 |           |(Verified/None)            |                       |
 +-----------+---------------------------+-----------------------+
+|%v         |Server Version             |                       |
++-----------+---------------------------+-----------------------+
 |%Y         |request year (YYYY)        |                       |
 +-----------+---------------------------+-----------------------+
 |%Z         |All request attributes     |                       |
diff --git a/doc/developer/DIFFS.rst b/doc/developer/DIFFS.rst
deleted file mode 100644 (file)
index 18bda0a..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-Submitting patches or diff's to the FreeRADIUS project
-======================================================
-
-For a person or company wishing to submit a change to the
-FreeRADIUS project, the process can sometimes be daunting if you're
-not familiar with "the system." This text is a collection of
-suggestions which can greatly increase the chances of your change
-being accepted.
-
-Only trivial patches will be accepted via email.
-Large patches, or patches that modify a number of files MUST be
-submitted as a pull-request via GitHub.
-
-See the following for more details:
-       - https://help.github.com/articles/fork-a-repo
-       - http://wiki.freeradius.org/contributing/GitHub
-
-
-Submitting patches via email
-----------------------------
-
-   1. "diff -u"
-
-      Use "diff -u" or "diff -urN" to create patches.
-
-      All changes to the source occur in the form of patches, as
-      generated by diff(1).  When creating your patch, make sure to
-      create it in "unified diff" format, as supplied by the '-u'
-      argument to diff(1). Patches should be based in the root source
-      directory, not in any lower subdirectory.
-
-      To create a patch for a single file, it is often sufficient to do:
-
-           SRCTREE=/home/user/src/freeradiusd/
-           MYFILE=src/modules/rlm_foo/foo.c
-
-           cd $SRCTREE
-           cp $MYFILE $MYFILE.orig
-           vi $MYFILE # make your change
-           diff -u $MYFILE.orig $MYFILE > /tmp/patch
-
-      To create a patch for multiple files, you should unpack a
-      "vanilla", or unmodified source tree, and generate a diff
-      against your own source tree. For example:
-
-           MYSRC=/home/user/src/freeradiusd-feature/
-
-          gunzip freeradiusd-version.tar.gz
-           tar xvf freeradiusd-version.tar
-           diff -urN freeradiusd-version $MYSRC > ~/feature-version.patch
-
-   2. Describe your changes.
-
-      Describe the technical detail of the change(s) your patch includes.
-
-      Be as specific as possible. The WORST descriptions possible
-      include things like "update file X", "bug fix for file X",
-      or "this patch includes updates for subsystem X. Please apply."
-
-      If your description starts to get long, that's a sign that you
-      probably need to split up your patch. See #3, next.
-
-   3. Separate your changes.
-
-      Separate each logical change into its own patch.
-
-      For example, if your changes include both bug fixes and
-      performance enhancements for a single module, separate those
-      changes into two or more patches.
-
-      On the other hand, if you make a single change to numerous
-      files, group those changes into a single patch. Thus a single
-      LOGICAL change is contained within a single patch.
-
-      If one patch depends on another patch in order for a change to
-      be complete, that is OK. Simply note "this patch depends on
-      patch X" in your patch description.
-
-   4. Select e-mail destination.
-
-      If you are on the developers mailing list, send the patch there.
-      freeradius-devel@lists.freeradius.org
-
-      Otherwise, send the patch to 'patches@freeradius.org'
-
-   5. No MIME, no links, no compression, no attachments. Just plain text.
-
-      The developers need to be able to read and comment on the
-      changes you are submitting. It is important for a developer to
-      be able to "quote" your changes, using standard e-mail tools, so
-      that they may comment on specific portions of your code.
-
-      For this reason, all patches should be submitting e-mail
-      "inline".
-
-      Do not attach the patch as a MIME attachment, compressed or
-      not. Many popular e-mail applications will not always transmit a
-      MIME attachment as plain text, making it impossible to comment
-      on your code. A MIME attachment also takes a bit more time to
-      process, decreasing the likelihood of your MIME-attached change
-      being accepted.
-
-      Compressed patches are generally rejected outright.  If the
-      developer has to do additional work to read your patch, the odds
-      are that it will be ignored completely.
-
-   6. E-mail size.
-
-      When sending patches, always follow step #5.
-
-      Large changes are not appropriate for mailing lists, and some
-      maintainers. If your patch, exceeds 40Kb in size, it is
-      preferred that you store your patch on an Internet-accessible
-      server, and provide instead a URL (link) pointing to your patch.
-
-   7. Name the version of the server.
-
-      It is important to note, either in the subject line or in the
-      patch description, the server version to which this patch
-      applies.
-
-   8. Don't get discouraged. Re-submit.
-
-      After you have submitted your change, be patient and wait. If
-      the patch is approved and applied, it will appear in the next
-      version of the server.
-
-      However, if your change doesn't appear in the next version of
-      the server, there could be any number of reasons. It's YOUR job
-      to narrow down those reasons, correct what was wrong, and submit
-      your updated change.
-
-      It is quite common a patch to be "dropped" without
-      comment. That's the nature of the system. If your patch is
-      dropped, it could be due to
-
-           A style issue (see section 2, below),
-           An e-mail formatting issue (see section item 5, above)
-           A technical problem with your change
-           Your patch got lost among other patches
-
-      When in doubt, re-submit.
diff --git a/doc/developer/contributing.rst b/doc/developer/contributing.rst
new file mode 100644 (file)
index 0000000..0b792e0
--- /dev/null
@@ -0,0 +1,112 @@
+Submitting patches or diff's to the FreeRADIUS project
+======================================================
+
+For a person or company wishing to submit a change to the FreeRADIUS project the process can sometimes be daunting if 
+you're not familiar with "the system." This text is a collection of suggestions which can greatly increase the chances 
+of your change being accepted.
+
+Note: Only trivial patches will be accepted via email. Large patches, or patches that modify a number of files MUST be
+submitted as a pull-request via GitHub.
+
+Hints and tips
+--------------
+
+1. Describe your changes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Describe the technical detail of the change(s) your patch or commit includes.
+
+Be as specific as possible. The WORST descriptions possible include things like "update file X", "bug fix for file X",
+or "this patch includes updates for subsystem X. Please apply."
+
+If your description starts to get long, that's a sign that you probably need to split up your commit. See #3, next.
+
+2. Separate your changes
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Separate each logical change into its own commit.
+
+For example, if your changes include both bug fixes and performance enhancements for a single module, separate those
+changes into two or more patches.
+
+On the other hand, if you make a single change to numerous files, group those changes into a single commit. 
+Thus a single LOGICAL change is contained within a single commit.
+
+If one commit depends on another commit in order for a change to be complete, that is OK. Simply note "this commit
+depends on commit X" in the extended commit description.
+
+If your commit includes significant whitespace changes these should also be broken out into another, separate, commit.
+
+Submitting patches via GitHub
+-----------------------------
+
+See the following links for more details about submitting via github:
+
+- https://help.github.com/articles/fork-a-repo
+- http://wiki.freeradius.org/contributing/GitHub
+
+Submitting patches via email
+----------------------------
+
+1. "diff -u"
+~~~~~~~~~~~~
+Use ``diff -u`` or ``diff -urN`` to create patches.
+
+All changes to the source occur in the form of patches, as generated by diff(1).  When creating your patch, make sure to
+create it in "unified diff" format, as supplied by the '-u' argument to diff(1). Patches should be based in the root 
+source directory, not in any lower subdirectory.
+
+To create a patch for a single file, it is often sufficient to do::
+
+   SRCTREE=/home/user/src/freeradiusd/
+   MYFILE=src/modules/rlm_foo/foo.c
+
+   cd $SRCTREE
+   cp $MYFILE $MYFILE.orig
+   vi $MYFILE # make your change
+   diff -u $MYFILE.orig $MYFILE > /tmp/patch
+
+To create a patch for multiple files, you should unpack a "vanilla", or unmodified source tree, and generate a diff
+against your own source tree. For example::
+
+   MYSRC=/home/user/src/freeradiusd-feature/
+
+   gunzip freeradiusd-version.tar.gz
+   tar xvf freeradiusd-version.tar
+   diff -urN freeradiusd-version $MYSRC > ~/feature-version.patch
+
+
+2. Select e-mail destination
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+If you are on the developers mailing list, send the patch there. freeradius-devel@lists.freeradius.org
+
+Otherwise, send the patch to 'patches@freeradius.org'
+
+3. No MIME, no links, no compression, no attachments. Just plain text
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The developers need to be able to read and comment on the changes you are submitting. It is important for a developer 
+to be able to "quote" your changes, using standard e-mail tools, so that they may comment on specific portions of your 
+code.
+
+For this reason, all patches should be submitting e-mail "inline".
+
+Do not attach the patch as a MIME attachment, compressed or not. Many popular e-mail applications will not always 
+transmit a MIME attachment as plain text, making it impossible to comment on your code. A MIME attachment also takes 
+a bit more time to process, decreasing the likelihood of your MIME-attached change being accepted.
+
+Compressed patches are generally rejected outright.  If the developer has to do additional work to read your patch, 
+the odds are that it will be ignored completely.
+
+4. E-mail size
+~~~~~~~~~~~~~~
+
+Large changes are not appropriate for mailing lists, and some maintainers. If your patch, exceeds 5Kb in size, you
+must submit the patch via GitHub instead.
+
+5. Name the version of the server
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+It is important to note, either in the subject line or in the patch description, the server version to which this patch
+applies.
index f147171..b25be66 100644 (file)
@@ -981,7 +981,7 @@ An example is listed below::
     #this is the basedn to do searches on a user
     basedn = ou=users,ou=radius,dc=mydomain,dc=com
     #notice the username is the stripped user-name or user-name
-    filter = (uid=%{Stripped-User-Name:-{User-Name}})
+    filter = (uid=%{%{Stripped-User-Name}:-%{User-Name}})
     start_tls = no
     tls_mode = no
     #this maps ldap attributetypes to radius attributes
@@ -1000,7 +1000,7 @@ An example is listed below::
     #with --with-edir option.
     #edir_account_policy_check=no
     groupname_attribute = radiusGroupName
-    groupmembership_filter = (&(uid=%{Stripped-User-Name:-%{User-Name}})
+    groupmembership_filter = (&(uid=%{%{Stripped-User-Name}:-%{User-Name}})
     (objectclass=radiusprofile))
     groupmembership_attribute = radiusGroupName
     timeout = 3
@@ -1517,7 +1517,7 @@ edit radiusd.conf::
             identity = "uid=freeradius,ou=admins,ou=radius,dc=mydomain,dc=com"
             password = example
             basedn = "ou=users,ou=radius,dc=mydomain,dc=com"
-            filter = "(&(uid=%{Stripped-User-Name:-%{User-Name}})
+            filter = "(&(uid=%{%{Stripped-User-Name}:-%{User-Name}})
     (objectclass=radiusprofile)"
             start_tls = no
             tls_mode = no
@@ -1530,7 +1530,7 @@ edit radiusd.conf::
             #password_header = "{clear}"
             password_attribute = userPassword
             groupname_attribute = radiusGroupName
-            groupmembership_filter = "(&(uid=%{Stripped-User-Name:-%{User-Name}}))
+            groupmembership_filter = "(&(uid=%{%{Stripped-User-Name}:-%{User-Name}}))
     (objectclass=radiusProfile)"
             groupmembership_attribute = radiusGroupName
             timeout = 3
index a60349b..eda5c4c 100644 (file)
@@ -3,7 +3,7 @@
 This release adds support for Microsoft Statement-of-Health (SoH), which is
 a form of network access protection.
 
-Client supprot is present in Windows XP SP3, Vista and 7.
+Client support is present in Windows XP SP3, Vista and 7.
 
 SoH data can come in from several places:
 
index 4b9fb8e..d8830cc 100644 (file)
@@ -303,22 +303,6 @@ INLINE_SIMPLE_STRUCTS  = YES
 
 TYPEDEF_HIDES_STRUCT   = YES
 
-# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
-# determine which symbols to keep in memory and which to flush to disk.
-# When the cache is full, less often used symbols will be written to disk.
-# For small to medium size projects (<1000 input files) the default value is
-# probably good enough. For larger projects a too small cache size can cause
-# doxygen to be busy swapping symbols to and from disk most of the time
-# causing a significant performance penalty.
-# If the system has enough physical memory increasing the cache will improve the
-# performance by keeping more symbols in memory. Note that the value works on
-# a logarithmic scale so increasing the size by one will roughly double the
-# memory usage. The cache size is given by this formula:
-# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
-# corresponding to a cache size of 2^16 = 65536 symbols
-
-SYMBOL_CACHE_SIZE      = 4
-
 #---------------------------------------------------------------------------
 # Build related configuration options
 #---------------------------------------------------------------------------
@@ -522,12 +506,6 @@ MAX_INITIALIZER_LINES  = 30
 
 SHOW_USED_FILES        = YES
 
-# If the sources in your project are distributed over multiple directories
-# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
-# in the documentation. The default is NO.
-
-SHOW_DIRECTORIES       = YES
-
 # Set the SHOW_FILES tag to NO to disable the generation of the Files page.
 # This will remove the Files entry from the Quick Index and from the
 # Folder Tree View (if specified). The default is YES.
@@ -910,12 +888,6 @@ HTML_COLORSTYLE_GAMMA  = 80
 
 HTML_TIMESTAMP         = YES
 
-# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
-# files or namespaces will be aligned in HTML using tables. If set to
-# NO a bullet list will be used.
-
-HTML_ALIGN_MEMBERS     = YES
-
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
 # page has loaded. For this to work a browser that supports
@@ -1098,11 +1070,6 @@ ENUM_VALUES_PER_LINE   = 1
 
 GENERATE_TREEVIEW      = NO
 
-# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
-# and Class Hierarchy pages using a tree view instead of an ordered list.
-
-USE_INLINE_TREES       = YES
-
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
 # used to set the initial width (in pixels) of the frame in which the tree
 # is shown.
@@ -1723,7 +1690,7 @@ MSCFILE_DIRS           =
 # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
 # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
 
-DOT_GRAPH_MAX_NODES    = 50
+DOT_GRAPH_MAX_NODES    = 100
 
 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
 # graphs generated by dot. A depth value of 3 means that only nodes reachable
@@ -1733,7 +1700,7 @@ DOT_GRAPH_MAX_NODES    = 50
 # code bases. Also note that the size of a graph can be further restricted by
 # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
 
-MAX_DOT_GRAPH_DEPTH    = 0
+MAX_DOT_GRAPH_DEPTH    = 4
 
 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
 # background. This is disabled by default, because dot on Windows does not
index 894699a..35343f7 100644 (file)
@@ -1,14 +1,16 @@
-.TH RADCLIENT 1 "2 April 2009" "" "FreeRADIUS Daemon"
+.TH RADCLIENT 1 "28 March 2014" "" "FreeRADIUS Daemon"
 .SH NAME
 radclient - send packets to a RADIUS server, show reply
 .SH SYNOPSIS
 .B radclient
 .RB [ \-4 ]
 .RB [ \-6 ]
-.RB [ \-d
-.IR raddb_directory ]
 .RB [ \-c
 .IR count ]
+.RB [ \-d
+.IR raddb_directory ]
+.RB [ \-D
+.IR dictionary_directory ]
 .RB [ \-f
 .IR file ]
 .RB [ \-F ]
@@ -53,14 +55,21 @@ Use IPv6
 .IP \-c\ \fIcount\fP
 Send each packet \fIcount\fP times.
 .IP \-d\ \fIraddb_directory\fP
-The directory that contains the RADIUS dictionary files. Defaults to
+The directory that contains the user dictionary file. Defaults to
 \fI/etc/raddb\fP.
-.IP \-f\ \fIfile\fP
+.IP \-D\ \fIdictionary_directory\fP
+The directory that contains the main dictionary file. Defaults to
+\fI/usr/share/freeradius\fP.
+.IP \-f\ \fIfile[:file]\fP
 File to read the attribute/value pairs from. If this is not specified,
 they are read from stdin.  This option can be specified multiple
 times, in which case packets are sent in order by file, and within
 each file, by first packet to last packet.  A blank line separates
-logical packets within a file.
+logical packets within a file.  If a pair of files separated by a
+colon is specified, the second file will be used to filter the
+responses to requests from the first. The number of requests and
+filters must be the same.  A summary of filter results will be displayed
+if -s is passed.
 .IP \-F
 Print the file name, packet number and reply code.
 .IP \-h
index 0366394..bfee503 100644 (file)
@@ -35,13 +35,13 @@ The default is 'no'.  If you need to parse an old style Cistron
 file, set this option to 'cistron'.
 .IP key
 This option lets you set the attribute to use as a key to find
-entries.  The default is "%{Stripped-User-Name:-%{User-Name}}".  Note
+entries.  The default is "%{%{Stripped-User-Name}:-%{User-Name}}".  Note
 that the key MUST supply real data.  Dynamic attributes like "Group"
 will not work, because the "Group" attribute can only be used as a
 comparison, to see if a user is in a Unix group.  It will not return
 the name of the Unix group that a user is in.
 .PP
-If you want to use groups as a key, see the \fIrlm_passed\fP, which
+If you want to use groups as a key, see the \fIrlm_passwd\fP, which
 will create a real attribute that contains the group name.
 .PP
 This configuration entry enables you to have configurations that
@@ -67,7 +67,7 @@ modules {
 .br
     compat = no
 .br
-    key = %{Stripped-User-Name:-%{User-Name}}
+    key = %{%{Stripped-User-Name}:-%{User-Name}}
 .br
   }
 .br
index ccf3321..3e3167b 100644 (file)
@@ -10,7 +10,7 @@
 .RE
 .sp
 ..
-.TH rlm_pap 5 "6 June 2008" "" "FreeRADIUS Module"
+.TH rlm_pap 5 "17 April 2014" "" "FreeRADIUS Module"
 .SH NAME
 rlm_pap \- FreeRADIUS Module
 .SH DESCRIPTION
@@ -29,14 +29,14 @@ from a database.
 .SH CONFIGURATION
 .PP
 The only relevant configuration item is:
-.IP auto_header
-If set to "yes", the module will look inside of the User-Password
-attribute for the headers {crypt}, {clear}, etc., and will
-automatically create the appropriate attribute, with the correct
-value.
+.IP normify
+The default is "yes".  This means that the module will try to convert
+hex passwords and base64-encoded passwords to "normalized" form.
+However, some clear text passwords may be erroneously converted.
+Setting this to "no" prevents that conversion.
 .PP
-This module understands many kinds of password hashing methods, as
-given by the following table.
+The module looks for the Password-With-Header attribute to find the
+"known good password.  The header is given by the following table.
 .PP
 .DS
 .br
@@ -70,6 +70,9 @@ formats.  It will automatically handle Base-64 encoded data, hex
 strings, and binary data, and convert them to a format that the server
 can use.
 .PP
+If there is no Password-With-Header attribute, the module looks for
+Cleartext-Password, NT-Password, Crypt-Password, etc.
+.PP
 It is important to understand the difference between the User-Password
 and Cleartext-Password attributes.  The Cleartext-Password attribute
 is the "known good" password for the user.  Simply supplying the
index 9025c41..3fb0cac 100644 (file)
@@ -10,7 +10,7 @@
 .RE
 .sp
 ..
-.TH unlang 5 "16 July 2013" "" "FreeRADIUS Processing un-language"
+.TH unlang 5 "15 April 2014" "" "FreeRADIUS Processing un-language"
 .SH NAME
 unlang \- FreeRADIUS Processing un\-language
 .SH DESCRIPTION
@@ -93,18 +93,14 @@ returned false, and if the specified condition evaluates to true.
 .DE
 .IP foreach
 .br
-Loops over a named variable, running the block for each copy of the
-named variable.  The return value of the block is the return value of
-the last statement executed.  The loop can be exited early by using
-the "break" keyword.  Unlike other languages, "break" here means "exit
-the loop at the next iteration", not "exit the loop now".  The result
-is that any statements after the "break" keyword will still be
-executed.  We recommend using "break" only when it is the last
-statement in a "foreach" block.
-
-The attribute name is just the name, e.g. reply:Reply-Message, with
-none of the usual variable referenced %{...}.  This is because it is a
-reference to the attribute, and not an expansion of the attribute.
+Loops over values of an attribute, running the block for each value.
+The return value of the block is the return value of the last
+statement executed.  The loop can be exited early by using the "break"
+keyword.  Unlike other languages, "break" here means "exit the loop at
+the next iteration", not "exit the loop now".  The result is that any
+statements after the "break" keyword will still be executed.  We
+recommend using "break" only when it is the last statement in a
+"foreach" block.
 
 Inside of the "foreach" block, the attribute which is being looped
 over can be referenced as "Foreach-Variable-#".  Where "#" is the
@@ -113,7 +109,7 @@ loops can be nested up to eight (8) deep, though this is not
 recommended.
 
 .DS
-       foreach Attribute-Name {
+       foreach &Attribute-Reference {
 .br
                ...
 .br
@@ -121,15 +117,25 @@ recommended.
 .DE
 .IP switch
 .br
-Evaluate the given string, and choose the first matching "case"
-statement inside of the current block.  If the string is surrounded by
-double quotes, it is expanded as described in the DATA TYPES section,
-below.
+A "switch" statement takes one argument, and contains a series of
+"case" statements.  When a "switch" statement is encountered, the
+argument from the "switch" is evaluated in turn against the argument
+from each "case" statement.  The first "case" statement which matches
+is executed.  All other "case" statements are ignored.  A default
+"case" statement can be defined, by omitting its argument.
+
+If the argument is a double quoted string (e.g. "%{exec:1 + 2}", it is
+expanded as described in the DATA TYPES section, below.  The match is
+then performed on the string returned from the expansion.  If the
+argument is an attribute reference (e.g. &User-Name), then the match
+is performed on the value of that attribute.  Otherwise, the argument
+is taken to be a literal string, and and matching is done via simple
+comparison.
 
 No statement other than "case" can appear in a "switch" block.
 
 .DS
-       switch "string" {
+       switch <argument> {
 .br
                ...
 .br
@@ -137,23 +143,30 @@ No statement other than "case" can appear in a "switch" block.
 .DE
 .IP case
 .br
-Define a static string to match a parent "switch" statement.  The
-strings given here are not expanded as is done with the parent
-"switch" statement.
+Provides a place-holder which matches the argument of a parent
+"switch" statment.
 
 A "case" statement cannot appear outside of a "switch" block.
 
+If the argument is a double quoted string (e.g. "%{exec:1 + 2}", it is
+expanded as described in the DATA TYPES section, below.  The match is
+then performed on the string returned from the expansion.  If the
+argument is an attribute reference (e.g. &User-Name), then the match
+is performed on the value of that attribute.  Otherwise, the argument
+is taken to be a literal string, and and matching is done via simple
+comparison.
+
 .DS
-       case string {
+       case <argument> {
 .br
                ...
 .br
        }
 .DE
 
-A default entry can be defined by omitting the static string.  This
-entry will be used if no other "case" entry matches.  Only one default
-entry can exist in a "switch" section.
+A default entry can be defined by omitting the argument, as given
+below.  This entry will be used if no other "case" entry matches.
+Only one default entry can exist in a "switch" section.
 
 .DS
        case {
@@ -170,7 +183,7 @@ the current block.
 .DS
        update <list> {
 .br
-               attribute = value
+               Attribute-Reference = value
 .br
                ...
 .br
@@ -204,7 +217,7 @@ See raddb/sites-available/originate-coa for additional information.
 
 The only contents permitted in an "update" section are attributes and
 values.  The contents of the "update" section are described in the
-ATTRIBUTES section below.
+ATTRIBUTE REFERENCE and ATTRIBUTE ASSIGNMENT sections below.
 .IP redundant
 This section contains a simple list of modules.  The first module is
 called when the section is being processed.  If the first module
@@ -280,6 +293,62 @@ or update an attribute list.
 .br
        }
 .DE
+.SH ATTRIBUTE REFERENCES
+
+Attributes may be referenced via the following syntax:
+.DS
+       Attribute-Name
+       Attribute-Name:TAG
+       Attribute-Name[NUM]
+       <list>:Attribute-Name
+       <list>:Attribute-Name:TAG[NUM]
+.DE
+Where <list> is one of "request", "reply", "control", "proxy-request",
+"proxy-reply", or "outer.request", "outer.reply", "outer.control",
+"outer.proxy-request", or "outer.proxy-reply". just as with the
+"update" section, above.  The "<list>:" prefix is optional, and if
+omitted, is assumed to refer to the "request" list.
+
+The TAG portion is a decimal integer between 1 and 31.  Please see RFC
+2868 for more information about tags.  Tags can only be used for
+attributes which are marked in the dictionary as "has_tag".
+
+The NUM portion is used when there are multiple attributes of the same
+name in a list.  The "Attribute-Name" reference will return the first
+attribute.  Using an array offset allows the policy to refer to the
+second and subsequent attributes.
+
+When an attribute name is encountered, the given list is examined for
+an attribute of the given name.  Some examples are:
+.DS
+       User-Name
+.br
+       request:User-Name # same as above
+.br
+       reply:User-Name
+.br
+       Tunnel-Password:1
+.br
+       Cisco-AVPAir[2]
+.br
+       outer.request:User-Name # from inside of a TTLS/PEAP tunnel
+.DE
+Note that unlike C, there is no way to define new attributes at
+run-time.  They MUST be declared in a dictionary file, and loaded when
+the server starts.
+
+All attributes are defined in the dictionaries that accompany the
+server.  These definitions define only the name and type, and do not
+define the value of the attribute.  When the server receives a packet,
+it uses the packet contents to look up entries in the dictionary, and
+instantiates attributes with a name taken from the dictionaries, and a
+value taken from the packet contents.  This process means that if an
+attribute does not exist, it is usually because it was not contained
+in a packet that the server received.
+
+Once the attribute is instantiated, it is added to a list.  It can
+then be referenced, updated, replaced, etc.
+
 .SH CONDITIONS
 The conditions are similar to C conditions in syntax, though
 quoted strings are supported, as with the Unix shell.
@@ -368,6 +437,11 @@ attribute is compared to the literal string "Filter-Id".
 Where the left-hand side is an attribute, the "&" can be omitted.
 However, it is allowed for completeness.  e.g. The comparison
 "(&User-Name == &Filter-Id)" is equivalent to the example above.
+
+We recommend using attribute references instead of printing
+attributes to a string, via (User-Name == "%{Filter-Id}").
+Attribute references will be faster and more efficient.
+
 .RE
 .IP Casts
 .DS
@@ -433,7 +507,7 @@ as seen in the examples below.
 .IP """strings"""
 .RS
 Double-quoted strings are expanded by inserting the value of any
-variables (see VARIABLES, below) before being evaluated.  If
+attributes (see VARIABLES, below) before being evaluated.  If
 the result is a number it is evaluated in a numerical context.
 
 String length is limited by line-length, usually about 8000
@@ -452,7 +526,7 @@ output as a string.  This behavior is much the same as that of Unix
 shells.
 
 Note that for security reasons, the input string is split into command
-and arguments before variable expansion is done.
+and arguments before string expansion is done.
 
 For performance reasons, we suggest that the use of back-quoted
 strings be kept to a minimum.  Executing external programs is
@@ -473,61 +547,17 @@ expression match should be done in a case-insensitive fashion.
 If the comparison operator is "=~", then parantheses in the regular
 expression will define variables containing the matching text, as
 described below in the VARIABLES section.
-.SH VARIABLES
-Run-time variables are referenced using the following syntax
+.SH EXPANSIONS
+Attributes are expanded using the ATTRIBUTE REFERENCE syntax
+described above, and surrounding the reference with "%{...}"
 
 .DS
-       %{Variable-Name}
+       %{Attribute-Reference}
 .DE
 
-Note that unlike C, there is no way to declare variables, or to refer
-to them outside of a string context.  All references to variables MUST
-be contained inside of a double-quoted or back-quoted string.
-
-Many potential variables are defined in the dictionaries that
-accompany the server.  These definitions define only the name and
-type, and do not define the value of the variable.  When the server
-receives a packet, it uses the packet contents to look up entries in
-the dictionary, and instantiates variables with a name taken from the
-dictionaries, and a value taken from the packet contents.  This
-process means that if a variable does not exist, it is usually because
-it was not mentioned in a packet that the server received.
-
-Once the variable is instantiated, it is added to an appropriate
-attribute list, as described below.  In many cases, attributes and
-variables are inter-changeble, and are often talked about that way.
-However, variables can also refer to run-time calls to modules, which
-may perform operations like SQL SELECTs, and which may return the
-result as the value of the variable.
-.PP
-Referencing attribute lists
-.RS
-Attribute lists may be referenced via the following syntax
-
-.DS
-       %{<list>:Attribute-Name}
-.DE
-
-Where <list> is one of "request", "reply", "control", "proxy-request",
-"proxy-reply", or "outer.request", "outer.reply", "outer.control",
-"outer.proxy-request", or "outer.proxy-reply". just as with the
-"update" section, above.  The "<list>:" prefix is optional, and if
-omitted, is assumed to refer to the "request" list.
-
-When a variable is encountered, the given list is examined for an
-attribute of the given name.  If found, the variable reference in the
-string is replaced with the value of that attribute.  Some examples are:
-
-.DS
-       %{User-Name}
-.br
-       %{request:User-Name} # same as above
-.br
-       %{reply:User-Name}
-.br
-       %{outer.request:User-Name} # from inside of a TTLS/PEAP tunnel
-.DE
-.RE
+The result will be a string which contains the value of the attribute
+which was referenced, as a printable string.  If the attribute does
+not exist, the result will be an empty string.
 .PP
 Results of regular expression matches
 .RS
@@ -543,7 +573,7 @@ Obtaining results from databases
 .RS
 It is useful to query a database for some information, and to use the
 result in a condition.  The following syntax will call a module, pass
-it the given string, and replace the variable reference with the
+it the given string, and replace the string expansion with the
 resulting string returned from the module.
 
 .DS
@@ -595,27 +625,7 @@ The hex value of the Attribute-Name, as a series of hex digits.
 e.g. If a request contains "Framed-IP-Address = 127.0.0.1", the expansion
 of %{hex:Framed-IP-Address} will yeild "0x7f000001".
 
-.IP %{Attribute-Name[index]}
-Reference the N'th occurance of the given attribute.  The syntax
-%{<list>:Attribute-Name[index]} may also be used.  The indexes start
-at zero.  This feature is NOT available for non-attribute dynamic
-translations, like %{sql:...}.
-
-For example, %{User-Name[0]} is the same as %{User-Name}
-
-The variable %{Cisco-AVPair[2]} will reference the value of the
-THIRD Cisco-AVPair attribute (if it exists) in the request packet,
-.IP %{Attribute-Name[#]}
-Returns the total number of attributes of that name in the relevant
-attribute list.  The number will usually be between 0 and 200.
-
-For most requests, %{request:User-Name[#]} == 1
-.IP %{Attribute-Name[*]}
-Expands to a single string, with the value of each array
-member separated by a newline.
-.IP %{#Attribute-Name[index]}
-Expands to the length of the string %{Attribute-Name[index]}.
-.SH ATTRIBUTES
+.SH ATTRIBUTE ASSIGNMENTS
 The attribute lists described above may be edited by listing one or
 more attributes in an "update" section.  Once the attributes have been
 defined, they may be referenced as described above in the VARIABLES
@@ -626,17 +636,15 @@ attribute and value has to be all on one line in the configuration
 file.  There is no need for commas or semi-colons after the value.
 
 .DS
-       Attribute-Name = value
+       Attribute-Reference = value
 .DE
 .PP
-Attribute names
+Attribute Reference
 .RS
-The Attribute-Name must be a name previously defined in a dictionary.
-If an undefined name is used, the server will return an error, and
-will not start.
+The Attribute-Reference must be a reference (see above), using a name
+previously defined in a dictionary.  If an undefined name is used, the
+server will return an error, and will not start.
 
-The names can be qualified with a list prefix.  For example,
-"request:User-Name" is usually a synonym for "User-Name".
 .RE
 .IP Operators
 The operator used to assign the value of the attribute may be one of
@@ -700,6 +708,13 @@ used, both attributes must have the same data type.  For example,
 "User-Name := &NAS-Port" is invalid, because "User-Name" is a string,
 and "NAS-Port" is an integer.
 
+We recommend using the form "Attribute-1 = &Attribute-2" for updates,
+instead of "Attribute-1 = "%{Attribute-2}".  The first version will
+copy the attribute data, no matter what its form.  The second
+version will print the Attribute-2 to a string, and then parse it to
+create the value for Attribute-1.  This second version is slower
+and more fragile than the first one.
+
 When the value is an attribute-specific string, it can be a string,
 integer, IP address, etc.  The value may be expanded as described
 above in the DATA TYPES section, above.  For example, specifying
@@ -715,7 +730,7 @@ it directly, or by having the address returned from a database query,
 or by having the address returned as the output of a program that is
 executed.
 
-When string values are finally assigned to a variable, they can have a
+When string values are finally assigned to an attribute, they can have a
 maximum length of 253 characters.  This limit is due in part to both
 protocol and internal server requirements.  That is, the strings in
 the language can be nearly 8k in length, say for a long SQL query.
diff --git a/man/man8/radconf2xml.8 b/man/man8/radconf2xml.8
deleted file mode 100644 (file)
index 03aebfe..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-.TH RADCONF2XML 8
-.SH NAME
-radconf2xml - converts radiusd.conf to XML
-.SH SYNOPSIS
-.B radconf2xml
-.RB [ \-d
-.IR raddb_dir ]
-.RB [ \-h ]
-.RB [ \-n
-.IR name ]
-.RB [ \-o
-.IR out_file ]
-
-.SH DESCRIPTION
-\fBradconf2xml\fP reads the radiusd configuration file specified by
-\fIraddb_dir\fP/\fIname\fP.conf and converts it to XML format.
-The result is written to \fIout_file\fP which by default is stdout.
-
-.SH OPTIONS
-
-.IP \-d\ \fIraddb_dir\fP
-The radius configuration directory (typically /etc/raddb).
-.IP \-d\ \fIname\fP
-The name of the configuration file without the .conf extension.
-Defaults to radiusd.
-.IP \-h
-Print usage help information.
-.IP \-d\ \fIout_file\fP
-The output file the XML formatted file will be written to.
-Defaults to stdout.
-
-.SH SEE ALSO
-radiusd(8),
-.SH AUTHORS
-Alan DeKok <aland@deployingradius.com>
index e899b19..7424e88 100644 (file)
@@ -221,7 +221,7 @@ from the hints file. Authentication is then based on the contents of
 the UNIX \fI/etc/passwd\fP file. However it is also possible to define all
 users, and their passwords, in this file.
 .SH SEE ALSO
-rradiusd.conf(5), users(5), huntgroups(5), hints(5),
+radiusd.conf(5), users(5), huntgroups(5), hints(5),
 dictionary(5), raddebug(8)
 .SH AUTHOR
 The FreeRADIUS Server Project (http://www.freeradius.org)
index a1ed7a7..590c023 100644 (file)
@@ -1,9 +1,13 @@
 Upgrading to Version 3.0
 ========================
 
-The configuration for 3.0 is *largely* compatible with the 2.x.x
-configuration.  However, it is NOT possible to simply use the 2.x.x
-configuration as-is.  Instead, you should re-create it.
+.. contents:: Sections
+   :depth: 2
+
+.. important:: 
+   The configuration for 3.0 is *largely* compatible with the 2.x.x
+   configuration.  However, it is NOT possible to simply use the 2.x.x
+   configuration as-is.  Instead, you should re-create it.
 
 Security
 --------
@@ -22,33 +26,7 @@ The list of moved options is::
   status_server
 
 These entries should be moved from "radiusd.conf" to the "security"
-subsection of that file
-
-Modules Directory
------------------
-
-As of version 3.0, the ``modules/`` directory no longer exists.
-
-Instead, all "example" modules have been put into the
-``mods-available/`` directory.  Modules which can be loaded by the
-server are placed in the ``mods-enabled/`` directory.  All of the
-modules in that directory will be loaded.  This means that the
-"instantiate" section of radiusd.conf is less important.
-
-Modules can be enabled by creating a soft link.  For module ``foo``, do::
-
-  $ cd raddb
-  $ ln -s mods-available/foo mods-enabled/foo
-
-To create "local" versions of the modules, we suggest copying the file
-instead.  This leaves the original file (with documentation) in the
-``mods-available/`` directory.  Local changes should go into the
-``mods-enabled/`` directory.
-
-Module-specific configuration files are now in the ``mods-config/``
-directory.  This change allows for better organization, and means that
-there are fewer files in the main ``raddb`` directory.  See
-``mods-config/README.rst`` for more details.
+subsection of that file.
 
 Naming
 ------
@@ -74,17 +52,51 @@ distinct words separated by underscores ``_``.
 
 The configuration items ``file``, ``script_file``, ``module``,
 ``detail``, ``detailfile``, ``attrsfile``, ``perm``, ``dirperm``,
-``detailperm``, and ``hostname`` are deprecated. As well as any
-faux portmanteaus, and configuration items that used hyphens
-as word delimiters.
-Please update your module configuration to use the new syntax.
+``detailperm``, and ``hostname`` are deprecated. As well as any false
+portmanteaus, and configuration items that used hyphens as word
+delimiters.  e.g. ``foo-bar`` has been changed to ``foo_bar``.  Please
+update your module configuration to use the new syntax.
 
 In most cases the server will tell you the replacement config item to
 use.  As always, run the server in debugging mode to see these
 messages.
 
+Modules Directory
+-----------------
+
+As of version 3.0, the ``modules/`` directory no longer exists.
+
+Instead, all "example" modules have been put into the
+``mods-available/`` directory.  Modules which can be loaded by the
+server are placed in the ``mods-enabled/`` directory.  All of the
+modules in that directory will be loaded.  This means that the
+``instantiate`` section of radiusd.conf is less important.  The only
+reason to list a module in the ``instantiate`` section is to force
+ordering when the modules are loaded.
+
+Modules can be enabled by creating a soft link.  For module ``foo``, do::
+
+  $ cd raddb
+  $ ln -s mods-available/foo mods-enabled/foo
+
+To create "local" versions of the modules, we suggest copying the file
+instead.  This leaves the original file (with documentation) in the
+``mods-available/`` directory.  Local changes should go into the
+``mods-enabled/`` directory.
+
+Module-specific configuration files are now in the ``mods-config/``
+directory.  This change allows for better organization, and means that
+there are fewer files in the main ``raddb`` directory.  See
+``mods-config/README.rst`` for more details.
+
+Changed Modules
+---------------
+
+The following modules have been changed.
+
+
 rlm_sql
--------
+~~~~~~~
 
 The SQL configuration has been moved from ``sql.conf`` to
 ``mods-available/sql``.  The ``sqlippool.conf`` file has also been
@@ -105,7 +117,7 @@ that there are fewer different configuration items.  The mapping
 between the configuration items is::
 
   num_sql_socks                        -> pool { max }
-  connect_failure_retry_delay  -> NOT SUPPORTED
+  connect_failure_retry_delay  -> pool { retry_delay }
   lifetime                     -> pool { lifetime }
   max_queries                  -> pool { uses }
 
@@ -127,33 +139,33 @@ You can now use a NULL SQL database::
   driver = rlm_sql_null
 
 This is an empty driver which will always return "success".  It is
-intended to be used to replace the ``sql_log`` module, and in
+intended to be used to replace the ``sql_log`` module, and to work in
 conjunction with the ``radsqlrelay`` program.  Simply take your normal
 configuration for raddb/mods-enabled/sql, and set::
 
   driver = rlm_sql_null
   ...
-  logfile = ${radacctdir}/log.sql
+  logfile = ${radacctdir}/sql.log
 
-And all of the SQL queries will be logged to that file.  The
-connection pool        will still need to be configured for the NULL SQL
-driver, but the defaults will work.
+All of the SQL queries will be logged to that file.  The connection
+pool does not need to be configured for the ``null`` SQL driver.  It
+can be left as-is, or deleted from the SQL configuration file.
 
 rlm_sql_sybase
---------------
+~~~~~~~~~~~~~~
 
 The ``rlm_sql_sybase`` module has been renamed to ``rlm_sql_freetds``
 and the old ``rlm_sql_freetds`` module has been removed.
 
 ``rlm_sql_sybase`` used the newer ct-lib API, and ``rlm_sql_freetds``
-used and older API and was incomplete.
+used an older API and was incomplete.
 
 The new ``rlm_sql_freetds`` module now also supports database
 selection on connection startup so ``use`` statements no longer
 have to be included in queries.
 
 sql/dialup.conf
----------------
+~~~~~~~~~~~~~~~
 
 Queries for post-auth and accounting calls have been re-arranged.  The
 SQL module will now expand the 'reference' configuration item in the
@@ -185,38 +197,51 @@ Alternatively a 2.x.x config may be patched to work with the
 3.0 module by adding the following::
 
   accounting {
-       reference = "%{tolower:type.%{Acct-Status-Type}.query}"
-       type {
-               accounting-on {
-                       query = "${....accounting_onoff_query}"
-               }
-               accounting-off {
-                       query = "${....accounting_onoff_query}"
-               }
-               start {
-                       query = "${....accounting_start_query}"
-                       query = "${....accounting_start_query_alt}"
-               }
-               interim-update {
-                       query = "${....accounting_update_query}"
-                       query = "${....accounting_update_query_alt}"
-               }
-               stop {
-                       query = "${....accounting_stop_query}"
-                       query = "${....accounting_stop_query_alt}"
-               }
-       }
+       reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+       type {
+               accounting-on {
+                       query = "${....accounting_onoff_query}"
+               }
+               accounting-off {
+                       query = "${....accounting_onoff_query}"
+               }
+               start {
+                       query = "${....accounting_start_query}"
+                       query = "${....accounting_start_query_alt}"
+               }
+               interim-update {
+                       query = "${....accounting_update_query}"
+                       query = "${....accounting_update_query_alt}"
+               }
+               stop {
+                       query = "${....accounting_stop_query}"
+                       query = "${....accounting_stop_query_alt}"
+               }
+       }
   }
 
   post-auth {
-       query = "${..postauth_query}"
+       query = "${..postauth_query}"
   }
 
 In general, it is safer to migrate the configuration rather than
 trying to "patch" it, to make it look like a v2 configuration.
 
+Note that the sub-sections holding the queries are labelled
+``accounting-on``, and not ``accounting_on``.  The reason is that the
+names of these sections are taken directly from the
+``Accounting-Request`` packet, and the ``Acct-Status-Type`` field.
+The ``sql`` module looks at the value of that field, and then looks
+for a section of that name, in order to find the query to use.
+
+That process means that the server can be extended to support any new
+value of ``Acct-Status-Type``, simply by adding a named sub-section,
+and a query.  This behavior is preferable to that of v2, which had
+hard-coded queries for certain ``Acct-Status-Type`` values, and was
+ignored all other values.
+
 rlm_ldap
---------
+~~~~~~~~
 
 The LDAP module configuration has been substantially changed.  Please
 read ``raddb/mods-available/ldap``.  It now uses a connection pool,
@@ -235,8 +260,8 @@ Users upgrading from 2.x.x who used to call the ldap module in
 ``post-auth`` should now set ``edir_autz = yes``, and remove the ``ldap``
 module from the ``post-auth`` section.
 
-rlm_ldap/LDAP-Group
--------------------
+rlm_ldap and LDAP-Group
+~~~~~~~~~~~~~~~~~~~~~~~
 
 In 2.x.x the registration of the ``LDAP-Group`` pair comparison was done
 by the last instance of rlm_ldap to be instantiated. In 3.0 this has
@@ -244,17 +269,55 @@ changed so that only the default ``ldap {}`` instance registers
 ``LDAP-Group``.
 
 If ``<instance>-LDAP-Group`` is already used throughout your configuration
-no changes need to be made.
+no changes will be needed.
+
+rlm_ldap authentication
+~~~~~~~~~~~~~~~~~~~~~~~
+
+In 2.x.x the LDAP module had a ``set_auth_type`` configuration item,
+which forced ``Auth-Type := ldap``. This was removed in 3.x.x as it
+often did not work, and was not consistent with the rest of the
+server.  We generally recommend that LDAP should be used as a
+database, and that FreeRADIUS should do authentication.
+
+The only reason to use ``Auth-Type := ldap`` is when the LDAP server
+will not supply the "known good" password to FreeRADIUS, *and* where
+the Access-Request contains User-Password.  This situation happens
+only for Active Directory.  If you think you need to force ``Auth-Type
+:= ldap`` in other situations, you are very likely to be wrong.
+
+The following is an example of what should be inserted into the
+``authorize {}`` and ``authenticate {}`` sections of the relevant
+virtual-servers, to get functionality equivalent to v2.x::
+
+  authorize {
+    ...
+    ldap
+    if ((ok || updated) && User-Password) {
+      update control {
+       Auth-Type := ldap
+      }
+    }
+    ...
+  }
+  
+  authenticate {
+    ...
+    Auth-Type ldap {
+      ldap   
+    }
+    ...
+  }
 
 rlm_eap
--------
+~~~~~~~
 
 The EAP configuration has been moved from ``eap.conf`` to
 ``mods-available/eap``.  A new ``pwd`` subsection has been added for
 EAP-PWD.
 
 rlm_expiration & rlm_logintime
--------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The rlm_expiration and rlm_logintime modules no longer add a ``Reply-Message``,
 the same behaviour can be achieved checking the return code of the module and
@@ -268,74 +331,61 @@ adding the ``Reply-Message`` with unlang::
   }
 
 rlm_unix
---------
+~~~~~~~~
 
-The unix module does not have an "authenticate" section.  So you
-cannot set "Auth-Type := System".  The "unix" module has also been
-deleted from the examples in sites-available/.  Listing it there has
-been deprecated for many years.
+The ``unix`` module does not have an ``authenticate`` section.  So you
+cannot set ``Auth-Type := System``.  The ``unix`` module has also been
+deleted from the examples in ``sites-available/``.  Listing it there
+has been deprecated for many years.
 
 The PAP module can do crypt authentication.  It should be used instead
 of Unix authentication.
 
-The Unix module still can pull the passwords from /etc/passwd, or
-/etc/shadow.  This is done by listing it in the "authorize" section,
-as is done in the sites-available/ examples.
-
-
-RadSec
-------
-
-RadSec (or RADIUS over TLS) is now supported.  RADIUS over bare TCP
-is also supported, but is recommended only for secure networks.
-
-See ``sites-available/tls`` for complete details on using TLS.  The server
-can both receive incoming TLS connections, and also originate outgoing
-TLS connections.
-
-The TLS configuration is taken from the old EAP-TLS configuration.  It
-is largely identical to the old EAP-TLS configuration, so it should be
-simple to use and configure.  It re-uses much of the EAP-TLS code,
-so it is well-tested and reliable.
+The Unix module still can pull the passwords from ``/etc/passwd``, or
+``/etc/shadow``.  This is done by listing it in the ``authorize``
+section, as is done in the examples in ``sites-available/``.  However,
+some systems using NIS or NSS will not supply passwords to the
+``unix`` module.  For those systems, we recommend putting users and
+passwords into a database, instead of relying on ``/etc/passwd``.
 
-Once RadSec is enabled, normal debugging mode will not work.  This is
-because the TLS code requires threading to work properly.  Instead of doing::
+New Modules
+-----------
 
-  $ radiusd -X
+rlm_date
+~~~~~~~~
 
-you will need to do::
+Instances of rlm_date register an xlat method which can translate
+integer and date values to an arbitrarily formatted date time
+string, or an arbitrarily formated time string to an integer, 
+depending on the attribute type passed.
 
-  $ radiusd -fxx -l stdout
+rlm_rest
+~~~~~~~~
 
-Sorry, but that's the price to pay for using RadSec.
+The ``rest`` module is used to translate RADIUS requests into 
+RESTfull HTTP requests. Currently supported body types are JSON
+and POST.
 
-PAP and User-Password
----------------------
+rlm_unpack
+~~~~~~~~~~
 
-From version 3.0 onwards the server no longer supports authenticating
-against a cleartext password in the 'User-Password' attribute. Any
-occurences of this (for instance, in the users file) should now be changed
-to 'Cleartext-Password' instead.
+The ``unpack`` module is used to turn data buried inside of binary
+attributes.  e.g. if we have ``Class = 0x00000001020304`` then::
 
-If this is not done, authentication will likely fail.  The server will
-also print a helpful message in debugging mode.
+  Tmp-Integer-0 := "%{unpack:&Class 4 short}"
 
-If it really is impossible to do this, the following unlang inserted above
-the call to the pap module may be used to copy User-Password to the correct
-attribute::
+will unpack octets 4 and 5 as a "short", which has value 0x0304.
+All integers are assumed to be in network byte order.
 
-  if (!control:Cleartext-Password && control:User-Password) {
-    update control {
-      Cleartext-Password := "%{control:User-Password}"
-    }
-  }
+rlm_yubikey
+~~~~~~~~~~~
 
-However, this should only be seen as a temporary, not permanent, fix.
-It is better to fix your databases to contain the correct
-configuration.
+The ``yubikey`` module can be used to forward yubikey OTP token
+values to a Yubico validation server, or decrypt the token 
+using a PSK.
 
 Deleted Modules
-===============
+---------------
 
 The following modules have been deleted, and are no longer supported
 in Version 3.  If you are using one of these modules, your
@@ -343,7 +393,7 @@ configuration can probably be changed to not need it.  Otherwise email
 the freeradius-devel list, and ask about the module.
 
 rlm_acct_unique
----------------
+~~~~~~~~~~~~~~~
 
 This module has been replaced by the "acct_unique" policy.  See
 raddb/policy.d/accounting.
@@ -355,13 +405,13 @@ same database at the same time.  They will calculate different values
 for Acct-Unique-Id.
 
 rlm_acctlog
------------
+~~~~~~~~~~~
 
 You should use rlm_linelog instead.  That module has a superset of the
 acctlog functionality.
 
 rlm_attr_rewrite
-----------------
+~~~~~~~~~~~~~~~~
 
 The attr_rewrite module looked for an attribute, and then re-wrote it,
 or created a new attribute.  All of that can be done in "unlang".
@@ -377,7 +427,7 @@ A sample configuration in "unlang" is::
 We suggest updating all uses of attr_rewrite to use unlang instead.
 
 rlm_checkval
-------------
+~~~~~~~~~~~~
 
 The checkval module compared two attributes.  All of that can be done in "unlang"::
 
@@ -388,7 +438,7 @@ The checkval module compared two attributes.  All of that can be done in "unlang
 We suggest updating all uses of checkval to use unlang instead.
 
 rlm_dbm
--------
+~~~~~~~
 
 No one seems to use it.  There is no sample configuration for it.
 There is no speed advantage to using it over the "files" module.
@@ -397,20 +447,20 @@ Modern systems are fast enough that 10K entries can be read from the
 real database such as SQL.
 
 rlm_fastusers
--------------
+~~~~~~~~~~~~~
 
 No one seems to use it.  It has been deprecated since Version 2.0.0.
 The "files" module was rewritten so that the "fastusers" module was no
 longer necessary.
 
 rlm_policy
-----------
+~~~~~~~~~~
 
 No one seems to use it.  Almost all of its functionality is available
 via "unlang".
 
 rlm_sim_files
--------------
+~~~~~~~~~~~~~
 
 The rlm_sim_files module has been deleted.  It was never marked "stable",
 and was never used in a production environment.  There are better ways
@@ -420,10 +470,188 @@ If you want similar functionality, see rlm_passwd.  It can read CSV
 files, and create attributes from them.
 
 rlm_sql_log
------------
+~~~~~~~~~~~
 
 This has been replaced with the "null" sql driver.  See
 raddb/mods-available/sql for an example configuration.
 
 The main SQL module has more functionality than rlm_sql_log, and
 results in less code in the server.
+
+Other Functionality
+-------------------
+
+The following is a list of new / changed functionality.
+
+RadSec
+~~~~~~
+
+RadSec (or RADIUS over TLS) is now supported.  RADIUS over bare TCP
+is also supported, but is recommended only for secure networks.
+
+See ``sites-available/tls`` for complete details on using TLS.  The server
+can both receive incoming TLS connections, and also originate outgoing
+TLS connections.
+
+The TLS configuration is taken from the old EAP-TLS configuration.  It
+is largely identical to the old EAP-TLS configuration, so it should be
+simple to use and configure.  It re-uses much of the EAP-TLS code,
+so it is well-tested and reliable.
+
+Once RadSec is enabled, normal debugging mode will not work.  This is
+because the TLS code requires threading to work properly.  Instead of doing::
+
+  $ radiusd -X
+
+you will need to do::
+
+  $ radiusd -fxx -l stdout
+
+That's the price to pay for using RadSec.  This limitation may be
+lifted in a future version of the server.
+
+
+PAP and User-Password
+~~~~~~~~~~~~~~~~~~~~~
+
+From version 3.0 onwards the server no longer supports authenticating
+against a cleartext password in the 'User-Password' attribute. Any
+occurences of this (for instance, in the users file) should now be changed
+to 'Cleartext-Password' instead.
+
+e.g. change entries like this::
+
+  bob User-Password == "hello"
+
+to ones like this::
+
+  bob Cleartext-Password := "hello"
+
+
+If this is not done, authentication will likely fail.  The server will
+also print a helpful message in debugging mode.
+
+If it really is impossible to do this, the following unlang inserted above
+the call to the pap module may be used to copy User-Password to the correct
+attribute::
+
+  if (!control:Cleartext-Password && control:User-Password) {
+    update control {
+      Cleartext-Password := "%{control:User-Password}"
+    }
+  }
+
+However, this should only be seen as a temporary, not permanent, fix.
+It is better to fix your databases to use the correct configuration.
+
+Unlang
+~~~~~~
+
+The unlang policy language is compatible with v2, but has a number of
+new features.  See ``man unlang`` for complete documentation.
+
+ERRORS
+
+Many more errors are caught when the server is starting up.  Syntax
+errors in ``unlang`` are caught, and a helpful error message is
+printed.  The error message points to the exact place where the error
+occurred::
+
+  ./raddb/sites-enabled/default[230]: Parse error in condition
+  ERROR:  if (User-Name ! "bob") {
+  ERROR:                ^ Invalid operator
+
+``update`` sections are more generic.  Instead of doing ``update
+reply``, you can do the following::
+
+  update {
+         reply:Class := 0x0000
+         control:Cleartext-Password := "hello"
+  }
+
+This change means that you need fewer ``update`` sections.
+
+COMPARISONS
+
+Attribute comparisons can be done via the ``&`` operator.  When you
+needed to compare two attributes, the old comparison style was::
+
+  if (User-Name == "%{control:Tmp-String-0}") {
+
+This syntax is inefficient, as the ``Tmp-String-0`` attribute would be
+printed to an intermediate string, causing unnecessary work.  You can
+now instead compare the two attributes directly::
+
+  if (&User-Name == &control:Tmp-String-0) {
+
+See ``man unlang`` for more details.
+
+CASTS
+
+Casts are now permitted.  This allows you to force type-specific
+comparisons::
+
+  if (<ipaddr>"%{sql: SELECT...}" == 127.0.0.1) {
+
+This forces the string returned by the SELECT to be treated as an IP
+address, and compare to ``127.0.0.1``.  Previously, the comparison
+would have been done as a simple string comparison.
+
+NETWORKS
+
+IP networks are now supported::
+
+  if (127.0.0.1/32 == 127.0.0.1) {
+
+Will be ``true``.  The various comparison operators can be used to
+check IP network membership::
+
+  if (127/8 > 127.0.0.1) {
+
+Returns ``true``, because ``127.0.0.1`` is within the ``127/8``
+network.  However, the following comparison will return ``false``::
+
+  if (127/8 > 192.168.0.1) {
+
+because ``192.168.0.1`` is outside of the ``127/8`` network.
+
+OPTIMIZATION
+
+As ``unlang`` is now pre-compiled, many compile-time optimizations are
+done.  This means that the debug output may not be exactly the same as
+what is in the configuration files::
+
+  if (0 && (User-Name == "bob')) {
+
+The result will always be ``false``, as the ``if 0`` prevents the
+following ``&& ...`` from being evaluated.
+
+Not only that, but the entire contents of that section will be ignored
+entirely::
+
+  if (0) {
+      this_module_does_not_exist
+      and_this_one_does_not_exist_either
+  }
+
+In v2, that configuration would result in a parse error, as there is
+no module called ``this_module_does_not_exist``.  In v3, that text is
+ignored.  This ability allows you to have dynamic configurations where
+certain parts are used (or not) depending on compile-time configuration.
+
+Similarly, conditions which always evaluate to ``true`` will be
+optimized away::
+
+  if (1) {
+      files
+  }
+
+That configuration will never show the ``if (1)`` output in debugging mode.
+
+
+Dialup_admin
+------------
+
+The dialip_admin directory has been removed.  No one stepped forward
+to maintain it, and the code had not been changed in many years.
+
index d130a41..87301a9 100644 (file)
@@ -2,7 +2,7 @@
 #  The list of files to install.
 #
 LOCAL_FILES :=         clients.conf dictionary templates.conf experimental.conf \
-                       proxy.conf radiusd.conf trigger.conf README.rst
+                       proxy.conf radiusd.conf trigger.conf README.rst panic.gdb
 
 DEFAULT_SITES :=       default inner-tunnel
 LOCAL_SITES :=         $(addprefix raddb/sites-enabled/,$(DEFAULT_SITES))
@@ -11,7 +11,7 @@ DEFAULT_MODULES :=    always attr_filter cache_eap chap \
                        detail detail.log digest dhcp dynamic_clients eap \
                        echo exec expiration expr files linelog logintime \
                        mschap ntlm_auth pap passwd preprocess radutmp realm \
-                       replicate soh sradutmp unix utf8
+                       replicate soh sradutmp unix unpack utf8
 
 LOCAL_MODULES :=       $(addprefix raddb/mods-enabled/,$(DEFAULT_MODULES))
 
@@ -32,10 +32,10 @@ INSTALL_RADDB_DIRS :=       $(R)$(raddbdir)/ $(addprefix $(R)$(raddbdir)/, $(RADDB_DIR
 
 # Grab files from the various subdirectories
 INSTALL_FILES :=       $(wildcard raddb/sites-available/* raddb/mods-available/*) \
-                       $(addprefix raddb/,$(LOCAL_FILES)) \
-                       $(addprefix raddb/certs/,$(LOCAL_CERT_FILES)) \
-                       $(shell find raddb/mods-config -type f -print) \
-                       $(shell find raddb/policy.d -type f -print)
+                       $(addprefix raddb/,$(LOCAL_FILES)) \
+                       $(addprefix raddb/certs/,$(LOCAL_CERT_FILES)) \
+                       $(shell find raddb/mods-config -type f -print) \
+                       $(shell find raddb/policy.d -type f -print)
 
 # Re-write local files to installed files, filtering out editor backups
 INSTALL_RADDB :=       $(patsubst raddb/%,$(R)$(raddbdir)/%,\
index 6af3c11..0613df9 100644 (file)
@@ -118,7 +118,7 @@ serial:
 
 random:
        @if [ -c /dev/urandom ] ; then \
-               dd if=/dev/urandom of=./random count=10 >/dev/null 2>&1; \
+               ln -sf /dev/urandom random; \
        else \
                date > ./random; \
        fi
index e70b506..c9d939b 100755 (executable)
@@ -34,7 +34,7 @@ fi
 if [ ! -f dh ]; then
   openssl dhparam -out dh 1024 || exit 1
   if [ -e /dev/urandom ] ; then
-       dd if=/dev/urandom of=./random count=10 >/dev/null 2>&1;
+       ln -sf /dev/urandom random
   else
        date > ./random;
   fi
index 7819b5a..3e6eaf6 100644 (file)
 #  format is still accepted.
 #
 client localhost {
-       #  Allowed values are:
-       #       dotted quad (1.2.3.4)
-       #          hostname     (radius.example.com)
+       #  Only *one* of ipaddr, ipv4addr, ipv6addr may be specified for
+       #  a client.
+       #
+       #  ipaddr will accept IPv4 or IPv6 addresses with optional CIDR
+       #  notation '/<mask>' to specify ranges.
+       #
+       #  ipaddr will accept domain names e.g. example.org resolving
+       #  them via DNS.
+       #
+       #  If both A and AAAA records are found, A records will be
+       #  used in preference to AAAA.
        ipaddr = 127.0.0.1
 
-       #  OR, you can use an IPv6 address, but not both
-       #  at the same time.
+       #  Same as ipaddr but allows v4 addresses only. Requires A
+       #  record for domain names.
+#      ipv4addr = *    # any.  127.0.0.1 == localhost
+
+       #  Same as ipaddr but allows v6 addresses only. Requires AAAA
+       #  record for domain names.
 #      ipv6addr = ::   # any.  ::1 == localhost
 
        #
@@ -125,7 +137,7 @@ client localhost {
        #  domain name, or the IP address.
        #
        #  It is accepted for compatibility with 1.x, but it is no
-       #  longer necessary in 2.0
+       #  longer necessary in >= 2.0
        #
 #      shortname = localhost
 
@@ -181,6 +193,17 @@ client localhost {
 #      coa_server = coa
 
        #
+       #  Response window for proxied packets.  If non-zero,
+       #  then the lower of (home, client) response_window
+       #  will be used.
+       #
+       #  i.e. it can be used to lower the response_window
+       #  packets from one client to a home server.  It cannot
+       #  be used to raise the response_window.
+       #
+#      response_window = 10.0
+
+       #
        #  Connection limiting for clients using "proto = tcp".
        #
        #  This section is ignored for clients sending UDP traffic
@@ -216,20 +239,20 @@ client localhost {
 }
 
 # IPv6 Client
-#client ::1 {
+#client localhost_ipv6 {
+#      ipv6addr        = ::1
 #      secret          = testing123
-#      shortname       = localhost
 #}
-#
+
 # All IPv6 Site-local clients
-#client fe80::/16 {
+#client sitelocal_ipv6 {
+#      ipv6addr        = fe80::/16
 #      secret          = testing123
-#      shortname       = localhost
 #}
 
-#client some.host.org {
+#client example.org {
+#      ipaddr          = radius.example.org
 #      secret          = testing123
-#      shortname       = localhost
 #}
 
 #
@@ -237,28 +260,14 @@ client localhost {
 #  When a client request comes in, the BEST match is chosen.
 #  i.e. The entry from the smallest possible network.
 #
-#client 192.0.2.0/24 {
+#client private-network-1 {
+#      ipaddr          = 192.0.2.0/24
 #      secret          = testing123-1
-#      shortname       = private-network-1
-#}
-#
-#client 198.51.100.0/24 {
-#      secret          = testing123-2
-#      shortname       = private-network-2
 #}
 
-
-#client 203.0.113.1 {
-#      # secret and password are mapped through the "secrets" file.
-#      secret          = testing123
-#      shortname       = liv1
-
-# The following three fields are optional, but may be used by
-# checkrad.pl for simultaneous usage checks
-
-#      nas_type        = livingston
-#      login           = !root
-#      password        = someadminpas
+#client private-network-2 {
+#      ipaddr          = 198.51.100.0/24
+#      secret          = testing123-2
 #}
 
 #######################################################################
@@ -274,7 +283,8 @@ client localhost {
 #  will then accept ONLY the clients listed in this section.
 #
 #clients per_socket_clients {
-#      client 192.0.2.4 {
+#      client socket_client {
+#              ipaddr = 192.0.2.4
 #              secret = testing123
 #      }
 #}
similarity index 51%
rename from raddb/dictionary.in
rename to raddb/dictionary
index ca0064b..eed5d70 100644 (file)
@@ -1,9 +1,19 @@
 #
-#      This is the master dictionary file, which references the
-#      pre-defined dictionary files included with the server.
+#      This is the local dictionary file which can be
+#      edited by local administrators.  It will be loaded
+#      AFTER the main dictionary files are loaded.
 #
-#      Any new/changed attributes MUST be placed in this file, as
-#      the pre-defined dictionaries SHOULD NOT be edited.
+#      As of version 3.0.2, FreeRADIUS will automatically
+#      load the main dictionary files from
+#
+#              ${prefix}/share/freeradius/dictionary
+#
+#      It is no longer necessary for this file to $INCLUDE
+#      the main dictionaries.  However, if the $INCLUDE
+#      line is here, nothing bad will happen.
+#
+#      Any new/changed attributes MUST be placed in this file.
+#      The pre-defined dictionaries SHOULD NOT be edited.
 #
 #      See "man dictionary" for documentation on its format.
 #
 #
 
 #
-#      The filename given here should be an absolute path.
-#
-$INCLUDE       @prefix@/share/freeradius/dictionary
-
+#      All local attributes and $INCLUDE's should go into
+#      this file.
 #
-#      All additional attributes an $INCLUDE's should go into
-#      a file "dictionary.local".
 
 #      If you want to add entries to the dictionary file,
 #      which are NOT going to be placed in a RADIUS packet,
@@ -36,18 +42,8 @@ $INCLUDE     @prefix@/share/freeradius/dictionary
 #
 
 #
-#      These attributes are examples.  Don't edit them here.
-#      Instead, create a "dictionary.local" file, and place
-#      them there.
+#      These attributes are examples
 #
 #ATTRIBUTE     My-Local-String         3000    string
 #ATTRIBUTE     My-Local-IPAddr         3001    ipaddr
 #ATTRIBUTE     My-Local-Integer        3002    integer
-
-#
-#      Include dictionary.local, IF it exists.  Otherwise, ignore it.
-#
-#      This file WILL NOT EVER be created, edited, or modified
-#      by FreeRADIUS.
-#
-$INCLUDE-      dictionary.local
index c28187f..de3f130 100644 (file)
@@ -3,29 +3,59 @@
 #  $Id$
 
 #
-# The "always" module is here for debugging purposes. Each
-# instance simply returns the same result, always, without
-# doing anything.
-always fail {
-       rcode = fail
-}
+#  The "always" module is here for debugging purposes, or
+#  for use in complex policies.
+#  Instance simply returns the same result, always, without
+#  doing anything.
+#
+#  rcode may be one of the following values:
+#  - reject   - Reject the user.
+#  - fail     - Simulate or indicate a failure.
+#  - ok       - Simulate or indicate a success.
+#  - handled  - Indicate that the request has been handled,
+#               stop processing, and send response if set.
+#  - invalid  - Indicate that the request is invalid.
+#  - userlock - Indicate that the user account has been
+#               locked out.
+#  - notfound - Indicate that a user account can't be found.
+#  - noop     - Simulate a no-op.
+#  - updated  - Indicate that the request has been updated.
+#
+#  If an instance is listed in a session {}  section, 
+#  this simulates a user having <integer> sessions.
+#  
+#  simulcount = <integer>
+#
+#  If an instance is listed in a session {}  section, 
+#  this simulates the user having multilink
+#  sessions.
+#
+#  mpp = <integer>
+#
 always reject {
        rcode = reject
 }
-always noop {
-       rcode = noop
+always fail {
+       rcode = fail
+}
+always ok {
+       rcode = ok
 }
 always handled {
        rcode = handled
 }
-always updated {
-       rcode = updated
+always invalid {
+       rcode = invalid
+}
+always userlock {
+       rcode = userlock
 }
 always notfound {
        rcode = notfound
 }
-always ok {
-       rcode = ok
-       simulcount = 0
-       mpp = no
+always noop {
+       rcode = noop
+}
+always updated {
+       rcode = updated
 }
index 125a6c4..1caff07 100644 (file)
 # proxied servers, to make sure we send back to our RADIUS client
 # only allowed attributes.
 attr_filter attr_filter.post-proxy {
+       key = "%{Realm}"
        filename = ${modconfdir}/${.:name}/post-proxy
 }
 
 # attr_filter - filters the attributes in the packets we send to
 # the RADIUS home servers.
 attr_filter attr_filter.pre-proxy {
+       key = "%{Realm}"
        filename = ${modconfdir}/${.:name}/pre-proxy
 }
 
diff --git a/raddb/mods-available/couchbase b/raddb/mods-available/couchbase
new file mode 100644 (file)
index 0000000..303bbbb
--- /dev/null
@@ -0,0 +1,116 @@
+couchbase {
+       #
+       # List of Couchbase hosts (hosts may be space, tab, comma or semi-colon separated).
+       # Ports are optional if servers are listening on the standard port.
+       # Complete pool urls are preferred.
+       #
+       server = "http://cb01.blargs.com:8091/pools/ http://cb04.blargs.com:8091/pools/"
+
+       # Couchbase bucket name
+       bucket = "radius"
+
+       # Couchbase bucket password (optional)
+       #password = "password"
+
+       # Couchbase accounting document key (unlang supported)
+       acct_key = "radacct_%{%{Acct-Unique-Session-Id}:-%{Acct-Session-Id}}"
+
+       # Value for the 'docType' element in the json body for accounting documents
+       doctype = "radacct"
+
+       ## Accounting document expire time in seconds (0 = never)
+       expire = 2592000
+
+       #
+       # Map attribute names to json element names for accounting.
+       #
+       # Configuration items are in the format:
+       # <element name> = '<radius attribute>'
+       #
+       # Attribute names should be single quoted.
+       #
+       # Note: Atrributes not in this map will not be recorded.
+       #
+       map {
+               sessionId           = 'Acct-Session-Id'
+               uniqueId            = 'Acct-Unique-Session-Id'
+               lastStatus          = 'Acct-Status-Type'
+               authentic           = 'Acct-Authentic'
+               userName            = 'User-Name'
+               strippedUserName    = 'Stripped-User-Name'
+               strippedUserDomain  = 'Stripped-User-Domain'
+               realm               = 'Realm'
+               nasIpAddress        = 'NAS-IP-Address'
+               nasIdentifier       = 'NAS-Identifier'
+               nasPort             = 'NAS-Port'
+               calledStationId     = 'Called-Station-Id'
+               calledStationSSID   = 'Called-Station-SSID'
+               callingStationId    = 'Calling-Station-Id'
+               framedIpAddress     = 'Framed-IP-Address'
+               nasPortType         = 'NAS-Port-Type'
+               connectInfo         = 'Connect-Info'
+               sessionTime         = 'Acct-Session-Time'
+               inputPackets        = 'Acct-Input-Packets'
+               outputPackets       = 'Acct-Output-Packets'
+               inputOctets         = 'Acct-Input-Octets'
+               outputOctets        = 'Acct-Output-Octets'
+               inputGigawords      = 'Acct-Input-Gigawords'
+               outputGigawords     = 'Acct-Output-Gigawords'
+               lastUpdated         = 'Event-Timestamp'
+       }
+
+       # Couchbase document key for user documents (unlang supported)
+       user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"
+
+       #
+       #  The connection pool is new for 3.0, and will be used in many
+       #  modules, for all kinds of connection-related activity.
+       #
+       pool {
+               # Number of connections to start
+               start = ${thread[pool].start_servers}
+
+               # Minimum number of connections to keep open
+               min = ${thread[pool].min_spare_servers}
+
+               # Maximum number of connections
+               #
+               # If these connections are all in use and a new one
+               # is requested, the request will NOT get a connection.
+               #
+               # NOTE: This should be greater than or equal to "min" above.
+               max = ${thread[pool].max_servers}
+
+               # Spare connections to be left idle
+               #
+               # NOTE: Idle connections WILL be closed if "idle_timeout"
+               # is set.  This should be less than or equal to "max" above.
+               spare = ${thread[pool].max_spare_servers}
+
+               # Number of uses before the connection is closed
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
+               uses = 0
+
+               # The lifetime (in seconds) of the connection
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
+               lifetime = 0
+
+               # The idle timeout (in seconds).  A connection which is
+               # unused for this length of time will be closed.
+               #
+               # NOTE: A setting of 0 means infinite (no timeout).
+               idle_timeout = 1200
+
+               # NOTE: All configuration settings are enforced.  If a
+               # connection is closed because of "idle_timeout",
+               # "uses", or "lifetime", then the total number of
+               # connections MAY fall below "min".  When that
+               # happens, it will open a new connection.  It will
+               # also log a WARNING message.
+               #
+               # The solution is to either lower the "min" connections,
+               # or increase lifetime/idle_timeout.
+       }
+}
index edd61cd..f235eb9 100644 (file)
@@ -53,7 +53,7 @@ detail {
        # The user that the server runs as must be in the specified
        # system group otherwise this will fail to work.
        #
-#      group = freerad
+#      group = ${security.group}
 
        #
        #  Every entry in the detail file has a header which
index 12ec296..8d459c0 100644 (file)
@@ -14,9 +14,15 @@ sqlippool dhcp_sqlippool {
        # Client's MAC address is mapped to Calling-Station-Id in policy.conf
        pool_key = "%{Calling-Station-Id}"
 
-       # For now, it only works with MySQL.
+       # For now, it works with MySQL.
        $INCLUDE ${modconfdir}/sql/ippool-dhcp/mysql/queries.conf
 
+       # It may also work with sqlite - this is very experimental.
+       # Comment out the above line and add the following include.
+       # To use sqlite you need to add '%' to safe_characters in
+       # raddb/mods-config/sql/main/sqlite/queries.conf.
+       # $INCLUDE ${modconfdir}/sql/ippool-dhcp/sqlite/queries.conf
+
        sqlippool_log_exists = "DHCP: Existing IP: %{reply:Framed-IP-Address} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
 
        sqlippool_log_success = "DHCP: Allocated IP: %{reply:Framed-IP-Address} from %{control:Pool-Name} (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} user %{User-Name})"
index 6d1ef0d..0fffa88 100644 (file)
@@ -60,10 +60,10 @@ eap {
 
        #
        #  Help prevent DoS attacks by limiting the number of
-       #  sessions that the server is tracking.  Most systems
-       #  can handle ~30 EAP sessions/s, so the default limit
-       #  of 4096 should be OK.
-       max_sessions = 4096
+       #  sessions that the server is tracking.  For simplicity,
+       #  this is taken from the "max_requests" directive in
+       #  radiusd.conf.
+       max_sessions = ${max_requests}
 
        # Supported EAP-types
 
index c7a813b..c14992f 100644 (file)
@@ -4,6 +4,8 @@
 
 # Livingston-style 'users' file
 #
+# See "man users" for more information.
+#
 files {
        # Search for files in a subdirectory of mods-config which
        # matches this instance of the files module.
@@ -12,21 +14,17 @@ files {
        # The default key attribute to use for matches.  The content
        # of this attribute is used to match the "name" of the
        # entry.
-       #key = "%{Stripped-User-Name:-%{User-Name}}"
+       #key = "%{%{Stripped-User-Name}:-%{User-Name}}"
 
-       # Sets a common file for all sections which do not have
-       # specific files configured. It's recommended that
-       # per section instances of 'files' are used, as per section
-       # files will be deprecated in a future release.
+       #  The old "users" style file is now located here.
        filename = ${moddir}/authorize
 
+       #  This is accepted for backwards compatibility
+       #  It will be removed in a future release.
        usersfile = ${moddir}/authorize
+
+       #  These are accepted for backwards compatibility.
+       #  They will be renamed in a future release.
        acctusersfile = ${moddir}/accounting
        preproxy_usersfile = ${moddir}/pre-proxy
-
-       #  If you want to use the old Cistron 'users' file
-       #  with FreeRADIUS, you should change the next line
-       #  to 'compat = cistron'.  You can the copy your 'users'
-       #  file from Cistron.
-       compat = no
 }
index 5a74e9d..5340540 100644 (file)
@@ -18,7 +18,7 @@ idn {
        #
        #  Allow use of unassigned Unicode code points.
        #
-       allow_unassigned = no
+       allow_unassigned = no
 
        #
        #  Prohibit underscores and other invalid characters in domain
index d52dc9b..1d3305b 100644 (file)
 #              DEFAULT Group == teachers, Pool-Name := "teachers"
 #              DEFAULT Group == other, Pool-Name := "DEFAULT"
 #
-# ********* IF YOU CHANGE THE RANGE PARAMETERS YOU MUST *********
-# ********* THEN ERASE THE DB FILES                     *********
+# Note: If you change the range parameters you must then erase the
+#       db files.
 #
 ippool main_pool {
+       #  The main db file used to allocate addresses.
+       filename = ${db_dir}/db.ippool
 
-       #  range-start,range-stop:
-       #       The start and end ip addresses for this pool.
+       #  The start and end ip addresses for this pool.
        range_start = 192.0.2.1
        range_stop = 192.0.2.254
 
-       #  netmask:
-       #       The network mask used for this pool.
+       #  The network mask used for this pool.
        netmask = 255.255.255.0
 
-       #  cache_size:
-       #       The gdbm cache size for the db files. Should
-       #       be equal to the number of ip's available in
-       #       the ip pool
+       #  The gdbm cache size for the db files. Should
+       #  be equal to the number of ip's available in
+       #  the ip pool
        cache_size = 800
 
-       # session-db:
-       #       The main db file used to allocate addresses.
-       session_db = ${db_dir}/db.ippool
-
-       # ip-index:
-       #       Helper db index file used in multilink
+       #  Helper db index file used in multilink
        ip_index = ${db_dir}/db.ipindex
 
-       # override:
-       #       If set, the Framed-IP-Address already in the
-       #       reply (if any) will be discarded, and replaced
-       #       with a Framed-IP-Address assigned here.
+       #  If set, the Framed-IP-Address already in the
+       #  reply (if any) will be discarded, and replaced
+       #  ith a Framed-IP-Address assigned here.
        override = no
 
-       # maximum-timeout:
-       #       Specifies the maximum time in seconds that an
-       #       entry may be active.  If set to zero, means
-       #       "no timeout".  The default value is 0
+       #  Specifies the maximum time in seconds that an
+       #  entry may be active.  If set to zero, means
+       #  "no timeout".  The default value is 0
        maximum_timeout = 0
 
-       # key:
-       #       The key to use for the session database (which
-       #       holds the allocated ip's) normally it should
-       #       just be the nas ip/port (which is the default).
+       #  The key to use for the session database (which
+       #  holds the allocated ip's) normally it should
+       #  just be the nas ip/port (which is the default).
        #
-       #       If your NAS sends the same value of NAS-Port
-       #       all requests, the key should be based on some
-       #       other attribute that is in ALL requests, AND
-       #       is unique to each machine needing an IP address.
-       #key = "%{NAS-IP-Address} %{NAS-Port}"
+       #  If your NAS sends the same value of NAS-Port
+       #  all requests, the key should be based on some
+       #  other attribute that is in ALL requests, AND
+       #  is unique to each machine needing an IP address.
+#      key = "%{NAS-IP-Address} %{NAS-Port}"
 }
index d17b8b8..6868ff3 100644 (file)
@@ -16,44 +16,52 @@ krb5 {
        #
        #  The context pool is only used if the underlying libkrb5 reported
        #  that it was thread safe at compile time.
+       #
        pool {
-               # Number of contexts to create
-               start = 10
+               # Number of connections to start
+               start = ${thread[pool].start_servers}
 
-               # Minimum number of contexts to keep available
-               min = 4
+               # Minimum number of connections to keep open
+               min = ${thread[pool].min_spare_servers}
 
-               # Maximum number of contexts
+               # Maximum number of connections
                #
-               # If these contexts are all in use and a new one
+               # If these connections are all in use and a new one
                # is requested, the request will NOT get a connection.
-               max = 10
+               #
+               # NOTE: This should be greater than or equal to "min" above.
+               max = ${thread[pool].max_servers}
 
-               # Spare contexts to be left idle
+               # Spare connections to be left idle
                #
-               # NOTE: Idle contexts WILL be closed if "idle_timeout"
-               # is set.
-               spare = 3
+               # NOTE: Idle connections WILL be closed if "idle_timeout"
+               # is set.  This should be less than or equal to "max" above.
+               spare = ${thread[pool].max_spare_servers}
 
-               # Number of uses before the context is freed
-               # 0 means "infinite"
+               # Number of uses before the connection is closed
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
                uses = 0
 
-               # The lifetime (in seconds) of the context
+               # The lifetime (in seconds) of the connection
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
                lifetime = 0
 
-               # idle timeout (in seconds).  A context which is
-               # unused for this length of time will be freed.
-               idle_timeout = 60
+               # The idle timeout (in seconds).  A connection which is
+               # unused for this length of time will be closed.
+               #
+               # NOTE: A setting of 0 means infinite (no timeout).
+               idle_timeout = 0
 
                # NOTE: All configuration settings are enforced.  If a
-               # context is closed because of "idle_timeout",
+               # connection is closed because of "idle_timeout",
                # "uses", or "lifetime", then the total number of
-               # contexts MAY fall below "min".  When that
-               # happens, it will create a new context.  It will
+               # connections MAY fall below "min".  When that
+               # happens, it will open a new connection.  It will
                # also log a WARNING message.
                #
-               # The solution is to either lower the "min" contexts,
+               # The solution is to either lower the "min" connections,
                # or increase lifetime/idle_timeout.
        }
 }
index 44b38ce..af3f155 100644 (file)
@@ -3,28 +3,19 @@
 #  $Id$
 
 #
-# Lightweight Directory Access Protocol (LDAP)
+#  Lightweight Directory Access Protocol (LDAP)
 #
 ldap {
-       #
-       #  Note that this needs to match the name in the LDAP
-       #  server certificate, if you're using ldaps.
-       #
-       #  The ldap client libraries can do fail-over from one
-       #  server to another.  Enable this by specifying
-       #  multiple host names, separated by commas.
-       #
-       #       e.g. server = "ldap1.example.org,ldap2.example.org"
-       #
-       #  Otherwise, it will use just one server.
-       server = "ldap.example.org"
+       #  Note that this needs to match the name(s) in the LDAP server
+       #  certificate, if you're using ldaps.  See OpenLDAP documentation
+       #  for the behavioral semantics of specifying more than one host.
+       server = "ldap.rrdns.example.org ldap.rrdns.example.org ldap.example.org"
 
-       #  Port to connect on, defaults to 389. Setting this to
-       #  636 will enable LDAPS if start_tls (see below) is not
-       #  able to be used.
+       #  Port to connect on, defaults to 389. Setting this to 636 will enable
+       #  LDAPS if start_tls (see below) is not able to be used.
 #      port = 389
 
-       # Administrator account for searching and possibly modifying.
+       #  Administrator account for searching and possibly modifying.
 #      identity = "cn=admin,dc=example,dc=org"
 #      password = mypass
 
@@ -33,8 +24,28 @@ ldap {
 #      base_dn = "dc=example,dc=org"
 
        #
+       #  Generic valuepair attribute
+       #
+       
+       #  If set, this will attribute will be retrieved in addition to any
+       #  mapped attributes.
+       #
+       #  Values should be in the format:
+       #       <radius attr> <op> <value>
+       #
+       #  Where:
+       #       <radius attr>:  Is the attribute you wish to create
+       #                       with any valid list and request qualifiers.
+       #       <op>:           Is any assignment attribute (=, :=, +=, -=).
+       #       <value>:        Is the value to parse into the new valuepair.
+       #                       If the attribute name is wrapped in double
+       #                       quotes it will be xlat expanded.
+#      valuepair_attribute = "radiusAttribute"
+
+       #
        #  Mapping of LDAP directory attributes to RADIUS dictionary attributes.
        #
+       
        #  WARNING: Although this format is almost identical to the unlang
        #  update section format, it does *NOT* mean that you can use other
        #  unlang constructs in module configuration files.
@@ -58,7 +69,6 @@ ldap {
        #  Note: LDAP attribute names should be single quoted unless you want
        #  the name value to be derived from an xlat expansion, or an
        #  attribute ref.
-       #
        update {
                control:Password-With-Header    += 'userPassword'
 #              control:NT-Password             := 'ntPassword'
@@ -66,47 +76,47 @@ ldap {
 #              reply:Tunnel-Type               := 'radiusTunnelType'
 #              reply:Tunnel-Medium-Type        := 'radiusTunnelMediumType'
 #              reply:Tunnel-Private-Group-ID   := 'radiusTunnelPrivategroupId'
-       }
 
-       #
-       #  Generic valuepair attribute
-       #  If set, this will attribute will be retrieved in addition to any
-       #  mapped attributes.
-       #
-       #  Values should be in the format:
-       #       <radius attr> <op> <value>
-       #
-       #  Where:
-       #       <radius attr>:  Is the attribute you wish to create
-       #                       with any valid list and request qualifiers.
-       #       <op>:           Is any assignment attribute (=, :=, +=, -=).
-       #       <value>:        Is the value to parse into the new valuepair.
-       #                       If the attribute name is wrapped in double
-       #                       quotes it will be xlat expanded.
-       #
-#      valuepair_attribute = "radiusAttribute"
+               #  These are provided for backwards compatibility.
+               #  Where only a list is specified as the RADIUS attribute,
+               #  the value of the LDAP attribute is parsed as a valuepair
+               #  in the same format as the 'valuepair_attribute' (above).
+#              control:                        += 'radiusCheckAttributes'
+#              reply:                          += 'radiusReplyAttributes'
+       }
 
-       # Set to yes if you have eDirectory and want to use the universal
-       # password mechanism.
+       #  Set to yes if you have eDirectory and want to use the universal
+       #  password mechanism.
 #      edir = no
 
-       # Set to yes if you want to bind as the user after retrieving the
-       # Cleartext-Password. This will consume the login grace, and
-       # verify user authorization.
+       #  Set to yes if you want to bind as the user after retrieving the
+       #  Cleartext-Password. This will consume the login grace, and
+       #  verify user authorization.
 #      edir_autz = no
 
+       #  Note: set_auth_type was removed in v3.x.x
+       #  Equivalent functionality can be achieved by adding the following
+       #  stanza to the authorize {} section of your virtual server.
+       #
+       #    ldap
+       #    if ((ok || updated) && User-Password) {
+       #        update {
+       #            control:Auth-Type := ldap
+       #        }
+       #    }
+       
        #
        #  User object identification.
        #
        user {
-               #   Where to start searching in the tree for users
+               #  Where to start searching in the tree for users
                base_dn = "${..base_dn}"
 
                #  Filter for user objects, should be specific enough
                #  to identify a single user object.
                filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
 
-               # Search scope, may be 'base', 'one', sub' or 'children'
+               #  Search scope, may be 'base', 'one', sub' or 'children'
 #              scope = 'sub'
 
                #  If this is undefined, anyone is authorised.
@@ -142,7 +152,7 @@ ldap {
        #  User membership checking.
        #
        group {
-               #   Where to start searching in the tree for groups
+               #  Where to start searching in the tree for groups
                base_dn = "${..base_dn}"
 
                #  Filter for group objects, should match all available
@@ -180,12 +190,17 @@ ldap {
                #
                #  This feature is intended to be used with rlm_cache.
                #
-               #  If you with to use this feature, you should enable
+               #  If you wish to use this feature, you should enable
                #  the type that matches the format of your check items
                #  i.e. if your groups are specified as DNs then enable
                #  cacheable_dn else enable cacheable_name.
 #              cacheable_name = "no"
 #              cacheable_dn = "no"
+
+               #  Override the normal cache attribute (<inst>-LDAP-Group)
+               #  and create a custom attribute.  This can help if multiple
+               #  module instances are used in fail-over.
+#              cache_attribute = "LDAP-Cached-Membership"
        }
 
        #
@@ -229,8 +244,8 @@ ldap {
                #  Arbitrary attributes (accessible by %{client:<attr>}) are not yet supported.
                #
                #  The following attributes are required:
-               #    * identifier - IPv4 address, or IPv4 address with prefix, or hostname)
-               #    * secret - RADIUS shared secret
+               #    * identifier - IPv4 address, or IPv4 address with prefix, or hostname.
+               #    * secret - RADIUS shared secret.
                #
                #  The following attributes are optional:
                #    * shortname - Friendly name associated with the client
@@ -242,23 +257,22 @@ ldap {
                #  Schemas are available in doc/schemas/ldap for openldap and eDirectory
                #
                attribute {
-                       identifier = 'radiusClientIdentifier'
-                       secret = 'radiusClientSecret'
-#                      shortname = 'radiusClientShortname'
-#                      nas_type = 'radiusClientType'
-#                      virtual_server = 'radiusClientVirtualServer'
-#                      require_message_authenticator = 'radiusClientRequireMa'
+                       identifier                      = 'radiusClientIdentifier'
+                       secret                          = 'radiusClientSecret'
+#                      shortname                       = 'radiusClientShortname'
+#                      nas_type                        = 'radiusClientType'
+#                      virtual_server                  = 'radiusClientVirtualServer'
+#                      require_message_authenticator   = 'radiusClientRequireMa'
                }
        }
 
-       #
        #  Load clients on startup
-       #
 #      read_clients = no
 
        #
        #  Modify user object on receiving Accounting-Request
        #
+
        #  Useful for recording things like the last time the user logged
        #  in, or the Acct-Session-ID for CoA/DM.
        #
@@ -275,7 +289,6 @@ ldap {
        #  WARNING: If using the ':=' operator with a multi-valued LDAP
        #  attribute, all instances of the attribute will be removed and
        #  replaced with a single attribute.
-       #
        accounting {
                reference = "%{tolower:type.%{Acct-Status-Type}}"
 
@@ -309,6 +322,7 @@ ldap {
                }
        }
 
+       #
        #  LDAP connection-specific options.
        #
        #  These options set timeouts, keep-alives, etc. for the connections.
@@ -323,29 +337,28 @@ ldap {
                chase_referrals = yes
                rebind = yes
 
-               # seconds to wait for LDAP query to finish. default: 20
+               #  Seconds to wait for LDAP query to finish. default: 20
                timeout = 10
 
-               #  seconds LDAP server has to process the query (server-side
+               #  Seconds LDAP server has to process the query (server-side
                #  time limit). default: 20
                #
                #  LDAP_OPT_TIMELIMIT is set to this value.
                timelimit = 3
 
-               #
-               #  seconds to wait for response of the server. (network
-               #   failures) default: 10
+               #  Seconds to wait for response of the server. (network
+               #  failures) default: 10
                #
                #  LDAP_OPT_NETWORK_TIMEOUT is set to this value.
                net_timeout = 1
 
-               # LDAP_OPT_X_KEEPALIVE_IDLE
+               #  LDAP_OPT_X_KEEPALIVE_IDLE
                idle = 60
 
-               # LDAP_OPT_X_KEEPALIVE_PROBES
+               #  LDAP_OPT_X_KEEPALIVE_PROBES
                probes = 3
 
-               # LDAP_OPT_X_KEEPALIVE_INTERVAL
+               #  LDAP_OPT_X_KEEPALIVE_INTERVAL
                interval = 3
 
                #  ldap_debug: debug flag for LDAP SDK
@@ -402,46 +415,54 @@ ldap {
        #  The connection pool is new for 3.0, and will be used in many
        #  modules, for all kinds of connection-related activity.
        #
+       #  When the server is not threaded, the connection pool
+       #  limits are ignored, and only one connection is used.
        pool {
-               # Number of connections to start
+               #  Number of connections to start
                start = 5
 
-               # Minimum number of connections to keep open
+               #  Minimum number of connections to keep open
                min = 4
 
-               # Maximum number of connections
+               #  Maximum number of connections
                #
-               # If these connections are all in use and a new one
-               # is requested, the request will NOT get a connection.
-               max = 10
+               #  If these connections are all in use and a new one
+               #  is requested, the request will NOT get a connection.
+               #
+               #  Setting 'max' to LESS than the number of threads means
+               #  that some threads may starve, and you will see errors
+               #  like "No connections available and at max connection limit"
+               #
+               #  Setting 'max' to MORE than the number of threads means
+               #  that there are more connections than necessary.
+               max = ${thread[pool].max_servers}
 
-               # Spare connections to be left idle
+               #  Spare connections to be left idle
                #
-               # NOTE: Idle connections WILL be closed if "idle_timeout"
-               # is set.
+               #  NOTE: Idle connections WILL be closed if "idle_timeout"
+               #  is set.
                spare = 3
 
-               # Number of uses before the connection is closed
+               #  Number of uses before the connection is closed
                #
-               # 0 means "infinite"
+               #  0 means "infinite"
                uses = 0
 
-               # The lifetime (in seconds) of the connection
+               #  The lifetime (in seconds) of the connection
                lifetime = 0
 
-               # idle timeout (in seconds).  A connection which is
-               # unused for this length of time will be closed.
+               #  Idle timeout (in seconds).  A connection which is
+               #  unused for this length of time will be closed.
                idle_timeout = 60
 
-               # NOTE: All configuration settings are enforced.  If a
-               # connection is closed because of "idle_timeout",
-               # "uses", or "lifetime", then the total number of
-               # connections MAY fall below "min".  When that
-               # happens, it will open a new connection.  It will
-               # also log a WARNING message.
+               #  NOTE: All configuration settings are enforced.  If a
+               #  connection is closed because of "idle_timeout",
+               #  "uses", or "lifetime", then the total number of
+               #  connections MAY fall below "min".  When that
+               #  happens, it will open a new connection.  It will
+               #  also log a WARNING message.
                #
-               # The solution is to either lower the "min" connections,
-               # or increase lifetime/idle_timeout.
+               #  The solution is to either lower the "min" connections,
+               #  or increase lifetime/idle_timeout.
        }
-
 }
index 111bbe2..779752b 100644 (file)
@@ -26,12 +26,12 @@ linelog {
        permissions = 0600
 
        #
-       # The Unix group of the log file.
+       # The Unix group which owns the log file.
        #
        # The user that freeradius runs as must be in the specified
        # group, otherwise it will not be possible to set the group.
        #
-       # group = freerad
+       # group = ${security.group}
 
        #
        # If logging via syslog, the facility can be set here. Otherwise
@@ -63,30 +63,39 @@ linelog {
        #
        #  Reference the Packet-Type (Access-Request, etc.)  If it doesn't
        #  exist, reference the "format" entry, above.
-       reference = "%{%{Packet-Type}:-format}"
+       reference = "messages.%{%{Packet-Type}:-default}"
 
        #
-       #  Followed by a series of log messages.
-       Access-Request = "Requested access: %{User-Name}"
-       Access-Reject = "Rejected access: %{User-Name}"
-       Access-Challenge = "Sent challenge: %{User-Name}"
+       #  The messages defined here are taken from the "reference"
+       #  expansion, above.
+       #
+       messages {
+               default = "Unknown packet type %{Packet-Type}"
+
+               Access-Request = "Requested access: %{User-Name}"
+               Access-Reject = "Rejected access: %{User-Name}"
+               Access-Challenge = "Sent challenge: %{User-Name}"
+       }
+}
 
+#
+#  Another example, for accounting packets.
+#
+linelog log_accounting {
        #
-       #  The log messages can be grouped into sections and
-        #  sub-sections, too.  The "reference" item needs to have a "."
-       #  for every section.  e.g. reference = foo.bar will reference
-       #  the "foo" section, "bar" configuration item.
+       #  Used if the expansion of "reference" fails.
        #
+       format = ""
 
-       #
-       #  Used if:     reference = "foo.bar".
-       foo {
-           bar = "Example log.  Please ignore"
-       }
+       filename = ${logdir}/linelog-accounting
+
+       permissions = 0600
+
+       reference = "Accounting-Request.%{%{Acct-Status-Type}:-unknown}"
 
        #
        #  Another example:
-       #      reference = "Accounting-Request.%{%{Acct-Status-Type}:-unknown}"
+       #      
        #
        Accounting-Request {
                Start = "Connect: [%{User-Name}] (did %{Called-Station-Id} cli %{Calling-Station-Id} port %{NAS-Port} ip %{Framed-IP-Address})"
@@ -99,7 +108,6 @@ linelog {
                Accounting-Off = "NAS %{Packet-Src-IP-Address} (%{NAS-IP-Address}) just went offline"
 
                # don't log anything for other Acct-Status-Types.
-               unknown = ""
+               unknown = "NAS %{Packet-Src-IP-Address} (%{NAS-IP-Address}) sent unknown Acct-Status-Type %{Acct-Status-Type}"
        }
-
 }
index 5c199a1..0038ecd 100644 (file)
 #
 #  http://www.openldap.org/faq/data/cache/347.html
 pap {
-       #  The "auto_header" configuration item can be set to "yes".
-       #  In this case, the module will look inside of the User-Password
-       #  attribute for the headers {crypt}, {clear}, etc., and will
-       #  automatically create the attribute on the right-hand side,
-       #  with the correct value.
-       auto_header = no
-
        #  By default the server will use heuristics to try and automatically
        #  handle base64 or hex encoded passwords. This behaviour can be
        #  stopped by setting the following to "no".
index b852fa6..11bd224 100644 (file)
 
 #  An example configuration for using /etc/passwd.
 #
-#  We do NOT recommend using the configuration below.  See the "unix"
-#  module, or the "pam" module for a cleaner way to get system passwords.
-#  Using this configuration means that the server will find *only* those
-#  passwords which are in /etc/passwd, and will *ignore* all of the
-#  passwords in NIS, LDAP, etc.
+#  This is an example which will NOT WORK if you have shadow passwords,
+#  NIS, etc.  The "unix" module is normally responsible for reading
+#  system passwords.  You should use it instead of this example.
 #
 passwd etc_passwd {
        filename = /etc/passwd
index 31e0ba6..3d9428e 100644 (file)
@@ -20,15 +20,40 @@ perl {
        #  %RAD_CHECK           Check items
        #  %RAD_REQUEST         Attributes from the request
        #  %RAD_REPLY           Attributes for the reply
+       #  %RAD_REQUEST_PROXY   Attributes from the proxied request
+       #  %RAD_REQUEST_PROXY_REPLY Attributes from the proxy reply
+       #
+       #  The interface between FreeRADIUS and Perl is strings.
+       #  That is, attributes of type "octets" are converted to
+       #  printable strings, such as "0xabcdef".  If you want to
+       #  access the binary values of the attributes, you should
+       #  call the Perl "pack" function.  Then to send any binary
+       #  data back to FreeRADIUS, call the Perl "unpack" function,
+       #  so that the contents of the hashes are printable strings.
+       #
+       #  IP addresses are sent as strings, e.g. "192.0.2.25", and
+       #  not as a 4-byte binary value.  The same applies to other
+       #  attribute data types.
+       #
+       #  Attributes of type "string" are copied to Perl as-is.
+       #  They are not escaped or interpreted.
        #
        #  The return codes from functions in the perl_script
        #  are passed directly back to the server.  These
-       #  codes are defined in doc/configurable_failover,
-       #  src/include/modules.h (RLM_MODULE_REJECT, etc),
-       #  and are pre-defined in the 'example.pl' program
-       #  which is included.
+       #  codes are defined in mods-config/example.pl
        #
 
+       # You can define configuration items (and nested sub-sections) in perl "config" section.
+       # These items will be accessible in the perl script through %RAD_PERLCONF hash.
+       # For instance: $RAD_PERLCONF{'name'} $RAD_PERLCONF{'sub-config'}->{'name'}
+       #
+       #config {
+       #       name = "value"
+       #       sub-config {
+       #               name = "value of name from config.sub-config"
+       #       }
+       #}
+       
        #
        #  List of functions in the module to call.
        #  Uncomment and change if you want to use function
index 7c05a65..82319c0 100644 (file)
@@ -24,7 +24,7 @@ radutmp {
        #  characters, so that will limit the possible choices
        #  of keys.
        #
-       #  You may want instead: %{Stripped-User-Name:-%{User-Name}}
+       #  You may want instead: %{%{Stripped-User-Name}:-%{User-Name}}
        username = %{User-Name}
 
 
index 5ad25f1..8d9a041 100644 (file)
@@ -25,30 +25,55 @@ redis {
        #  connection pool.
        #
        pool {
-               # start this many connections
-               start = 1
+               # Number of connections to start
+               start = ${thread[pool].start_servers}
 
-               # Keep at least "min" connections open
-               min = 1
+               # Minimum number of connections to keep open
+               min = ${thread[pool].min_spare_servers}
 
-               # No more than "max" connections at any one time
-               max = 10
+               # Maximum number of connections
+               #
+               # If these connections are all in use and a new one
+               # is requested, the request will NOT get a connection.
+               #
+               # NOTE: This should be greater than or equal to "min" above.
+               max = ${thread[pool].max_servers}
 
-               # try to keep "spare" connections
-               spare = 0
+               # Spare connections to be left idle
+               #
+               # NOTE: Idle connections WILL be closed if "idle_timeout"
+               # is set.  This should be less than or equal to "max" above.
+               spare = ${thread[pool].max_spare_servers}
 
-               # If we have spare connections for "cleanup_delay" seconds,
-               # start deleting them
-               cleanup_delay = 300
+               # Number of uses before the connection is closed
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
+               uses = 0
 
-               # connections last no more than "lifetime" seconds.
+               # The lifetime (in seconds) of the connection
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
                lifetime = 86400
 
-               # close idle connections are "idle_timeout" seconds
+               # The pool is checked for free connections every
+               # "cleanup_interval".  If there are free connections,
+               # then one of them is closed.
+               cleanup_interval = 300
+
+               # The idle timeout (in seconds).  A connection which is
+               # unused for this length of time will be closed.
+               #
+               # NOTE: A setting of 0 means infinite (no timeout).
                idle_timeout = 600
 
-               # allow no more than "uses" queries through a connection.
-               # after that, close it and open a new one.
-               uses = 0
+               # NOTE: All configuration settings are enforced.  If a
+               # connection is closed because of "idle_timeout",
+               # "uses", or "lifetime", then the total number of
+               # connections MAY fall below "min".  When that
+               # happens, it will open a new connection.  It will
+               # also log a WARNING message.
+               #
+               # The solution is to either lower the "min" connections,
+               # or increase lifetime/idle_timeout.
        }
 }
index e56ea16..c6045d1 100644 (file)
@@ -38,6 +38,26 @@ rest {
        # comment out the configuration item below.
        connect_uri = "http://127.0.0.1/"
 
+       #
+       #  The following config items can be used in each of the sections.
+       #  The sections themselves reflect the sections in the server.
+       #  For example if you list rest in the authorize section of a virtual server,
+       #  the settings from the authorize section here will be used.
+       #
+       #  The following config items may be listed in any of the sections:
+       #    uri          - to send the request to.
+       #    method       - HTTP method to use, one of 'get', 'post', 'put', 'delete'.
+       #    body         - The format of the HTTP body sent to the remote server.
+       #                   May be 'none', 'post' or 'json', defaults to 'none'.
+       #    tls          - TLS settings for HTTPS.
+       #    auth         - HTTP auth method to use, one of 'none', 'srp', 'basic',
+       #                   'digest', 'digest-ie', 'gss-negotiate', 'ntlm',
+       #                   'ntlm-winbind', 'any', 'safe'. defaults to 'none'.
+       #    username     - User to authenticate as, will be expanded.
+       #    password     - Password to use for authentication, will be expanded.
+       #    require_auth - Require HTTP authentication.
+       #    timeout      - HTTP request timeout in seconds, defaults to 4.
+       #
        authorize {
                uri = "${..connect_uri}/user/%{User-Name}/mac/%{Called-Station-ID}?section=authorize"
                method = "get"
@@ -70,33 +90,39 @@ rest {
        #
        pool {
                # Number of connections to start
-               start = 5
+               start = ${thread[pool].start_servers}
 
                # Minimum number of connections to keep open
-               min = 4
+               min = ${thread[pool].min_spare_servers}
 
                # Maximum number of connections
                #
                # If these connections are all in use and a new one
                # is requested, the request will NOT get a connection.
-               max = 10
+               #
+               # NOTE: This should be greater than or equal to "min" above.
+               max = ${thread[pool].max_servers}
 
                # Spare connections to be left idle
                #
                # NOTE: Idle connections WILL be closed if "idle_timeout"
-               # is set.
-               spare = 3
+               # is set.  This should be less than or equal to "max" above.
+               spare = ${thread[pool].max_spare_servers}
 
                # Number of uses before the connection is closed
                #
-               # 0 means "infinite"
+               # NOTE: A setting of 0 means infinite (no limit).
                uses = 0
 
                # The lifetime (in seconds) of the connection
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
                lifetime = 0
 
-               # idle timeout (in seconds).  A connection which is
+               # The idle timeout (in seconds).  A connection which is
                # unused for this length of time will be closed.
+               #
+               # NOTE: A setting of 0 means infinite (no timeout).
                idle_timeout = 60
 
                # NOTE: All configuration settings are enforced.  If a
index 4997a80..130bfa2 100644 (file)
 #
 #
 
-#
-#  Several drivers accept specific options, to set them a config section
-#  matching the name of the driver should be added to the sql instance.
-#
-#  Driver specific options are:
-#
-#  sqlite {
-#      # Path to the sqlite database
-#      filename = "/my/sqlite/database.db"
-#
-#      # If the file above does not exist and bootstrap is set
-#      # a new database file will be created, and the SQL statements
-#      # contained within the file will be executed.
-#      bootstrap = "/my/sqlite/schema.sql"
-#  }
-#
-#  mysql {
-#      # If any of the below files are set tls encryption is enabled
-#      tls {
-#              ca_file = "/etc/ssl/certs/my_ca.crt"
-#              ca_path = "/etc/ssl/certs/"
-#              certificate_file = "/etc/ssl/certs/private/client.crt"
-#              private_key_file = "/etc/ssl/certs/private/client.key"
-#              cipher = "DHE-RSA-AES256-SHA:AES128-SHA"
-#      }
-#  }
-
 sql {
        # The sub-module to use to execute queries. This should match
        # the database you're attempting to connect to.
@@ -57,6 +30,41 @@ sql {
        #
        driver = "rlm_sql_null"
 
+#
+#      Several drivers accept specific options, to set them, a
+#      config section with the the name as the driver should be added
+#      to the sql instance.
+#
+#      Driver specific options are:
+#
+#      sqlite {
+#              # Path to the sqlite database
+#              filename = "/my/sqlite/database.db"
+#
+#              # If the file above does not exist and bootstrap is set
+#              # a new database file will be created, and the SQL statements
+#              # contained within the file will be executed.
+#              bootstrap = "/my/sqlite/schema.sql"
+#      }
+#
+#      mysql {
+#              # If any of the below files are set tls encryption is enabled
+#              tls {
+#                      ca_file = "/etc/ssl/certs/my_ca.crt"
+#                      ca_path = "/etc/ssl/certs/"
+#                      certificate_file = "/etc/ssl/certs/private/client.crt"
+#                      private_key_file = "/etc/ssl/certs/private/client.key"
+#                      cipher = "DHE-RSA-AES256-SHA:AES128-SHA"
+#              }
+#      }
+#
+#      postgres {
+#              # Send application_name to the postgres server
+#              # Only supported in PG 9.0 and greater. Defaults to yes.
+#              send_application_name = yes
+#      }
+#
+
        # The dialect of SQL you want to use, this should usually match
        # the driver you selected above.
        #
@@ -124,6 +132,9 @@ sql {
        #  The connection pool is new for 3.0, and will be used in many
        #  modules, for all kinds of connection-related activity.
        #
+       # When the server is not threaded, the connection pool
+       # limits are ignored, and only one connection is used.
+       #
        pool {
                # Number of connections to start
                start = 5
@@ -135,7 +146,15 @@ sql {
                #
                # If these connections are all in use and a new one
                # is requested, the request will NOT get a connection.
-               max = 10
+               #
+               # Setting 'max' to LESS than the number of threads means
+               # that some threads may starve, and you will see errors
+               # like "No connections available and at max connection limit"
+               #
+               # Setting 'max' to MORE than the number of threads means
+               # that there are more connections than necessary.
+               #
+               max = ${thread[pool].max_servers}
 
                # Spare connections to be left idle
                #
index cb47892..89d6d40 100644 (file)
@@ -40,7 +40,7 @@
 #
 sqlcounter dailycounter {
        sql_module_instance = sql
-       dialect = ${sql_module_instance}.dialect
+       dialect = ${modules.sql.dialect}
 
        counter_name = Daily-Session-Time
        check_name = Max-Daily-Session
@@ -54,7 +54,7 @@ sqlcounter dailycounter {
 
 sqlcounter monthlycounter {
        sql_module_instance = sql
-       dialect = ${sql_module_instance}.dialect
+       dialect = ${modules.sql.dialect}
 
        counter_name = Monthly-Session-Time
        check_name = Max-Monthly-Session
@@ -67,9 +67,9 @@ sqlcounter monthlycounter {
 
 sqlcounter noresetcounter {
        sql_module_instance = sql
-       dialect = ${sql_module_instance}.dialect
+       dialect = ${modules.sql.dialect}
 
-        counter_name = Max-All-Session-Time
+       counter_name = Max-All-Session-Time
        check_name = Max-All-Session
        key = User-Name
        reset = never
@@ -84,12 +84,12 @@ sqlcounter noresetcounter {
 #  attribute.
 sqlcounter expire_on_login {
        sql_module_instance = sql
-       dialect = ${sql_module_instance}.dialect
+       dialect = ${modules.sql.dialect}
 
-        counter_name = Expire-After-Initial-Login
-        check_name = Expire-After
-        key = User-Name
-        reset = never
+       counter_name = Expire-After-Initial-Login
+       check_name = Expire-After
+       key = User-Name
+       reset = never
 
        $INCLUDE ${modconfdir}/sql/counter/${dialect}/${.:instance}.conf
 }
diff --git a/raddb/mods-available/unbound b/raddb/mods-available/unbound
new file mode 100644 (file)
index 0000000..9fd9b1f
--- /dev/null
@@ -0,0 +1,4 @@
+unbound dns {
+       # filename = "${raddbdir}/mods-config/unbound/default.conf"
+       # timeout = 3000
+}
index 916df4c..5165139 100644 (file)
@@ -4,13 +4,15 @@
 
 # Unix /etc/passwd style authentication
 #
+#  This module calls the system functions to get the "known good"
+#  password.  This password is usually in the "crypt" form, and is
+#  incompatible with CHAP, MS-CHAP, PEAP, etc.
+#
+#  If passwords are in /etc/shadow, you will need to set the "group"
+#  configuration in radiusd.conf.  Look for "shadow", and follow the
+#  instructions there.
+#
 unix {
-       #  As of 1.1.0, the Unix module no longer reads,
-       #  or caches /etc/passwd, /etc/shadow, or /etc/group.
-       #  If you wish to cache those files, see the passwd
-       #  module.
-       #
-
        #
        #  The location of the "wtmp" file.
        #  The only use for 'radlast'.  If you don't use
diff --git a/raddb/mods-available/unpack b/raddb/mods-available/unpack
new file mode 100644 (file)
index 0000000..2a1e130
--- /dev/null
@@ -0,0 +1,42 @@
+# -*- text -*-
+#
+#  $Id$
+
+#
+#  This module is useful only for 'xlat'.  To use it,
+#  add it to the raddb/mods-enabled/ directory.  Then,
+#  use it on the right-hand side of a variable assignment.
+#
+#  ... = "%{unpack:data 1 integer}"
+#
+#  The arguments are three fields:
+#
+#      data
+#              Either &Attribute-Name
+#              the name of the attribute to unpack.
+#              MUST be a "string" or "octets" type.
+#
+#              or 0xabcdef
+#              e.g. hex data.
+#
+#      1
+#              The offset into the string from which
+#              it starts unpacking.  The offset starts
+#              at zero, for the first attribute.
+#
+#      integer
+#              the data type to unpack at that offset.
+#              e.g. integer, ipaddr, byte, short, etc.
+#
+#  e.g. if we have Class = 0x00000001020304, then
+#
+#      %{unpack:&Class 4 short}
+#
+#  will unpack octets 4 and 5 as a "short", which has
+#  value 0x0304.
+#
+#  This module is used when vendors put multiple fields
+#  into one attribute of type "octets".
+#
+unpack {
+}
index 6bd43db..00d842b 100644 (file)
@@ -11,6 +11,15 @@ yubikey {
 #      id_length = 12
 
        #
+       #  If true, the authorize method of rlm_yubikey will attempt to split the
+       #  value of User-Password, into the user's password, and the OTP token.
+       #
+       #  If enabled and successful, the value of User-Password will be truncated
+       #  and request:Yubikey-OTP will be added.
+       #
+#      split = yes
+
+       #
        #  Decrypt mode - Tokens will be decrypted and processed locally
        #
        #  The module itself does not provide persistent storage as this
@@ -85,33 +94,39 @@ yubikey {
                #
                pool {
                        # Number of connections to start
-                       start = 5
+                       start = ${thread[pool].start_servers}
 
                        # Minimum number of connections to keep open
-                       min = 4
+                       min = ${thread[pool].min_spare_servers}
 
                        # Maximum number of connections
                        #
                        # If these connections are all in use and a new one
                        # is requested, the request will NOT get a connection.
-                       max = 10
+                       #
+                       # NOTE: This should be greater than or equal to "min" above.
+                       max = ${thread[pool].max_servers}
 
                        # Spare connections to be left idle
                        #
                        # NOTE: Idle connections WILL be closed if "idle_timeout"
-                       # is set.
-                       spare = 3
+                       # is set.  This should be less than or equal to "max" above.
+                       spare = ${thread[pool].max_spare_servers}
 
                        # Number of uses before the connection is closed
                        #
-                       # 0 means "infinite"
+                       # NOTE: A setting of 0 means infinite (no limit).
                        uses = 0
 
                        # The lifetime (in seconds) of the connection
+                       #
+                       # NOTE: A setting of 0 means infinite (no limit).
                        lifetime = 0
 
-                       # idle timeout (in seconds).  A connection which is
+                       # The idle timeout (in seconds).  A connection which is
                        # unused for this length of time will be closed.
+                       #
+                       # NOTE: A setting of 0 means infinite (no timeout).
                        idle_timeout = 60
 
                        # Cycle over all connections in a pool instead of concentrating
index fafac84..322d33a 100644 (file)
@@ -20,4 +20,4 @@
 #  Replace the User-Name with the Stripped-User-Name, if it exists.
 #
 #DEFAULT
-#      User-Name := "%{Stripped-User-Name:-%{User-Name}}"
+#      User-Name := "%{%{Stripped-User-Name}:-%{User-Name}}"
index 0f5d15a..7292e23 100644 (file)
@@ -28,4 +28,4 @@
 #  User-Name from the original request.
 #
 #DEFAULT
-#      User-Name := `%{Stripped-User-Name:-%{User-Name}}`
+#      User-Name := `%{%{Stripped-User-Name}:-%{User-Name}}`
similarity index 96%
rename from src/modules/rlm_perl/example.pl
rename to raddb/mods-config/perl/example.pl
index d2554eb..ac95aca 100644 (file)
@@ -42,6 +42,8 @@ our (%RAD_REQUEST, %RAD_REPLY, %RAD_CHECK);
 #my %RAD_REPLY;
 #This is for check items
 #my %RAD_CHECK;
+# This is configuration items from "config" perl module configuration section
+#my %RAD_PERLCONF;
 
 #
 # This the remapping of return values
@@ -66,14 +68,6 @@ use constant L_ERR=>   4;
 use constant   L_PROXY=> 5;
 use constant   L_ACCT=>  6;
 
-# Same as src/include/radiusd.h
-use constant   L_DBG=>   1;
-use constant   L_AUTH=>  2;
-use constant   L_INFO=>  3;
-use constant   L_ERR=>   4;
-use constant   L_PROXY=> 5;
-use constant   L_ACCT=>  6;
-
 #  Global variables can persist across different calls to the module.
 #
 #
index 055451d..97c4661 100644 (file)
@@ -1,21 +1,33 @@
-       # This query properly handles calls that span from the
-       # previous reset period into the current period but
-       # involves more work for the SQL server than those
-       # below
-       query = "SELECT SUM(acctsessiontime - \
-                 GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
-                 FROM radacct WHERE username = '%{${key}}' AND \
-                 UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'"
+#
+#  This query properly handles calls that span from the
+#  previous reset period into the current period but
+#  involves more work for the SQL server than those
+#  below
+#
+query = "\
+       SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
+       FROM radacct \
+       WHERE username = '%{${key}}' \
+       AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'"
 
-       # This query ignores calls that started in a previous
-       # reset period and continue into into this one. But it
-       # is a little easier on the SQL server
-#      query = "SELECT SUM(acctsessiontime) FROM radacct WHERE \
-#                username = '%{${key}}' AND acctstarttime > FROM_UNIXTIME('%b')"
+#
+#  This query ignores calls that started in a previous
+#  reset period and continue into into this one. But it
+#  is a little easier on the SQL server
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct \
+#      WHERE username = '%{${key}}' \
+#      AND acctstarttime > FROM_UNIXTIME('%b')"
 
-       # This query is the same as above, but demonstrates an
-       # additional counter parameter '%e' which is the
-       # timestamp for the end of the period
-#      query = "SELECT SUM(acctsessiontime) FROM radacct \
-#                WHERE username = '%{${key}}' AND acctstarttime BETWEEN \
-#                FROM_UNIXTIME('%b') AND FROM_UNIXTIME('%e')"
+#
+#  This query is the same as above, but demonstrates an
+#  additional counter parameter '%e' which is the
+#  timestamp for the end of the period
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct \
+#      WHERE username = '%{${key}}' \
+#      AND acctstarttime BETWEEN FROM_UNIXTIME('%b') AND FROM_UNIXTIME('%e')"
index dc2e912..97e1bc5 100644 (file)
@@ -1,5 +1,6 @@
-        query = "SELECT TIME_TO_SEC(TIMEDIFF(NOW(), acctstarttime)) \
-                 FROM radacct \
-                 WHERE UserName='%{${key}}' \
-                 ORDER BY acctstarttime \
-                 LIMIT 1;"
+query = "\
+       SELECT TIMESTAMPDIFF(SECOND, acctstarttime, NOW()) \
+       FROM radacct \
+       WHERE UserName='%{${key}}' \
+       ORDER BY acctstarttime \
+       LIMIT 1;"
index f64179c..6d93d15 100644 (file)
@@ -1,21 +1,34 @@
-       # This query properly handles calls that span from the
-       # previous reset period into the current period but
-       # involves more work for the SQL server than those
-       # below
-       query = "SELECT SUM(acctsessiontime - \
-                 GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
-                 FROM radacct WHERE username='%{${key}}' AND \
-                 UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'"
+#
+#  This query properly handles calls that span from the
+#  previous reset period into the current period but
+#  involves more work for the SQL server than those
+#  below
+#
+query = "\
+       SELECT SUM(acctsessiontime - GREATEST((%b - UNIX_TIMESTAMP(acctstarttime)), 0)) \
+       FROM radacct \
+       WHERE username='%{${key}}' \
+       AND UNIX_TIMESTAMP(acctstarttime) + acctsessiontime > '%b'"
 
-       # This query ignores calls that started in a previous
-       # reset period and continue into into this one. But it
-       # is a little easier on the SQL server
-#      query = "SELECT SUM(acctsessiontime) FROM radacct WHERE \
-#                username='%{${key}}' AND acctstarttime > FROM_UNIXTIME('%b')"
+#
+#  This query ignores calls that started in a previous
+#  reset period and continue into into this one. But it
+#  is a little easier on the SQL server
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct\
+#      WHERE username='%{${key}}' \
+#      AND acctstarttime > FROM_UNIXTIME('%b')"
 
-       # This query is the same as above, but demonstrates an
-       # additional counter parameter '%e' which is the
-       # timestamp for the end of the period
-#      query = "SELECT SUM(acctsessiontime) FROM radacct \
-#                WHERE username='%{${key}}' AND acctstarttime BETWEEN \
-#                FROM_UNIXTIME('%b') AND FROM_UNIXTIME('%e')"
+#
+#  This query is the same as above, but demonstrates an
+#  additional counter parameter '%e' which is the
+#  timestamp for the end of the period
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct \
+#      WHERE username='%{${key}}' \
+#      AND acctstarttime BETWEEN FROM_UNIXTIME('%b') \
+#      AND FROM_UNIXTIME('%e')"
index 0bedf0b..abcb21b 100644 (file)
@@ -1,3 +1,4 @@
-        query = "SELECT IFNULL(SUM(AcctSessionTime),0) \
-                FROM radacct \
-                WHERE UserName='%{${key}}'"
+query = "\
+       SELECT IFNULL(SUM(AcctSessionTime),0) \
+       FROM radacct \
+       WHERE UserName='%{${key}}'"
index 48ef816..64802bf 100644 (file)
@@ -1,21 +1,34 @@
-       # This query properly handles calls that span from the
-       # previous reset period into the current period but
-       # involves more work for the SQL server than those
-       # below
-       query = "SELECT SUM(AcctSessionTime - \
-                 GREATER((%b - AcctStartTime::ABSTIME::INT4), 0)) \
-                 FROM radacct WHERE UserName='%{${key}}' AND \
-                 AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%b'"
+#
+#  This query properly handles calls that span from the
+#  previous reset period into the current period but
+#  involves more work for the SQL server than those
+#  below
+#
+query = "\
+       SELECT SUM(AcctSessionTime - GREATER((%b - AcctStartTime::ABSTIME::INT4), 0)) \
+       FROM radacct \
+       WHERE UserName='%{${key}}' \
+       AND AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%b'"
 
-       # This query ignores calls that started in a previous
-       # reset period and continue into into this one. But it
-       # is a little easier on the SQL server
-#      query = "SELECT SUM(AcctSessionTime) FROM radacct WHERE \
-#                UserName='%{${key}}' AND AcctStartTime::ABSTIME::INT4 > '%b'"
+#
+#  This query ignores calls that started in a previous
+#  reset period and continue into into this one. But it
+#  is a little easier on the SQL server
+#
+#query = "\
+#      SELECT SUM(AcctSessionTime) \
+#      FROM radacct \
+#      WHERE UserName='%{${key}}' \
+#      AND AcctStartTime::ABSTIME::INT4 > '%b'"
 
-       # This query is the same as above, but demonstrates an
-       # additional counter parameter '%e' which is the
-       # timestamp for the end of the period
-#      query = "SELECT SUM(AcctSessionTime) FROM radacct \
-#                WHERE UserName='%{${key}}' AND AcctStartTime::ABSTIME::INT4 \
-#                BETWEEN '%b' AND '%e'"
+#
+#  This query is the same as above, but demonstrates an
+#  additional counter parameter '%e' which is the
+#  timestamp for the end of the period
+#
+#query = "\
+#      SELECT SUM(AcctSessionTime) \
+#      FROM radacct \
+#      WHERE UserName='%{${key}}' \
+#      AND AcctStartTime::ABSTIME::INT4 BETWEEN '%b' \
+#      AND '%e'"
index dc2e912..c4ce096 100644 (file)
@@ -1,5 +1,6 @@
-        query = "SELECT TIME_TO_SEC(TIMEDIFF(NOW(), acctstarttime)) \
-                 FROM radacct \
-                 WHERE UserName='%{${key}}' \
-                 ORDER BY acctstarttime \
-                 LIMIT 1;"
+query = "\
+       SELECT TIME_TO_SEC(TIMEDIFF(NOW(), acctstarttime)) \
+       FROM radacct \
+       WHERE UserName='%{${key}}' \
+       ORDER BY acctstarttime \
+       LIMIT 1;"
index f88680c..eb831a4 100644 (file)
@@ -1,22 +1,31 @@
-       # This query properly handles calls that span from the
-       # previous reset period into the current period but
-       # involves more work for the SQL server than those
-       # below
-       query = "SELECT SUM(AcctSessionTime - \
-                GREATER((%b - AcctStartTime::ABSTIME::INT4), 0)) \
-                FROM radacct WHERE UserName='%{${key}}' AND \
-                AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%b'"
+#  This query properly handles calls that span from the
+#  previous reset period into the current period but
+#  involves more work for the SQL server than those
+#  below
+query = "\
+       SELECT SUM(AcctSessionTime - GREATER((%b - AcctStartTime::ABSTIME::INT4), 0)) \
+       FROM radacct \
+       WHERE UserName='%{${key}}' \
+       AND AcctStartTime::ABSTIME::INT4 + AcctSessionTime > '%b'"
 
+#
+#  This query ignores calls that started in a previous
+#  reset period and continue into into this one. But it
+#  is a little easier on the SQL server
+#
+#query = "\
+#      SELECT SUM(AcctSessionTime) \
+#      FROM radacct \
+#      WHERE UserName='%{${key}}' \
+#      AND AcctStartTime::ABSTIME::INT4 > '%b'"
 
-       # This query ignores calls that started in a previous
-       # reset period and continue into into this one. But it
-       # is a little easier on the SQL server
-#      query = "SELECT SUM(AcctSessionTime) FROM radacct WHERE \
-#                UserName='%{${key}}' AND AND AcctStartTime::ABSTIME::INT4 > '%b'"
-
-       # This query is the same as above, but demonstrates an
-       # additional counter parameter '%e' which is the
-       # timestamp for the end of the period
-#      query = "SELECT SUM(AcctSessionTime) FROM radacct \
-#                WHERE UserName='%{${key}}' AND AcctStartTime::ABSTIME::INT4 \
-#                BETWEEN '%b' AND '%e'"
+#
+#  This query is the same as above, but demonstrates an
+#  additional counter parameter '%e' which is the
+#  timestamp for the end of the period
+#
+#query = "\
+#      SELECT SUM(AcctSessionTime) \
+#      FROM radacct \
+#      WHERE UserName='%{${key}}' \
+#      AND AcctStartTime::ABSTIME::INT4 BETWEEN '%b' AND '%e'"
index 6b680aa..ac5182e 100644 (file)
@@ -1 +1,4 @@
-       query = "SELECT SUM(AcctSessionTime) FROM radacct WHERE UserName='%{${key}}'"
+query = "\
+       SELECT SUM(AcctSessionTime) \
+       FROM radacct \
+       WHERE UserName='%{${key}}'"
index a4422e3..6befdcc 100644 (file)
@@ -1,21 +1,33 @@
-       # This query properly handles calls that span from the
-       # previous reset period into the current period but
-       # involves more work for the SQL server than those
-       # below
-       query = "SELECT SUM(acctsessiontime - \
-                 GREATEST((%b - strftime('%s', acctstarttime)), 0)) \
-                 FROM radacct WHERE username = '%{${key}}' AND \
-                 (strftime('%s', acctstarttime) + acctsessiontime) > %b"
+#
+#  This query properly handles calls that span from the
+#  previous reset period into the current period but
+#  involves more work for the SQL server than those
+#  below
+#
+query = "\
+       SELECT SUM(acctsessiontime - GREATEST((%b - strftime('%%s', acctstarttime)), 0)) \
+       FROM radacct \
+       WHERE username = '%{${key}}' \
+       AND (strftime('%%s', acctstarttime) + acctsessiontime) > %b"
 
-       # This query ignores calls that started in a previous
-       # reset period and continue into into this one. But it
-       # is a little easier on the SQL server
-#      query = "SELECT SUM(acctsessiontime) FROM radacct WHERE \
-#                username = '%{${key}}' AND acctstarttime > %b"
+#
+#  This query ignores calls that started in a previous
+#  reset period and continue into into this one. But it
+#  is a little easier on the SQL server
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct \
+#      WHERE \username = '%{${key}}' \
+#      AND acctstarttime > %b"
 
-       # This query is the same as above, but demonstrates an
-       # additional counter parameter '%e' which is the
-       # timestamp for the end of the period
-#      query = "SELECT SUM(acctsessiontime) FROM radacct \
-#                WHERE username = '%{${key}}' AND acctstarttime BETWEEN \
-#                %b AND %e"
+#
+#  This query is the same as above, but demonstrates an
+#  additional counter parameter '%e' which is the
+#  timestamp for the end of the period
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) FROM radacct \
+#      WHERE username = '%{${key}}' \
+#      AND acctstarttime BETWEEN %b \
+#      AND %e"
index 8a38f5a..f4e95a5 100644 (file)
@@ -1,5 +1,6 @@
-        query = "SELECT GREATEST(strftime('%s', NOW()) - strftime('%s', acctstarttime), 0) AS expires \
-                 FROM radacct \
-                 WHERE username = '%{${key}}' \
-                 ORDER BY acctstarttime \
-                 LIMIT 1;"
+query = "\
+       SELECT GREATEST(strftime('%%s', NOW()) - strftime('%%s', acctstarttime), 0) AS expires \
+       FROM radacct \
+       WHERE username = '%{${key}}' \
+       ORDER BY acctstarttime \
+       LIMIT 1;"
index a4422e3..5bb8140 100644 (file)
@@ -1,21 +1,34 @@
-       # This query properly handles calls that span from the
-       # previous reset period into the current period but
-       # involves more work for the SQL server than those
-       # below
-       query = "SELECT SUM(acctsessiontime - \
-                 GREATEST((%b - strftime('%s', acctstarttime)), 0)) \
-                 FROM radacct WHERE username = '%{${key}}' AND \
-                 (strftime('%s', acctstarttime) + acctsessiontime) > %b"
+#
+#  This query properly handles calls that span from the
+#  previous reset period into the current period but
+#  involves more work for the SQL server than those
+#  below
+#
+query = "\
+       SELECT SUM(acctsessiontime - GREATEST((%b - strftime('%%s', acctstarttime)), 0)) \
+       FROM radacct \
+       WHERE username = '%{${key}}' AND \
+       (strftime('%%s', acctstarttime) + acctsessiontime) > %b"
 
-       # This query ignores calls that started in a previous
-       # reset period and continue into into this one. But it
-       # is a little easier on the SQL server
-#      query = "SELECT SUM(acctsessiontime) FROM radacct WHERE \
-#                username = '%{${key}}' AND acctstarttime > %b"
+#
+#  This query ignores calls that started in a previous
+#  reset period and continue into into this one. But it
+#  is a little easier on the SQL server
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct \
+#      WHERE username = '%{${key}}' \
+#      AND acctstarttime > %b"
 
-       # This query is the same as above, but demonstrates an
-       # additional counter parameter '%e' which is the
-       # timestamp for the end of the period
-#      query = "SELECT SUM(acctsessiontime) FROM radacct \
-#                WHERE username = '%{${key}}' AND acctstarttime BETWEEN \
-#                %b AND %e"
+#
+#  This query is the same as above, but demonstrates an
+#  additional counter parameter '%e' which is the
+#  timestamp for the end of the period
+#
+#query = "\
+#      SELECT SUM(acctsessiontime) \
+#      FROM radacct \
+#      WHERE username = '%{${key}}' \
+#      AND acctstarttime BETWEEN %b \
+#      AND %e"
index 65e5dbf..ac2d869 100644 (file)
@@ -1,3 +1,4 @@
-        query = "SELECT IFNULL(SUM(acctsessiontime),0) \
-                FROM radacct \
-                WHERE username = '%{${key}}'"
+query = "\
+       SELECT IFNULL(SUM(acctsessiontime),0) \
+       FROM radacct \
+       WHERE username = '%{${key}}'"
index 37d4e0d..f8f18ca 100644 (file)
@@ -1,8 +1,8 @@
 # -*- text -*-
-
-##
-##  Queries to update the CUI table.
-##
+#
+#  cui/mysql/queries.conf -- Queries to update a MySQL CUI table.
+#
+#  $Id$
 
 post-auth {
        query = "\
@@ -11,7 +11,9 @@ post-auth {
                VALUES \
                        ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', \
                        '%{User-Name}', '%{reply:Chargeable-User-Identity}', NULL) \
-               ON DUPLICATE KEY UPDATE lastaccounting='0000-00-00 00:00:00', cui='%{reply:Chargeable-User-Identity}'"
+               ON DUPLICATE KEY UPDATE \
+                       lastaccounting='0000-00-00 00:00:00', \
+                       cui='%{reply:Chargeable-User-Identity}'"
 
 }
 
@@ -23,26 +25,26 @@ accounting {
                                UPDATE ${....cui_table} SET \
                                        lastaccounting = CURRENT_TIMESTAMP \
                                WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
                interim-update {
                        query ="\
                                UPDATE ${....cui_table} SET \
                                        lastaccounting = CURRENT_TIMESTAMP \
                                WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
                stop {
                        query ="\
-                               DELETE FROM ${....cui_table} WHERE \
-                                       clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               DELETE FROM ${....cui_table} \
+                               WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
        }
 }
index f7b4aaf..6c2215f 100644 (file)
@@ -1,15 +1,16 @@
 # -*- text -*-
-
-##
-##  Queries to update the CUI table.
-##
+#
+#  cui/postgresql/queries.conf -- Queries to update a PostgreSQL CUI table.
+#
+#  $Id$
 
 post-auth {
        query = "\
                INSERT INTO ${..cui_table} \
                        (clientipaddress, callingstationid, username, cui) \
                VALUES \
-                       ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', '%{User-Name}', '%{reply:Chargeable-User-Identity}')"
+                       ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', \
+                       '%{User-Name}', '%{reply:Chargeable-User-Identity}')"
 
 }
 
@@ -21,26 +22,26 @@ accounting {
                                UPDATE ${....cui_table} SET \
                                        lastaccounting = now() \
                                WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
                interim-update {
                        query ="\
                                UPDATE ${....cui_table} SET \
                                        lastaccounting = now() \
                                WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
                stop {
                        query ="\
-                               DELETE FROM ${....cui_table} WHERE \
-                                       clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               DELETE FROM ${....cui_table} \
+                               WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
        }
 }
index 68cd221..3b24401 100644 (file)
@@ -9,6 +9,6 @@ CREATE TABLE cui (
 );
 
 CREATE RULE postauth_query AS ON INSERT TO cui
-        WHERE EXISTS(SELECT 1 FROM cui WHERE (username, clientipaddress, callingstationid)=(NEW.username, NEW.clientipaddress, NEW.callingstationid))
-        DO INSTEAD UPDATE cui SET lastaccounting ='-infinity'::timestamp with time zone, cui=NEW.cui WHERE (username, clientipaddress, callingstationid)=(NEW.username, NEW.clientipaddress, NEW.callingstationid);
+       WHERE EXISTS(SELECT 1 FROM cui WHERE (username, clientipaddress, callingstationid)=(NEW.username, NEW.clientipaddress, NEW.callingstationid))
+       DO INSTEAD UPDATE cui SET lastaccounting ='-infinity'::timestamp with time zone, cui=NEW.cui WHERE (username, clientipaddress, callingstationid)=(NEW.username, NEW.clientipaddress, NEW.callingstationid);
 
index 37d4e0d..41741eb 100644 (file)
@@ -1,17 +1,16 @@
 # -*- text -*-
-
-##
-##  Queries to update the CUI table.
-##
+#
+#  cui/sqlite/queries.conf -- Queries to update a sqlite CUI table.
+#
+#  $Id$
 
 post-auth {
        query = "\
-               INSERT IGNORE INTO ${..cui_table} \
+               INSERT OR REPLACE INTO ${..cui_table} \
                        (clientipaddress, callingstationid, username, cui, lastaccounting) \
                VALUES \
                        ('%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}', '%{Calling-Station-Id}', \
-                       '%{User-Name}', '%{reply:Chargeable-User-Identity}', NULL) \
-               ON DUPLICATE KEY UPDATE lastaccounting='0000-00-00 00:00:00', cui='%{reply:Chargeable-User-Identity}'"
+                       '%{User-Name}', '%{reply:Chargeable-User-Identity}', NULL)"
 
 }
 
@@ -23,26 +22,26 @@ accounting {
                                UPDATE ${....cui_table} SET \
                                        lastaccounting = CURRENT_TIMESTAMP \
                                WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
                interim-update {
                        query ="\
                                UPDATE ${....cui_table} SET \
                                        lastaccounting = CURRENT_TIMESTAMP \
                                WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
                stop {
                        query ="\
-                               DELETE FROM ${....cui_table} WHERE \
-                                       clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
-                                       AND callingstationid = '%{Calling-Station-Id}' \
-                                       AND username = '%{User-Name}' \
-                                       AND cui = '%{Chargeable-User-Identity}'"
+                               DELETE FROM ${....cui_table} \
+                               WHERE clientipaddress = '%{%{Packet-Src-IPv6-Address}:-%{Packet-Src-IP-Address}}' \
+                               AND callingstationid = '%{Calling-Station-Id}' \
+                               AND username = '%{User-Name}' \
+                               AND cui = '%{Chargeable-User-Identity}'"
                }
        }
 }
index 3e8defc..ac9476e 100644 (file)
 # -*- text -*-
-##
-## ippool.conf -- MySQL queries for rlm_sqlippool
-##
-##     $Id$
-
-# ## This series of queries allocates an IP address
-# allocate_clear = "UPDATE ${ippool_table} \
-#  SET nasipaddress = '', pool_key = 0, \
-#  callingstationid = '', username = '', \
-#  expiry_time = NULL \
-#  WHERE pool_key = '${pool_key}'"
-
-## This series of queries allocates an IP address
-## (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
-## then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
-## from the WHERE clause)
-
-allocate_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, \
-  callingstationid = '', username = '', \
-  expiry_time = NULL \
-  WHERE expiry_time <= NOW() - INTERVAL 1 SECOND \
-  AND nasipaddress = '%{Nas-IP-Address}'"
-
-
-
-## The ORDER BY clause of this query tries to allocate the same IP-address
-## which user had last session...
-allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
- WHERE pool_name = '%{control:Pool-Name}' AND (expiry_time < NOW() OR expiry_time IS NULL) \
- ORDER BY (username <> '%{User-Name}'), \
- (callingstationid <> '%{Calling-Station-Id}'), \
- expiry_time \
- LIMIT 1 \
- FOR UPDATE"
-
-# ## If you prefer to allocate a random IP address every time, i
-# ## use this query instead
-# allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
-#  WHERE pool_name = '%{control:Pool-Name}' \
-#  AND expiry_time IS NULL \
-#  ORDER BY RAND() \
-#  LIMIT 1 \
-#  FOR UPDATE"
-
-
-
-## If an IP could not be allocated, check to see if the pool exists or not
-## This allows the module to differentiate between a full pool and no pool
-## Note: If you are not running redundant pool modules this query may be
-## commented out to save running this query every time an ip is not allocated.
-pool_check = "SELECT id FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' LIMIT 1"
-
-
-## This is the final IP Allocation query, which saves the allocated ip details
-allocate-update = "UPDATE ${ippool_table} \
- SET nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
- callingstationid = '%{Calling-Station-Id}', username = '%{User-Name}', \
- expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE framedipaddress = '%I' AND expiry_time IS NULL"
-
-
-
-## This series of queries frees an IP number when an accounting
-## START record arrives
-start_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{NAS-IP-Address}' AND  pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-## This series of queries frees an IP number when an accounting
-## STOP record arrives
+#
+#  ippool-dhcp/mysql/queries.conf -- MySQL queries for rlm_sqlippool
+#
+#  $Id$
+
+#
+# This series of queries allocates an IP address
+#
+#allocate_clear = "\
+#      UPDATE ${ippool_table} \
+#      SET \
+#              nasipaddress = '', \
+#              pool_key = 0, \
+#              callingstationid = '', \
+#              username = '', \
+#              expiry_time = NULL \
+#      WHERE pool_key = '${pool_key}'"
+
+#
+#  This series of queries allocates an IP address
+#  (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE expiry_time <= NOW() - INTERVAL 1 SECOND \
+       AND nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  The ORDER BY clause of this query tries to allocate the same IP-address
+#  which user had last session...
+#
+allocate_find = "\
+       SELECT framedipaddress \
+       FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' \
+       AND (expiry_time < NOW() OR expiry_time IS NULL) \
+       ORDER BY \
+               (username <> '%{User-Name}'), \
+               (callingstationid <> '%{Calling-Station-Id}'), \
+               expiry_time \
+       LIMIT 1 \
+       OR UPDATE"
+
+#
+#  If you prefer to allocate a random IP address every time, use this query instead
+#
+#allocate_find = "\
+#      SELECT framedipaddress \
+#      FROM ${ippool_table} \
+#      WHERE pool_name = '%{control:Pool-Name}' \
+#      AND expiry_time IS NULL \
+#      ORDER BY RAND() \
+#      LIMIT 1 \
+#      FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see if the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be
+#  commented out to save running this query every time an ip is not allocated.
+#
+pool_check = "\
+       SELECT id \
+       FROM ${ippool_table} \
+       WHERE pool_name='%{control:Pool-Name}' \
+       LIMIT 1"
+
+#
+#  This is the final IP Allocation query, which saves the allocated ip details
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', \
+               pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-Id}', \
+               username = '%{User-Name}', \
+               expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
+       WHERE framedipaddress = '%I' AND expiry_time IS NULL"
+
+#
+#  This series of queries frees an IP number when an accounting
+#  START record arrives
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting
+#  STOP record arrives
+#
 stop_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees an IP number when an accounting
-## ALIVE record arrives
-alive_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting ON record arrives
-on_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting OFF record arrives
-off_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting
+#  ALIVE record arrives
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting ON record arrives
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting OFF record arrives
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
diff --git a/raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf b/raddb/mods-config/sql/ippool-dhcp/oracle/queries.conf
new file mode 100644 (file)
index 0000000..673547b
--- /dev/null
@@ -0,0 +1,175 @@
+# -*- text -*-
+#
+#  ippool-dhcp/oracle/queries.conf -- Oracle queries for dhcp-ippool
+#
+#  $id: 416d59802a1321c16b936bb5e63c288ca3634bcd $
+
+#
+#  "START TRANSACTION" not required with Oracle
+#
+allocate_begin = ""
+start_begin = ""
+alive_begin = ""
+stop_begin = ""
+on_begin = ""
+off_begin = ""
+
+#
+#  This query allocates an IP address from the Pool
+#  It query tries to allocate to the user
+#  either the same IP-address that they had last session
+#  or the IP which has been unused for the longest period of time
+#
+allocate_find = "\
+       WITH POOLS AS (\
+               SELECT * \
+               FROM ${ippool_table} \
+               WHERE pool_name = '%{control:Pool-Name}' \
+               AND (\
+                       pool_key = '${pool_key}' \
+                       OR expiry_time = (\
+                               SELECT MIN(expiry_time) \
+                               FROM ${ippool_table} \
+                               WHERE pool_name = '%{control:Pool-Name}' \
+                               AND expiry_time < CURRENT_TIMESTAMP AND pool_key != '${pool_key}'\
+                       )\
+               )\
+       ) \
+       SELECT framedipaddress \
+       FROM (\
+               SELECT framedipaddress \
+               FROM POOLS \
+               WHERE pool_key = '${pool_key}' \
+               OR (\
+                       NOT EXISTS (\
+                               SELECT 1 \
+                               FROM POOLS \
+                               WHERE pool_key = '${pool_key}'\
+                       )\
+               )\
+       ) WHERE ROWNUM = 1 FOR UPDATE"
+
+#
+#  This function is available if you want to use multiple pools
+#
+#allocate_find = "\
+       SELECT msqlippool('%{SQL-User-Name}','%{control:Pool-Name}') \
+       FROM dual"
+
+#
+#  If you prefer to allocate a random IP address every time, use this query instead
+#
+#allocate_find = "\
+#      SELECT framedipaddress \
+#      FROM ${ippool_table}\
+#      WHERE framedipaddress = (\
+#              SELECT framedipaddress \
+#              FROM (\
+#                      SELECT framedipaddress \
+#                      FROM ${ippool_table} \
+#                      WHERE pool_name = '%{control:Pool-Name}' \
+#                      AND expiry_time < CURRENT_TIMESTAMP \
+#                      ORDER BY DBMS_RANDOM.VALUE\
+#              ) \
+#              WHERE ROWNUM = 1\
+#      ) \
+#      FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see whether the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be commented
+#  out to save running this query every time an ip is not allocated.
+#
+#pool_check = "\
+#      SELECT id \
+#      FROM (\
+#              SELECT id \
+#              FROM ${ippool_table} \
+#              WHERE pool_name = '%{control:Pool-Name}'\
+#      ) WHERE ROWNUM = 1"
+
+#
+#  This query marks the IP address handed out by "allocate_find" as used
+#  for the period of "lease_duration" after which time it may be reused.
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', \
+               pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-id}', \
+               username = '%{SQL-User-Name}', \
+               expiry_time = CURRENT_TIMESTAMP + INTERVAL '${lease_duration}' SECOND(1) \
+       WHERE framedipaddress = '%I'"
+
+#
+#  This query frees the IP address assigned to "pool_key" when a new request
+#  comes in for the same "pool_key". This means that either you are losing
+#  accounting Stop records or you use Calling-Station-id instead of NAS-Port
+#  as your "pool_key" and your users are able to reconnect before your NAS
+#  has timed out their previous session. (Generally on wireless networks)
+#  (Note: If your pool_key is set to Calling-Station-id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{NAS-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = CURRENT_TIMESTAMP - INTERVAL '1' SECOND(1) \
+       WHERE pool_key = '${pool_key}'"
+
+#
+#  This query extends an IP address lease by "lease_duration" when an accounting
+#  START record arrives
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = CURRENT_TIMESTAMP + INTERVAL '${lease_duration}' SECOND(1) \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_name = '%{control:Pool-Name}' \
+       AND pool_key = '${pool_key}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This query frees an IP address when an accounting
+#  STOP record arrives
+#
+stop_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = CURRENT_TIMESTAMP - INTERVAL '1' SECOND(1) \
+       WHERE pool_key = '${pool_key}'"
+
+#
+#  This query extends an IP address lease by "lease_duration" when an accounting
+#  ALIVE record arrives
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = CURRENT_TIMESTAMP + INTERVAL '${lease_duration}' SECOND(1) \
+       WHERE pool_key = '${pool_key}' \
+       AND pool_name = '%{control:Pool-Name}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This query frees all IP addresses allocated to a NAS when an
+#  accounting ON record arrives from that NAS
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = CURRENT_TIMESTAMP - INTERVAL '1' SECOND(1) \
+       WHERE nasipaddress = '%{NAS-IP-Address}'"
+
+#
+#  This query frees all IP addresses allocated to a NAS when an
+#  accounting OFF record arrives from that NAS
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = CURRENT_TIMESTAMP - INTERVAL '1' SECOND(1) \
+       WHERE nasipaddress = '%{NAS-IP-Address}'"
diff --git a/raddb/mods-config/sql/ippool-dhcp/oracle/schema.sql b/raddb/mods-config/sql/ippool-dhcp/oracle/schema.sql
new file mode 100644 (file)
index 0000000..95ceb8e
--- /dev/null
@@ -0,0 +1,28 @@
+CREATE TABLE radippool (
+       id                      INT PRIMARY KEY,
+       pool_name               VARCHAR(30) NOT NULL,
+       framedipaddress         VARCHAR(30) NOT NULL,
+       nasipaddress            VARCHAR(30) NOT NULL,
+       pool_key                VARCHAR(64) NOT NULL,
+       calledstationid         VARCHAR(64),
+       callingstationid        VARCHAR(64) NOT NULL,
+       expiry_time             TIMESTAMP(0) NOT NULL,
+       username                VARCHAR(100)
+);
+
+CREATE INDEX radippool_poolname_ipaddr ON radippool (pool_name, framedipaddress);
+CREATE INDEX radippool_poolname_expire ON radippool (pool_name, expiry_time);
+CREATE INDEX radippool_nasipaddr_key ON radippool (nasipaddress, pool_key);
+CREATE INDEX radippool_nasipaddr_calling ON radippool (nasipaddress, callingstationid);
+
+CREATE SEQUENCE radippool_seq START WITH 1 INCREMENT BY 1;
+
+CREATE OR REPLACE TRIGGER radippool_serialnumber
+       BEFORE INSERT OR UPDATE OF id ON radippool
+       FOR EACH ROW
+       BEGIN
+               IF ( :NEW.id = 0 OR :NEW.id IS NULL ) THEN
+                       SELECT radippool_seq.NEXTVAL INTO :NEW.id FROM dual;
+               END IF;
+       END;
+/
index 31866f1..8709a56 100644 (file)
 # -*- text -*-
-##
-## ippool.conf -- SQLite queries for rlm_sqlippool
-##
-##     $Id$
-
-# ## This series of queries allocates an IP address
-# allocate_clear = "UPDATE ${ippool_table} \
-#  SET nasipaddress = '', pool_key = 0, \
-#  callingstationid = '', username = '', \
-#  expiry_time = NULL \
-#  WHERE pool_key = '${pool_key}'"
-
-## This series of queries allocates an IP address
-## (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
-## then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
-## from the WHERE clause)
-
-allocate_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, \
-  callingstationid = '', username = '', \
-  expiry_time = NULL \
-  WHERE expiry_time <= NOW() - INTERVAL 1 SECOND \
-  AND nasipaddress = '%{Nas-IP-Address}'"
-
-
-
-## The ORDER BY clause of this query tries to allocate the same IP-address
-## which user had last session...
-allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
- WHERE pool_name = '%{control:Pool-Name}' AND (expiry_time < NOW() OR expiry_time IS NULL) \
- ORDER BY (username <> '%{User-Name}'), \
- (callingstationid <> '%{Calling-Station-Id}'), \
- expiry_time \
- LIMIT 1 \
- FOR UPDATE"
-
-# ## If you prefer to allocate a random IP address every time, i
-# ## use this query instead
-# allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
-#  WHERE pool_name = '%{control:Pool-Name}' \
-#  AND expiry_time IS NULL \
-#  ORDER BY RAND() \
-#  LIMIT 1 \
-#  FOR UPDATE"
-
-
-
-## If an IP could not be allocated, check to see if the pool exists or not
-## This allows the module to differentiate between a full pool and no pool
-## Note: If you are not running redundant pool modules this query may be
-## commented out to save running this query every time an ip is not allocated.
-pool_check = "SELECT id FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' LIMIT 1"
-
-
-## This is the final IP Allocation query, which saves the allocated ip details
-allocate_update = "UPDATE ${ippool_table} \
- SET nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
- callingstationid = '%{Calling-Station-Id}', username = '%{User-Name}', \
- expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE framedipaddress = '%I' AND expiry_time IS NULL"
-
-
-
-## This series of queries frees an IP number when an accounting
-## START record arrives
-start_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{NAS-IP-Address}' AND  pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-## This series of queries frees an IP number when an accounting
-## STOP record arrives
-stop_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees an IP number when an accounting
-## ALIVE record arrives
-alive_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting ON record arrives
-on_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting OFF record arrives
-off_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
+#
+#  ippool-dhcp/sqlite/queries.conf -- SQLite queries for rlm_sqlippool
+#
+#  $Id$
+
+#
+#  This series of queries allocates an IP address
+#
+#allocate_clear = "\
+#      UPDATE ${ippool_table} \
+#      SET \
+#              nasipaddress = '', \
+#              pool_key = 0, \
+#              callingstationid = '', \
+#              username = '', \
+#              expiry_time = NULL \
+#      WHERE pool_key = '${pool_key}'"
+
+#
+#  This series of queries allocates an IP address
+#  (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE expiry_time <= datetime(strftime('%%s', 'now') - 1, 'unixepoch') \
+       AND nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  The ORDER BY clause of this query tries to allocate the same IP-address
+#  which user had last session...
+#
+allocate_find = "\
+       SELECT framedipaddress \
+       FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' \
+       AND (\
+               ((expiry_time < datetime('now')) OR expiry_time IS NULL) \
+               OR (callingstationid = '%{Calling-Station-Id}') \
+               AND expiry_time > datetime('now')\
+       ) \
+       ORDER BY \
+               (callingstationid <> '%{Calling-Station-Id}'), \
+               expiry_time \
+       LIMIT 1"
+
+#
+# If you prefer to allocate a random IP address every time, use this query instead
+#
+#allocate_find = "\
+#      SELECT framedipaddress FROM ${ippool_table} \
+#      WHERE pool_name = '%{control:Pool-Name}' \
+#      AND expiry_time IS NULL \
+#      ORDER BY RAND() \
+#      LIMIT 1 \
+#      FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see if the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be
+#  commented out to save running this query every time an ip is not allocated.
+#
+pool_check = "\
+       SELECT id \
+       FROM ${ippool_table} \
+       WHERE pool_name='%{control:Pool-Name}' \
+       LIMIT 1"
+
+#
+#  This is the final IP Allocation query, which saves the allocated ip details
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', \
+               pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-Id}', \
+               username = '%{User-Name}', \
+               expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch') \
+       WHERE framedipaddress = '%I' \
+       AND expiry_time IS NULL"
+
+#
+#  The following queries are not used for DHCP IP assignment.
+#
+
+#
+#  This series of queries frees an IP number when an accounting START record arrives
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch') \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting STOP record arrives
+#
+stop_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting ALIVE record arrives
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch') \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting ON record arrives
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE \nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting OFF record arrives
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
diff --git a/raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql b/raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql
new file mode 100644 (file)
index 0000000..9004b36
--- /dev/null
@@ -0,0 +1,18 @@
+CREATE TABLE radippool (
+       id                      int PRIMARY KEY,
+       pool_name               varchar(30) NOT NULL,
+       framedipaddress         varchar(30) NOT NULL,
+       nasipaddress            varchar(30) NOT NULL DEFAULT '',
+       pool_key                varchar(64) NOT NULL DEFAULT '',
+       calledstationid         varchar(64),
+       callingstationid        varchar(64) NOT NULL DEFAULT '',
+       expiry_time             timestamp DEFAULT NULL,
+       username                varchar(100)
+);
+-- Example of how to put IPs in the pool
+-- INSERT INTO radippool (id, pool_name, framedipaddress) VALUES (1, 'local', '192.168.5.10');
+-- INSERT INTO radippool (id, pool_name, framedipaddress) VALUES (2, 'local', '192.168.5.11');
+-- INSERT INTO radippool (id, pool_name, framedipaddress) VALUES (3, 'local', '192.168.5.12');
+-- INSERT INTO radippool (id, pool_name, framedipaddress) VALUES (4, 'local', '192.168.5.13');
+
index 416d598..ecdb8be 100644 (file)
 # -*- text -*-
-##
-## ippool.conf -- MySQL queries for rlm_sqlippool
-##
-##     $Id$
-
-# ## This series of queries allocates an IP address
-# allocate_clear = "UPDATE ${ippool_table} \
-#  SET nasipaddress = '', pool_key = 0, \
-#  callingstationid = '', username = '', \
-#  expiry_time = NULL \
-#  WHERE pool_key = '${pool_key}'"
-
-## This series of queries allocates an IP address
-## (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
-## then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
-## from the WHERE clause)
-
-allocate_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, \
-  callingstationid = '', username = '', \
-  expiry_time = NULL \
-  WHERE expiry_time <= NOW() - INTERVAL 1 SECOND \
-  AND nasipaddress = '%{Nas-IP-Address}'"
-
-
-
-## The ORDER BY clause of this query tries to allocate the same IP-address
-## which user had last session...
-allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
- WHERE pool_name = '%{control:Pool-Name}' AND (expiry_time < NOW() OR expiry_time IS NULL) \
- ORDER BY (username <> '%{User-Name}'), \
- (callingstationid <> '%{Calling-Station-Id}'), \
- expiry_time \
- LIMIT 1 \
- FOR UPDATE"
-
-# ## If you prefer to allocate a random IP address every time, i
-# ## use this query instead
-# allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
-#  WHERE pool_name = '%{control:Pool-Name}' \
-#  AND expiry_time IS NULL \
-#  ORDER BY RAND() \
-#  LIMIT 1 \
-#  FOR UPDATE"
-
-
-
-## If an IP could not be allocated, check to see if the pool exists or not
-## This allows the module to differentiate between a full pool and no pool
-## Note: If you are not running redundant pool modules this query may be
-## commented out to save running this query every time an ip is not allocated.
-pool_check = "SELECT id FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' LIMIT 1"
-
-
-## This is the final IP Allocation query, which saves the allocated ip details
-allocate_update = "UPDATE ${ippool_table} \
- SET nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
- callingstationid = '%{Calling-Station-Id}', username = '%{User-Name}', \
- expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE framedipaddress = '%I' AND expiry_time IS NULL"
-
-
-
-## This series of queries frees an IP number when an accounting
-## START record arrives
-start_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{NAS-IP-Address}' AND  pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-## This series of queries frees an IP number when an accounting
-## STOP record arrives
-stop_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees an IP number when an accounting
-## ALIVE record arrives
-alive_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting ON record arrives
-on_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting OFF record arrives
-off_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
+#
+#  ippool/mysql/queries.conf -- MySQL queries for rlm_sqlippool
+#
+#  $Id$
+
+#
+#  This series of queries allocates an IP address
+#
+#allocate_clear = "\
+#      UPDATE ${ippool_table} \
+#      SET \
+#              nasipaddress = '', \
+#              pool_key = 0, \
+#              callingstationid = '', \
+#              username = '', \
+#              expiry_time = NULL \
+#      WHERE pool_key = '${pool_key}'"
+
+#
+#  This series of queries allocates an IP address
+#  (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE expiry_time <= NOW() - INTERVAL 1 SECOND \
+       AND nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  The ORDER BY clause of this query tries to allocate the same IP-address
+#  which user had last session...
+#
+allocate_find = "\
+       SELECT framedipaddress FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' \
+       AND (expiry_time < NOW() OR expiry_time IS NULL) \
+       ORDER BY \
+               (username <> '%{User-Name}'), \
+               (callingstationid <> '%{Calling-Station-Id}'), \
+               expiry_time \
+       LIMIT 1 \
+       FOR UPDATE"
+
+#
+#  If you prefer to allocate a random IP address every time, use this query instead.
+#
+#allocate_find = "\
+#      SELECT framedipaddress FROM ${ippool_table} \
+#      WHERE pool_name = '%{control:Pool-Name}' \
+#      AND expiry_time IS NULL \
+#      ORDER BY \
+#              RAND() \
+#      LIMIT 1 \
+#      FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see if the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be
+#  commented out to save running this query every time an ip is not allocated.
+#
+pool_check = "\
+       SELECT id \
+       FROM ${ippool_table} \
+       WHERE pool_name='%{control:Pool-Name}' \
+       LIMIT 1"
+
+#
+#  This is the final IP Allocation query, which saves the allocated ip details.
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-Id}', \
+               username = '%{User-Name}', expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
+       WHERE framedipaddress = '%I' \
+       AND expiry_time IS NULL"
+
+#
+#  This series of queries frees an IP number when an accounting START record arrives.
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting STOP record arrives.
+#
+stop_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting ALIVE record arrives.
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting ON record arrives
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting OFF record arrives
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
index 570a5a3..06d37f8 100644 (file)
@@ -1,8 +1,8 @@
 # -*- text -*-
-##
-## ippool.conf -- Oracle queries for rlm_sqlippool
-##
-##  $Id$
+#
+#  ippool/oracle/queries.conf -- Oracle queries for rlm_sqlippool
+#
+#  $Id$
 
 allocate_begin = "commit"
 start_begin = "commit"
@@ -11,100 +11,152 @@ stop_begin = "commit"
 on_begin = "commit"
 off_begin = "commit"
 
- ## This query allocates an IP address from the Pool
- ## The ORDER BY clause of this query tries to allocate the same IP-address
- ## to the user that they had last session...
- allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
-  WHERE pool_name = '%{control:Pool-Name}' AND expiry_time < current_timestamp \
-  AND rownum <= 1 \
-  ORDER BY (username <> '%{SQL-User-Name}'), \
-  (callingstationid <> '%{Calling-Station-Id}'), expiry_time \
-  FOR UPDATE"
-
- ## This function is available if you want to use multiple pools
-# allocate_find = "select msqlippool('%{SQL-User-Name}','%{control:Pool-Name}') from dual"
-
- ## If you prefer to allocate a random IP address every time, use this query instead
- #allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
- # WHERE pool_name = '%{control:Pool-Name}' AND expiry_time < current_timestamp \
-#  AND rownum <= 1 \
- # ORDER BY RANDOM() \
- # FOR UPDATE"
-
-
- ## If an IP could not be allocated, check to see whether the pool exists or not
- ## This allows the module to differentiate between a full pool and no pool
- ## Note: If you are not running redundant pool modules this query may be commented
- ## out to save running this query every time an ip is not allocated.
- pool_check = "SELECT id FROM (SELECT id FROM ${ippool_table} \
-  WHERE pool_name='%{control:Pool-Name}') WHERE ROWNUM = 1"
-
-
- ## This query marks the IP address handed out by "allocate-find" as used
- ## for the period of "lease_duration" after which time it may be reused.
- allocate_update = "UPDATE ${ippool_table} \
-  SET nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
-  callingstationid = '%{Calling-Station-Id}', username = '%{SQL-User-Name}', \
-  expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1) \
-  WHERE framedipaddress = '%I'"
-
-
- ## This query frees the IP address assigned to "pool_key" when a new request
- ## comes in for the same "pool_key". This means that either you are losing
- ## accounting Stop records or you use Calling-Station-Id instead of NAS-Port
- ## as your "pool_key" and your users are able to reconnect before your NAS
- ## has timed out their previous session. (Generally on wireless networks)
- ## (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
- ## then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
- ## from the WHERE clause)
- allocate_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = current_timestamp - INTERVAL '1' second(1) \
-  WHERE pool_key = '${pool_key}'"
-
-
- ## This query extends an IP address lease by "lease_duration" when an accounting
- ## START record arrives
- start_update = "UPDATE ${ippool_table} \
-  SET expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1) \
-  WHERE nasipaddress = '%{NAS-IP-Address}' \
-  AND pool_key = '${pool_key}'"
-
-
- ## This query frees an IP address when an accounting
- ## STOP record arrives
- stop_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = current_timestamp - INTERVAL '1' second(1) \
-  WHERE nasipaddress = '%{Nas-IP-Address}' \
-  AND pool_key = '${pool_key}' \
-  AND username = '%{SQL-User-Name}' \
-  AND callingstationid = '%{Calling-Station-Id}'"
-
-
-## This query extends an IP address lease by "lease_duration" when an accounting
-## ALIVE record arrives
-alive_update = "UPDATE ${ippool_table} \
-  SET expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1) \
-  WHERE nasipaddress = '%{Nas-IP-Address}' \
-  AND pool_key = '${pool_key}' \
-  AND framedipaddress = '%{Framed-IP-Address}' \
-  AND username = '%{SQL-User-Name}' \
-  AND callingstationid = '%{Calling-Station-Id}'"
-
-
-## This query frees all IP addresses allocated to a NAS when an
-## accounting ON record arrives from that NAS
-on_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = current_timestamp - INTERVAL '1' second(1) \
-  WHERE nasipaddress = '%{Nas-IP-Address}'"
-
-
-## This query frees all IP addresses allocated to a NAS when an
-## accounting OFF record arrives from that NAS
-off_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = current_timestamp - INTERVAL '1' second(1) \
-  WHERE nasipaddress = '%{Nas-IP-Address}'"
-
+#
+#  This query allocates an IP address from the Pool
+#  The ORDER BY clause of this query tries to allocate the same IP-address
+#  to the user that they had last session...
+#
+allocate_find = "\
+       SELECT framedipaddress \
+       FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' \
+       AND expiry_time < current_timestamp \
+       AND rownum <= 1 \
+       ORDER BY \
+               (username <> '%{SQL-User-Name}'), \
+               (callingstationid <> '%{Calling-Station-Id}'), \
+               expiry_time \
+       FOR UPDATE"
+
+#
+#  This function is available if you want to use multiple pools
+#
+#allocate_find = "\
+#      SELECT msqlippool('%{SQL-User-Name}','%{control:Pool-Name}') \
+#      FROM dual"
+
+#
+#  If you prefer to allocate a random IP address every time, use this query instead
+#
+#allocate_find = "\
+#      SELECT framedipaddress \
+#      FROM ${ippool_table} \
+#      WHERE pool_name = '%{control:Pool-Name}' \
+#      AND expiry_time < current_timestamp \
+#      AND rownum <= 1 \
+#      ORDER BY RANDOM() \
+#      FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see whether the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be commented
+#  out to save running this query every time an ip is not allocated.
+#
+pool_check = "\
+       SELECT id \
+       FROM (\
+               SELECT id \
+               FROM ${ippool_table} \
+               WHERE pool_name='%{control:Pool-Name}'\
+       ) \
+       WHERE ROWNUM = 1"
+
+#
+#  This query marks the IP address handed out by "allocate-find" as used
+#  for the period of "lease_duration" after which time it may be reused.
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', \
+               pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-Id}', \
+               username = '%{SQL-User-Name}', \
+               expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1) \
+       WHERE framedipaddress = '%I'"
+
+#
+#  This query frees the IP address assigned to "pool_key" when a new request
+#  comes in for the same "pool_key". This means that either you are losing
+#  accounting Stop records or you use Calling-Station-Id instead of NAS-Port
+#  as your "pool_key" and your users are able to reconnect before your NAS
+#  has timed out their previous session. (Generally on wireless networks)
+#  (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = current_timestamp - INTERVAL '1' second(1) \
+       WHERE pool_key = '${pool_key}'"
+
+#
+#  This query extends an IP address lease by "lease_duration" when an accounting
+#  START record arrives
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1) \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}'"
+
+#
+#  This query frees an IP address when an accounting STOP record arrives
+#
+stop_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = current_timestamp - INTERVAL '1' second(1) \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{SQL-User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}'"
+
+#
+#  This query extends an IP address lease by "lease_duration" when an accounting
+#  ALIVE record arrives
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = current_timestamp + INTERVAL '${lease_duration}' second(1) \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND framedipaddress = '%{Framed-IP-Address}' \
+       AND username = '%{SQL-User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}'"
+
+#
+#  This query frees all IP addresses allocated to a NAS when an
+#  accounting ON record arrives from that NAS
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = current_timestamp - INTERVAL '1' second(1) \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  This query frees all IP addresses allocated to a NAS when an
+#  accounting OFF record arrives from that NAS
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = current_timestamp - INTERVAL '1' second(1) \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
index 2583b1d..c85c293 100644 (file)
@@ -1,13 +1,13 @@
 CREATE TABLE radippool (
-        id                      INT PRIMARY KEY,
-        pool_name               VARCHAR(30) NOT NULL,
-        framedipaddress         VARCHAR(30) NOT NULL,
-        nasipaddress            VARCHAR(30) NOT NULL,
-        pool_key                INT NOT NULL,
-        CalledStationId         VARCHAR(64),
-        CallingStationId        VARCHAR(64) NOT NULL,
-        expiry_time             timestamp(0) NOT NULL,
-        username                VARCHAR(100)
+       id                      INT PRIMARY KEY,
+       pool_name               VARCHAR(30) NOT NULL,
+       framedipaddress         VARCHAR(30) NOT NULL,
+       nasipaddress            VARCHAR(30) NOT NULL,
+       pool_key                INT NOT NULL,
+       CalledStationId         VARCHAR(64),
+       CallingStationId        VARCHAR(64) NOT NULL,
+       expiry_time             timestamp(0) NOT NULL,
+       username                VARCHAR(100)
 );
 
 CREATE INDEX radippool_poolname_ipaadr ON radippool (pool_name, framedipaddress);
@@ -18,11 +18,11 @@ CREATE INDEX radippool_nasipaddr_calling ON radippool (nasipaddress, callingstat
 CREATE SEQUENCE radippool_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER radippool_serialnumber
-        BEFORE INSERT OR UPDATE OF id ON radippool
-        FOR EACH ROW
-        BEGIN
-                if ( :new.id = 0 or :new.id is null ) then
-                        SELECT radippool_seq.nextval into :new.id from dual;
-                end if;
-        END;
+       BEFORE INSERT OR UPDATE OF id ON radippool
+       FOR EACH ROW
+       BEGIN
+               if ( :new.id = 0 or :new.id is null ) then
+                       SELECT radippool_seq.nextval into :new.id from dual;
+               end if;
+       END;
 /
index a1125ec..38465e8 100644 (file)
 # -*- text -*-
-##
-## ippool.conf -- PostgreSQL queries for rlm_sqlippool
-##
-##     $Id$
-
- ## This query allocates an IP address from the Pool
- ## The ORDER BY clause of this query tries to allocate the same IP-address
- ## to the user that they had last session...
- allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
-  WHERE pool_name = '%{control:Pool-Name}' AND expiry_time < 'now'::timestamp(0) \
-  ORDER BY (username <> '%{SQL-User-Name}'), \
-  (callingstationid <> '%{Calling-Station-Id}'), expiry_time \
-  LIMIT 1 \
-  FOR UPDATE"
-
- ## If you prefer to allocate a random IP address every time, use this query instead
- #allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
- # WHERE pool_name = '%{control:Pool-Name}' AND expiry_time < 'now'::timestamp(0) \
- # ORDER BY RANDOM() \
- # LIMIT 1 \
- # FOR UPDATE"
-
-
- ## If an IP could not be allocated, check to see whether the pool exists or not
- ## This allows the module to differentiate between a full pool and no pool
- ## Note: If you are not running redundant pool modules this query may be commented
- ## out to save running this query every time an ip is not allocated.
- pool_check = "SELECT id FROM ${ippool_table} \
-  WHERE pool_name='%{control:Pool-Name}' LIMIT 1"
-
-
- ## This query marks the IP address handed out by "allocate-find" as used
- ## for the period of "lease_duration" after which time it may be reused.
- allocate_update = "UPDATE ${ippool_table} \
-  SET nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
-  callingstationid = '%{Calling-Station-Id}', username = '%{SQL-User-Name}', \
-  expiry_time = 'now'::timestamp(0) + '${lease_duration} second'::interval \
-  WHERE framedipaddress = '%I'"
-
-
- ## This query frees the IP address assigned to "pool_key" when a new request
- ## comes in for the same "pool_key". This means that either you are losing
- ## accounting Stop records or you use Calling-Station-Id instead of NAS-Port
- ## as your "pool_key" and your users are able to reconnect before your NAS
- ## has timed out their previous session. (Generally on wireless networks)
- ## (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
- ## then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
- ## from the WHERE clause)
- allocate_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = 'now'::timestamp(0) - '1 second'::interval \
-  WHERE nasipaddress = '%{NAS-IP-Address}' \
-  AND pool_key = '${pool_key}'"
-
-
- ## This query extends an IP address lease by "lease_duration" when an accounting
- ## START record arrives
- start_update = "UPDATE ${ippool_table} \
-  SET expiry_time = 'now'::timestamp(0) + '${lease_duration} second'::interval \
-  WHERE nasipaddress = '%{NAS-IP-Address}' \
-  AND pool_key = '${pool_key}'"
-
-
- ## This query frees an IP address when an accounting
- ## STOP record arrives
- stop_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = 'now'::timestamp(0) - '1 second'::interval \
-  WHERE nasipaddress = '%{Nas-IP-Address}' \
-  AND pool_key = '${pool_key}' \
-  AND username = '%{SQL-User-Name}' \
-  AND callingstationid = '%{Calling-Station-Id}' \
-  AND framedipaddress = '%{Framed-IP-Address}'"
-
-
- ## This query extends an IP address lease by "lease_duration" when an accounting
- ## ALIVE record arrives
- alive_update = "UPDATE ${ippool_table} \
-  SET expiry_time = 'now'::timestamp(0) + '${lease_duration} seconds'::interval \
-  WHERE nasipaddress = '%{Nas-IP-Address}' \
-  AND pool_key = '${pool_key}' \
-  AND framedipaddress = '%{Framed-IP-Address}' \
-  AND username = '%{SQL-User-Name}' \
-  AND callingstationid = '%{Calling-Station-Id}'"
-
-
- ## This query frees all IP addresses allocated to a NAS when an
- ## accounting ON record arrives from that NAS
- on_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = 'now'::timestamp(0) - '1 second'::interval \
-  WHERE nasipaddress = '%{Nas-IP-Address}'"
-
-
- ## This query frees all IP addresses allocated to a NAS when an
- ## accounting OFF record arrives from that NAS
- off_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, callingstationid = '', \
-  expiry_time = 'now'::timestamp(0) - '1 second'::interval \
-  WHERE nasipaddress = '%{Nas-IP-Address}'"
-
+#
+#  ippool/postgresql/queries.conf -- PostgreSQL queries for rlm_sqlippool
+#
+#  $Id$
+
+#
+#  This query allocates an IP address from the Pool
+#  The ORDER BY clause of this query tries to allocate the same IP-address
+#  to the user that they had last session...
+#
+allocate_find = "\
+       SELECT framedipaddress \
+       FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' \
+       AND expiry_time < 'now'::timestamp(0) \
+       ORDER BY \
+               (username <> '%{SQL-User-Name}'), \
+               (callingstationid <> '%{Calling-Station-Id}'), \
+               expiry_time \
+       LIMIT 1 \
+       FOR UPDATE"
+
+#
+#  If you prefer to allocate a random IP address every time, use this query instead
+#
+allocate_find = "\
+       SELECT framedipaddress FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' AND expiry_time < 'now'::timestamp(0) \
+       ORDER BY RANDOM() \
+       LIMIT 1 \
+       FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see whether the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be commented
+#  out to save running this query every time an ip is not allocated.
+#
+pool_check = "\
+       SELECT id \
+       FROM ${ippool_table} \
+       WHERE pool_name='%{control:Pool-Name}' \
+       LIMIT 1"
+
+#
+#  This query marks the IP address handed out by "allocate-find" as used
+#  for the period of "lease_duration" after which time it may be reused.
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', \
+               pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-Id}', \
+               username = '%{SQL-User-Name}', \
+               expiry_time = 'now'::timestamp(0) + '${lease_duration} second'::interval \
+       WHERE framedipaddress = '%I'"
+
+#
+#  This query frees the IP address assigned to "pool_key" when a new request
+#  comes in for the same "pool_key". This means that either you are losing
+#  accounting Stop records or you use Calling-Station-Id instead of NAS-Port
+#  as your "pool_key" and your users are able to reconnect before your NAS
+#  has timed out their previous session. (Generally on wireless networks)
+#  (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = 'now'::timestamp(0) - '1 second'::interval \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}'"
+
+#
+#  This query extends an IP address lease by "lease_duration" when an accounting
+#  START record arrives
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = 'now'::timestamp(0) + '${lease_duration} second'::interval \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}'"
+
+#
+#  This query frees an IP address when an accounting
+#  STOP record arrives
+#
+stop_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = 'now'::timestamp(0) - '1 second'::interval \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{SQL-User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This query extends an IP address lease by "lease_duration" when an accounting
+#  ALIVE record arrives
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = 'now'::timestamp(0) + '${lease_duration} seconds'::interval \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND framedipaddress = '%{Framed-IP-Address}' \
+       AND username = '%{SQL-User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}'"
+
+#
+#  This query frees all IP addresses allocated to a NAS when an
+#  accounting ON record arrives from that NAS
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = 'now'::timestamp(0) - '1 second'::interval \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  This query frees all IP addresses allocated to a NAS when an
+#  accounting OFF record arrives from that NAS
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               expiry_time = 'now'::timestamp(0) - '1 second'::interval \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
index 0966df6..e912bd3 100644 (file)
 # -*- text -*-
-##
-## ippool.conf -- SQLite queries for rlm_sqlippool
-##
-##     $Id$
-
-# ## This series of queries allocates an IP address
-# allocate_clear = "UPDATE ${ippool_table} \
-#  SET nasipaddress = '', pool_key = 0, \
-#  callingstationid = '', username = '', \
-#  expiry_time = NULL \
-#  WHERE pool_key = '${pool_key}'"
-
-## This series of queries allocates an IP address
-## (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
-## then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
-## from the WHERE clause)
-
- allocate_clear = "UPDATE ${ippool_table} \
-  SET nasipaddress = '', pool_key = 0, \
-  callingstationid = '', username = '', \
-  expiry_time = NULL \
-  WHERE expiry_time <= NOW() - INTERVAL 1 SECOND \
-  AND nasipaddress = '%{Nas-IP-Address}'"
-
-
-
-## The ORDER BY clause of this query tries to allocate the same IP-address
-## which user had last session...
-allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
- WHERE pool_name = '%{control:Pool-Name}' AND (expiry_time < NOW() OR expiry_time IS NULL) \
- ORDER BY (username <> '%{User-Name}'), \
- (callingstationid <> '%{Calling-Station-Id}'), \
- expiry_time \
- LIMIT 1 \
- FOR UPDATE"
-
-# ## If you prefer to allocate a random IP address every time, i
-# ## use this query instead
-# allocate_find = "SELECT framedipaddress FROM ${ippool_table} \
-#  WHERE pool_name = '%{control:Pool-Name}' \
-#  AND expiry_time IS NULL \
-#  ORDER BY RAND() \
-#  LIMIT 1 \
-#  FOR UPDATE"
-
-
-
-## If an IP could not be allocated, check to see if the pool exists or not
-## This allows the module to differentiate between a full pool and no pool
-## Note: If you are not running redundant pool modules this query may be
-## commented out to save running this query every time an ip is not allocated.
-pool_check = "SELECT id FROM ${ippool_table} \
- WHERE pool_name='%{control:Pool-Name}' LIMIT 1"
-
-
-## This is the final IP Allocation query, which saves the allocated ip details
-allocate_update = "UPDATE ${ippool_table} \
- SET nasipaddress = '%{NAS-IP-Address}', pool_key = '${pool_key}', \
- callingstationid = '%{Calling-Station-Id}', username = '%{User-Name}', \
- expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE framedipaddress = '%I' AND expiry_time IS NULL"
-
-
-
-## This series of queries frees an IP number when an accounting
-## START record arrives
-start_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{NAS-IP-Address}' AND  pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-## This series of queries frees an IP number when an accounting
-## STOP record arrives
-stop_clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees an IP number when an accounting
-## ALIVE record arrives
-alive_update = "UPDATE ${ippool_table} \
- SET expiry_time = NOW() + INTERVAL ${lease_duration} SECOND \
- WHERE nasipaddress = '%{Nas-IP-Address}' AND pool_key = '${pool_key}' \
- AND username = '%{User-Name}' \
- AND callingstationid = '%{Calling-Station-Id}' \
- AND framedipaddress = '%{Framed-IP-Address}'"
-
-
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting ON record arrives
-on-clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
-
-## This series of queries frees the IP numbers allocate to a
-## NAS when an accounting OFF record arrives
-off-clear = "UPDATE ${ippool_table} \
- SET nasipaddress = '', pool_key = 0, callingstationid = '', username = '', \
- expiry_time = NULL \
- WHERE nasipaddress = '%{Nas-IP-Address}'"
+#
+#  ippool/sqlite/queries.conf -- SQLite queries for rlm_sqlippool
+#
+#  $Id$
+
+#
+#  This series of queries allocates an IP address
+#
+#allocate_clear = "\
+#      UPDATE ${ippool_table} \
+#      SET \
+#              nasipaddress = '', pool_key = 0, \
+#              callingstationid = '', username = '', \
+#              expiry_time = NULL \
+#      WHERE pool_key = '${pool_key}'"
+
+#
+#  This series of queries allocates an IP address
+#  (Note: If your pool_key is set to Calling-Station-Id and not NAS-Port
+#  then you may wish to delete the "AND nasipaddress = '%{Nas-IP-Address}'
+#  from the WHERE clause)
+#
+allocate_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE expiry_time <= datetime(strftime('%%s', 'now') - 1, 'unixepoch') \
+       AND nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  The ORDER BY clause of this query tries to allocate the same IP-address
+#  which user had last session...
+#
+allocate_find = "\
+       SELECT framedipaddress \
+       FROM ${ippool_table} \
+       WHERE pool_name = '%{control:Pool-Name}' \
+       AND (expiry_time < datetime('now') OR expiry_time IS NULL) \
+       ORDER BY \
+               (username <> '%{User-Name}'), \
+               (callingstationid <> '%{Calling-Station-Id}'), \
+               expiry_time \
+       LIMIT 1 \
+       FOR UPDATE"
+
+#
+#   If you prefer to allocate a random IP address every time, i
+#   use this query instead
+#
+
+#allocate_find = "\
+#      SELECT framedipaddress \
+#      FROM ${ippool_table} \
+#      WHERE pool_name = '%{control:Pool-Name}' \
+#      AND expiry_time IS NULL \
+#      ORDER BY RAND() \
+#      LIMIT 1 \
+#      FOR UPDATE"
+
+#
+#  If an IP could not be allocated, check to see if the pool exists or not
+#  This allows the module to differentiate between a full pool and no pool
+#  Note: If you are not running redundant pool modules this query may be
+#  commented out to save running this query every time an ip is not allocated.
+#
+pool_check = "\
+       SELECT id \
+       FROM ${ippool_table} \
+       WHERE pool_name='%{control:Pool-Name}' \
+       LIMIT 1"
+
+#
+#  This is the final IP Allocation query, which saves the allocated ip details
+#
+allocate_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '%{NAS-IP-Address}', \
+               pool_key = '${pool_key}', \
+               callingstationid = '%{Calling-Station-Id}', \
+               username = '%{User-Name}', \
+               expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch') \
+       WHERE framedipaddress = '%I' \
+       AND expiry_time IS NULL"
+
+#
+#  This series of queries frees an IP number when an accounting START record arrives
+#
+start_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch') \
+       WHERE nasipaddress = '%{NAS-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting STOP record arrives
+#
+stop_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees an IP number when an accounting
+#  ALIVE record arrives
+#
+alive_update = "\
+       UPDATE ${ippool_table} \
+       SET \
+               expiry_time = datetime(strftime('%%s', 'now') + ${lease_duration}, 'unixepoch') \
+       WHERE nasipaddress = '%{Nas-IP-Address}' \
+       AND pool_key = '${pool_key}' \
+       AND username = '%{User-Name}' \
+       AND callingstationid = '%{Calling-Station-Id}' \
+       AND framedipaddress = '%{Framed-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting ON record arrives
+#
+on_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
+
+#
+#  This series of queries frees the IP numbers allocate to a
+#  NAS when an accounting OFF record arrives
+#
+off_clear = "\
+       UPDATE ${ippool_table} \
+       SET \
+               nasipaddress = '', \
+               pool_key = 0, \
+               callingstationid = '', \
+               username = '', \
+               expiry_time = NULL \
+       WHERE nasipaddress = '%{Nas-IP-Address}'"
 
index 5d0d09d..6fcea6e 100644 (file)
 # -*- text -*-
-##
-## dialup.conf -- MSSQL configuration for default schema (schema.sql)
-##
-##     $Id$
+#
+#  main/mssql/queries.conf -- MSSQL configuration for default schema (schema.sql)
+#
+#  $Id$
 
-       # Safe characters list for sql queries. Everything else is replaced
-       # with their mime-encoded equivalents.
-       # The default list should be ok
-       #safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
+# Safe characters list for sql queries. Everything else is replaced
+# with their mime-encoded equivalents.
+# The default list should be ok
+#safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
 
-       #######################################################################
-       #  Query config:  Username
-       #######################################################################
-       # This is the username that will get substituted, escaped, and added
-       # as attribute 'SQL-User-Name'.  '%{SQL-User-Name}' should be used
-       # below everywhere a username substitution is needed so you you can
-       # be sure the username passed from the client is escaped properly.
-       #
-       # Uncomment the next line, if you want the sql_user_name to mean:
-       #
-       #    Use Stripped-User-Name, if it's there.
-       #    Else use User-Name, if it's there,
-       #    Else use hard-coded string "none" as the user name.
-       #sql_user_name = "%{%{Stripped-User-Name}:-%{User-Name:-none}}"
-       #
-       sql_user_name = "%{User-Name}"
+#######################################################################
+#  Query config:  Username
+#######################################################################
+# This is the username that will get substituted, escaped, and added
+# as attribute 'SQL-User-Name'.  '%{SQL-User-Name}' should be used
+# below everywhere a username substitution is needed so you you can
+# be sure the username passed from the client is escaped properly.
+#
+# Uncomment the next line, if you want the sql_user_name to mean:
+#
+#    Use Stripped-User-Name, if it's there.
+#    Else use User-Name, if it's there,
+#    Else use hard-coded string "none" as the user name.
+#sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-none}}"
+#
+sql_user_name = "%{User-Name}"
 
+#######################################################################
+#  Authorization Queries
+#######################################################################
+#  These queries compare the check items for the user
+#  in ${authcheck_table} and setup the reply items in
+#  ${authreply_table}.  You can use any query/tables
+#  you want, but the return data for each row MUST
+#  be in the  following order:
+#
+#  0. Row ID (currently unused)
+#  1. UserName/GroupName
+#  2. Item Attr Name
+#  3. Item Attr Value
+#  4. Item Attr Operation
+#######################################################################
+# Query for case sensitive usernames was removed. Please contact with me,
+# if you know analog of STRCMP functions for MS SQL.
 
-       #######################################################################
-       #  Authorization Queries
-       #######################################################################
-       #  These queries compare the check items for the user
-       #  in ${authcheck_table} and setup the reply items in
-       #  ${authreply_table}.  You can use any query/tables
-       #  you want, but the return data for each row MUST
-       #  be in the  following order:
-       #
-       #  0. Row ID (currently unused)
-       #  1. UserName/GroupName
-       #  2. Item Attr Name
-       #  3. Item Attr Value
-       #  4. Item Attr Operation
-       #######################################################################
-       # Query for case sensitive usernames was removed. Please contact with me,
-       # if you know analog of STRCMP functions for MS SQL.
+authorize_check_query = "\
+       SELECT id, UserName, Attribute, Value, op \
+       FROM ${authcheck_table} \
+       WHERE Username = '%{SQL-User-Name}' \
+       ORDER BY id"
 
-       authorize_check_query = "SELECT id,UserName,Attribute,Value,op \
-                                FROM ${authcheck_table} \
-                                WHERE Username = '%{SQL-User-Name}' \
-                                ORDER BY id"
+authorize_reply_query = "\
+       SELECT id, UserName, Attribute, Value, op \
+       FROM ${authreply_table} \
+       WHERE Username = '%{SQL-User-Name}' \
+       ORDER BY id"
 
-       authorize_reply_query = "SELECT id,UserName,Attribute,Value,op \
-                                FROM ${authreply_table} \
-                                WHERE Username = '%{SQL-User-Name}' \
-                                ORDER BY id"
+authorize_group_check_query = "\
+       SELECT \
+               ${groupcheck_table}.id,${groupcheck_table}.GroupName, \
+               ${groupcheck_table}.Attribute,${groupcheck_table}.Value, \
+               ${groupcheck_table}.op \
+       FROM ${groupcheck_table},${usergroup_table} \
+       WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
+       AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName \
+       ORDER BY ${groupcheck_table}.id"
 
-       authorize_group_check_query = "SELECT ${groupcheck_table}.id,${groupcheck_table}.GroupName, \
-                                               ${groupcheck_table}.Attribute,${groupcheck_table}.Value, \
-                                               ${groupcheck_table}.op \
-                                      FROM ${groupcheck_table},${usergroup_table} \
-                                      WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
-                                      AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName \
-                                      ORDER BY ${groupcheck_table}.id"
-       authorize_group_reply_query = "SELECT ${groupreply_table}.id,${groupreply_table}.GroupName, \
-                                               ${groupreply_table}.Attribute,${groupreply_table}.Value, \
-                                               ${groupreply_table}.op \
-                                      FROM ${groupreply_table},${usergroup_table} \
-                                      WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
-                                      AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName \
-                                      ORDER BY ${groupreply_table}.id"
+authorize_group_reply_query = "\
+       SELECT \
+               ${groupreply_table}.id, ${groupreply_table}.GroupName, \
+               ${groupreply_table}.Attribute,${groupreply_table}.Value, \
+               ${groupreply_table}.op \
+       FROM ${groupreply_table},${usergroup_table} \
+       WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
+       AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName \
+       ORDER BY ${groupreply_table}.id"
 
-       group_membership_query = "SELECT groupname \
-          FROM ${usergroup_table} \
-          WHERE username = '%{SQL-User-Name}' \
-          ORDER BY priority"
+group_membership_query = "\
+       SELECT groupname \
+       FROM ${usergroup_table} \
+       WHERE username = '%{SQL-User-Name}' \
+       ORDER BY priority"
 
-       #######################################################################
-       # Accounting and Post-Auth Queries
-       #######################################################################
-       # These queries insert/update accounting and authentication records.
-       # The query to use is determined by the value of 'reference'.
-       # This value is used as a configuration path and should resolve to one
-       # or more 'query's. If reference points to multiple queries, and a query
-       # fails, the next query is executed.
-       #
-       # Behaviour is identical to the old 1.x/2.x module, except we can now
-       # fail between N queries, and query selection can be based on any
-       # combination of attributes, or custom 'Acct-Status-Type' values.
-       #######################################################################
-       accounting {
-               reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+#######################################################################
+# Accounting and Post-Auth Queries
+#######################################################################
+# These queries insert/update accounting and authentication records.
+# The query to use is determined by the value of 'reference'.
+# This value is used as a configuration path and should resolve to one
+# or more 'query's. If reference points to multiple queries, and a query
+# fails, the next query is executed.
+#
+# Behaviour is identical to the old 1.x/2.x module, except we can now
+# fail between N queries, and query selection can be based on any
+# combination of attributes, or custom 'Acct-Status-Type' values.
+#######################################################################
+accounting {
+       reference = "%{tolower:type.%{Acct-Status-Type}.query}"
 
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/accounting.sql
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/accounting.sql
 
-               type {
-                       accounting-on {
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               AcctStopTime='%S', \
-                                               AcctSessionTime=unix_timestamp('%S') - \
-                                                       unix_timestamp(AcctStartTime), \
-                                               AcctTerminateCause='%{Acct-Terminate-Cause}', \
-                                               AcctStopDelay = %{Acct-Delay-Time:-0} \
-                                       WHERE AcctStopTime = 0 \
-                                       AND NASIPAddress = '%{NAS-IP-Address}' \
-                                       AND AcctStartTime <= '%S'"
-                       }
+       type {
+               accounting-on {
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStopTime='%S', \
+                                       AcctSessionTime=unix_timestamp('%S') - \
+                                               unix_timestamp(AcctStartTime), \
+                                       AcctTerminateCause='%{%{Acct-Terminate-Cause}:-NAS-Reboot}', \
+                                       AcctStopDelay = %{%{Acct-Delay-Time}:-0} \
+                               WHERE AcctStopTime = 0 \
+                               AND NASIPAddress = '%{NAS-IP-Address}' \
+                               AND AcctStartTime <= '%S'"
+               }
 
-                       accounting-off {
-                               query = "${..accounting-on.query}"
-                       }
+               accounting-off {
+                       query = "${..accounting-on.query}"
+               }
 
-                       start {
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (AcctSessionId,         AcctUniqueId,           UserName, \
-                                               Realm,                  NASIPAddress,           NASPort, \
-                                               NASPortType,            AcctStartTime,          AcctSessionTime, \
-                                               AcctAuthentic,          ConnectInfo_start,      ConnectInfo_stop, \
-                                               AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
-                                               CallingStationId,       AcctTerminateCause,     ServiceType, \
-                                               FramedProtocol,         FramedIPAddress,        AcctStartDelay, \
-                                               AcctStopDelay,          XAscendSessionSvrKey) \
-                                       VALUES(\
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port-Id}', \
-                                               '%{NAS-Port-Type}', \
-                                               '%S', \
-                                               '0', \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '', \
-                                               '0', \
-                                               '0', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}', \
-                                               '%{Acct-Delay-Time}', \
-                                               '0', \
-                                               '%{X-Ascend-Session-Svr-Key}')"
+               start {
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (AcctSessionId,         AcctUniqueId,           UserName, \
+                                       Realm,                  NASIPAddress,           NASPort, \
+                                       NASPortType,            AcctStartTime,          AcctSessionTime, \
+                                       AcctAuthentic,          ConnectInfo_start,      ConnectInfo_stop, \
+                                       AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
+                                       CallingStationId,       AcctTerminateCause,     ServiceType, \
+                                       FramedProtocol,         FramedIPAddress,        AcctStartDelay, \
+                                       AcctStopDelay,          XAscendSessionSvrKey) \
+                               VALUES(\
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port-Id}', \
+                                       '%{NAS-Port-Type}', \
+                                       '%S', \
+                                       '0', \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       '', \
+                                       '0', \
+                                       '0', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}', \
+                                       '%{Acct-Delay-Time}', \
+                                       '0', \
+                                       '%{X-Ascend-Session-Svr-Key}')"
 
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               AcctStartTime = '%S', \
-                                               AcctStartDelay = '%{Acct-Delay-Time:-0}', \
-                                               ConnectInfo_start = '%{Connect-Info}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress = '%{NAS-IP-Address}' \
-                                       AND AcctStopTime = 0"
-                       }
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStartTime = '%S', \
+                                       AcctStartDelay = '%{%{Acct-Delay-Time}:-0}', \
+                                       ConnectInfo_start = '%{Connect-Info}' \
+                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
+                               AND UserName = '%{SQL-User-Name}' \
+                               AND NASIPAddress = '%{NAS-IP-Address}' \
+                               AND AcctStopTime = 0"
+               }
 
-                       interim-update {
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               FramedIPAddress = '%{Framed-IP-Address}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress= '%{NAS-IP-Address}' \
-                                       AND AcctStopTime = 0"
+               interim-update {
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       FramedIPAddress = '%{Framed-IP-Address}' \
+                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
+                               AND UserName = '%{SQL-User-Name}' \
+                               AND NASIPAddress= '%{NAS-IP-Address}' \
+                               AND AcctStopTime = 0"
 
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (AcctSessionId,         AcctUniqueId,           UserName, \
-                                               Realm,                  NASIPAddress,           NASPort, \
-                                               NASPortType,            AcctSessionTime,        AcctAuthentic, \
-                                               ConnectInfo_start,      AcctInputOctets,        AcctOutputOctets, \
-                                               CalledStationId,        CallingStationId,       ServiceType, \
-                                               FramedProtocol,         FramedIPAddress,        AcctStartDelay, \
-                                               XAscendSessionSvrKey) \
-                                       VALUES(\
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port-Id}', \
-                                               '%{NAS-Port-Type}', \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '', \
-                                               '%{Acct-Input-Octets}', \
-                                               '%{Acct-Output-Octets}', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}', \
-                                               '0', \
-                                               '%{X-Ascend-Session-Svr-Key}')"
-                       }
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (AcctSessionId,         AcctUniqueId,           UserName, \
+                                       Realm,                  NASIPAddress,           NASPort, \
+                                       NASPortType,            AcctSessionTime,        AcctAuthentic, \
+                                       ConnectInfo_start,      AcctInputOctets,        AcctOutputOctets, \
+                                       CalledStationId,        CallingStationId,       ServiceType, \
+                                       FramedProtocol,         FramedIPAddress,        AcctStartDelay, \
+                                       XAscendSessionSvrKey) \
+                               VALUES(\
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port-Id}', \
+                                       '%{NAS-Port-Type}', \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '', \
+                                       '%{Acct-Input-Octets}', \
+                                       '%{Acct-Output-Octets}', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}', \
+                                       '0', \
+                                       '%{X-Ascend-Session-Svr-Key}')"
+               }
 
-                       stop {
-                               query = "\
-                                       UPDATE ${....acct_table2} \
-                                       SET \
-                                               AcctStopTime = '%S', \
-                                               AcctSessionTime = '%{Acct-Session-Time}', \
-                                               AcctInputOctets = '%{Acct-Input-Octets}', \
-                                               AcctOutputOctets = '%{Acct-Output-Octets}', \
-                                               AcctTerminateCause = '%{Acct-Terminate-Cause}', \
-                                               AcctStopDelay = '%{Acct-Delay-Time:-0}', \
-                                               ConnectInfo_stop = '%{Connect-Info}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress = '%{NAS-IP-Address}' \
-                                       AND AcctStopTime = 0"
+               stop {
+                       query = "\
+                               UPDATE ${....acct_table2} \
+                               SET \
+                                       AcctStopTime = '%S', \
+                                       AcctSessionTime = '%{Acct-Session-Time}', \
+                                       AcctInputOctets = '%{Acct-Input-Octets}', \
+                                       AcctOutputOctets = '%{Acct-Output-Octets}', \
+                                       AcctTerminateCause = '%{Acct-Terminate-Cause}', \
+                                       AcctStopDelay = '%{%{Acct-Delay-Time}:-0}', \
+                                       ConnectInfo_stop = '%{Connect-Info}' \
+                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
+                               AND UserName = '%{SQL-User-Name}' \
+                               AND NASIPAddress = '%{NAS-IP-Address}' \
+                               AND AcctStopTime = 0"
 
-                               query = "\
-                                       INSERT into ${....acct_table2} \
-                                               (AcctSessionId,         AcctUniqueId,           UserName, \
-                                               Realm,                  NASIPAddress,           NASPort, \
-                                               NASPortType,            AcctStopTime,           AcctSessionTime, \
-                                               AcctAuthentic,          ConnectInfo_start,      ConnectInfo_stop, \
-                                               AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
-                                               CallingStationId,       AcctTerminateCause,     ServiceType, \
-                                               FramedProtocol,         FramedIPAddress,        AcctStartDelay, \
-                                               AcctStopDelay) \
-                                       VALUES(\
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port-Id}', \
-                                               '%{NAS-Port-Type}', \
-                                               '%S', \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '', \
-                                               '%{Connect-Info}', \
-                                               '%{Acct-Input-Octets}', \
-                                               '%{Acct-Output-Octets}', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Acct-Terminate-Cause}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}', \
-                                               '0', \
-                                               '%{Acct-Delay-Time:-0}')"
-                       }
+                       query = "\
+                               INSERT into ${....acct_table2} \
+                                       (AcctSessionId,         AcctUniqueId,           UserName, \
+                                       Realm,                  NASIPAddress,           NASPort, \
+                                       NASPortType,            AcctStopTime,           AcctSessionTime, \
+                                       AcctAuthentic,          ConnectInfo_start,      ConnectInfo_stop, \
+                                       AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
+                                       CallingStationId,       AcctTerminateCause,     ServiceType, \
+                                       FramedProtocol,         FramedIPAddress,        AcctStartDelay, \
+                                       AcctStopDelay) \
+                               VALUES(\
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port-Id}', \
+                                       '%{NAS-Port-Type}', \
+                                       '%S', \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '', \
+                                       '%{Connect-Info}', \
+                                       '%{Acct-Input-Octets}', \
+                                       '%{Acct-Output-Octets}', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Acct-Terminate-Cause}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}', \
+                                       '0', \
+                                       '%{%{Acct-Delay-Time}:-0}')"
                }
        }
+}
 
-       post-auth {
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/post-auth.sql
-       }
+post-auth {
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/post-auth.sql
+}
index c8c3b6f..f892049 100644 (file)
@@ -95,11 +95,11 @@ GO
 
 /****** Object:  Table [radusergroup]    Script Date: 16.04.08 19:44:11 ******/
 CREATE TABLE [radpostauth] (
-        [id] [int] IDENTITY (1, 1) NOT NULL ,
-        [userName] [varchar] (64) NOT NULL ,
-        [pass] [varchar] (64) NOT NULL ,
-        [reply] [varchar] (32) NOT NULL ,
-        [authdate] [datetime] NOT NULL
+       [id] [int] IDENTITY (1, 1) NOT NULL ,
+       [userName] [varchar] (64) NOT NULL ,
+       [pass] [varchar] (64) NOT NULL ,
+       [reply] [varchar] (32) NOT NULL ,
+       [authdate] [datetime] NOT NULL
 )
 GO
 
@@ -189,14 +189,14 @@ ALTER TABLE [radusergroup] WITH NOCHECK ADD
 GO
 
 ALTER TABLE [radpostauth] WITH NOCHECK ADD
-        CONSTRAINT [DF_radpostauth_userName] DEFAULT ('') FOR [userName],
-        CONSTRAINT [DF_radpostauth_pass] DEFAULT ('') FOR [pass],
-        CONSTRAINT [DF_radpostauth_reply] DEFAULT ('') FOR [reply],
-        CONSTRAINT [DF_radpostauth_authdate] DEFAULT (getdate()) FOR [authdate],
-        CONSTRAINT [PK_radpostauth] PRIMARY KEY NONCLUSTERED
-        (
-                [id]
-        ) ON [PRIMARY]
+       CONSTRAINT [DF_radpostauth_userName] DEFAULT ('') FOR [userName],
+       CONSTRAINT [DF_radpostauth_pass] DEFAULT ('') FOR [pass],
+       CONSTRAINT [DF_radpostauth_reply] DEFAULT ('') FOR [reply],
+       CONSTRAINT [DF_radpostauth_authdate] DEFAULT (getdate()) FOR [authdate],
+       CONSTRAINT [PK_radpostauth] PRIMARY KEY NONCLUSTERED
+       (
+               [id]
+       ) ON [PRIMARY]
 GO
 
  CREATE  INDEX [UserName] ON [radacct]([UserName]) ON [PRIMARY]
index c90d276..0b3c210 100644 (file)
 # -*- text -*-
-##
-## dialup.conf -- MySQL configuration for default schema (schema.sql)
-##
-##     $Id$
-
-       # Safe characters list for sql queries. Everything else is replaced
-       # with their mime-encoded equivalents.
-       # The default list should be ok
-       #safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
-
-       #######################################################################
-       #  Connection config
-       #######################################################################
-       # The character set is not configurable. The default character set of
-       # the mysql client library is used. To control the character set,
-       # create/edit my.cnf (typically in /etc/mysql/my.cnf or /etc/my.cnf)
-       # and enter
-       # [client]
-       # default-character-set = utf8
-       #
-
-       #######################################################################
-       #  Query config:  Username
-       #######################################################################
-       # This is the username that will get substituted, escaped, and added
-       # as attribute 'SQL-User-Name'. '%{SQL-User-Name}' should be used below
-       # everywhere a username substitution is needed so you you can be sure
-       # the username passed from the client is escaped properly.
-       #
-       # Uncomment the next line, if you want the sql_user_name to mean:
-       #
-       #       Use Stripped-User-Name, if it's there.
-       #       Else use User-Name, if it's there,
-       #       Else use hard-coded string "DEFAULT" as the user name.
-       #sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-DEFAULT}}"
-       #
-       sql_user_name = "%{User-Name}"
-
-       #######################################################################
-       # Default profile
-       #######################################################################
-       # This is the default profile. It is found in SQL by group membership.
-       # That means that this profile must be a member of at least one group
-       # which will contain the corresponding check and reply items.
-       # This profile will be queried in the authorize section for every user.
-       # The point is to assign all users a default profile without having to
-       # manually add each one to a group that will contain the profile.
-       # The SQL module will also honor the User-Profile attribute. This
-       # attribute can be set anywhere in the authorize section (ie the users
-       # file). It is found exactly as the default profile is found.
-       # If it is set then it will *overwrite* the default profile setting.
-       # The idea is to select profiles based on checks on the incoming packets,
-       # not on user group membership. For example:
-       # -- users file --
-       # DEFAULT       Service-Type == Outbound-User, User-Profile := "outbound"
-       # DEFAULT       Service-Type == Framed-User, User-Profile := "framed"
-       #
-       # By default the default_user_profile is not set
-       #
-       #default_user_profile = "DEFAULT"
-
-       #######################################################################
-       # NAS Query
-       #######################################################################
-       # This query retrieves the radius clients
-       #
-       # 0. Row ID (currently unused)
-       # 1. Name (or IP address)
-       # 2. Shortname
-       # 3. Type
-       # 4. Secret
-       # 5. Server
-       #######################################################################
-
-       client_query = "SELECT id, nasname, shortname, type, secret, server FROM ${client_table}"
-
-       #######################################################################
-       # Authorization Queries
-       #######################################################################
-       # These queries compare the check items for the user
-       # in ${authcheck_table} and setup the reply items in
-       # ${authreply_table}. You can use any query/tables
-       # you want, but the return data for each row MUST
-       # be in the following order:
-       #
-       # 0. Row ID (currently unused)
-       # 1. UserName/GroupName
-       # 2. Item Attr Name
-       # 3. Item Attr Value
-       # 4. Item Attr Operation
-       #######################################################################
-       # Use these for case sensitive usernames.
-#      authorize_check_query = "\
+#
+#  main/mysql/queries.conf-- MySQL configuration for default schema (schema.sql)
+#
+#  $Id$
+
+# Safe characters list for sql queries. Everything else is replaced
+# with their mime-encoded equivalents.
+# The default list should be ok
+#safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
+
+#######################################################################
+#  Connection config
+#######################################################################
+# The character set is not configurable. The default character set of
+# the mysql client library is used. To control the character set,
+# create/edit my.cnf (typically in /etc/mysql/my.cnf or /etc/my.cnf)
+# and enter
+# [client]
+# default-character-set = utf8
+#
+
+#######################################################################
+#  Query config:  Username
+#######################################################################
+# This is the username that will get substituted, escaped, and added
+# as attribute 'SQL-User-Name'. '%{SQL-User-Name}' should be used below
+# everywhere a username substitution is needed so you you can be sure
+# the username passed from the client is escaped properly.
+#
+# Uncomment the next line, if you want the sql_user_name to mean:
+#
+#      Use Stripped-User-Name, if it's there.
+#      Else use User-Name, if it's there,
+#      Else use hard-coded string "DEFAULT" as the user name.
+#sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-DEFAULT}}"
+#
+sql_user_name = "%{User-Name}"
+
+#######################################################################
+# Default profile
+#######################################################################
+# This is the default profile. It is found in SQL by group membership.
+# That means that this profile must be a member of at least one group
+# which will contain the corresponding check and reply items.
+# This profile will be queried in the authorize section for every user.
+# The point is to assign all users a default profile without having to
+# manually add each one to a group that will contain the profile.
+# The SQL module will also honor the User-Profile attribute. This
+# attribute can be set anywhere in the authorize section (ie the users
+# file). It is found exactly as the default profile is found.
+# If it is set then it will *overwrite* the default profile setting.
+# The idea is to select profiles based on checks on the incoming packets,
+# not on user group membership. For example:
+# -- users file --
+# DEFAULT      Service-Type == Outbound-User, User-Profile := "outbound"
+# DEFAULT      Service-Type == Framed-User, User-Profile := "framed"
+#
+# By default the default_user_profile is not set
+#
+#default_user_profile = "DEFAULT"
+
+#######################################################################
+# NAS Query
+#######################################################################
+# This query retrieves the radius clients
+#
+# 0. Row ID (currently unused)
+# 1. Name (or IP address)
+# 2. Shortname
+# 3. Type
+# 4. Secret
+# 5. Server
+#######################################################################
+
+client_query = "\
+       SELECT id, nasname, shortname, type, secret, server \
+       FROM ${client_table}"
+
+#######################################################################
+# Authorization Queries
+#######################################################################
+# These queries compare the check items for the user
+# in ${authcheck_table} and setup the reply items in
+# ${authreply_table}. You can use any query/tables
+# you want, but the return data for each row MUST
+# be in the following order:
+#
+# 0. Row ID (currently unused)
+# 1. UserName/GroupName
+# 2. Item Attr Name
+# 3. Item Attr Value
+# 4. Item Attr Operation
+#######################################################################
+# Use these for case sensitive usernames.
+
+#authorize_check_query = "\
 #      SELECT id, username, attribute, value, op \
 #      FROM ${authcheck_table} \
 #      WHERE username = BINARY '%{SQL-User-Name}' \
 #      ORDER BY id"
 
-#      authorize_reply_query = "\
+#authorize_reply_query = "\
 #      SELECT id, username, attribute, value, op \
 #      FROM ${authreply_table} \
 #      WHERE username = BINARY '%{SQL-User-Name}' \
 #      ORDER BY id"
 
-       # The default queries are case insensitive. (for compatibility with
-       # older versions of FreeRADIUS)
-       authorize_check_query = "\
+#
+#  The default queries are case insensitive. (for compatibility with
+#  older versions of FreeRADIUS)
+#
+authorize_check_query = "\
        SELECT id, username, attribute, value, op \
        FROM ${authcheck_table} \
        WHERE username = '%{SQL-User-Name}' \
        ORDER BY id"
 
-       authorize_reply_query = "\
+authorize_reply_query = "\
        SELECT id, username, attribute, value, op \
        FROM ${authreply_table} \
        WHERE username = '%{SQL-User-Name}' \
        ORDER BY id"
 
-       # Use these for case sensitive usernames.
-#      group_membership_query = "\
+#
+#  Use these for case sensitive usernames.
+#
+group_membership_query = "\
 #      SELECT groupname \
 #      FROM ${usergroup_table} \
 #      WHERE username = BINARY '%{SQL-User-Name}' \
 #      ORDER BY priority"
 
-       group_membership_query = "\
+group_membership_query = "\
        SELECT groupname \
        FROM ${usergroup_table} \
        WHERE username = '%{SQL-User-Name}' \
        ORDER BY priority"
 
-       authorize_group_check_query = "\
+authorize_group_check_query = "\
        SELECT id, groupname, attribute, \
        Value, op \
        FROM ${groupcheck_table} \
        WHERE groupname = '%{Sql-Group}' \
        ORDER BY id"
 
-       authorize_group_reply_query = "\
+authorize_group_reply_query = "\
        SELECT id, groupname, attribute, \
        value, op \
        FROM ${groupreply_table} \
        WHERE groupname = '%{Sql-Group}' \
        ORDER BY id"
 
-       #######################################################################
-       # Simultaneous Use Checking Queries
-       #######################################################################
-       # simul_count_query     - query for the number of current connections
-       #                       - If this is not defined, no simultaneouls use checking
-       #                       - will be performed by this module instance
-       # simul_verify_query    - query to return details of current connections
-       #                               for verification
-       #                       - Leave blank or commented out to disable verification step
-       #                       - Note that the returned field order should not be changed.
-       #######################################################################
-
-       # Uncomment simul_count_query to enable simultaneous use checking
-#      simul_count_query = "\
+#######################################################################
+# Simultaneous Use Checking Queries
+#######################################################################
+# simul_count_query    - query for the number of current connections
+#                      - If this is not defined, no simultaneouls use checking
+#                      - will be performed by this module instance
+# simul_verify_query   - query to return details of current connections
+#                              for verification
+#                      - Leave blank or commented out to disable verification step
+#                      - Note that the returned field order should not be changed.
+#######################################################################
+
+#
+#  Uncomment simul_count_query to enable simultaneous use checking
+#
+#simul_count_query = "\
 #      SELECT COUNT(*) \
 #      FROM ${acct_table1} \
 #      WHERE username = '%{SQL-User-Name}' \
 #      AND acctstoptime IS NULL"
 
-       simul_verify_query = "\
-       SELECT radacctid, acctsessionid, username, \
-       nasipaddress, nasportid, framedipaddress, \
-       callingstationid, framedprotocol \
+simul_verify_query = "\
+       SELECT \
+               radacctid, acctsessionid, username, nasipaddress, nasportid, framedipaddress, \
+               callingstationid, framedprotocol \
        FROM ${acct_table1} \
        WHERE username = '%{SQL-User-Name}' \
        AND acctstoptime IS NULL"
 
-       #######################################################################
-       # Accounting and Post-Auth Queries
-       #######################################################################
-       # These queries insert/update accounting and authentication records.
-       # The query to use is determined by the value of 'reference'.
-       # This value is used as a configuration path and should resolve to one
-       # or more 'query's. If reference points to multiple queries, and a query
-       # fails, the next query is executed.
-       #
-       # Behaviour is identical to the old 1.x/2.x module, except we can now
-       # fail between N queries, and query selection can be based on any
-       # combination of attributes, or custom 'Acct-Status-Type' values.
-       #######################################################################
-       accounting {
-               reference = "%{tolower:type.%{Acct-Status-Type}.query}"
-
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/accounting.sql
-
-               column_list = "\
-                       acctsessionid,          acctuniqueid,           username, \
-                       realm,                  nasipaddress,           nasportid, \
-                       nasporttype,            acctstarttime,          acctupdatetime, \
-                       acctstoptime,           acctsessiontime,        acctauthentic, \
-                       connectinfo_start,      connectinfo_stop,       acctinputoctets, \
-                       acctoutputoctets,       calledstationid,        callingstationid, \
-                       acctterminatecause,     servicetype,            framedprotocol, \
-                       framedipaddress"
-
-               type {
-                       accounting-on {
-                               #
-                               #  Bulk terminate all sessions associated with a given NAS
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               acctstoptime = FROM_UNIXTIME(\
-                                                       %{integer:Event-Timestamp}), \
-                                               acctsessiontime = '%{integer:Event-Timestamp}' \
-                                                       - UNIX_TIMESTAMP(acctstarttime), \
-                                               acctterminatecause = '%{Acct-Terminate-Cause}' \
-                                       WHERE acctstoptime IS NULL \
-                                       AND nasipaddress   = '%{NAS-IP-Address}' \
-                                       AND acctstarttime <= FROM_UNIXTIME(\
-                                               %{integer:Event-Timestamp})"
-                       }
-
-                       accounting-off {
-                               query = "${..accounting-on.query}"
-                       }
-
-                       start {
-                               #
-                               #  Insert a new record into the sessions table
-                               #
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (${...column_list}) \
-                                       VALUES \
-                                               ('%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port}', \
-                                               '%{NAS-Port-Type}', \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp}), \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp}), \
-                                               NULL, \
-                                               '0', \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '', \
-                                               '0', \
-                                               '0', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}')"
-
-                               #
-                               #  Key constraints prevented us from inserting a new session,
-                               #  use the alternate query to update an existing session.
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table1} SET \
-                                               acctstarttime   = FROM_UNIXTIME(\
-                                                       %{integer:Event-Timestamp}), \
-                                               acctupdatetime  = FROM_UNIXTIME(\
-                                                       %{integer:Event-Timestamp}), \
-                                               connectinfo_start = '%{Connect-Info}' \
-                                       WHERE acctsessionid = '%{Acct-Session-Id}' \
-                                       AND username            = '%{SQL-User-Name}' \
-                                       AND nasipaddress        = '%{NAS-IP-Address}'"
-                       }
-
-                       interim-update {
-                               #
-                               #  Update an existing session and calculate the interval
-                               #  between the last data we received for the session and this
-                               #  update. This can be used to find stale sessions.
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               acctupdatetime  = (\
-                                                       @acctupdatetime_old:=acctupdatetime), \
-                                               acctupdatetime  = FROM_UNIXTIME(\
-                                                       %{integer:Event-Timestamp}), \
-                                               acctinterval    = %{integer:Event-Timestamp} - \
-                                                       UNIX_TIMESTAMP(@acctupdatetime_old), \
-                                               framedipaddress = '%{Framed-IP-Address}', \
-                                               acctsessiontime = '%{Acct-Session-Time}', \
-                                               acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
-                                                       << 32 | '%{%{Acct-Input-Octets}:-0}', \
-                                               acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
-                                                       << 32 | '%{%{Acct-Output-Octets}:-0}' \
-                                       WHERE acctsessionid     = '%{Acct-Session-Id}' \
-                                       AND username            = '%{SQL-User-Name}' \
-                                       AND nasipaddress        = '%{NAS-IP-Address}'"
-
-                               #
-                               #  The update condition matched no existing sessions. Use
-                               #  the values provided in the update to create a new session.
-                               #
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (${...column_list}) \
-                                       VALUES \
-                                               ('%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port}', \
-                                               '%{NAS-Port-Type}', \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp} - \
-                                                       %{%{Acct-Session-Time}:-0}), \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp}), \
-                                               NULL, \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '', \
-                                               '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
-                                                       '%{%{Acct-Input-Octets}:-0}', \
-                                               '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
-                                                       '%{%{Acct-Output-Octets}:-0}', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}')"
-                       }
-
-                       stop {
-                               #
-                               #  Session has terminated, update the stop time and statistics.
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table2} SET \
-                                               acctstoptime    = FROM_UNIXTIME(\
-                                                       %{integer:Event-Timestamp}), \
-                                               acctsessiontime = '%{Acct-Session-Time}', \
-                                               acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
-                                                       << 32 | '%{%{Acct-Input-Octets}:-0}', \
-                                               acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
-                                                       << 32 | '%{%{Acct-Output-Octets}:-0}', \
-                                               acctterminatecause = '%{Acct-Terminate-Cause}', \
-                                               connectinfo_stop = '%{Connect-Info}' \
-                                       WHERE acctsessionid     = '%{Acct-Session-Id}' \
-                                       AND username            = '%{SQL-User-Name}' \
-                                       AND nasipaddress        = '%{NAS-IP-Address}'"
-
-                               #
-                               #  The update condition matched no existing sessions. Use
-                               #  the values provided in the update to create a new session.
-                               #
-                               query = "\
-                                       INSERT INTO ${....acct_table2} \
-                                               (${...column_list}) \
-                                       VALUES \
-                                               ('%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port}', \
-                                               '%{NAS-Port-Type}', \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp} - \
-                                                       %{%{Acct-Session-Time}:-0}), \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp}), \
-                                               FROM_UNIXTIME(%{integer:Event-Timestamp}), \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', '', \
-                                               '%{Connect-Info}', \
-                                               '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
-                                                       '%{%{Acct-Input-Octets}:-0}', \
-                                               '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
-                                                       '%{%{Acct-Output-Octets}:-0}', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Acct-Terminate-Cause}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}')"
-                       }
+#######################################################################
+# Accounting and Post-Auth Queries
+#######################################################################
+# These queries insert/update accounting and authentication records.
+# The query to use is determined by the value of 'reference'.
+# This value is used as a configuration path and should resolve to one
+# or more 'query's. If reference points to multiple queries, and a query
+# fails, the next query is executed.
+#
+# Behaviour is identical to the old 1.x/2.x module, except we can now
+# fail between N queries, and query selection can be based on any
+# combination of attributes, or custom 'Acct-Status-Type' values.
+#######################################################################
+accounting {
+       reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/accounting.sql
+
+       column_list = "\
+               acctsessionid,          acctuniqueid,           username, \
+               realm,                  nasipaddress,           nasportid, \
+               nasporttype,            acctstarttime,          acctupdatetime, \
+               acctstoptime,           acctsessiontime,        acctauthentic, \
+               connectinfo_start,      connectinfo_stop,       acctinputoctets, \
+               acctoutputoctets,       calledstationid,        callingstationid, \
+               acctterminatecause,     servicetype,            framedprotocol, \
+               framedipaddress"
+
+       type {
+               accounting-on {
+                       #
+                       #  Bulk terminate all sessions associated with a given NAS
+                       #
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       acctstoptime = FROM_UNIXTIME(\
+                                               %{integer:Event-Timestamp}), \
+                                       acctsessiontime = '%{integer:Event-Timestamp}' \
+                                               - UNIX_TIMESTAMP(acctstarttime), \
+                                       acctterminatecause = '%{%{Acct-Terminate-Cause}:-NAS-Reboot}' \
+                               WHERE acctstoptime IS NULL \
+                               AND nasipaddress   = '%{NAS-IP-Address}' \
+                               AND acctstarttime <= FROM_UNIXTIME(\
+                                       %{integer:Event-Timestamp})"
                }
-       }
 
-       #######################################################################
-       # Authentication Logging Queries
-       #######################################################################
-       # postauth_query        - Insert some info after authentication
-       #######################################################################
-
-       post-auth {
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/post-auth.sql
-
-               query = "\
-                       INSERT INTO ${..postauth_table} \
-                               (username, pass, reply, authdate) \
-                       VALUES ( \
-                               '%{SQL-User-Name}', \
-                               '%{%{User-Password}:-%{Chap-Password}}', \
-                               '%{reply:Packet-Type}', \
-                               '%S')"
+               accounting-off {
+                       query = "${..accounting-on.query}"
+               }
+
+               start {
+                       #
+                       #  Insert a new record into the sessions table
+                       #
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES \
+                                       ('%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port}', \
+                                       '%{NAS-Port-Type}', \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       NULL, \
+                                       '0', \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       '', \
+                                       '0', \
+                                       '0', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}')"
+
+                       #
+                       #  Key constraints prevented us from inserting a new session,
+                       #  use the alternate query to update an existing session.
+                       #
+                       query = "\
+                               UPDATE ${....acct_table1} SET \
+                                       acctstarttime   = FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       acctupdatetime  = FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       connectinfo_start = '%{Connect-Info}' \
+                               WHERE acctsessionid = '%{Acct-Session-Id}' \
+                               AND username            = '%{SQL-User-Name}' \
+                               AND nasipaddress        = '%{NAS-IP-Address}'"
+               }
+
+               interim-update {
+                       #
+                       #  Update an existing session and calculate the interval
+                       #  between the last data we received for the session and this
+                       #  update. This can be used to find stale sessions.
+                       #
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       acctupdatetime  = (@acctupdatetime_old:=acctupdatetime), \
+                                       acctupdatetime  = FROM_UNIXTIME(\
+                                               %{integer:Event-Timestamp}), \
+                                       acctinterval    = %{integer:Event-Timestamp} - \
+                                               UNIX_TIMESTAMP(@acctupdatetime_old), \
+                                       framedipaddress = '%{Framed-IP-Address}', \
+                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
+                                               << 32 | '%{%{Acct-Input-Octets}:-0}', \
+                                       acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
+                                               << 32 | '%{%{Acct-Output-Octets}:-0}' \
+                               WHERE acctsessionid     = '%{Acct-Session-Id}' \
+                               AND username            = '%{SQL-User-Name}' \
+                               AND nasipaddress        = '%{NAS-IP-Address}'"
+
+                       #
+                       #  The update condition matched no existing sessions. Use
+                       #  the values provided in the update to create a new session.
+                       #
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES \
+                                       ('%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port}', \
+                                       '%{NAS-Port-Type}', \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp} - \
+                                               %{%{Acct-Session-Time}:-0}), \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       NULL, \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       '', \
+                                       '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
+                                               '%{%{Acct-Input-Octets}:-0}', \
+                                       '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
+                                               '%{%{Acct-Output-Octets}:-0}', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}')"
+               }
+
+               stop {
+                       #
+                       #  Session has terminated, update the stop time and statistics.
+                       #
+                       query = "\
+                               UPDATE ${....acct_table2} SET \
+                                       acctstoptime    = FROM_UNIXTIME(\
+                                               %{integer:Event-Timestamp}), \
+                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctinputoctets = '%{%{Acct-Input-Gigawords}:-0}' \
+                                               << 32 | '%{%{Acct-Input-Octets}:-0}', \
+                                       acctoutputoctets = '%{%{Acct-Output-Gigawords}:-0}' \
+                                               << 32 | '%{%{Acct-Output-Octets}:-0}', \
+                                       acctterminatecause = '%{Acct-Terminate-Cause}', \
+                                       connectinfo_stop = '%{Connect-Info}' \
+                               WHERE acctsessionid     = '%{Acct-Session-Id}' \
+                               AND username            = '%{SQL-User-Name}' \
+                               AND nasipaddress        = '%{NAS-IP-Address}'"
+
+                       #
+                       #  The update condition matched no existing sessions. Use
+                       #  the values provided in the update to create a new session.
+                       #
+                       query = "\
+                               INSERT INTO ${....acct_table2} \
+                                       (${...column_list}) \
+                               VALUES \
+                                       ('%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port}', \
+                                       '%{NAS-Port-Type}', \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp} - \
+                                               %{%{Acct-Session-Time}:-0}), \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       FROM_UNIXTIME(%{integer:Event-Timestamp}), \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', '', \
+                                       '%{Connect-Info}', \
+                                       '%{%{Acct-Input-Gigawords}:-0}' << 32 | \
+                                               '%{%{Acct-Input-Octets}:-0}', \
+                                       '%{%{Acct-Output-Gigawords}:-0}' << 32 | \
+                                               '%{%{Acct-Output-Octets}:-0}', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Acct-Terminate-Cause}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}')"
+               }
        }
+}
+
+#######################################################################
+# Authentication Logging Queries
+#######################################################################
+# postauth_query       - Insert some info after authentication
+#######################################################################
+
+post-auth {
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/post-auth.sql
+
+       query = "\
+               INSERT INTO ${..postauth_table} \
+                       (username, pass, reply, authdate) \
+               VALUES ( \
+                       '%{SQL-User-Name}', \
+                       '%{%{User-Password}:-%{Chap-Password}}', \
+                       '%{reply:Packet-Type}', \
+                       '%S')"
+}
index 60d97ec..c5185be 100644 (file)
@@ -28,7 +28,7 @@ CREATE TABLE radacct (
   acctupdatetime datetime NULL default NULL,
   acctstoptime datetime NULL default NULL,
   acctinterval int(12) default NULL,
-  acctsessiontime unsigned int(12) default NULL,
+  acctsessiontime int(12) unsigned default NULL,
   acctauthentic varchar(32) default NULL,
   connectinfo_start varchar(50) default NULL,
   connectinfo_stop varchar(50) default NULL,
index 7ad0140..ca22f5f 100644 (file)
 # -*- text -*-
-##
-## dialup.conf -- Oracle configuration for default schema (schema.sql)
-##
-##     $Id$
+#
+#  main/oracle/queries.conf -- Oracle configuration for default schema (schema.sql)
+#
+#  $Id$
 
-       #######################################################################
-       #  Query config:  Username
-       #######################################################################
-       # This is the username that will get substituted, escaped, and added
-       # as attribute 'SQL-User-Name'.  '%{SQL-User-Name}' should be used below
-       # everywhere a username substitution is needed so you you can be sure
-       # the username passed from the client is escaped properly.
-       #
-       #  Uncomment the next line, if you want the sql_user_name to mean:
-       #
-       #    Use Stripped-User-Name, if it's there.
-       #    Else use User-Name, if it's there,
-       #    Else use hard-coded string "DEFAULT" as the user name.
-       #sql_user_name = "%{Stripped-User-Name:-%{User-Name:-DEFAULT}}"
-       #
-       sql_user_name = "%{User-Name}"
+#######################################################################
+#  Query config:  Username
+#######################################################################
+# This is the username that will get substituted, escaped, and added
+# as attribute 'SQL-User-Name'.  '%{SQL-User-Name}' should be used below
+# everywhere a username substitution is needed so you you can be sure
+# the username passed from the client is escaped properly.
+#
+#  Uncomment the next line, if you want the sql_user_name to mean:
+#
+#    Use Stripped-User-Name, if it's there.
+#    Else use User-Name, if it's there,
+#    Else use hard-coded string "DEFAULT" as the user name.
+#sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-DEFAULT}}"
+#
+sql_user_name = "%{User-Name}"
 
-       #######################################################################
-       #  Default profile
-       #######################################################################
-       # This is the default profile. It is found in SQL by group membership.
-       # That means that this profile must be a member of at least one group
-       # which will contain the corresponding check and reply items.
-       # This profile will be queried in the authorize section for every user.
-       # The point is to assign all users a default profile without having to
-       # manually add each one to a group that will contain the profile.
-       # The SQL module will also honor the User-Profile attribute. This
-       # attribute can be set anywhere in the authorize section (ie the users
-       # file). It is found exactly as the default profile is found.
-       # If it is set then it will *overwrite* the default profile setting.
-       # The idea is to select profiles based on checks on the incoming packets,
-       # not on user group membership. For example:
-       # -- users file --
-       # DEFAULT       Service-Type == Outbound-User, User-Profile := "outbound"
-       # DEFAULT       Service-Type == Framed-User, User-Profile := "framed"
-       #
-       # By default the default_user_profile is not set
-       #
-       #default_user_profile = "DEFAULT"
-       #
-       # Determines if we will query the default_user_profile or the User-Profile
-       # if the user is not found. If the profile is found then we consider the user
-       # found. By default this is set to 'no'.
-       #
-       #query_on_not_found = no
+#######################################################################
+#  Default profile
+#######################################################################
+# This is the default profile. It is found in SQL by group membership.
+# That means that this profile must be a member of at least one group
+# which will contain the corresponding check and reply items.
+# This profile will be queried in the authorize section for every user.
+# The point is to assign all users a default profile without having to
+# manually add each one to a group that will contain the profile.
+# The SQL module will also honor the User-Profile attribute. This
+# attribute can be set anywhere in the authorize section (ie the users
+# file). It is found exactly as the default profile is found.
+# If it is set then it will *overwrite* the default profile setting.
+# The idea is to select profiles based on checks on the incoming packets,
+# not on user group membership. For example:
+# -- users file --
+# DEFAULT      Service-Type == Outbound-User, User-Profile := "outbound"
+# DEFAULT      Service-Type == Framed-User, User-Profile := "framed"
+#
+# By default the default_user_profile is not set
+#
+#default_user_profile = "DEFAULT"
+#
+# Determines if we will query the default_user_profile or the User-Profile
+# if the user is not found. If the profile is found then we consider the user
+# found. By default this is set to 'no'.
+#
+#query_on_not_found = no
 
 
-       #######################################################################
-       #  NAS Query
-       #######################################################################
-       #  This query retrieves the radius clients
-       #
-       #  0. Row ID (currently unused)
-       #  1. Name (or IP address)
-       #  2. Shortname
-       #  3. Type
-       #  4. Secret
-       #  5. Virtual server
-       #######################################################################
+#######################################################################
+#  NAS Query
+#######################################################################
+#  This query retrieves the radius clients
+#
+#  0. Row ID (currently unused)
+#  1. Name (or IP address)
+#  2. Shortname
+#  3. Type
+#  4. Secret
+#  5. Virtual server
+#######################################################################
 
-       client_query = "SELECT id, nasname, shortname, type, secret, server FROM ${client_table}"
-       #######################################################################
-       #  Authorization Queries
-       #######################################################################
-       #  These queries compare the check items for the user
-       #  in ${authcheck_table} and setup the reply items in
-       #  ${authreply_table}.  You can use any query/tables
-       #  you want, but the return data for each row MUST
-       #  be in the  following order:
-       #
-       #  0. Row ID (currently unused)
-       #  1. UserName/GroupName
-       #  2. Item Attr Name
-       #  3. Item Attr Value
-       #  4. Item Attr Operation
-       #######################################################################
-       #
-       # WARNING: Oracle is case sensitive
-       #
-       # The main difference between MySQL and Oracle queries is the date format.
-       # You must use the TO_DATE function to transform the radius date format to
-       # the Oracle date format, and put NULL otherwise '0' in a void date field.
-       #
-       #######################################################################
+client_query = "\
+       SELECT id, nasname, shortname, type, secret, server \
+       FROM ${client_table}"
 
-       authorize_check_query = "SELECT id,UserName,Attribute,Value,op FROM ${authcheck_table} WHERE Username = '%{SQL-User-Name}' ORDER BY id"
-       authorize_reply_query = "SELECT id,UserName,Attribute,Value,op FROM ${authreply_table} WHERE Username = '%{SQL-User-Name}' ORDER BY id"
+#######################################################################
+#  Authorization Queries
+#######################################################################
+#  These queries compare the check items for the user
+#  in ${authcheck_table} and setup the reply items in
+#  ${authreply_table}.  You can use any query/tables
+#  you want, but the return data for each row MUST
+#  be in the  following order:
+#
+#  0. Row ID (currently unused)
+#  1. UserName/GroupName
+#  2. Item Attr Name
+#  3. Item Attr Value
+#  4. Item Attr Operation
+#######################################################################
+#
+# WARNING: Oracle is case sensitive
+#
+# The main difference between MySQL and Oracle queries is the date format.
+# You must use the TO_DATE function to transform the radius date format to
+# the Oracle date format, and put NULL otherwise '0' in a void date field.
+#
+#######################################################################
 
-       authorize_group_check_query = "SELECT ${groupcheck_table}.id,${groupcheck_table}.GroupName,${groupcheck_table}.Attribute,${groupcheck_table}.Value,${groupcheck_table}.op  FROM ${groupcheck_table},${usergroup_table} WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName ORDER BY ${groupcheck_table}.id"
-       authorize_group_reply_query = "SELECT ${groupreply_table}.id,${groupreply_table}.GroupName,${groupreply_table}.Attribute,${groupreply_table}.Value,${groupreply_table}.op  FROM ${groupreply_table},${usergroup_table} WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName ORDER BY ${groupreply_table}.id"
+authorize_check_query = "\
+       SELECT id, UserName, Attribute, Value, op \
+       FROM ${authcheck_table} \
+       WHERE Username = '%{SQL-User-Name}' \
+       ORDER BY id"
 
-       #######################################################################
-       # Simultaneous Use Checking Queries
-       #######################################################################
-       # simul_count_query     - query for the number of current connections
-       #                       - If this is not defined, no simultaneouls use checking
-       #                       - will be performed by this module instance
-       # simul_verify_query    - query to return details of current connections for verification
-       #                       - Leave blank or commented out to disable verification step
-       #                       - Note that the returned field order should not be changed.
-       #######################################################################
+authorize_reply_query = "\
+       SELECT id, UserName, Attribute, Value, op \
+       FROM ${authreply_table} \
+       WHERE Username = '%{SQL-User-Name}' \
+       ORDER BY id"
 
-       # Uncomment simul_count_query to enable simultaneous use checking
-       # simul_count_query = "SELECT COUNT(*) FROM ${acct_table1} WHERE UserName='%{SQL-User-Name}' AND AcctStopTime IS NULL"
-       simul_verify_query = "SELECT RadAcctId, AcctSessionId, UserName, NASIPAddress, NASPortId, FramedIPAddress, CallingStationId, FramedProtocol FROM ${acct_table1} WHERE UserName='%{SQL-User-Name}' AND AcctStopTime IS NULL"
+authorize_group_check_query = "\
+       SELECT \
+               ${groupcheck_table}.id, ${groupcheck_table}.GroupName, ${groupcheck_table}.Attribute, \
+               ${groupcheck_table}.Value,${groupcheck_table}.op \
+       FROM ${groupcheck_table}, ${usergroup_table} \
+       WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
+       AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName \
+       ORDER BY ${groupcheck_table}.id"
 
-       #######################################################################
-       # Group Membership Queries
-       #######################################################################
-       # group_membership_query        - Check user group membership
-       #######################################################################
+authorize_group_reply_query = "\
+       SELECT \
+               ${groupreply_table}.id, ${groupreply_table}.GroupName, ${groupreply_table}.Attribute, \
+               ${groupreply_table}.Value, ${groupreply_table}.op \
+       FROM ${groupreply_table}, ${usergroup_table} \
+       WHERE ${usergroup_table}.Username = '%{SQL-User-Name}' \
+       AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName \
+       ORDER BY ${groupreply_table}.id"
 
-       group_membership_query = "SELECT GroupName FROM ${usergroup_table} WHERE UserName='%{SQL-User-Name}'"
+#######################################################################
+# Simultaneous Use Checking Queries
+#######################################################################
+# simul_count_query    - query for the number of current connections
+#                      - If this is not defined, no simultaneouls use checking
+#                      - will be performed by this module instance
+# simul_verify_query   - query to return details of current connections for verification
+#                      - Leave blank or commented out to disable verification step
+#                      - Note that the returned field order should not be changed.
+#######################################################################
 
-       #######################################################################
-       # Accounting and Post-Auth Queries
-       #######################################################################
-       # These queries insert/update accounting and authentication records.
-       # The query to use is determined by the value of 'reference'.
-       # This value is used as a configuration path and should resolve to one
-       # or more 'query's. If reference points to multiple queries, and a query
-       # fails, the next query is executed.
-       #
-       # Behaviour is identical to the old 1.x/2.x module, except we can now
-       # fail between N queries, and query selection can be based on any
-       # combination of attributes, or custom 'Acct-Status-Type' values.
-       #######################################################################
-       accounting {
-               reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+#
+#  Uncomment simul_count_query to enable simultaneous use checking
+#
+#simul_count_query = "\
+#      SELECT COUNT(*) \
+#      FROM ${acct_table1} \
+#      WHERE UserName = '%{SQL-User-Name}' \
+#      AND AcctStopTime IS NULL"
 
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
+simul_verify_query = "\
+       SELECT \
+               RadAcctId, AcctSessionId, UserName, NASIPAddress, NASPortId, \
+               FramedIPAddress, CallingStationId, FramedProtocol \
+       FROM ${acct_table1} \
+       WHERE UserName='%{SQL-User-Name}' \
+       AND AcctStopTime IS NULL"
+
+#######################################################################
+# Group Membership Queries
+#######################################################################
+# group_membership_query       - Check user group membership
+#######################################################################
+
+group_membership_query = "\
+       SELECT GroupName \
+       FROM ${usergroup_table} \
+       WHERE UserName='%{SQL-User-Name}'"
+
+#######################################################################
+# Accounting and Post-Auth Queries
+#######################################################################
+# These queries insert/update accounting and authentication records.
+# The query to use is determined by the value of 'reference'.
+# This value is used as a configuration path and should resolve to one
+# or more 'query's. If reference points to multiple queries, and a query
+# fails, the next query is executed.
+#
+# Behaviour is identical to the old 1.x/2.x module, except we can now
+# fail between N queries, and query selection can be based on any
+# combination of attributes, or custom 'Acct-Status-Type' values.
+#######################################################################
+accounting {
+       reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
 #              logfile = ${logdir}/accounting.sql
 
-               type {
-                       accounting-on {
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               AcctStopTime = TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
-                                               AcctSessionTime = round((TO_DATE('%S','yyyy-mm-dd hh24:mi:ss') - \
-                                                       TO_DATE(TO_CHAR(acctstarttime, 'yyyy-mm-dd hh24:mi:ss'),'yyyy-mm-dd hh24:mi:ss'))*86400), \
-                                               AcctTerminateCause='%{Acct-Terminate-Cause}', \
-                                               AcctStopDelay = %{Acct-Delay-Time:-0} \
-                                       WHERE AcctStopTime IS NULL \
-                                       AND NASIPAddress = '%{NAS-IP-Address}' \
-                                       AND AcctStartTime <= TO_DATE('%S','yyyy-mm-dd hh24:mi:ss')"
-                       }
+       type {
+               accounting-on {
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStopTime = TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
+                                       AcctSessionTime = round((TO_DATE('%S','yyyy-mm-dd hh24:mi:ss') - \
+                                               TO_DATE(TO_CHAR(acctstarttime, 'yyyy-mm-dd hh24:mi:ss'),'yyyy-mm-dd hh24:mi:ss'))*86400), \
+                                       AcctTerminateCause='%{%{Acct-Terminate-Cause}:-NAS-Reboot}', \
+                                       AcctStopDelay = %{%{Acct-Delay-Time}:-0} \
+                               WHERE AcctStopTime IS NULL \
+                               AND NASIPAddress = '%{NAS-IP-Address}' \
+                               AND AcctStartTime <= TO_DATE('%S','yyyy-mm-dd hh24:mi:ss')"
+               }
 
-                       accounting-off {
-                               query = "${..accounting-on.query}"
-                       }
+               accounting-off {
+                       query = "${..accounting-on.query}"
+               }
 
-                       start {
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (RadAcctId,             AcctSessionId,          AcctUniqueId, \
-                                               UserName,               Realm,                  NASIPAddress, \
-                                               NASPortId,              NASPortType,            AcctStartTime, \
-                                               AcctStopTime,           AcctSessionTime,        AcctAuthentic, \
-                                               ConnectInfo_start,      ConnectInfo_stop,       AcctInputOctets, \
-                                               AcctOutputOctets,       CalledStationId,        CallingStationId, \
-                                               AcctTerminateCause,     ServiceType,            FramedProtocol, \
-                                               FramedIPAddress,        AcctStartDelay,         AcctStopDelay, \
-                                               XAscendSessionSvrKey) \
-                                       VALUES(\
-                                               '', \
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port-Id}', \
-                                               '%{NAS-Port-Type}', \
-                                               TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
-                                               NULL, \
-                                               '0', \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '', \
-                                               '0', \
-                                               '0', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}', \
-                                               '%{Acct-Delay-Time}', \
-                                               '0', \
-                                               '%{X-Ascend-Session-Svr-Key}')"
+               start {
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (RadAcctId,             AcctSessionId,          AcctUniqueId, \
+                                       UserName,               Realm,                  NASIPAddress, \
+                                       NASPortId,              NASPortType,            AcctStartTime, \
+                                       AcctStopTime,           AcctSessionTime,        AcctAuthentic, \
+                                       ConnectInfo_start,      ConnectInfo_stop,       AcctInputOctets, \
+                                       AcctOutputOctets,       CalledStationId,        CallingStationId, \
+                                       AcctTerminateCause,     ServiceType,            FramedProtocol, \
+                                       FramedIPAddress,        AcctStartDelay,         AcctStopDelay, \
+                                       XAscendSessionSvrKey) \
+                               VALUES(\
+                                       '', \
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port-Id}', \
+                                       '%{NAS-Port-Type}', \
+                                       TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
+                                       NULL, \
+                                       '0', \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       '', \
+                                       '0', \
+                                       '0', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}', \
+                                       '%{Acct-Delay-Time}', \
+                                       '0', \
+                                       '%{X-Ascend-Session-Svr-Key}')"
 
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               AcctStartTime = TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
-                                               AcctStartDelay = '%{Acct-Delay-Time:-0}', \
-                                               ConnectInfo_start = '%{Connect-Info}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress = '%{NAS-IP-Address}' \
-                                       AND AcctStopTime IS NULL"
-                       }
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStartTime = TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
+                                       AcctStartDelay = '%{%{Acct-Delay-Time}:-0}', \
+                                       ConnectInfo_start = '%{Connect-Info}' \
+                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
+                               AND UserName = '%{SQL-User-Name}' \
+                               AND NASIPAddress = '%{NAS-IP-Address}' \
+                               AND AcctStopTime IS NULL"
+               }
 
-                       interim-update {
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               FramedIPAddress = NULLIF('%{Framed-IP-Address}', ''), \
-                                               AcctSessionTime = '%{Acct-Session-Time}', \
-                                               AcctInputOctets = '%{Acct-Input-Octets}' + \
-                                                       ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
-                                               AcctOutputOctets = '%{Acct-Output-Octets}' +  \
-                                                       ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296) \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress= '%{NAS-IP-Address}' \
-                                       AND AcctStopTime IS NULL"
+               interim-update {
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       FramedIPAddress = NULLIF('%{Framed-IP-Address}', ''), \
+                                       AcctSessionTime = '%{Acct-Session-Time}', \
+                                       AcctInputOctets = '%{Acct-Input-Octets}' + \
+                                               ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
+                                       AcctOutputOctets = '%{Acct-Output-Octets}' +  \
+                                               ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296) \
+                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
+                               AND UserName = '%{SQL-User-Name}' \
+                               AND NASIPAddress= '%{NAS-IP-Address}' \
+                               AND AcctStopTime IS NULL"
 
-                               query = "\
-                                       INSERT into ${....acct_table1} \
-                                               (RadAcctId,             AcctSessionId,          AcctUniqueId, \
-                                               UserName,               Realm,                  NASIPAddress, \
-                                               NASPortId,              NASPortType,            AcctStartTime, \
-                                               AcctSessionTime,        AcctAuthentic,          ConnectInfo_start, \
-                                               AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
-                                               CallingStationId,       ServiceType,            FramedProtocol, \
-                                               FramedIPAddress,        AcctStartDelay,         XAscendSessionSvrKey) \
-                                       VALUES(\
-                                               '', \
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port-Id}', \
-                                               '%{NAS-Port-Type}', \
-                                               NULL, \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '', \
-                                               '%{Acct-Input-Octets}' + \
-                                                       ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
-                                               '%{Acct-Output-Octets}' +  \
-                                                       ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296), \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}', \
-                                               '0', \
-                                               '%{X-Ascend-Session-Svr-Key}')"
-                       }
+                       query = "\
+                               INSERT into ${....acct_table1} \
+                                       (RadAcctId,             AcctSessionId,          AcctUniqueId, \
+                                       UserName,               Realm,                  NASIPAddress, \
+                                       NASPortId,              NASPortType,            AcctStartTime, \
+                                       AcctSessionTime,        AcctAuthentic,          ConnectInfo_start, \
+                                       AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
+                                       CallingStationId,       ServiceType,            FramedProtocol, \
+                                       FramedIPAddress,        AcctStartDelay,         XAscendSessionSvrKey) \
+                               VALUES(\
+                                       '', \
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port-Id}', \
+                                       '%{NAS-Port-Type}', \
+                                       NULL, \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '', \
+                                       '%{Acct-Input-Octets}' + \
+                                               ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
+                                       '%{Acct-Output-Octets}' +  \
+                                               ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296), \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}', \
+                                       '0', \
+                                       '%{X-Ascend-Session-Svr-Key}')"
+               }
 
-                       stop {
-                               query = "\
-                                       UPDATE ${....acct_table2} \
-                                       SET \
-                                               AcctStopTime = TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
-                                               AcctSessionTime = '%{Acct-Session-Time}', \
-                                               AcctInputOctets = '%{Acct-Input-Octets}' + \
-                                                       ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
-                                               AcctOutputOctets = '%{Acct-Output-Octets}' +  \
-                                                       ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296), \
-                                               AcctTerminateCause = '%{Acct-Terminate-Cause}', \
-                                               AcctStopDelay = '%{Acct-Delay-Time:-0}', \
-                                               ConnectInfo_stop = '%{Connect-Info}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress = '%{NAS-IP-Address}' \
-                                       AND AcctStopTime IS NULL"
+               stop {
+                       query = "\
+                               UPDATE ${....acct_table2} \
+                               SET \
+                                       AcctStopTime = TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
+                                       AcctSessionTime = '%{Acct-Session-Time}', \
+                                       AcctInputOctets = '%{Acct-Input-Octets}' + \
+                                               ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
+                                       AcctOutputOctets = '%{Acct-Output-Octets}' +  \
+                                               ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296), \
+                                       AcctTerminateCause = '%{Acct-Terminate-Cause}', \
+                                       AcctStopDelay = '%{%{Acct-Delay-Time}:-0}', \
+                                       ConnectInfo_stop = '%{Connect-Info}' \
+                               WHERE AcctSessionId = '%{Acct-Session-Id}' \
+                               AND UserName = '%{SQL-User-Name}' \
+                               AND NASIPAddress = '%{NAS-IP-Address}' \
+                               AND AcctStopTime IS NULL"
 
-                               query = "\
-                                       "INSERT into ${....acct_table2} \
-                                               (RadAcctId,             AcctSessionId,          AcctUniqueId, \
-                                                UserName,              Realm,                  NASIPAddress, \
-                                                NASPortId,             NASPortType,            AcctStartTime, \
-                                                AcctStopTime,          AcctSessionTime,        AcctAuthentic, \
-                                                ConnectInfo_start,     ConnectInfo_stop,       AcctInputOctets, \
-                                                AcctOutputOctets,      CalledStationId,        CallingStationId, \
-                                                AcctTerminateCause,    ServiceType,            FramedProtocol, \
-                                                FramedIPAddress,       AcctStartDelay,         AcctStopDelay) \
-                                       VALUES(\
-                                               '', \
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port-Id}', \
-                                               '%{NAS-Port-Type}', \
-                                               NULL, \
-                                               TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '', \
-                                               '%{Connect-Info}', \
-                                               '%{Acct-Input-Octets}' + \
-                                                       ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
-                                               '%{Acct-Output-Octets}' + \
-                                                       ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296), \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Acct-Terminate-Cause}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}', \
-                                               '0', \
-                                               '%{Acct-Delay-Time:-0}')"
+                       query = "\
+                               INSERT into ${....acct_table2} \
+                                       (RadAcctId,             AcctSessionId,          AcctUniqueId, \
+                                        UserName,              Realm,                  NASIPAddress, \
+                                        NASPortId,             NASPortType,            AcctStartTime, \
+                                        AcctStopTime,          AcctSessionTime,        AcctAuthentic, \
+                                        ConnectInfo_start,     ConnectInfo_stop,       AcctInputOctets, \
+                                        AcctOutputOctets,      CalledStationId,        CallingStationId, \
+                                        AcctTerminateCause,    ServiceType,            FramedProtocol, \
+                                        FramedIPAddress,       AcctStartDelay,         AcctStopDelay) \
+                               VALUES(\
+                                       '', \
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port-Id}', \
+                                       '%{NAS-Port-Type}', \
+                                       NULL, \
+                                       TO_DATE('%S','yyyy-mm-dd hh24:mi:ss'), \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '', \
+                                       '%{Connect-Info}', \
+                                       '%{Acct-Input-Octets}' + \
+                                               ('%{%{Acct-Input-Gigawords}:-0}' * 4294967296), \
+                                       '%{Acct-Output-Octets}' + \
+                                               ('%{%{Acct-Output-Gigawords}:-0}' * 4294967296), \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Acct-Terminate-Cause}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}', \
+                                       '0', \
+                                       '%{%{Acct-Delay-Time}:-0}')"
 
-                       }
                }
        }
+}
 
-       #######################################################################
-       # Authentication Logging Queries
-       #######################################################################
-       # postauth_query                - Insert some info after authentication
-       #######################################################################
+#######################################################################
+# Authentication Logging Queries
+#######################################################################
+# postauth_query                - Insert some info after authentication
+#######################################################################
 
-       post-auth {
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/post-auth.sql
-               query = "\
-                       INSERT INTO ${..postauth_table} \
-                               (username, pass, reply, authdate) \
-                   VALUES (\
+post-auth {
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/post-auth.sql
+       query = "\
+               INSERT INTO ${..postauth_table} \
+                       (username, pass, reply, authdate) \
+               VALUES (\
                        '%{User-Name}', \
                        '%{%{User-Password}:-%{Chap-Password}}', \
                        '%{reply:Packet-Type}', \
                        TO_TIMESTAMP('%S','YYYY-MM-DDHH24:MI:SS'))"
-       }
+}
index 4339698..c11295f 100644 (file)
@@ -171,44 +171,44 @@ CREATE TABLE realms (
 CREATE SEQUENCE realms_seq START WITH 1 INCREMENT BY 1;
 
 CREATE TABLE radhuntgroup (
-        id              INT PRIMARY KEY,
-        GroupName VARCHAR(64) NOT NULL,
-        Nasipaddress VARCHAR(15) UNIQUE NOT NULL,
-        NASPortID VARCHAR(15)
+       id              INT PRIMARY KEY,
+       GroupName VARCHAR(64) NOT NULL,
+       Nasipaddress VARCHAR(15) UNIQUE NOT NULL,
+       NASPortID VARCHAR(15)
 );
 
 CREATE SEQUENCE radhuntgroup_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER radhuntgroup_serialnumber
-        BEFORE INSERT OR UPDATE OF id ON radhuntgroup
-        FOR EACH ROW
-        BEGIN
-                if ( :new.id = 0 or :new.id is null ) then
-                        SELECT radhuntgroup_seq.nextval into :new.id from dual;
-                end if;
-        END;
+       BEFORE INSERT OR UPDATE OF id ON radhuntgroup
+       FOR EACH ROW
+       BEGIN
+               if ( :new.id = 0 or :new.id is null ) then
+                       SELECT radhuntgroup_seq.nextval into :new.id from dual;
+               end if;
+       END;
 
 CREATE TABLE radpostauth (
-          id            INT PRIMARY KEY,
-          UserName      VARCHAR(64) NOT NULL,
-          Pass          VARCHAR(64),
-          Reply         VARCHAR(64),
-          AuthDate     DATE
+         id            INT PRIMARY KEY,
+         UserName      VARCHAR(64) NOT NULL,
+         Pass          VARCHAR(64),
+         Reply         VARCHAR(64),
+         AuthDate      DATE
 );
 
 CREATE SEQUENCE radpostauth_seq START WITH 1 INCREMENT BY 1;
 
 CREATE OR REPLACE TRIGGER radpostauth_TRIG
-        BEFORE INSERT OR UPDATE OF id ON radpostauth
-        FOR EACH ROW
-        BEGIN
-                if ( :new.id = 0 or :new.id is null ) then
-                        SELECT radpostauth_seq.nextval into :new.id from dual;
-                end if;
-                if (:new.AuthDate is null) then
-                  select sysdate into :new.AuthDate from dual;
-                end if;
-        END;
+       BEFORE INSERT OR UPDATE OF id ON radpostauth
+       FOR EACH ROW
+       BEGIN
+               if ( :new.id = 0 or :new.id is null ) then
+                       SELECT radpostauth_seq.nextval into :new.id from dual;
+               end if;
+               if (:new.AuthDate is null) then
+                 select sysdate into :new.AuthDate from dual;
+               end if;
+       END;
 
 /
 
@@ -216,15 +216,15 @@ CREATE OR REPLACE TRIGGER radpostauth_TRIG
  * Table structure for table 'nas'
  */
 CREATE TABLE nas (
-        id              INT PRIMARY KEY,
-        nasname         VARCHAR(128),
-        shortname       VARCHAR(32),
-        type            VARCHAR(30),
-        ports           INT,
-        secret          VARCHAR(60),
-        server          VARCHAR(64),
-        community       VARCHAR(50),
-        description     VARCHAR(200)
+       id              INT PRIMARY KEY,
+       nasname         VARCHAR(128),
+       shortname       VARCHAR(32),
+       type            VARCHAR(30),
+       ports           INT,
+       secret          VARCHAR(60),
+       server          VARCHAR(64),
+       community       VARCHAR(50),
+       description     VARCHAR(200)
 );
 CREATE SEQUENCE nas_seq START WITH 1 INCREMENT BY 1;
 
index 9596a7d..ec97316 100644 (file)
@@ -190,31 +190,31 @@ CREATE TRUSTED LANGUAGE "plpgsql" HANDLER "plpgsql_call_handler";
 
 CREATE OR REPLACE FUNCTION strip_dot (VARCHAR) RETURNS TIMESTAMPTZ AS '
  DECLARE
-        original_timestamp ALIAS FOR $1;
+       original_timestamp ALIAS FOR $1;
  BEGIN
        IF original_timestamp = '''' THEN
                RETURN NULL;
-        END IF;
-        IF substring(original_timestamp from 1 for 1) = ''.'' THEN
-                RETURN substring(original_timestamp from 2);
-        ELSE
-                RETURN original_timestamp;
-        END IF;
+       END IF;
+       IF substring(original_timestamp from 1 for 1) = ''.'' THEN
+               RETURN substring(original_timestamp from 2);
+       ELSE
+               RETURN original_timestamp;
+       END IF;
  END;
 ' LANGUAGE 'plpgsql';
 
 
 CREATE OR REPLACE FUNCTION pick_id (VARCHAR, VARCHAR) RETURNS VARCHAR AS '
  DECLARE
-        h323confid ALIAS FOR $1;
-        callid ALIAS FOR $2;
+       h323confid ALIAS FOR $1;
+       callid ALIAS FOR $2;
  BEGIN
-        IF h323confid <> '''' THEN
-                RETURN h323confid;
-        END IF;
-        IF callid <> '''' THEN
-                RETURN callid;
-        END IF;
+       IF h323confid <> '''' THEN
+               RETURN h323confid;
+       END IF;
+       IF callid <> '''' THEN
+               RETURN callid;
+       END IF;
        RETURN NULL;
  END;
 ' LANGUAGE 'plpgsql';
index 945bafb..37f42a0 100644 (file)
 CREATE OR REPLACE FUNCTION upd_radgroups() RETURNS trigger AS'
 
 DECLARE
-        v_groupname varchar;
+       v_groupname varchar;
 
 BEGIN
-        SELECT INTO v_groupname GroupName FROM radusergroup WHERE CalledStationId = NEW.CalledStationId AND UserName = NEW.UserName;
-        IF FOUND THEN
-                UPDATE radacct SET GroupName = v_groupname WHERE RadAcctId = NEW.RadAcctId;
-        END IF;
+       SELECT INTO v_groupname GroupName FROM radusergroup WHERE CalledStationId = NEW.CalledStationId AND UserName = NEW.UserName;
+       IF FOUND THEN
+               UPDATE radacct SET GroupName = v_groupname WHERE RadAcctId = NEW.RadAcctId;
+       END IF;
 
-        RETURN NEW;
+       RETURN NEW;
 END
 
 'LANGUAGE plpgsql;
index da4fb97..9f1449c 100644 (file)
@@ -20,7 +20,7 @@
        #    Else use User-Name, if it's there,
        #    Else use hard-coded string "none" as the user name.
        #
-       #sql_user_name = "%{Stripped-User-Name:-%{User-Name:-none}}"
+       #sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-none}}"
        #
        sql_user_name = "%{User-Name}"
 
                        start {
                                query = "INSERT INTO ${....acct_table1}%{h323-call-type} \
                                                (RadiusServerName, UserName, NASIPAddress, AcctTime, CalledStationId, \
-                                                CallingStationId, AcctDelayTime, h323gwid, h323callorigin, \
-                                                h323setuptime, H323ConnectTime, callid) \
+                                                CallingStationId, AcctDelayTime, h323gwid, h323callorigin, \
+                                                h323setuptime, H323ConnectTime, callid) \
                                        VALUES(\
                                                '${radius_server_name}', '%{SQL-User-Name}', \
                                                '%{NAS-IP-Address}', now(), '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', '%{Acct-Delay-Time:-0}', '%{h323-gw-id}', \
+                                               '%{Calling-Station-Id}', '%{%{Acct-Delay-Time}:-0}', '%{h323-gw-id}', \
                                                '%{h323-call-origin}', strip_dot('%{h323-setup-time}'), \
                                                strip_dot('%{h323-connect-time}'), pick_id('%{h323-conf-id}', \
                                                '%{call-id}'))"
                        stop {
                                query = "INSERT INTO $....acct_table2}%{h323-call-type} \
                                                (RadiusServerName, UserName, NASIPAddress, AcctTime, \
-                                                AcctSessionTime, AcctInputOctets, AcctOutputOctets, CalledStationId, \
-                                                CallingStationId, AcctDelayTime, H323RemoteAddress, H323VoiceQuality, \
-                                                CiscoNASPort, h323callorigin, callid, h323connecttime, \
-                                                h323disconnectcause, h323disconnecttime, h323gwid, h323setuptime) \
+                                                AcctSessionTime, AcctInputOctets, AcctOutputOctets, CalledStationId, \
+                                                CallingStationId, AcctDelayTime, H323RemoteAddress, H323VoiceQuality, \
+                                                CiscoNASPort, h323callorigin, callid, h323connecttime, \
+                                                h323disconnectcause, h323disconnecttime, h323gwid, h323setuptime) \
                                        VALUES(\
                                                '${radius_server_name}', '%{SQL-User-Name}', '%{NAS-IP-Address}', \
-                                               NOW(),  '%{Acct-Session-Time:-0}', \
-                                               '%{Acct-Input-Octets:-0}', '%{Acct-Output-Octets:-0}', \
+                                               NOW(),  '%{%{Acct-Session-Time}:-0}', \
+                                               '%{%{Acct-Input-Octets}:-0}', '%{%{Acct-Output-Octets}:-0}', \
                                                '%{Called-Station-Id}', '%{Calling-Station-Id}', \
-                                               '%{Acct-Delay-Time:-0}', NULLIF('%{h323-remote-address}', '')::inet, \
+                                               '%{%{Acct-Delay-Time}:-0}', NULLIF('%{h323-remote-address}', '')::inet, \
                                                NULLIF('%{h323-voice-quality}','')::integer, \
                                                NULLIF('%{Cisco-NAS-Port}', ''), \
                                                '%{h323-call-origin}', pick_id('%{h323-conf-id}', '%{call-id}'), \
                                                strip_dot('%{h323-connect-time}'), '%{h323-disconnect-cause}', \
                                                strip_dot('%{h323-disconnect-time}'), '%{h323-gw-id}', \
                                                strip_dot('%{h323-setup-time}'))"
-                       }
-               }
-       }
+                       }
+               }
+       }
index f91bd5e..1a498f1 100644 (file)
-       # -*- text -*-
-       ##
-       ## dialup.conf -- PostgreSQL configuration for default schema (schema.sql)
-       ##
-       ##      $Id$
-
-       # Safe characters list for sql queries. Everything else is replaced
-       # with their mime-encoded equivalents.
-       # The default list should be ok
-       # safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
-
-       #######################################################################
-       #  Query config:  Username
-       #######################################################################
-       # This is the username that will get substituted, escaped, and added
-       # as attribute 'SQL-User-Name'.  '%{SQL-User-Name}' should be used
-       # below everywhere a username substitution is needed so you you can
-       # be sure the username passed from the client is escaped properly.
-       #
-       # Uncomment the next line, if you want the sql_user_name to mean:
-       #
-       #    Use Stripped-User-Name, if it's there.
-       #    Else use User-Name, if it's there,
-       #    Else use hard-coded string "none" as the user name.
-       #
-       #sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-none}}"
-
-       sql_user_name = "%{User-Name}"
-
-       #######################################################################
-       #  Default profile
-       #######################################################################
-       # This is the default profile. It is found in SQL by group membership.
-       # That means that this profile must be a member of at least one group
-       # which will contain the corresponding check and reply items.
-       # This profile will be queried in the authorize section for every user.
-       # The point is to assign all users a default profile without having to
-       # manually add each one to a group that will contain the profile.
-       # The SQL module will also honor the User-Profile attribute. This
-       # attribute can be set anywhere in the authorize section (ie the users
-       # file). It is found exactly as the default profile is found.
-       # If it is set then it will *overwrite* the default profile setting.
-       # The idea is to select profiles based on checks on the incoming
-       # packets, not on user group membership. For example:
-       # -- users file --
-       # DEFAULT       Service-Type == Outbound-User, User-Profile := "outbound"
-       # DEFAULT       Service-Type == Framed-User, User-Profile := "framed"
-       #
-       # By default the default_user_profile is not set
-       #
-       # default_user_profile = "DEFAULT"
-
-       #######################################################################
-       #  NAS Query
-       #######################################################################
-       #  This query retrieves the radius clients
-       #
-       #  0. Row ID (currently unused)
-       #  1. Name (or IP address)
-       #  2. Shortname
-       #  3. Type
-       #  4. Secret
-       #  5. Server
-       #######################################################################
-
-       client_query = "SELECT id, nasname, shortname, type, secret, server FROM ${client_table}"
-
-       #######################################################################
-       #  Authorization Queries
-       #######################################################################
-       #  These queries compare the check items for the user
-       #  in ${authcheck_table} and setup the reply items in
-       #  ${authreply_table}.  You can use any query/tables
-       #  you want, but the return data for each row MUST
-       #  be in the  following order:
-       #
-       #  0. Row ID (currently unused)
-       #  1. UserName/GroupName
-       #  2. Item Attr Name
-       #  3. Item Attr Value
-       #  4. Item Attr Operation
-       #######################################################################
-
-       # Use these for case insensitive usernames. WARNING: Slower queries!
-       # authorize_check_query = "SELECT id, UserName, Attribute, Value, Op \
-       #   FROM ${authcheck_table} \
-       #   WHERE LOWER(UserName) = LOWER('%{SQL-User-Name}') \
-       #   ORDER BY id"
-       # authorize_reply_query = "SELECT id, UserName, Attribute, Value, Op \
-       #   FROM ${authreply_table} \
-       #   WHERE LOWER(UserName) = LOWER('%{SQL-User-Name}') \
-       #   ORDER BY id"
-
-       authorize_check_query = "SELECT id, UserName, Attribute, Value, Op \
-         FROM ${authcheck_table} \
-         WHERE Username = '%{SQL-User-Name}' \
-         ORDER BY id"
-
-       authorize_reply_query = "SELECT id, UserName, Attribute, Value, Op \
-         FROM ${authreply_table} \
-         WHERE Username = '%{SQL-User-Name}' \
-         ORDER BY id"
-
-       # Use these for case insensitive usernames. WARNING: Slower queries!
-       # authorize_group_check_query = "SELECT ${groupcheck_table}.id, ${groupcheck_table}.GroupName, \
-       #   ${groupcheck_table}.Attribute, ${groupcheck_table}.Value, ${groupcheck_table}.Op \
-       #   FROM ${groupcheck_table}, ${usergroup_table} \
-       #   WHERE LOWER(${usergroup_table}.UserName) = LOWER('%{SQL-User-Name}') AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName \
-       #   ORDER BY ${groupcheck_table}.id"
-       # authorize_group_reply_query = "SELECT ${groupreply_table}.id, ${groupreply_table}.GroupName, \
-       #   ${groupreply_table}.Attribute, ${groupreply_table}.Value, ${groupreply_table}.Op \
-       #   FROM ${groupreply_table}, ${usergroup_table} \
-       #   WHERE LOWER(${usergroup_table}.UserName) = LOWER('%{SQL-User-Name}') AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName \
-       #   ORDER BY ${groupreply_table}.id"
-
-       authorize_group_check_query = "SELECT id, GroupName, Attribute, Value, op \
-         FROM ${groupcheck_table} \
-         WHERE GroupName = '%{Sql-Group}' \
-         ORDER BY id"
-
-       authorize_group_reply_query = "SELECT id, GroupName, Attribute, Value, op \
-         FROM ${groupreply_table} \
-         WHERE GroupName = '%{Sql-Group}' \
-         ORDER BY id"
-
-       #######################################################################
-       # Simultaneous Use Checking Queries
-       #######################################################################
-       # simul_count_query     - query for the number of current connections
-       #                       - If this is not defined, no simultaneous use checking
-       #                       - will be performed by this module instance
-       # simul_verify_query    - query to return details of current connections for verification
-       #                       - Leave blank or commented out to disable verification step
-       #                       - Note that the returned field order should not be changed.
-       #######################################################################
-
-       # Uncomment simul_count_query to enable simultaneous use checking
-       # simul_count_query = "SELECT COUNT(*) FROM ${acct_table1} WHERE UserName='%{SQL-User-Name}' AND AcctStopTime IS NULL"
-       # simul_verify_query = "SELECT RadAcctId, AcctSessionId, UserName, NASIPAddress, NASPortId, FramedIPAddress, CallingStationId, FramedProtocol FROM ${acct_table1} WHERE UserName='%{SQL-User-Name}' AND AcctStopTime IS NULL"
-
-       #######################################################################
-       # Group Membership Queries
-       #######################################################################
-       # group_membership_query        - Check user group membership
-       #######################################################################
-
-       # Use these for case insensitive usernames. WARNING: Slower queries!
-       # group_membership_query = "SELECT GroupName FROM ${usergroup_table} WHERE LOWER(UserName) = LOWER('%{SQL-User-Name}') ORDER BY priority"
-
-       group_membership_query = "SELECT GroupName FROM ${usergroup_table} WHERE UserName='%{SQL-User-Name}' ORDER BY priority"
-
-       #######################################################################
-       # Accounting and Post-Auth Queries
-       #######################################################################
-       # These queries insert/update accounting and authentication records.
-       # The query to use is determined by the value of 'reference'.
-       # This value is used as a configuration path and should resolve to one
-       # or more 'query's. If reference points to multiple queries, and a query
-       # fails, the next query is executed.
-       #
-       # Behaviour is identical to the old 1.x/2.x module, except we can now
-       # fail between N queries, and query selection can be based on any
-       # combination of attributes, or custom 'Acct-Status-Type' values.
-       #######################################################################
-       accounting {
-               reference = "%{tolower:type.%{Acct-Status-Type}.query}"
-
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/accounting.sql
-
-               type {
-                       accounting-on {
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               AcctStopTime = ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval), \
-                                               AcctSessionTime = (EXTRACT(EPOCH FROM ('%S'::timestamp with time zone - AcctStartTime::timestamp with time zone \
-                                                       - '%{%{Acct-Delay-Time}:-0}'::interval)))::BIGINT, \
-                                               AcctTerminateCause = '%{Acct-Terminate-Cause}', \
-                                               AcctStopDelay = 0 \
-                                       WHERE AcctStopTime IS NULL \
-                                       AND NASIPAddress= '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
-                                       AND AcctStartTime <= '%S'::timestamp"
-                       }
-
-                       accounting-off {
-                               query = "${..accounting-on.query}"
-                       }
-
-                       start {
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (AcctSessionId,         AcctUniqueId,           UserName, \
-                                               Realm,                  NASIPAddress,           NASPortId, \
-                                               NASPortType,            AcctStartTime,          AcctAuthentic, \
-                                               ConnectInfo_start,      CalledStationId,        CallingStationId, \
-                                               ServiceType,            FramedProtocol,         FramedIPAddress, \
-                                               AcctStartDelay,         XAscendSessionSvrKey) \
-                                       VALUES(\
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               NULLIF('%{Realm}', ''), \
-                                               '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
-                                               %{%{NAS-Port}:-NULL}, \
-                                               '%{NAS-Port-Type}', \
-                                               ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval), \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               NULLIF('%{Framed-IP-Address}', '')::inet, \
-                                               0, \
-                                               '%{X-Ascend-Session-Svr-Key}')"
-
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               AcctStartTime = ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval), \
-                                               AcctStartDelay = 0, \
-                                               ConnectInfo_start = '%{Connect-Info}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
-                                       AND AcctStopTime IS NULL"
-                       }
-
-                       interim-update {
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
-                                               AcctSessionTime = '%{Acct-Session-Time}', \
-                                               AcctInputOctets = (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Input-Octets}:-0}'::bigint), \
-                                               AcctOutputOctets = (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Output-Octets}:-0}'::bigint) \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress= '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
-                                       AND AcctStopTime IS NULL"
-
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (AcctSessionId,         AcctUniqueId,           UserName, \
-                                               Realm,                  NASIPAddress,           NASPortId, \
-                                               NASPortType,            AcctStartTime,          AcctSessionTime, \
-                                               AcctAuthentic,          AcctInputOctets,        AcctOutputOctets, \
-                                               CalledStationId,        CallingStationId,       ServiceType, \
-                                               FramedProtocol,         FramedIPAddress,        XAscendSessionSvrKey) \
-                                       VALUES(\
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               NULLIF('%{Realm}', ''), \
-                                               '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
-                                               %{%{NAS-Port}:-NULL}, \
-                                               '%{NAS-Port-Type}', \
-                                               ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval - \
-                                                       '%{%{Acct-Session-Time}:-0}'::interval), \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Input-Octets}:-0}'::bigint), \
-                                               (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Output-Octets}:-0}'::bigint), \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               NULLIF('%{Framed-IP-Address}', '')::inet, \
-                                               '%{X-Ascend-Session-Svr-Key}')"
-                       }
-
-                       stop {
-                               query = "\
-                                       UPDATE ${....acct_table2} \
-                                       SET \
-                                               AcctStopTime = ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval), \
-                                               AcctSessionTime = CASE WHEN '%{Acct-Session-Time}' = ''\
-                                                       THEN \
-                                                               (EXTRACT(EPOCH FROM ('%S'::TIMESTAMP WITH TIME ZONE - \
-                                                               AcctStartTime::TIMESTAMP WITH TIME ZONE - \
-                                                               '%{%{Acct-Delay-Time}:-0}'::INTERVAL)))::BIGINT \
-                                                       ELSE \
-                                                               NULLIF('%{Acct-Session-Time}','')::BIGINT END, \
-                                               AcctInputOctets = (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Input-Octets}:-0}'::bigint), \
-                                               AcctOutputOctets = (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
+# -*- text -*-
+#
+#  main/postgresql/queries.conf -- PostgreSQL configuration for default schema (schema.sql)
+#
+#  $Id$
+
+# Safe characters list for sql queries. Everything else is replaced
+# with their mime-encoded equivalents.
+# The default list should be ok
+# safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
+
+#######################################################################
+#  Query config:  Username
+#######################################################################
+# This is the username that will get substituted, escaped, and added
+# as attribute 'SQL-User-Name'.  '%{SQL-User-Name}' should be used
+# below everywhere a username substitution is needed so you you can
+# be sure the username passed from the client is escaped properly.
+#
+# Uncomment the next line, if you want the sql_user_name to mean:
+#
+#    Use Stripped-User-Name, if it's there.
+#    Else use User-Name, if it's there,
+#    Else use hard-coded string "none" as the user name.
+#
+#sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-none}}"
+
+sql_user_name = "%{User-Name}"
+
+#######################################################################
+#  Default profile
+#######################################################################
+# This is the default profile. It is found in SQL by group membership.
+# That means that this profile must be a member of at least one group
+# which will contain the corresponding check and reply items.
+# This profile will be queried in the authorize section for every user.
+# The point is to assign all users a default profile without having to
+# manually add each one to a group that will contain the profile.
+# The SQL module will also honor the User-Profile attribute. This
+# attribute can be set anywhere in the authorize section (ie the users
+# file). It is found exactly as the default profile is found.
+# If it is set then it will *overwrite* the default profile setting.
+# The idea is to select profiles based on checks on the incoming
+# packets, not on user group membership. For example:
+# -- users file --
+# DEFAULT      Service-Type == Outbound-User, User-Profile := "outbound"
+# DEFAULT      Service-Type == Framed-User, User-Profile := "framed"
+#
+# By default the default_user_profile is not set
+#
+# default_user_profile = "DEFAULT"
+
+#######################################################################
+#  Open Query
+#######################################################################
+# This query is run whenever a new connection is opened.
+# It is commented out by default.
+#
+# If you have issues with connections hanging for too long, uncomment
+# the next line, and set the timeout in milliseconds.  As a general
+# rule, if the queries take longer than a second, something is wrong
+# with the database.
+#open_query = "set statement_timeout to 1000"
+
+#######################################################################
+#  NAS Query
+#######################################################################
+#  This query retrieves the radius clients
+#
+#  0. Row ID (currently unused)
+#  1. Name (or IP address)
+#  2. Shortname
+#  3. Type
+#  4. Secret
+#  5. Server
+#######################################################################
+
+client_query = "\
+       SELECT id, nasname, shortname, type, secret, server \
+       FROM ${client_table}"
+
+#######################################################################
+#  Authorization Queries
+#######################################################################
+#  These queries compare the check items for the user
+#  in ${authcheck_table} and setup the reply items in
+#  ${authreply_table}.  You can use any query/tables
+#  you want, but the return data for each row MUST
+#  be in the  following order:
+#
+#  0. Row ID (currently unused)
+#  1. UserName/GroupName
+#  2. Item Attr Name
+#  3. Item Attr Value
+#  4. Item Attr Operation
+#######################################################################
+
+#
+#  Use these for case insensitive usernames. WARNING: Slower queries!
+#
+#authorize_check_query = "\
+#      SELECT id, UserName, Attribute, Value, Op \
+#      FROM ${authcheck_table} \
+#      WHERE LOWER(UserName) = LOWER('%{SQL-User-Name}') \
+#      ORDER BY id"
+
+#authorize_reply_query = "\
+#      SELECT id, UserName, Attribute, Value, Op \
+#      FROM ${authreply_table} \
+#      WHERE LOWER(UserName) = LOWER('%{SQL-User-Name}') \
+#      ORDER BY id"
+
+authorize_check_query = "\
+       SELECT id, UserName, Attribute, Value, Op \
+       FROM ${authcheck_table} \
+       WHERE Username = '%{SQL-User-Name}' \
+       ORDER BY id"
+
+authorize_reply_query = "\
+       SELECT id, UserName, Attribute, Value, Op \
+       FROM ${authreply_table} \
+       WHERE Username = '%{SQL-User-Name}' \
+       ORDER BY id"
+
+#
+#  Use these for case insensitive usernames. WARNING: Slower queries!
+#
+#authorize_group_check_query = "\
+#      SELECT \
+#              ${groupcheck_table}.id, ${groupcheck_table}.GroupName, ${groupcheck_table}.Attribute, \
+#              ${groupcheck_table}.Value, ${groupcheck_table}.Op \
+#      FROM ${groupcheck_table}, ${usergroup_table} \
+#      WHERE LOWER(${usergroup_table}.UserName) = LOWER('%{SQL-User-Name}') \
+#      AND ${usergroup_table}.GroupName = ${groupcheck_table}.GroupName \
+#      ORDER BY ${groupcheck_table}.id"
+
+#authorize_group_reply_query = "\
+#      SELECT \
+#              ${groupreply_table}.id, ${groupreply_table}.GroupName, \
+#              ${groupreply_table}.Attribute, ${groupreply_table}.Value, ${groupreply_table}.Op \
+#      FROM ${groupreply_table}, ${usergroup_table} \
+#      WHERE LOWER(${usergroup_table}.UserName) = LOWER('%{SQL-User-Name}') \
+#      AND ${usergroup_table}.GroupName = ${groupreply_table}.GroupName \
+#      ORDER BY ${groupreply_table}.id"
+
+authorize_group_check_query = "\
+       SELECT id, GroupName, Attribute, Value, op \
+       FROM ${groupcheck_table} \
+       WHERE GroupName = '%{Sql-Group}' \
+       ORDER BY id"
+
+authorize_group_reply_query = "\
+       SELECT id, GroupName, Attribute, Value, op \
+       FROM ${groupreply_table} \
+       WHERE GroupName = '%{Sql-Group}' \
+       ORDER BY id"
+
+#######################################################################
+# Simultaneous Use Checking Queries
+#######################################################################
+# simul_count_query     - query for the number of current connections
+#                       - If this is not defined, no simultaneous use checking
+#                       - will be performed by this module instance
+# simul_verify_query    - query to return details of current connections for verification
+#                       - Leave blank or commented out to disable verification step
+#                       - Note that the returned field order should not be changed.
+#######################################################################
+
+#
+#  Uncomment simul_count_query to enable simultaneous use checking
+#
+#simul_count_query = "\
+#      SELECT COUNT(*) \
+#      FROM ${acct_table1} \
+#      WHERE UserName='%{SQL-User-Name}' \
+#      AND AcctStopTime IS NULL"
+
+#simul_verify_query = "\
+#      SELECT RadAcctId, AcctSessionId, UserName, NASIPAddress, NASPortId, FramedIPAddress, CallingStationId, \
+#              FramedProtocol \
+#      FROM ${acct_table1} \
+#      WHERE UserName='%{SQL-User-Name}' \
+#      AND AcctStopTime IS NULL"
+
+#######################################################################
+# Group Membership Queries
+#######################################################################
+# group_membership_query        - Check user group membership
+#######################################################################
+
+# Use these for case insensitive usernames. WARNING: Slower queries!
+#group_membership_query = "\
+#      SELECT GroupName \
+#      FROM ${usergroup_table} \
+#      WHERE LOWER(UserName) = LOWER('%{SQL-User-Name}') \
+#      ORDER BY priority"
+
+group_membership_query = "\
+       SELECT GroupName \
+       FROM ${usergroup_table} \
+       WHERE UserName='%{SQL-User-Name}' \
+       ORDER BY priority"
+
+#######################################################################
+# Accounting and Post-Auth Queries
+#######################################################################
+# These queries insert/update accounting and authentication records.
+# The query to use is determined by the value of 'reference'.
+# This value is used as a configuration path and should resolve to one
+# or more 'query's. If reference points to multiple queries, and a query
+# fails, the next query is executed.
+#
+# Behaviour is identical to the old 1.x/2.x module, except we can now
+# fail between N queries, and query selection can be based on any
+# combination of attributes, or custom 'Acct-Status-Type' values.
+#######################################################################
+
+accounting {
+       reference = "%{tolower:type.%{%{Acct-Status-Type}:-none}.query}"
+
+       session_identifier="(AcctUniqueId = '%{Acct-Unique-Session-Id}')"
+
+# Old method of identifying a session, should only be used for backwards
+# compatibility (and even then it's probably ok to use AcctUniqueId).
+#      session_identifier="\
+#              (AcctSessionId = '%{Acct-Session-Id}' \
+#              AND UserName = '%{SQL-User-Name}' \
+#              AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}')"
+
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/accounting.sql
+
+       column_list = "\
+               AcctSessionId,          AcctUniqueId,           UserName, \
+               Realm,                  NASIPAddress,           NASPortId, \
+               NASPortType,            AcctStartTime,          AcctUpdateTime, \
+               AcctStopTime,           AcctSessionTime,        AcctAuthentic, \
+               ConnectInfo_start,      ConnectInfo_Stop,       AcctInputOctets, \
+               AcctOutputOctets,       CalledStationId,        CallingStationId, \
+               AcctTerminateCause,     ServiceType,            FramedProtocol, \
+               FramedIpAddress"
+
+       type {
+               accounting-on {
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStopTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctSessionTime = (%{integer:Event-Timestamp} - EXTRACT(EPOCH FROM(AcctStartTime))), \
+                                       AcctTerminateCause = '%{%{Acct-Terminate-Cause}:-NAS-Reboot}', \
+                               WHERE AcctStopTime IS NULL \
+                               AND NASIPAddress= '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
+                               AND AcctStartTime <= '%S'::timestamp"
+               }
+
+               accounting-off {
+                       query = "${..accounting-on.query}"
+               }
+
+               start {
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES(\
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       NULLIF('%{Realm}', ''), \
+                                       '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
+                                       %{%{NAS-Port}:-NULL}, \
+                                       '%{NAS-Port-Type}', \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       NULL, \
+                                       0, \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       NULL, \
+                                       0, \
+                                       0, \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       NULL, \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       NULLIF('%{Framed-IP-Address}', '')::inet)"
+
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStartTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       ConnectInfo_start = '%{Connect-Info}' \
+                               WHERE ${...session_identifier} \
+                               AND AcctStopTime IS NULL"
+
+                       # and again where we don't have "AND AcctStopTime IS NULL"
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       AcctStartTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       ConnectInfo_start = '%{Connect-Info}' \
+                               WHERE ${...session_identifier}"
+               }
+
+               interim-update {
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
+                                       AcctSessionTime = %{%{Acct-Session-Time}:-NULL}, \
+                                       AcctInterval = (%{integer:Event-Timestamp} - EXTRACT(EPOCH FROM (COALESCE(AcctUpdateTime, AcctStartTime)))), \
+                                       AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctInputOctets = (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Input-Octets}:-0}'::bigint), \
+                                       AcctOutputOctets = (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Output-Octets}:-0}'::bigint) \
+                               WHERE ${...session_identifier} \
+                               AND AcctStopTime IS NULL"
+
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES(\
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       NULLIF('%{Realm}', ''), \
+                                       '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
+                                       %{%{NAS-Port}:-NULL}, \
+                                       '%{NAS-Port-Type}', \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       NULL, \
+                                       %{%{Acct-Session-Time}:-NULL}, \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       NULL, \
+                                       (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Input-Octets}:-0}'::bigint), \
+                                       (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
                                                '%{%{Acct-Output-Octets}:-0}'::bigint), \
-                                               AcctTerminateCause = '%{Acct-Terminate-Cause}', \
-                                               AcctStopDelay = 0, \
-                                               FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
-                                               ConnectInfo_stop = '%{Connect-Info}' \
-                                       WHERE AcctSessionId = '%{Acct-Session-Id}' \
-                                       AND UserName = '%{SQL-User-Name}' \
-                                       AND NASIPAddress = '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}' \
-                                       AND AcctStopTime IS NULL"
-
-                               query = "\
-                                       INSERT INTO ${....acct_table2} \
-                                               (AcctSessionId,         AcctUniqueId,           UserName, \
-                                               Realm,                  NASIPAddress,           NASPortId, \
-                                               NASPortType,            AcctStartTime,          AcctStopTime, \
-                                               AcctSessionTime,        AcctAuthentic,          ConnectInfo_stop, \
-                                               AcctInputOctets,        AcctOutputOctets,       CalledStationId, \
-                                               CallingStationId,       AcctTerminateCause,     ServiceType, \
-                                               FramedProtocol,         FramedIPAddress,        AcctStopDelay) \
-                                       VALUES(\
-                                               '%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               NULLIF('%{Realm}', ''), \
-                                               '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
-                                               %{%{NAS-Port}:-NULL}, \
-                                               '%{NAS-Port-Type}', \
-                                               ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval - \
-                                                       '%{%{Acct-Session-Time}:-0}'::interval), \
-                                               ('%S'::timestamp - '%{%{Acct-Delay-Time}:-0}'::interval), \
-                                               NULLIF('%{Acct-Session-Time}', '')::bigint, \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Input-Octets}:-0}'::bigint), \
-                                               (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
-                                                       '%{%{Acct-Output-Octets}:-0}'::bigint), \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Acct-Terminate-Cause}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               NULLIF('%{Framed-IP-Address}', '')::inet, 0)"
-                       }
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       NULL, \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       NULLIF('%{Framed-IP-Address}', '')::inet)"
                }
-       }
 
+               stop {
+                       query = "\
+                               UPDATE ${....acct_table2} \
+                               SET \
+                                       AcctStopTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctSessionTime = COALESCE(%{%{Acct-Session-Time}:-NULL}, \
+                                               (%{integer:Event-Timestamp} - EXTRACT(EPOCH FROM(AcctStartTime)))), \
+                                       AcctInputOctets = (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Input-Octets}:-0}'::bigint), \
+                                       AcctOutputOctets = (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Output-Octets}:-0}'::bigint), \
+                                       AcctTerminateCause = '%{Acct-Terminate-Cause}', \
+                                       FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
+                                       ConnectInfo_stop = '%{Connect-Info}' \
+                               WHERE ${...session_identifier} \
+                               AND AcctStopTime IS NULL"
+
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES(\
+                                       '%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       NULLIF('%{Realm}', ''), \
+                                       '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \
+                                       %{%{NAS-Port}:-NULL}, \
+                                       '%{NAS-Port-Type}', \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp} - %{%{Acct-Session-Time}:-0}), \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       NULLIF('%{Acct-Session-Time}', '')::bigint, \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       NULL, \
+                                       (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Input-Octets}:-0}'::bigint), \
+                                       (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Output-Octets}:-0}'::bigint), \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Acct-Terminate-Cause}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       NULLIF('%{Framed-IP-Address}', '')::inet)"
+
+                       # and again where we don't have "AND AcctStopTime IS NULL"
+                       query = "\
+                               UPDATE ${....acct_table2} \
+                               SET \
+                                       AcctStopTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctUpdateTime = TO_TIMESTAMP(%{integer:Event-Timestamp}), \
+                                       AcctSessionTime = COALESCE(%{%{Acct-Session-Time}:-NULL}, \
+                                               (%{integer:Event-Timestamp} - EXTRACT(EPOCH FROM(AcctStartTime)))), \
+                                       AcctInputOctets = (('%{%{Acct-Input-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Input-Octets}:-0}'::bigint), \
+                                       AcctOutputOctets = (('%{%{Acct-Output-Gigawords}:-0}'::bigint << 32) + \
+                                               '%{%{Acct-Output-Octets}:-0}'::bigint), \
+                                       AcctTerminateCause = '%{Acct-Terminate-Cause}', \
+                                       FramedIPAddress = NULLIF('%{Framed-IP-Address}', '')::inet, \
+                                       ConnectInfo_stop = '%{Connect-Info}' \
+                               WHERE ${...session_identifier}"
+               }
 
-       #######################################################################
-       # Authentication Logging Queries
-       #######################################################################
-       # postauth_query                - Insert some info after authentication
-       #######################################################################
-
-       post-auth {
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/post-auth.sql
-
-               query = "\
-                       INSERT INTO ${..postauth_table} \
-                               (username, pass, reply, authdate) \
-                       VALUES(\
-                               '%{User-Name}', \
-                               '%{%{User-Password}:-Chap-Password}', \
-                               '%{reply:Packet-Type}', \
-                               NOW())"
+               #
+               #  No Acct-Status-Type == ignore the packet
+               #
+               none {
+                    query = "SELECT true"
+               }
        }
+}
+
+
+#######################################################################
+# Authentication Logging Queries
+#######################################################################
+# postauth_query                - Insert some info after authentication
+#######################################################################
+
+post-auth {
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/post-auth.sql
+
+       query = "\
+               INSERT INTO ${..postauth_table} \
+                       (username, pass, reply, authdate) \
+               VALUES(\
+                       '%{User-Name}', \
+                       '%{%{User-Password}:-Chap-Password}', \
+                       '%{reply:Packet-Type}', \
+                       NOW())"
+}
index 1aff0a4..00b5e3b 100644 (file)
@@ -24,7 +24,9 @@ CREATE TABLE radacct (
        NASPortId               text,
        NASPortType             text,
        AcctStartTime           timestamp with time zone,
+       AcctUpdateTime          timestamp with time zone,
        AcctStopTime            timestamp with time zone,
+       AcctInterval            bigint,
        AcctSessionTime         bigint,
        AcctAuthentic           text,
        ConnectInfo_start       text,
@@ -36,17 +38,26 @@ CREATE TABLE radacct (
        AcctTerminateCause      text,
        ServiceType             text,
        FramedProtocol          text,
-       FramedIPAddress         inet,
-       AcctStartDelay          integer,
-       AcctStopDelay           integer
+       FramedIPAddress         inet
 );
 -- This index may be useful..
 -- CREATE UNIQUE INDEX radacct_whoson on radacct (AcctStartTime, nasipaddress);
 
--- For use by onoff-, update-, stop- and simul_* queries
-CREATE INDEX radacct_active_user_idx ON radacct (UserName, NASIPAddress, AcctSessionId) WHERE AcctStopTime IS NULL;
+-- For use by update-, stop- and simul_* queries
+CREATE INDEX radacct_active_session_idx ON radacct (AcctUniqueId) WHERE AcctStopTime IS NULL;
+
+-- Add if you you regularly have to replay packets
+-- CREATE INDEX radacct_session_idx ON radacct (AcctUniqueId);
+
+-- For backwards compatibility
+-- CREATE INDEX radacct_active_user_idx ON radacct (AcctSessionId, UserName, NASIPAddress) WHERE AcctStopTime IS NULL;
+
+-- For use by onoff-
+CREATE INDEX radacct_bulk_close ON radacct (NASIPAddress, AcctStartTime) WHERE AcctStopTime IS NULL;
+
 -- and for common statistic queries:
 CREATE INDEX radacct_start_user_idx ON radacct (AcctStartTime, UserName);
+
 -- and, optionally
 -- CREATE INDEX radacct_stop_user_idx ON radacct (acctStopTime, UserName);
 
index 945fa60..c91f543 100644 (file)
 # -*- text -*-
-##
-## dialup.conf -- SQLite configuration for default schema (schema.sql)
-##
-##     $Id$
+#
+#  main/sqlite/queries.conf -- SQLite configuration for default schema (schema.sql)
+#
+#  Id: e1e83bf94814ed8be6239977b7bacfed21c0cd6a $
 
-       # Safe characters list for sql queries. Everything else is replaced
-       # with their mime-encoded equivalents.
-       # The default list should be ok
-       #safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
+# Safe characters list for sql queries. Everything else is replaced
+# with their mime-encoded equivalents.
+# The default list should be ok
+#safe_characters = "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"
 
-       #######################################################################
-       #  Query config:  Username
-       #######################################################################
-       # This is the username that will get substituted, escaped, and added
-       # as attribute 'SQL-User-Name'. '%{SQL-User-Name}' should be used below
-       # everywhere a username substitution is needed so you you can be sure
-       # the username passed from the client is escaped properly.
-       #
-       # Uncomment the next line, if you want the sql_user_name to mean:
-       #
-       #       Use Stripped-User-Name, if it's there.
-       #       Else use User-Name, if it's there,
-       #       Else use hard-coded string "DEFAULT" as the user name.
-       #sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-DEFAULT}}"
-       #
-       sql_user_name = "%{User-Name}"
+#######################################################################
+#  Query config:  Username
+#######################################################################
+# This is the username that will get substituted, escaped, and added
+# as attribute 'SQL-User-Name'. '%{SQL-User-Name}' should be used below
+# everywhere a username substitution is needed so you you can be sure
+# the username passed from the client is escaped properly.
+#
+# Uncomment the next line, if you want the sql_user_name to mean:
+#
+#      Use Stripped-User-Name, if it's there.
+#      Else use User-Name, if it's there,
+#      Else use hard-coded string "DEFAULT" as the user name.
+#sql_user_name = "%{%{Stripped-User-Name}:-%{%{User-Name}:-DEFAULT}}"
+#
+sql_user_name = "%{User-Name}"
 
-       #######################################################################
-       # Default profile
-       #######################################################################
-       # This is the default profile. It is found in SQL by group membership.
-       # That means that this profile must be a member of at least one group
-       # which will contain the corresponding check and reply items.
-       # This profile will be queried in the authorize section for every user.
-       # The point is to assign all users a default profile without having to
-       # manually add each one to a group that will contain the profile.
-       # The SQL module will also honor the User-Profile attribute. This
-       # attribute can be set anywhere in the authorize section (ie the users
-       # file). It is found exactly as the default profile is found.
-       # If it is set then it will *overwrite* the default profile setting.
-       # The idea is to select profiles based on checks on the incoming packets,
-       # not on user group membership. For example:
-       # -- users file --
-       # DEFAULT       Service-Type == Outbound-User, User-Profile := "outbound"
-       # DEFAULT       Service-Type == Framed-User, User-Profile := "framed"
-       #
-       # By default the default_user_profile is not set
-       #
-       #default_user_profile = "DEFAULT"
+#######################################################################
+# Default profile
+#######################################################################
+# This is the default profile. It is found in SQL by group membership.
+# That means that this profile must be a member of at least one group
+# which will contain the corresponding check and reply items.
+# This profile will be queried in the authorize section for every user.
+# The point is to assign all users a default profile without having to
+# manually add each one to a group that will contain the profile.
+# The SQL module will also honor the User-Profile attribute. This
+# attribute can be set anywhere in the authorize section (ie the users
+# file). It is found exactly as the default profile is found.
+# If it is set then it will *overwrite* the default profile setting.
+# The idea is to select profiles based on checks on the incoming packets,
+# not on user group membership. For example:
+# -- users file --
+# DEFAULT      Service-Type == Outbound-User, User-Profile := "outbound"
+# DEFAULT      Service-Type == Framed-User, User-Profile := "framed"
+#
+# By default the default_user_profile is not set
+#
+#default_user_profile = "DEFAULT"
 
-       #######################################################################
-       # NAS Query
-       #######################################################################
-       # This query retrieves the radius clients
-       #
-       # 0. Row ID (currently unused)
-       # 1. Name (or IP address)
-       # 2. Shortname
-       # 3. Type
-       # 4. Secret
-       # 5. Server
-       #######################################################################
+#######################################################################
+# NAS Query
+#######################################################################
+# This query retrieves the radius clients
+#
+# 0. Row ID (currently unused)
+# 1. Name (or IP address)
+# 2. Shortname
+# 3. Type
+# 4. Secret
+# 5. Server
+#######################################################################
 
-       client_query = "SELECT id, nasname, shortname, type, secret, server FROM ${client_table}"
+client_query = "\
+       SELECT id, nasname, shortname, type, secret, server \
+       FROM ${client_table}"
 
-       #######################################################################
-       # Authorization Queries
-       #######################################################################
-       # These queries compare the check items for the user
-       # in ${authcheck_table} and setup the reply items in
-       # ${authreply_table}. You can use any query/tables
-       # you want, but the return data for each row MUST
-       # be in the following order:
-       #
-       # 0. Row ID (currently unused)
-       # 1. UserName/GroupName
-       # 2. Item Attr Name
-       # 3. Item Attr Value
-       # 4. Item Attr Operation
-       #######################################################################
-       # Use these for case sensitive usernames.
-#      authorize_check_query = "\
+#######################################################################
+# Authorization Queries
+#######################################################################
+# These queries compare the check items for the user
+# in ${authcheck_table} and setup the reply items in
+# ${authreply_table}. You can use any query/tables
+# you want, but the return data for each row MUST
+# be in the following order:
+#
+# 0. Row ID (currently unused)
+# 1. UserName/GroupName
+# 2. Item Attr Name
+# 3. Item Attr Value
+# 4. Item Attr Operation
+#######################################################################
+
+#
+#  Use these for case sensitive usernames.
+#
+#authorize_check_query = "\
 #      SELECT id, username, attribute, value, op \
 #      FROM ${authcheck_table} \
 #      WHERE username = BINARY '%{SQL-User-Name}' \
 #      ORDER BY id"
 
-#      authorize_reply_query = "\
+#authorize_reply_query = "\
 #      SELECT id, username, attribute, value, op \
 #      FROM ${authreply_table} \
 #      WHERE username = BINARY '%{SQL-User-Name}' \
 #      ORDER BY id"
 
-       # The default queries are case insensitive. (for compatibility with
-       # older versions of FreeRADIUS)
-       authorize_check_query = "\
+#
+#  The default queries are case insensitive. (for compatibility with older versions of FreeRADIUS)
+#
+authorize_check_query = "\
        SELECT id, username, attribute, value, op \
        FROM ${authcheck_table} \
        WHERE username = '%{SQL-User-Name}' \
        ORDER BY id"
 
-       authorize_reply_query = "\
+authorize_reply_query = "\
        SELECT id, username, attribute, value, op \
        FROM ${authreply_table} \
        WHERE username = '%{SQL-User-Name}' \
        ORDER BY id"
 
-       # Use these for case sensitive usernames.
-#      group_membership_query = "\
+#
+# Use these for case sensitive usernames.
+#
+#group_membership_query = "\
 #      SELECT groupname \
 #      FROM ${usergroup_table} \
 #      WHERE username = BINARY '%{SQL-User-Name}' \
 #      ORDER BY priority"
 
-       group_membership_query = "\
+group_membership_query = "\
        SELECT groupname \
        FROM ${usergroup_table} \
        WHERE username = '%{SQL-User-Name}' \
        ORDER BY priority"
 
-       authorize_group_check_query = "\
+authorize_group_check_query = "\
        SELECT id, groupname, attribute, \
        Value, op \
        FROM ${groupcheck_table} \
        WHERE groupname = '%{Sql-Group}' \
        ORDER BY id"
 
-       authorize_group_reply_query = "\
+authorize_group_reply_query = "\
        SELECT id, groupname, attribute, \
        value, op \
        FROM ${groupreply_table} \
        WHERE groupname = '%{Sql-Group}' \
        ORDER BY id"
 
-       #######################################################################
-       # Simultaneous Use Checking Queries
-       #######################################################################
-       # simul_count_query     - query for the number of current connections
-       #                       - If this is not defined, no simultaneouls use checking
-       #                       - will be performed by this module instance
-       # simul_verify_query    - query to return details of current connections
-       #                               for verification
-       #                       - Leave blank or commented out to disable verification step
-       #                       - Note that the returned field order should not be changed.
-       #######################################################################
+#######################################################################
+# Simultaneous Use Checking Queries
+#######################################################################
+# simul_count_query    - query for the number of current connections
+#                      - If this is not defined, no simultaneouls use checking
+#                      - will be performed by this module instance
+# simul_verify_query   - query to return details of current connections
+#                              for verification
+#                      - Leave blank or commented out to disable verification step
+#                      - Note that the returned field order should not be changed.
+#######################################################################
 
-       # Uncomment simul_count_query to enable simultaneous use checking
-#      simul_count_query = "\
+#
+#  Uncomment simul_count_query to enable simultaneous use checking
+#
+#simul_count_query = "\
 #      SELECT COUNT(*) \
 #      FROM ${acct_table1} \
 #      WHERE username = '%{SQL-User-Name}' \
 #      AND acctstoptime IS NULL"
 
-       simul_verify_query = "\
-       SELECT radacctid, acctsessionid, username, \
-       nasipaddress, nasportid, framedipaddress, \
-       callingstationid, framedprotocol \
+simul_verify_query = "\
+       SELECT radacctid, acctsessionid, username, nasipaddress, nasportid, framedipaddress, \
+               callingstationid, framedprotocol \
        FROM ${acct_table1} \
        WHERE username = '%{SQL-User-Name}' \
        AND acctstoptime IS NULL"
 
-       #######################################################################
-       # Accounting and Post-Auth Queries
-       #######################################################################
-       # These queries insert/update accounting and authentication records.
-       # The query to use is determined by the value of 'reference'.
-       # This value is used as a configuration path and should resolve to one
-       # or more 'query's. If reference points to multiple queries, and a query
-       # fails, the next query is executed.
-       #
-       # Behaviour is identical to the old 1.x/2.x module, except we can now
-       # fail between N queries, and query selection can be based on any
-       # combination of attributes, or custom 'Acct-Status-Type' values.
-       #######################################################################
-       accounting {
-               reference = "%{tolower:type.%{Acct-Status-Type}.query}"
+#######################################################################
+# Accounting and Post-Auth Queries
+#######################################################################
+# These queries insert/update accounting and authentication records.
+# The query to use is determined by the value of 'reference'.
+# This value is used as a configuration path and should resolve to one
+# or more 'query's. If reference points to multiple queries, and a query
+# fails, the next query is executed.
+#
+# Behaviour is identical to the old 1.x/2.x module, except we can now
+# fail between N queries, and query selection can be based on any
+# combination of attributes, or custom 'Acct-Status-Type' values.
+#######################################################################
+accounting {
+       reference = "%{tolower:type.%{Acct-Status-Type}.query}"
 
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/accounting.sql
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/accounting.sql
 
-               column_list = "\
-                       acctsessionid,          acctuniqueid,           username, \
-                       realm,                  nasipaddress,           nasportid, \
-                       nasporttype,            acctstarttime,          acctupdatetime, \
-                       acctstoptime,           acctsessiontime,        acctauthentic, \
-                       connectinfo_start,      connectinfo_stop,       acctinputoctets, \
-                       acctoutputoctets,       calledstationid,        callingstationid, \
-                       acctterminatecause,     servicetype,            framedprotocol, \
-                       framedipaddress"
+       column_list = "\
+               acctsessionid,          acctuniqueid,           username, \
+               realm,                  nasipaddress,           nasportid, \
+               nasporttype,            acctstarttime,          acctupdatetime, \
+               acctstoptime,           acctsessiontime,        acctauthentic, \
+               connectinfo_start,      connectinfo_stop,       acctinputoctets, \
+               acctoutputoctets,       calledstationid,        callingstationid, \
+               acctterminatecause,     servicetype,            framedprotocol, \
+               framedipaddress"
 
-               type {
-                       accounting-on {
-                               #
-                               #  Bulk terminate all sessions associated with a given NAS
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               acctstoptime = %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               acctsessiontime = \
-                                                       %{%{integer:Event-Timestamp}:-strftime('%s', NOW())} \
-                                                       - strftime('%s', acctstarttime)), \
-                                               acctterminatecause = '%{Acct-Terminate-Cause}' \
-                                       WHERE acctstoptime IS NULL \
-                                       AND nasipaddress   = '%{NAS-IP-Address}' \
-                                       AND acctstarttime <= %{integer:Event-Timestamp}"
-                       }
+       type {
+               accounting-on {
+                       #
+                       #  Bulk terminate all sessions associated with a given NAS
+                       #
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       acctstoptime = %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       acctsessiontime = \
+                                               %{%{integer:Event-Timestamp}:-strftime('%s', 'now')} \
+                                               - strftime('%s', acctstarttime)), \
+                                       acctterminatecause = '%{Acct-Terminate-Cause}' \
+                               WHERE acctstoptime IS NULL \
+                               AND nasipaddress   = '%{NAS-IP-Address}' \
+                               AND acctstarttime <= %{integer:Event-Timestamp}"
+               }
 
-                       accounting-off {
-                               query = "${..accounting-on.query}"
-                       }
+               accounting-off {
+                       query = "${..accounting-on.query}"
+               }
 
-                       start {
-                               #
-                               #  Insert a new record into the sessions table
-                               #
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (${...column_list}) \
-                                       VALUES \
-                                               ('%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port}', \
-                                               '%{NAS-Port-Type}', \
-                                               %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               NULL, \
-                                               '0', \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '', \
-                                               '0', \
-                                               '0', \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}')"
+               start {
+                       #
+                       #  Insert a new record into the sessions table
+                       #
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES \
+                                       ('%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port}', \
+                                       '%{NAS-Port-Type}', \
+                                       %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       NULL, \
+                                       '0', \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       '', \
+                                       '0', \
+                                       '0', \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}')"
 
-                               #
-                               #  Key constraints prevented us from inserting a new session,
-                               #  use the alternate query to update an existing session.
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table1} SET \
-                                               acctstarttime   = %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               acctupdatetime  = %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               connectinfo_start = '%{Connect-Info}' \
-                                       WHERE acctsessionid = '%{Acct-Session-Id}' \
-                                       AND username            = '%{SQL-User-Name}' \
-                                       AND nasipaddress        = '%{NAS-IP-Address}'"
-                       }
+                       #
+                       #  Key constraints prevented us from inserting a new session,
+                       #  use the alternate query to update an existing session.
+                       #
+                       query = "\
+                               UPDATE ${....acct_table1} SET \
+                                       acctstarttime   = %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       acctupdatetime  = %{%{integer:Event-Timestamp}:-date('now'))}, \
+                                       connectinfo_start = '%{Connect-Info}' \
+                               WHERE acctsessionid = '%{Acct-Session-Id}' \
+                               AND username            = '%{SQL-User-Name}' \
+                               AND nasipaddress        = '%{NAS-IP-Address}'"
+               }
 
-                       interim-update {
-                               #
-                               #  Update an existing session and calculate the interval
-                               #  between the last data we received for the session and this
-                               #  update. This can be used to find stale sessions.
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table1} \
-                                       SET \
-                                               acctupdatetime  = %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               acctinterval    = 0, \
-                                               framedipaddress = '%{Framed-IP-Address}', \
-                                               acctsessiontime = '%{Acct-Session-Time}', \
-                                               acctinputoctets = %{%{Acct-Input-Gigawords}:-0} \
-                                                       << 32 | %{%{Acct-Input-Octets}:-0}, \
-                                               acctoutputoctets = %{%{Acct-Output-Gigawords}:-0} \
-                                                       << 32 | %{%{Acct-Output-Octets}:-0} \
-                                       WHERE acctsessionid     = '%{Acct-Session-Id}' \
-                                       AND username            = '%{SQL-User-Name}' \
-                                       AND nasipaddress        = '%{NAS-IP-Address}'"
+               interim-update {
+                       #
+                       #  Update an existing session and calculate the interval
+                       #  between the last data we received for the session and this
+                       #  update. This can be used to find stale sessions.
+                       #
+                       query = "\
+                               UPDATE ${....acct_table1} \
+                               SET \
+                                       acctupdatetime  = %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       acctinterval    = 0, \
+                                       framedipaddress = '%{Framed-IP-Address}', \
+                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctinputoctets = %{%{Acct-Input-Gigawords}:-0} \
+                                               << 32 | %{%{Acct-Input-Octets}:-0}, \
+                                       acctoutputoctets = %{%{Acct-Output-Gigawords}:-0} \
+                                               << 32 | %{%{Acct-Output-Octets}:-0} \
+                               WHERE acctsessionid     = '%{Acct-Session-Id}' \
+                               AND username            = '%{SQL-User-Name}' \
+                               AND nasipaddress        = '%{NAS-IP-Address}'"
 
-                               #
-                               #  The update condition matched no existing sessions. Use
-                               #  the values provided in the update to create a new session.
-                               #
-                               query = "\
-                                       INSERT INTO ${....acct_table1} \
-                                               (${...column_list}) \
-                                       VALUES \
-                                               ('%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port}', \
-                                               '%{NAS-Port-Type}', \
-                                               (%{%{integer:Event-Timestamp}:-NOW()} - %{%{Acct-Session-Time}:-0}), \
-                                               %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               NULL, \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '%{Connect-Info}', \
-                                               '', \
-                                               %{%{Acct-Input-Gigawords}:-0} << 32 | \
-                                                       %{%{Acct-Input-Octets}:-0}, \
-                                               %{%{Acct-Output-Gigawords}:-0} << 32 | \
-                                                       %{%{Acct-Output-Octets}:-0}, \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}')"
-                       }
+                       #
+                       #  The update condition matched no existing sessions. Use
+                       #  the values provided in the update to create a new session.
+                       #
+                       query = "\
+                               INSERT INTO ${....acct_table1} \
+                                       (${...column_list}) \
+                               VALUES \
+                                       ('%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port}', \
+                                       '%{NAS-Port-Type}', \
+                                       (%{%{integer:Event-Timestamp}:-strftime('%s', 'now')} - %{%{Acct-Session-Time}:-0}), \
+                                       %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       NULL, \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '%{Connect-Info}', \
+                                       '', \
+                                       %{%{Acct-Input-Gigawords}:-0} << 32 | \
+                                               %{%{Acct-Input-Octets}:-0}, \
+                                       %{%{Acct-Output-Gigawords}:-0} << 32 | \
+                                               %{%{Acct-Output-Octets}:-0}, \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}')"
+               }
 
-                       stop {
-                               #
-                               #  Session has terminated, update the stop time and statistics.
-                               #
-                               query = "\
-                                       UPDATE ${....acct_table2} SET \
-                                               acctstoptime    = %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               acctsessiontime = '%{Acct-Session-Time}', \
-                                               acctinputoctets = %{%{Acct-Input-Gigawords}:-0} \
-                                                       << 32 | %{%{Acct-Input-Octets}:-0}, \
-                                               acctoutputoctets = %{%{Acct-Output-Gigawords}:-0} \
-                                                       << 32 | %{%{Acct-Output-Octets}:-0}, \
-                                               acctterminatecause = '%{Acct-Terminate-Cause}', \
-                                               connectinfo_stop = '%{Connect-Info}' \
-                                       WHERE acctsessionid     = '%{Acct-Session-Id}' \
-                                       AND username            = '%{SQL-User-Name}' \
-                                       AND nasipaddress        = '%{NAS-IP-Address}'"
+               stop {
+                       #
+                       #  Session has terminated, update the stop time and statistics.
+                       #
+                       query = "\
+                               UPDATE ${....acct_table2} SET \
+                                       acctstoptime    = %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       acctsessiontime = '%{Acct-Session-Time}', \
+                                       acctinputoctets = %{%{Acct-Input-Gigawords}:-0} \
+                                               << 32 | %{%{Acct-Input-Octets}:-0}, \
+                                       acctoutputoctets = %{%{Acct-Output-Gigawords}:-0} \
+                                               << 32 | %{%{Acct-Output-Octets}:-0}, \
+                                       acctterminatecause = '%{Acct-Terminate-Cause}', \
+                                       connectinfo_stop = '%{Connect-Info}' \
+                               WHERE acctsessionid     = '%{Acct-Session-Id}' \
+                               AND username            = '%{SQL-User-Name}' \
+                               AND nasipaddress        = '%{NAS-IP-Address}'"
 
-                               #
-                               #  The update condition matched no existing sessions. Use
-                               #  the values provided in the update to create a new session.
-                               #
-                               query = "\
-                                       INSERT INTO ${....acct_table2} \
-                                               (${...column_list}) \
-                                       VALUES \
-                                               ('%{Acct-Session-Id}', \
-                                               '%{Acct-Unique-Session-Id}', \
-                                               '%{SQL-User-Name}', \
-                                               '%{Realm}', \
-                                               '%{NAS-IP-Address}', \
-                                               '%{NAS-Port}', \
-                                               '%{NAS-Port-Type}', \
-                                               (%{%{integer:Event-Timestamp}:-NOW()} - %{%{Acct-Session-Time}:-0}), \
-                                               %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               %{%{integer:Event-Timestamp}:-NOW()}, \
-                                               '%{Acct-Session-Time}', \
-                                               '%{Acct-Authentic}', \
-                                               '', \
-                                               '%{Connect-Info}', \
-                                               %{%{Acct-Input-Gigawords}:-0} << 32 | \
-                                                       %{%{Acct-Input-Octets}:-0}, \
-                                               %{%{Acct-Output-Gigawords}:-0} << 32 | \
-                                                       %{%{Acct-Output-Octets}:-0}, \
-                                               '%{Called-Station-Id}', \
-                                               '%{Calling-Station-Id}', \
-                                               '%{Acct-Terminate-Cause}', \
-                                               '%{Service-Type}', \
-                                               '%{Framed-Protocol}', \
-                                               '%{Framed-IP-Address}')"
-                       }
+                       #
+                       #  The update condition matched no existing sessions. Use
+                       #  the values provided in the update to create a new session.
+                       #
+                       query = "\
+                               INSERT INTO ${....acct_table2} \
+                                       (${...column_list}) \
+                               VALUES \
+                                       ('%{Acct-Session-Id}', \
+                                       '%{Acct-Unique-Session-Id}', \
+                                       '%{SQL-User-Name}', \
+                                       '%{Realm}', \
+                                       '%{NAS-IP-Address}', \
+                                       '%{NAS-Port}', \
+                                       '%{NAS-Port-Type}', \
+                                       %{%{integer:Event-Timestamp}:-strftime('%s', 'now')} - %{%{Acct-Session-Time}:-0}), \
+                                       %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       %{%{integer:Event-Timestamp}:-date('now')}, \
+                                       '%{Acct-Session-Time}', \
+                                       '%{Acct-Authentic}', \
+                                       '', \
+                                       '%{Connect-Info}', \
+                                       %{%{Acct-Input-Gigawords}:-0} << 32 | \
+                                               %{%{Acct-Input-Octets}:-0}, \
+                                       %{%{Acct-Output-Gigawords}:-0} << 32 | \
+                                               %{%{Acct-Output-Octets}:-0}, \
+                                       '%{Called-Station-Id}', \
+                                       '%{Calling-Station-Id}', \
+                                       '%{Acct-Terminate-Cause}', \
+                                       '%{Service-Type}', \
+                                       '%{Framed-Protocol}', \
+                                       '%{Framed-IP-Address}')"
                }
        }
+}
 
-       #######################################################################
-       # Authentication Logging Queries
-       #######################################################################
-       # postauth_query        - Insert some info after authentication
-       #######################################################################
+#######################################################################
+# Authentication Logging Queries
+#######################################################################
+# postauth_query       - Insert some info after authentication
+#######################################################################
 
-       post-auth {
-               # Write SQL queries to a logfile. This is potentially useful for bulk inserts
-               # when used with the rlm_sql_null driver.
-#              logfile = ${logdir}/post-auth.sql
+post-auth {
+       # Write SQL queries to a logfile. This is potentially useful for bulk inserts
+       # when used with the rlm_sql_null driver.
+#      logfile = ${logdir}/post-auth.sql
 
-               query = "\
-                       INSERT INTO ${..postauth_table} \
-                               (username, pass, reply, authdate) \
-                       VALUES ( \
-                               '%{SQL-User-Name}', \
-                               '%{%{User-Password}:-%{Chap-Password}}', \
-                               '%{reply:Packet-Type}', \
-                               '%S')"
-       }
+       query = "\
+               INSERT INTO ${..postauth_table} \
+                       (username, pass, reply, authdate) \
+               VALUES ( \
+                       '%{SQL-User-Name}', \
+                       '%{%{User-Password}:-%{Chap-Password}}', \
+                       '%{reply:Packet-Type}', \
+                        %{%{integer:Event-Timestamp}:-date('now')})"
+}
diff --git a/raddb/mods-config/unbound/default.conf b/raddb/mods-config/unbound/default.conf
new file mode 100644 (file)
index 0000000..9aac368
--- /dev/null
@@ -0,0 +1,2 @@
+server:
+ num-threads: 2
diff --git a/raddb/panic.gdb b/raddb/panic.gdb
new file mode 100644 (file)
index 0000000..3ae253a
--- /dev/null
@@ -0,0 +1,4 @@
+info locals
+info args
+thread apply all bt full
+quit
index 369b1c6..201f5e5 100644 (file)
@@ -46,7 +46,7 @@ acct_unique {
        #
        else {
                update request {
-                       Acct-Unique-Session-Id := "%{md5:%{User-Name},%{Acct-Session-ID},%{NAS-IP-Address},%{NAS-Identifier},%{NAS-Port-ID},%{NAS-Port}}"
+                       Acct-Unique-Session-Id := "%{md5:%{User-Name},%{Acct-Session-ID},%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}},%{NAS-Identifier},%{NAS-Port-ID},%{NAS-Port}}"
                 }
        }
 }
index cbf23af..b056ed1 100644 (file)
@@ -10,3 +10,13 @@ do_not_respond {
        handled
 }
 
+#
+#  Have the server accept the current request.
+#  Can only be called from authorize.
+#  Unlike calling the always module instance 'reject' the request will continue to be processed.
+#
+accept.authorize {
+       update control {
+               Auth-Type := accept
+       }
+}
index 0204d6f..178f185 100644 (file)
@@ -41,7 +41,7 @@ cui_require_operator_name = "no"
 cui.authorize {
        if ("%{client:add_cui}" == 'yes') {
                update request {
-                       Chargeable-User-Identity := '\\000'
+                       Chargeable-User-Identity := 0x00
                }
        }
 }
@@ -53,7 +53,7 @@ cui.authorize {
 cui.pre-proxy {
        if (("%{request:Packet-Type}" == 'Access-Request') && ("%{client:add_cui}" == 'yes')) {
                update proxy-request {
-                       Chargeable-User-Identity = '\\000'
+                       Chargeable-User-Identity = 0x00
                }
        }
 }
@@ -69,12 +69,14 @@ cui.post-auth {
        if (!control:Proxy-To-Realm && Chargeable-User-Identity && !reply:Chargeable-User-Identity && \
            (Operator-Name || ('${policy.cui_require_operator_name}' != 'yes')) ) {
                update reply {
-                       Chargeable-User-Identity = "%{sha1:${policy.cui_hash_key}%{tolower:%{User-Name}}%{%{Operator-Name}:-}}"
+                       Chargeable-User-Identity = "%{sha1:${policy.cui_hash_key}%{tolower:%{User-Name}%{%{Operator-Name}:-}}}"
                }
        }
+
        update reply {
-               User-Name -= "%{reply:User-Name}"
+               User-Name !* ANY        # remove User-Name from the reply for security
        }
+
        #
        #  The section below will store a CUI for the User in the DB.
        #  You need to configure the cuisql module and your database for this to work.
@@ -91,7 +93,7 @@ cui-inner.post-auth {
        if (outer.request:Chargeable-User-Identity && \
            (outer.request:Operator-Name || ('${policy.cui_require_operator_name}' != 'yes'))) {
                update reply {
-                       Chargeable-User-Identity := "%{sha1:${policy.cui_hash_key}%{tolower:%{User-Name}}%{%{outer.request:Operator-Name}:-}}"
+                       Chargeable-User-Identity := "%{sha1:${policy.cui_hash_key}%{tolower:%{User-Name}%{%{outer.request:Operator-Name}:-}}}"
                }
        }
 }
index 8aab94e..369700e 100644 (file)
@@ -1,46 +1,4 @@
 #
-#      Forbid all EAP types.  Enable this by putting "forbid_eap"
-#      into the "authorize" section.
-#
-forbid_eap {
-       if (EAP-Message) {
-               reject
-       }
-}
-
-#
-#      Forbid all non-EAP types outside of an EAP tunnel.
-#
-permit_only_eap {
-       if (!EAP-Message) {
-               #  We MAY be inside of a TTLS tunnel.
-               #  PEAP and EAP-FAST require EAP inside of
-               #  the tunnel, so this check is OK.
-               #  If so, then there MUST be an outer EAP message.
-               if (!"%{outer.request:EAP-Message}") {
-                       reject
-               }
-       }
-}
-
-#
-#      Remove Reply-Message from response if were doing EAP
-#
-#  Be RFC 3579 2.6.5 compliant - EAP-Message and Reply-Message should
-#  not be present in the same response.
-#
-remove_reply_message_if_eap {
-       if(reply:EAP-Message && reply:Reply-Message) {
-               update reply {
-                       Reply-Message !* ANY
-               }
-       }
-       else {
-               noop
-       }
-}
-
-#
 #      Example of forbidding all attempts to login via
 #      realms.
 #
@@ -58,13 +16,16 @@ deny_realms {
 #  what constitutes a user name.
 #
 filter_username {
+       if (!User-Name) {
+               noop
+       }
+
        #
-       #  reject mixed case
-       #  e.g. "UseRNaMe"
+       #  reject mixed case e.g. "UseRNaMe"
        #
-       if (User-Name != "%{tolower:%{User-Name}}") {
-               reject
-       }
+       #if (User-Name != "%{tolower:%{User-Name}}") {
+       #       reject
+       #}
 
        #
        #  reject all whitespace
@@ -94,7 +55,7 @@ filter_username {
        #
        if (User-Name =~ /\\.\\./ ) {
                update reply {
-                       Reply-Message += "Rejected: Username comtains ..s"
+                       Reply-Message += "Rejected: Username contains ..s"
                }
                reject
        }
index 1cb05e4..a16fa1e 100644 (file)
@@ -2,15 +2,15 @@
 #  The following policies are for the Operator-Name
 #  configuration.
 #
-#  The policies below can be called as just 'oprator-name' (not
-#  oprator-name.authorize etc..)  from the various config sections.
+#  The policies below can be called as just 'operator-name' (not
+#  operator-name.authorize etc..)  from the various config sections.
 #
 
 #  If you require that the Operator-Name be set
 #  for local clients then call the 'operator-name' policy
 #  in the authorize section of the virtual-server for your clients in clients.conf
 
-#  to inject an Operator-Name whilst proxying, call the
+#  To inject an Operator-Name whilst proxying, call the
 #  'operator-name' policy in the pre-proxy section of the virtual server
 #  No need to call this if you have already enabled this in
 #  the authorize section.
index ae8fedf..0180e74 100644 (file)
@@ -228,6 +228,8 @@ home_server localhost {
        #
        #  If the home server does not respond to a request within
        #  this time, this server will initiate "zombie_period".
+       #  The response window can be a number between 0.001 and 60.000
+       #  Though values on the low end are discouraged.
        #
        #  The response window is large because responses MAY be slow,
        #  especially when proxying across the Internet.
index 3958c19..307ae10 100644 (file)
@@ -46,9 +46,8 @@
 #      documented in that "man" page.  They are only documented here,
 #      in the comments.
 #
-#      As of 2.0.0, FreeRADIUS supports a simple processing language
-#      in the "authorize", "authenticate", "accounting", etc. sections.
-#      See "man unlang" for details.
+#      The "unlang" policy language can be used to create complex
+#      if / else policies.  See "man unlang" for details.
 #
 
 prefix = @prefix@
@@ -119,6 +118,42 @@ libdir = @libdir@
 #
 pidfile = ${run_dir}/${name}.pid
 
+#  panic_action: Command to execute if the server dies unexpectedly.
+#
+#  FOR PRODUCTION SYSTEMS, ACTIONS SHOULD ALWAYS EXIT.
+#  AN INTERACTIVE ACTION MEANS THE SERVER IS NOT RESPONDING TO REQUESTS.
+#  AN INTERACTICE ACTION MEANS THE SERVER WILL NOT RESTART.
+#
+#  THE SERVER MUST NOT BE ALLOWED EXECUTE UNTRUSTED PANIC ACTION CODE
+#  PATTACH CAN BE USED AS AN ATTACK VECTOR.
+#
+#  The panic action is a command which will be executed if the server
+#  receives a fatal, non user generated signal, i.e. SIGSEGV, SIGBUS,
+#  SIGABRT or SIGFPE.
+#
+#  This can be used to start an interactive debugging session so
+#  that information regarding the current state of the server can
+#  be acquired.
+#
+#  The following string substitutions are available:
+#  - %e   The currently executing program e.g. /sbin/radiusd
+#  - %p   The PID of the currently executing program e.g. 12345
+#
+#  Standard ${} substitutions are also allowed.
+#
+#  An example panic action for opening an interactive session in GDB would be:
+#
+#panic_action = "gdb %e %p"
+#
+#  Again, don't use that on a production system.
+#
+#  An example panic action for opening an automated session in GDB would be:
+#
+#panic_action = "gdb -silent -x ${raddbdir}/panic.gdb %e %p 2>&1 | tee ${logdir}/gdb-${name}-%p.log"
+#
+#  That command can be used on a production system.
+#
+
 #  max_request_time: The maximum time (in seconds) to handle a request.
 #
 #  Requests which take more time than this to process may be killed, and
@@ -306,6 +341,10 @@ log {
        #
 #      msg_goodpass = ""
 #      msg_badpass = ""
+
+       #  The message when the user exceeds the Simultaneous-Use limit.
+       #
+       msg_denied = "You are already logged in - access denied"
 }
 
 #  The program to execute to do concurrency checks.
@@ -435,6 +474,16 @@ security {
        #  See also raddb/sites-available/status
        #
        status_server = yes
+
+       #
+       #  allow_vulnerable_openssl: Allow the server to start with
+       #  versions of OpenSSL known to have critical vulnerabilities.
+       #
+       #  This check is based on the version number reported by libssl
+       #  and may not reflect patches applied to libssl by
+       #  distribution maintainers.
+       #
+       allow_vulnerable_openssl = no
 }
 
 # PROXY CONFIGURATION
index 1ffb3a6..170e2b1 100644 (file)
@@ -3,55 +3,6 @@
 #
 #      This is a virtual server that handles DHCP.
 #
-#              !!!! WARNING !!!!
-#
-#      This code is experimental, and SHOULD NOT be used in a
-#      production system.  It is intended for validation and
-#      experimentation ONLY.
-#
-#      In order for this to work, you will need to run configure:
-#
-#              $ ./configure --with-dhcp
-#              $ make
-#              $ vi share/dictionary
-#
-#      ## Un-comment the line containing $INCLUDE dictionary.dhcp
-#      ## Then, save the file.
-#
-#              $ make install
-#
-#      DHCP is NOT enabled by default.
-#
-#      The goal of this effort is to get the code in front of
-#      people who are interested in another DHCP server.
-#      We NEED FEEDBACK, patches, bug reports, etc.  Especially patches!
-#
-#      Please contribute, or this work will be nothing more than
-#      a curiosity.
-#
-#
-#      Q: What does it do?
-#      A: It allows the server to receive DHCP packets, and to
-#         respond with static, pre-configured DHCP responses.
-#
-#      Q: Does it do static/dynamic IP assignment?
-#      A: No.  Or, maybe.  Try it and see.
-#
-#      Q: Does it read ISC configuration or lease files?
-#      A: No.  Please submit patches.
-#
-#      Q: Does it have DHCP feature X?
-#      A: No.  Please submit patches.
-#
-#      Q: Does it support option 82?
-#      A: Yes.
-#
-#      Q: Does it support other options?
-#      A: Maybe.  See dictionary.dhcp.  Please submit patches.
-#
-#      Q: It doesn't seem to do much of anything!
-#      A: Exactly.
-#
 #      $Id$
 #
 ######################################################################
@@ -94,9 +45,34 @@ server dhcp {
 # The other only solution is to update FreeRADIUS to use BPF sockets.
 #
 listen {
+       #  This is a dhcp socket.
+       type = dhcp
+
+       #  IP address to listen on. Will usually be the IP of the
+       #  interface, or 0.0.0.0
        ipaddr = 127.0.0.1
+
+       #  source IP address for unicast packets sent by the
+       #  DHCP server.
+       #
+       #  The source IP for unicast packets is chosen from the first
+       #  one of the following items which returns a valid IP
+       #  address:
+       #
+       #       src_ipaddr
+       #       ipaddr
+       #       reply:DHCP-Server-IP-Address
+       #       reply:DHCP-DHCP-Server-Identifier
+       #
+       src_ipaddr = 127.0.0.1
+
+       #  The port should be 67 for a production network. Don't set
+       #  it to 67 on a production network unless you really know
+       #  what you're doing. Even if nothing is configured below, the
+       #  server may still NAK legitimate responses from clients.
        port = 6700
-       type = dhcp
+
+       #  Interface name we are listening on. See comments above.
 #      interface = lo0
 
        # The DHCP server defaults to allowing broadcast packets.
@@ -104,7 +80,9 @@ listen {
        # from a relay agent.  i.e. when *no* clients are on the same
        # LAN as the DHCP server.
        #
-       # It's set to "no" here for testing.
+       # It's set to "no" here for testing. It will usually want to
+       # be "yes" in production, unless you are only dealing with
+       # relayed packets.
        broadcast = no
 
        # On Linux if you're running the server as non-root, you
@@ -119,7 +97,30 @@ listen {
 #  Packets received on the socket will be processed through one
 #  of the following sections, named after the DHCP packet type.
 #  See dictionary.dhcp for the packet types.
+
+#  Return packets will be sent to, in preference order:
+#     DHCP-Gateway-IP-Address
+#     DHCP-Client-IP-Address
+#     DHCP-Your-IP-Address
+#  At least one of these attributes should be set at the end of each
+#  section for a response to be sent.
+
 dhcp DHCP-Discover {
+
+       #  Set the type of packet to send in reply.
+       #
+       #  The server will look at the DHCP-Message-Type attribute to
+       #  determine which type of packet to send in reply. Common
+       #  values would be DHCP-Offer, DHCP-Ack or DHCP-NAK. See
+       #  dictionary.dhcp for all the possible values.
+       #
+       #  DHCP-Do-Not-Respond can be used to tell the server to not
+       #  respond.
+       #
+       #  In the event that DHCP-Message-Type is not set then the
+       #  server will fall back to determining the type of reply
+       #  based on the rcode of this section.
+
        update reply {
               DHCP-Message-Type = DHCP-Offer
        }
@@ -148,13 +149,24 @@ dhcp DHCP-Discover {
        # ...
        #}
 
-       #  Or, allocate IPs from the DHCP pool in SQL.
+       #  Or, allocate IPs from the DHCP pool in SQL. You may need to
+       #  set the pool name here if you haven't set it elsewhere.
+#      update control {
+#              Pool-Name := "local"
+#      }
 #      dhcp_sqlippool
 
+       #  If DHCP-Message-Type is not set, returning "ok" or
+       #  "updated" from this section will respond with a DHCP-Offer
+       #  message.
+       #
+       #  Other rcodes will tell the server to not return any response.
        ok
 }
 
 dhcp DHCP-Request {
+
+       # Response packet type. See DHCP-Discover section above.
        update reply {
               DHCP-Message-Type = DHCP-Ack
        }
@@ -183,16 +195,47 @@ dhcp DHCP-Request {
        # ...
        #}
 
-       #  Or, allocate IPs from the DHCP pool in SQL.
+       #  Or, allocate IPs from the DHCP pool in SQL. You may need to
+       #  set the pool name here if you haven't set it elsewhere.
+#      update control {
+#              Pool-Name := "local"
+#      }
 #      dhcp_sqlippool
 
+       #  If DHCP-Message-Type is not set, returning "ok" or
+       #  "updated" from this section will respond with a DHCP-Ack
+       #  packet.
+       #
+       #  "handled" will not return a packet, all other rcodes will
+       #  send back a DHCP-NAK.
        ok
 }
 
-#  If there's no named section for the packet type, then the packet
-#  is processed through this section.
-dhcp {
-       # send a DHCP NAK.
+#
+#  Other DHCP packet types
+#
+#  There should be a separate section for each DHCP message type.
+#  By default this configuration will ignore them all. Any packet type
+#  not defined here will be responded to with a DHCP-NAK.
+
+dhcp DHCP-Decline {
+       update reply {
+              DHCP-Message-Type = DHCP-Do-Not-Respond
+       }
+       reject
+}
+
+dhcp DHCP-Inform {
+       update reply {
+              DHCP-Message-Type = DHCP-Do-Not-Respond
+       }
+       reject
+}
+
+dhcp DHCP-Release {
+       update reply {
+              DHCP-Message-Type = DHCP-Do-Not-Respond
+       }
        reject
 }
 
@@ -213,8 +256,22 @@ dhcp {
 #
 #  This lets you perform simple static IP assignment.
 #
+#  There is a preconfigured "mac2ip" module setup in
+#  mods-available/mac2ip. To use it do:
+#
+#    # cd raddb/
+#    # ln -s ../mods-available/mac2ip mods-enabled/mac2ip
+#    # mkdir mods-config/passwd
+#
+#  Then create the file mods-config/passwd/mac2ip with the above
+#  format.
+#
 ######################################################################
 
+
+#  This is an example only - see mods-available/mac2ip instead; do
+#  not uncomment these lines here.
+#
 #passwd mac2ip {
 #      filename = ${confdir}/mac2ip
 #      format = "*DHCP-Client-Hardware-Address:=DHCP-Your-IP-Address"
index 78ad906..cdfa617 100644 (file)
@@ -51,7 +51,7 @@ client dynamic {
 
        #
        #  Define the virtual server used to discover dynamic clients.
-       dynamic_clients = dynamic_client_server
+       dynamic_clients = dynamic_clients
 
        #
        #  The directory where client definitions are stored.  This
@@ -76,7 +76,7 @@ client dynamic {
 
 #
 #  This is the virtual server referenced above by "dynamic_clients".
-server dynamic_client_server {
+server dynamic_clients {
 
        #
        #  The only contents of the virtual server is the "authorize" section.
index 4287d5c..11b6c12 100644 (file)
@@ -323,7 +323,7 @@ post-auth {
        #  uncomment the section below, otherwise you may want
        #  to use  Chargeable-User-Identity attribute from RFC 4372.
        #  See further on.
-       #update outer.reply {
+       #update outer.reply {
        #  User-Name = "%{request:User-Name}"
        #}
        #
index 4652a9e..e7d4346 100644 (file)
@@ -17,7 +17,7 @@
 #      "status" port.
 #
 #      The server statistics are available ONLY on socket of type
-#      "status".  Qeuries for statistics sent to any other port
+#      "status".  Queries for statistics sent to any other port
 #      are ignored.
 #
 #      Similarly, a socket of type "status" will not process
index d8eff1c..211daab 100644 (file)
@@ -14,7 +14,7 @@
 #
 #  You WILL want to edit this to your local needs.  We suggest copying
 #  the "default" file here, and then editing it.  That way, any
-#  changes to the 'default" file will not affect this virtual server,
+#  changes to the "default" file will not affect this virtual server,
 #  and vice-versa.
 #
 #  When this virtual server receives the request, the original
index f64b31c..7f4420a 100644 (file)
@@ -35,7 +35,7 @@
 
 Summary: High-performance and highly configurable free RADIUS server
 Name: freeradius
-Version: 3.0.1
+Version: 3.0.3
 Release: 1%{?dist}
 License: GPLv2+ and LGPLv2+
 Group: System Environment/Daemons
@@ -387,6 +387,7 @@ rm -rf $RPM_BUILD_ROOT/%{_sysconfdir}/raddb/mods-config/sql/main/mssql
 %endif
 %if %{?_with_rlm_sql_oracle:0}%{!?_with_rlm_sql_oracle:1}
 rm -rf $RPM_BUILD_ROOT/%{_sysconfdir}/raddb/mods-config/sql/ippool/oracle
+rm -rf $RPM_BUILD_ROOT/%{_sysconfdir}/raddb/mods-config/sql/ippool-dhcp/oracle
 rm -rf $RPM_BUILD_ROOT/%{_sysconfdir}/raddb/mods-config/sql/main/oracle
 %endif
 
@@ -396,6 +397,7 @@ rm -rf $RPM_BUILD_ROOT/%{_includedir}
 
 # remove unsupported config files
 rm -f $RPM_BUILD_ROOT/%{_sysconfdir}/raddb/experimental.conf
+rm -rf $RPM_BUILD_ROOT/%{_sysconfdir}/raddb/mods-config/unbound
 
 # install doc files omitted by standard install
 for f in COPYRIGHT CREDITS INSTALL.rst README.rst; do
@@ -465,6 +467,7 @@ fi
 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/clients.conf
 %config(noreplace) /etc/raddb/hints
 %config(noreplace) /etc/raddb/huntgroups
+%attr(640,root,radiusd) %config(noreplace) /etc/raddb/panic.gdb
 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/README.rst
 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/proxy.conf
 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/radiusd.conf
@@ -530,7 +533,6 @@ fi
 %doc %{_mandir}/man5/rlm_unix.5.gz
 %doc %{_mandir}/man5/unlang.5.gz
 %doc %{_mandir}/man5/users.5.gz
-%doc %{_mandir}/man8/radconf2xml.8.gz
 %doc %{_mandir}/man8/radcrypt.8.gz
 %doc %{_mandir}/man8/raddebug.8.gz
 %doc %{_mandir}/man8/radiusd.8.gz
@@ -557,6 +559,7 @@ fi
 %{_libdir}/freeradius/rlm_chap.so
 %{_libdir}/freeradius/rlm_counter.so
 %{_libdir}/freeradius/rlm_cram.so
+%{_libdir}/freeradius/rlm_date.so
 %{_libdir}/freeradius/rlm_detail.so
 %{_libdir}/freeradius/rlm_dhcp.so
 %{_libdir}/freeradius/rlm_digest.so
@@ -593,6 +596,7 @@ fi
 %{_libdir}/freeradius/rlm_sql_sqlite.so
 %{_libdir}/freeradius/rlm_sqlcounter.so
 %{_libdir}/freeradius/rlm_sqlippool.so
+%{_libdir}/freeradius/rlm_unpack.so
 %{_libdir}/freeradius/rlm_unix.so
 %{_libdir}/freeradius/rlm_utf8.so
 %{_libdir}/freeradius/rlm_wimax.so
@@ -738,7 +742,10 @@ fi
 %dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql
 %dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql/ippool
 %dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql/ippool/oracle
+%dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql/ippool-dhcp
+%dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql/ippool-dhcp/oracle
 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/mods-config/sql/ippool/oracle/*
+%attr(640,root,radiusd) %config(noreplace) /etc/raddb/mods-config/sql/ippool-dhcp/oracle/*
 %dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql/main
 %dir %attr(750,root,radiusd) /etc/raddb/mods-config/sql/main/oracle
 %attr(640,root,radiusd) %config(noreplace) /etc/raddb/mods-config/sql/main/oracle/*
index fb7c963..0c43b8c 100644 (file)
@@ -75,9 +75,9 @@ endef
 #      delete references to ${BUILD_DIR}/make/include, the "config.mk"
 #      file adds these dependencies automatically.
 #      replace "build/" with "${BUILD_DIR}/" when it's in the middle of a line
-#      
+#
 #      remove sequential duplicate lines
-#      
+#
 #  2) Create empty dependencies from the files
 #
 #      COMMON
@@ -175,7 +175,7 @@ endef
 
 # ADD_TARGET_RULE.* - Parameterized "functions" that adds a new target to the
 #   Makefile.  There should be one ADD_TARGET_RULE definition for each
-#   type of target that is used in the build.  
+#   type of target that is used in the build.
 #
 #   New rules can be added by copying one of the existing ones, and
 #   replacing the line after the "mkdir"
@@ -263,7 +263,7 @@ endef
 define COMPILE_C_CMDS
        $(Q)mkdir -p $(dir $@)
        $(Q)$(ECHO) CC $<
-       $(Q)$(strip ${COMPILE.c} -o $@ -c -MD ${CFLAGS} ${SRC_CFLAGS} ${INCDIRS} \
+       $(Q)$(strip ${COMPILE.c} -o $@ -c -MD ${CPPFLAGS} ${CFLAGS} ${SRC_CFLAGS} ${INCDIRS} \
            ${SRC_INCDIRS} ${SRC_DEFS} ${DEFS} $<)
 endef
 
@@ -271,15 +271,15 @@ endef
 define ANALYZE_C_CMDS
        $(Q)mkdir -p $(dir $@)
        $(Q)$(ECHO) SCAN $<
-       $(Q)$(strip ${ANALYZE.c} --analyze -c $< ${CFLAGS} ${SRC_CFLAGS} ${INCDIRS} \
-           ${SRC_INCDIRS} ${SRC_DEFS} ${DEFS}) || (rm -f $@ && false)
+       ${Q}$(strip ${ANALYZE.c} --analyze -Xanalyzer -analyzer-output=html -c $< -o $@ ${CPPFLAGS} \
+           ${CFLAGS} ${SRC_CFLAGS} ${INCDIRS} ${SRC_INCDIRS} ${SRC_DEFS} ${DEFS}) || (rm -f $@ && false)
        $(Q)touch $@
 endef
 
 # COMPILE_CXX_CMDS - Commands for compiling C++ source code.
 define COMPILE_CXX_CMDS
        $(Q)mkdir -p $(dir $@)
-       $(Q)$(strip ${COMPILE.cxx} -o $@ -c -MD ${CXXFLAGS} ${SRC_CXXFLAGS} ${INCDIRS} \
+       $(Q)$(strip ${COMPILE.cxx} -o $@ -c -MD ${CPPFLAGS} ${CXXFLAGS} ${SRC_CXXFLAGS} ${INCDIRS} \
            ${SRC_INCDIRS} ${SRC_DEFS} ${DEFS} $<)
 endef
 
@@ -354,6 +354,14 @@ define INCLUDE_SUBMAKEFILE
         $${TGT}_SOURCES :=
         $${TGT}_MAN := $${MAN}
         $${TGT}_SUFFIX := $$(if $$(suffix $${TGT}),$$(suffix $${TGT}),.exe)
+
+        # If it's an EXE, ensure that transitive library linking works.
+        # i.e. we build libfoo.a which in turn requires -lbar.  So, the executable
+        # has to be linked to both libfoo.a and -lbar.
+        ifeq "$${$${TGT}_SUFFIX}" ".exe"
+                $${TGT}_LDLIBS += $$(filter-out %.a %.so %.la,$${$${TGT_PREREQS}_LDLIBS})
+        endif
+
         $${TGT}_BUILD := $$(if $$(suffix $${TGT}),$${BUILD_DIR}/lib,$${BUILD_DIR}/bin)
         $${TGT}_MAKEFILES += ${1}
         $${TGT}_CHECK_HEADERS := $${TGT_CHECK_HEADERS}
@@ -578,7 +586,7 @@ ECHO = echo
 # Define the "all" target (which simply builds all user-defined targets) as the
 # default goal.
 .PHONY: all
-all: 
+all:
 
 # Add "clean" rules to remove all build-generated files.
 .PHONY: clean
index 607934c..ce1d34e 100644 (file)
 #  define LD_LIBRARY_PATH_LOCAL                LD_LIBRARY_PATH
 #endif
 
-#if defined(sun)
+#if defined(__sun)
 #  define SHELL_CMD                    "/bin/sh"
 #  define DYNAMIC_LIB_EXT              "so"
 #  define MODULE_LIB_EXT               "so"
 #  define LIBRARIAN                    "ar"
 #  define LIBRARIAN_OPTS               "cr"
 #  define RANLIB                       "ranlib"
-#  define PIC_FLAG                     "-KPIC"
+#  define PIC_FLAG                     "-fPIC"
 #  define RPATH                                "-R"
 #  define SHARED_OPTS                  "-G"
 #  define MODULE_OPTS                  "-G"
@@ -396,9 +396,9 @@ static int snprintf(char *str, size_t n, char const *fmt, ...)
        va_list ap;
        int res;
 
-       va_start( ap, fmt );
-       res = vsnprintf( str, n, fmt, ap );
-       va_end( ap );
+       va_start(ap, fmt);
+       res = vsnprintf(str, n, fmt, ap);
+       va_end(ap);
        return res;
 }
 #endif
@@ -580,12 +580,26 @@ static int external_spawn(command_t *cmd, char const *file, char const **argv)
                        return execvp(argv[0], (char**)argv);
                }
                else {
-                       int statuscode;
-                       waitpid(pid, &statuscode, 0);
-                       if (WIFEXITED(statuscode)) {
-                               return WEXITSTATUS(statuscode);
+                       int status;
+                       waitpid(pid, &status, 0);
+
+                       /*
+                        *      Exited via exit(status)
+                        */
+                       if (WIFEXITED(status)) {
+                               return WEXITSTATUS(status);
                        }
-                       return 0;
+
+#ifdef WTERMSIG
+                       if (WIFSIGNALED(status)) {
+                               return WTERMSIG(status);
+                       }
+#endif
+
+                       /*
+                        *      Some other failure.
+                        */
+                       return 1;
                }
        }
 #endif
index 525e538..6d36a25 100644 (file)
@@ -33,7 +33,7 @@ ifeq "${LIBTOOL}" "JLIBTOOL"
     ${JLIBTOOL}: ${top_makedir}/jlibtool.c
        $(Q)mkdir -p $(dir $@)
        $(Q)echo CC jlibtool.c
-       $(Q)${CC} $< -o $@ ${DARWIN_CFLAGS}
+       $(Q)${CC} $< -o $@
 
     clean: jlibtool_clean
 
@@ -147,12 +147,12 @@ ifeq "${bm_shared_libs}" "yes"
     RELINK := local/
 
     # RPATH  : flags use to build executables that are installed,
-    #          with no dependency on the source. 
+    #          with no dependency on the source.
     # RELINL : flags use to build executables that can be run
     #          from the build directory / source tree.
     RPATH_FLAGS := -rpath ${libdir}
     RELINK_FLAGS := -rpath $(abspath ${BUILD_DIR})/lib/${RELINK}/.libs
-    
+
     RELINK_FLAGS_MIN := -rpath ${libdir}
 
     ifneq "${bm_static_libs}" "yes"
index 6426238..9e922fe 100755 (executable)
@@ -54,7 +54,7 @@ do
   t)   timeout="$OPTARG"
        [ "$timeout" = "0" ] && timeout=1000000
        ;;
-  u)   condition="(User-Name == $OPTARG)"
+  u)   condition="(User-Name == '$OPTARG')"
        ;;
   ?)   usage
        ;;
@@ -77,7 +77,7 @@ fi
 #  Debug to a file, and then tell us where the file is.
 #
 outfile=`$radmin -e "debug file radmin.debug.$$" -e "show debug file"`
-group=`$radmin -e "debug file radmin.debug.$$" -e "show config group"`
+group=`$radmin -e "debug file radmin.debug.$$" -e "show config security.group"`
 
 #
 #  If there was an error setting the debug output, re-set the
index ab342b4..d3b199c 100644 (file)
@@ -3,13 +3,11 @@
 #
 #      $Id$
 #
-.PHONY: format
 
 #
 #  This should only be run by hand, and then sanity checked by hand!
 #
-format: dictionary*
-       @for x in dictionary* ; do \
-               cat $$x | ./format.pl > tmp; \
-               mv tmp $$x; \
+format: $(wildcard dictionary*)
+       @for x in $(wildcard dictionary*) ; do \
+               ./format.pl $$x; \
        done
index daea3e6..70ce5dc 100644 (file)
@@ -127,6 +127,7 @@ $INCLUDE dictionary.3gpp
 $INCLUDE dictionary.3gpp2
 $INCLUDE dictionary.acc
 $INCLUDE dictionary.acme
+$INCLUDE dictionary.actelis
 $INCLUDE dictionary.airespace
 $INCLUDE dictionary.alcatel
 $INCLUDE dictionary.alcatel.sr
@@ -139,7 +140,9 @@ $INCLUDE dictionary.azaire
 $INCLUDE dictionary.ascend
 $INCLUDE dictionary.bay
 $INCLUDE dictionary.bintec
+$INCLUDE dictionary.bluecoat
 $INCLUDE dictionary.broadsoft
+$INCLUDE dictionary.brocade
 $INCLUDE dictionary.bskyb
 $INCLUDE dictionary.bt
 $INCLUDE dictionary.cablelabs
@@ -147,10 +150,13 @@ $INCLUDE dictionary.cabletron
 $INCLUDE dictionary.camiant
 $INCLUDE dictionary.chillispot
 $INCLUDE dictionary.cisco
+$INCLUDE dictionary.cisco.asa
 #
-#       The Cisco VPN300 dictionary is the same as the altiga one.
+#       The Cisco VPN300 dictionary uses the same Vendor ID as the ASA one.
 #       You shouldn't use both at the same time.
 #
+#   Note : the altiga dictionary, not listed here, also uses the same Vendor ID
+#
 #$INCLUDE dictionary.cisco.vpn3000
 $INCLUDE dictionary.cisco.vpn5000
 $INCLUDE dictionary.cisco.bbsm
@@ -164,7 +170,9 @@ $INCLUDE dictionary.efficientip
 $INCLUDE dictionary.eltex
 $INCLUDE dictionary.epygi
 $INCLUDE dictionary.erx
+$INCLUDE dictionary.equallogic
 $INCLUDE dictionary.ericsson
+$INCLUDE dictionary.ericsson.packet.core.networks
 $INCLUDE dictionary.extreme
 $INCLUDE dictionary.f5
 $INCLUDE dictionary.fdxtended
@@ -254,14 +262,9 @@ $INCLUDE dictionary.xedia
 $INCLUDE dictionary.xylan
 $INCLUDE dictionary.yubico
 $INCLUDE dictionary.zeus
+$INCLUDE dictionary.zte
 $INCLUDE dictionary.zyxel
 
-#      DHCP dictionary for DHCP functionality and use of dhcp module
-$INCLUDE dictionary.dhcp
-
-#      VQP dictionary for VMPS functionality and use of vmps module
-$INCLUDE dictionary.vqp
-
 #
 #      And finally the server internal attributes.
 #      These are attributes which NEVER go into a RADIUS packet.
diff --git a/share/dictionary.actelis b/share/dictionary.actelis
new file mode 100644 (file)
index 0000000..f6082d3
--- /dev/null
@@ -0,0 +1,17 @@
+# -*- text -*-
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
+##############################################################################
+#
+#      Actelis dictionary
+#
+#      $Id$
+#
+##############################################################################
+
+VENDOR         Actelis                 5468
+
+BEGIN-VENDOR   Actelis
+
+ATTRIBUTE      Actelis-Privilege       1       string
+
+END-VENDOR     Actelis
index 885ef5e..ee6ff4d 100644 (file)
@@ -1,7 +1,10 @@
 # -*- text -*-
-# Copyright (C) 2011 The FreeRADIUS Server project and contributors
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
+#
 #      Altiga vendor attributes
 #
+#      Altiga networks was aquired by Cisco in 2000.
+#
 #      $Id$
 #
 
@@ -28,15 +31,28 @@ ATTRIBUTE Altiga-PPTP-Min-Authentication-G/U      18    integer
 ATTRIBUTE Altiga-L2TP-Min-Authentication-G/U      19    integer
 ATTRIBUTE      Altiga-PPTP-Encryption-G                20      integer
 ATTRIBUTE      Altiga-L2TP-Encryption-G                21      integer
+ATTRIBUTE      Altiga-Argument-Authentication-Server-Type 22   integer
 ATTRIBUTE      Altiga-IPSec-L2L-Keepalives-G           25      integer
+ATTRIBUTE      Altiga-Argument-IPSec-Group-Name        26      integer
 ATTRIBUTE      Altiga-IPSec-Split-Tunnel-List-G        27      string
 ATTRIBUTE      Altiga-IPSec-Default-Domain-G           28      string
 ATTRIBUTE      Altiga-IPSec-Secondary-Domains-G        29      string
 ATTRIBUTE      Altiga-IPSec-Tunnel-Type-G              30      integer
 ATTRIBUTE      Altiga-IPSec-Mode-Config-G              31      integer
+ATTRIBUTE      Altiga-Argument-Authentication-Server-Priority 32       integer
 ATTRIBUTE      Altiga-IPSec-User-Group-Lock-G          33      integer
 ATTRIBUTE      Altiga-IPSec-Over-NAT-G                 34      integer
 ATTRIBUTE      Altiga-IPSec-Over-NAT-Port-Num-G        35      integer
+ATTRIBUTE      Altiga-Partitioning-Primary-DHCP        128     ipaddr
+ATTRIBUTE      Altiga-Partitioning-Secondary-DHCP      129     ipaddr
+ATTRIBUTE      Altiga-Partitioning-Premise-Rout        131     ipaddr
+ATTRIBUTE      Altiga-Partitioning-Partition-Max-Sessions 132  string
+ATTRIBUTE      Altiga-Partitioning-Mobile-IP-Key       133     string
+ATTRIBUTE      Altiga-Partitioning-Mobile-IP-Address   134     ipaddr
+ATTRIBUTE      Altiga-Partitioning-Mobile-IP-SPI       135     ipaddr
+ATTRIBUTE      Altiga-Partitioning-Strip-Realm         136     integer
+ATTRIBUTE      Altiga-Partitioning-Group               137     integer
+ATTRIBUTE      Altiga-Group-Name                       250     string
 
 #  Altiga value
 VALUE  Altiga-Allow-Alpha-Only-Passwords-G Allow               1
@@ -118,6 +134,13 @@ VALUE      Altiga-L2TP-Encryption-G        L2TP-128-Enc/Stateless-Req 13
 VALUE  Altiga-L2TP-Encryption-G        L2TP-40/128-Stateless-Req 14
 VALUE  Altiga-L2TP-Encryption-G        L2TP-40/128-Enc/Statls-Req 15
 
+VALUE  Altiga-Argument-Authentication-Server-Type FirstActiveServer 1
+VALUE  Altiga-Argument-Authentication-Server-Type RADIUS       1
+VALUE  Altiga-Argument-Authentication-Server-Type LDAP         2
+VALUE  Altiga-Argument-Authentication-Server-Type NT           3
+VALUE  Altiga-Argument-Authentication-Server-Type SDI          4
+VALUE  Altiga-Argument-Authentication-Server-Type Internal     5
+
 VALUE  Altiga-IPSec-L2L-Keepalives-G   ON                      1
 VALUE  Altiga-IPSec-L2L-Keepalives-G   OFF                     0
 
@@ -133,4 +156,7 @@ VALUE       Altiga-IPSec-User-Group-Lock-G  OFF                     0
 VALUE  Altiga-IPSec-Over-NAT-G         ON                      1
 VALUE  Altiga-IPSec-Over-NAT-G         OFF                     0
 
+VALUE  Altiga-Partitioning-Strip-Realm ON                      1
+VALUE  Altiga-Partitioning-Strip-Realm OFF                     0
+
 END-VENDOR Altiga
diff --git a/share/dictionary.bluecoat b/share/dictionary.bluecoat
new file mode 100644 (file)
index 0000000..d39c411
--- /dev/null
@@ -0,0 +1,22 @@
+# -*- text -*-
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
+#
+#      Blue Coat Dictionary
+#
+#      See also dictionary.packeteer for former Packeteer products.
+#
+
+VENDOR         BlueCoat                        14501
+
+BEGIN-VENDOR   BlueCoat
+
+ATTRIBUTE      Blue-Coat-Group                         1       string
+# Accepts multiple groups as comma-separated list.
+
+ATTRIBUTE      Blue-Coat-Authorization                 2       integer
+
+VALUE  Blue-Coat-Authorization         No-Access               0
+VALUE  Blue-Coat-Authorization         Read-Only-Access        1
+VALUE  Blue-Coat-Authorization         Read-Write-Access       2
+
+END-VENDOR      BlueCoat
diff --git a/share/dictionary.brocade b/share/dictionary.brocade
new file mode 100644 (file)
index 0000000..2c376bf
--- /dev/null
@@ -0,0 +1,23 @@
+# -*- text -*-
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
+#
+VENDOR         Brocade                         1588
+BEGIN-VENDOR   Brocade
+
+ATTRIBUTE      Brocade-Auth-Role                       1       string
+# Valid attribute values:
+# Admin BasicSwitchAdmin FabricAdmin Operator
+# SecurityAdmin SwitchAdmin User ZoneAdmin
+
+ATTRIBUTE      Brocade-AVPairs1                        2       string
+ATTRIBUTE      Brocade-AVPairs2                        3       string
+ATTRIBUTE      Brocade-AVPairs3                        4       string
+ATTRIBUTE      Brocade-AVPairs4                        5       string
+# Brocade-AVPairs1/2/3/4:
+# Optional, specifies Admin Domain or Virtual Fabric List
+
+ATTRIBUTE      Brocade-Passwd-ExpiryDate               6       string # Format: MM/DD/YYYY
+ATTRIBUTE      Brocade-Passwd-WarnPeriod               7       string # Format: integer in days
+
+END-VENDOR      Brocade
+
index 10738aa..3d31de6 100644 (file)
@@ -133,8 +133,12 @@ ATTRIBUTE  Cisco-Command-Code                      252     string
 ATTRIBUTE      Cisco-Control-Info                      253     string
 ATTRIBUTE      Cisco-Xmit-Rate                         255     integer
 
+VALUE  Cisco-Disconnect-Cause          No-Reason               0
+VALUE  Cisco-Disconnect-Cause          No-Disconnect           1
 VALUE  Cisco-Disconnect-Cause          Unknown                 2
+VALUE  Cisco-Disconnect-Cause          Call-Disconnect         3
 VALUE  Cisco-Disconnect-Cause          CLID-Authentication-Failure 4
+VALUE  Cisco-Disconnect-Cause          No-Modem-Available      9
 VALUE  Cisco-Disconnect-Cause          No-Carrier              10
 VALUE  Cisco-Disconnect-Cause          Lost-Carrier            11
 VALUE  Cisco-Disconnect-Cause          No-Detected-Result-Codes 12
@@ -147,6 +151,11 @@ VALUE      Cisco-Disconnect-Cause          Password-Fail           25
 VALUE  Cisco-Disconnect-Cause          Raw-TCP-Disabled        26
 VALUE  Cisco-Disconnect-Cause          Control-C-Detected      27
 VALUE  Cisco-Disconnect-Cause          EXEC-Program-Destroyed  28
+VALUE  Cisco-Disconnect-Cause          Close-Virtual-Connection        29
+VALUE  Cisco-Disconnect-Cause          End-Virtual-Connection  30
+VALUE  Cisco-Disconnect-Cause          Exit-Rlogin             31
+VALUE  Cisco-Disconnect-Cause          Invalid-Rlogin-Option   32
+VALUE  Cisco-Disconnect-Cause          Insufficient-Resources  33
 VALUE  Cisco-Disconnect-Cause          Timeout-PPP-LCP         40
 VALUE  Cisco-Disconnect-Cause          Failed-PPP-LCP-Negotiation 41
 VALUE  Cisco-Disconnect-Cause          Failed-PPP-PAP-Auth-Fail 42
@@ -154,9 +163,43 @@ VALUE      Cisco-Disconnect-Cause          Failed-PPP-CHAP-Auth    43
 VALUE  Cisco-Disconnect-Cause          Failed-PPP-Remote-Auth  44
 VALUE  Cisco-Disconnect-Cause          PPP-Remote-Terminate    45
 VALUE  Cisco-Disconnect-Cause          PPP-Closed-Event        46
+VALUE  Cisco-Disconnect-Cause          NCP-Closed-PPP          47
+VALUE  Cisco-Disconnect-Cause          MP-Error-PPP            48
+VALUE  Cisco-Disconnect-Cause          PPP-Maximum-Channels    49
+VALUE  Cisco-Disconnect-Cause          Tables-Full             50
+VALUE  Cisco-Disconnect-Cause          Resources-Full          51
+VALUE  Cisco-Disconnect-Cause          Invalid-IP-Address      52
+VALUE  Cisco-Disconnect-Cause          Bad-Hostname            53
+VALUE  Cisco-Disconnect-Cause          Bad-Port                54
+VALUE  Cisco-Disconnect-Cause          Reset-TCP               60
+VALUE  Cisco-Disconnect-Cause          TCP-Connection-Refused  61
+VALUE  Cisco-Disconnect-Cause          Timeout-TCP             62
+VALUE  Cisco-Disconnect-Cause          Foreign-Host-Close-TCP  63
+VALUE  Cisco-Disconnect-Cause          TCP-Network-Unreachable 64
+VALUE  Cisco-Disconnect-Cause          TCP-Host-Unreachable    65
+VALUE  Cisco-Disconnect-Cause          TCP-Network-Admin-Unreachable   66
+VALUE  Cisco-Disconnect-Cause          TCP-Port-Unreachable    67
 VALUE  Cisco-Disconnect-Cause          Session-Timeout         100
 VALUE  Cisco-Disconnect-Cause          Session-Failed-Security 101
 VALUE  Cisco-Disconnect-Cause          Session-End-Callback    102
 VALUE  Cisco-Disconnect-Cause          Invalid-Protocol        120
+VALUE  Cisco-Disconnect-Cause          RADIUS-Disconnect       150
+VALUE  Cisco-Disconnect-Cause          Local-Admin-Disconnect  151
+VALUE  Cisco-Disconnect-Cause          SNMP-Disconnect         152
+VALUE  Cisco-Disconnect-Cause          V110-Retries            160
+VALUE  Cisco-Disconnect-Cause          PPP-Authentication-Timeout      170
+VALUE  Cisco-Disconnect-Cause          Local-Hangup            180
+VALUE  Cisco-Disconnect-Cause          Remote-Hangup           185
+VALUE  Cisco-Disconnect-Cause          T1-Quiesced             190
+VALUE  Cisco-Disconnect-Cause          Call-Duration           195
+VALUE  Cisco-Disconnect-Cause          VPN-User-Disconnect     600
+VALUE  Cisco-Disconnect-Cause          VPN-Carrier-Loss        601
+VALUE  Cisco-Disconnect-Cause          VPN-No-Resources        602
+VALUE  Cisco-Disconnect-Cause          VPN-Bad-Control-Packet  603
+VALUE  Cisco-Disconnect-Cause          VPN-Admin-Disconnect    604
+VALUE  Cisco-Disconnect-Cause          VPN-Tunnel-Shut         605
+VALUE  Cisco-Disconnect-Cause          VPN-Local-Disconnect    606
+VALUE  Cisco-Disconnect-Cause          VPN-Session-Limit       607
+VALUE  Cisco-Disconnect-Cause          VPN-Call-Redirect       608
 
 END-VENDOR     Cisco
diff --git a/share/dictionary.cisco.asa b/share/dictionary.cisco.asa
new file mode 100644 (file)
index 0000000..493179a
--- /dev/null
@@ -0,0 +1,369 @@
+# -*- text -*-
+# Copyright (C) 2013 The FreeRADIUS Server project and contributors
+#
+#        Cisco Adaptative Security Appliance (ASA) Dictionary
+#
+#       http://www.cisco.com/en/US/docs/security/asa/asa90/configuration/guide/ref_extserver.html#wp1802187
+#
+#       $Id$
+#
+
+VENDOR         Cisco-ASA                       3076
+
+BEGIN-VENDOR   Cisco-ASA
+
+ATTRIBUTE      ASA-Simultaneous-Logins                 2       integer
+ATTRIBUTE      ASA-Primary-DNS                         5       string
+ATTRIBUTE      ASA-Secondary-DNS                       6       string
+ATTRIBUTE      ASA-Primary-WINS                        7       string
+ATTRIBUTE      ASA-Secondary-WINS                      8       string
+ATTRIBUTE      ASA-SEP-Card-Assignment                 9       integer
+ATTRIBUTE      ASA-Tunneling-Protocols                 11      integer
+ATTRIBUTE      ASA-IPsec-Sec-Association               12      string
+ATTRIBUTE      ASA-IPsec-Authentication                13      integer
+ATTRIBUTE      ASA-Banner1                             15      string
+ATTRIBUTE      ASA-IPsec-Allow-Passwd-Store            16      integer
+ATTRIBUTE      ASA-Use-Client-Address                  17      integer
+ATTRIBUTE      ASA-PPTP-Encryption                     20      integer
+ATTRIBUTE      ASA-L2TP-Encryption                     21      integer
+ATTRIBUTE      ASA-Group-Policy                        25      string
+ATTRIBUTE      ASA-IPsec-Split-Tunnel-List             27      string
+ATTRIBUTE      ASA-IPsec-Default-Domain                28      string
+ATTRIBUTE      ASA-IPsec-Split-DNS-Names               29      string
+ATTRIBUTE      ASA-IPsec-Tunnel-Type                   30      integer
+ATTRIBUTE      ASA-IPsec-Mode-Config                   31      integer
+ATTRIBUTE      ASA-IPsec-Over-UDP                      34      integer
+ATTRIBUTE      ASA-IPsec-Over-UDP-Port                 35      integer
+ATTRIBUTE      ASA-Banner2                             36      string
+ATTRIBUTE      ASA-PPTP-MPPC-Compression               37      integer
+ATTRIBUTE      ASA-L2TP-MPPC-Compression               38      integer
+ATTRIBUTE      ASA-IPsec-IP-Compression                39      integer
+ATTRIBUTE      ASA-IPsec-IKE-Peer-ID-Check             40      integer
+ATTRIBUTE      ASA-IKE-Keep-Alives                     41      integer
+ATTRIBUTE      ASA-IPsec-Auth-On-Rekey                 42      integer
+ATTRIBUTE      ASA-Required-Client-Firewall-Vendor-Code 45     integer
+ATTRIBUTE      ASA-Required-Client-Firewall-Product-Code 46    integer
+ATTRIBUTE      ASA-Required-Client-Firewall-Description 47     string
+ATTRIBUTE      ASA-Require-HW-Client-Auth              48      integer
+ATTRIBUTE      ASA-Required-Individual-User-Auth       49      integer
+ATTRIBUTE      ASA-Authenticated-User-Idle-Timeout     50      integer
+ATTRIBUTE      ASA-Cisco-IP-Phone-Bypass               51      integer
+ATTRIBUTE      ASA-IPsec-Split-Tunneling-Policy        55      integer
+ATTRIBUTE      ASA-IPsec-Required-Client-Firewall-Capability 56        integer
+ATTRIBUTE      ASA-IPsec-Client-Firewall-Filter-Name   57      string
+ATTRIBUTE      ASA-IPsec-Client-Firewall-Filter-Optional 58    integer
+ATTRIBUTE      ASA-IPsec-Backup-Servers                59      integer
+ATTRIBUTE      ASA-IPsec-Backup-Server-List            60      string
+ATTRIBUTE      ASA-DHCP-Network-Scope                  61      string
+ATTRIBUTE      ASA-Intercept-DHCP-Configure-Msg        62      integer
+ATTRIBUTE      ASA-MS-Client-Subnet-Mask               63      integer
+ATTRIBUTE      ASA-Allow-Network-Extension-Mode        64      integer
+ATTRIBUTE      ASA-Authorization-Type                  65      integer
+ATTRIBUTE      ASA-Authorization-Required              66      integer
+ATTRIBUTE      ASA-Authorization-DN-Field              67      string
+ATTRIBUTE      ASA-Authorization-DN-Field              67      string
+ATTRIBUTE      ASA-IKE-KeepAlive-Confidence-Interval   68      integer
+ATTRIBUTE      ASA-WebVPN-Content-Filter-Parameters    69      integer
+ATTRIBUTE      ASA-WebVPN-HTML-Filter                  69      integer
+ATTRIBUTE      ASA-WebVPN-URL-List                     71      string
+ATTRIBUTE      ASA-WebVPN-Port-Forwarding-List         72      string
+ATTRIBUTE      ASA-WebVPN-Access-List                  73      string
+ATTRIBUTE      ASA-WebVPNACL                           73      string
+ATTRIBUTE      ASA-WebVPN-HTTP-Proxy-IP-Address        74      string
+ATTRIBUTE      ASA-Cisco-LEAP-Bypass                   75      integer
+ATTRIBUTE      ASA-WebVPN-Default-Homepage             76      string
+ATTRIBUTE      ASA-Client-Type-Version-Limiting        77      string
+ATTRIBUTE      ASA-WebVPN-Group-based-HTTP/HTTPS-Proxy-Exception-List  78      string
+ATTRIBUTE      ASA-WebVPN-Port-Forwarding-Name         79      string
+ATTRIBUTE      ASA-IE-Proxy-Server                     80      string
+ATTRIBUTE      ASA-IE-Proxy-Server-Policy              81      integer
+ATTRIBUTE      ASA-IE-Proxy-Exception-List             82      string
+ATTRIBUTE      ASA-IE-Proxy-Bypass-Local               83      integer
+ATTRIBUTE      ASA-IKE-Keepalive-Retry-Interval        84      integer
+ATTRIBUTE      ASA-Tunnel-Group-Lock                   85      string
+ATTRIBUTE      ASA-Access-List-Inbound                 86      string
+ATTRIBUTE      ASA-Access-List-Outbound                87      string
+ATTRIBUTE      ASA-Perfect-Forward-Secrecy-Enable      88      integer
+ATTRIBUTE      ASA-NAC-Enable                          89      integer
+ATTRIBUTE      ASA-NAC-Status-Query-Timer              90      integer
+ATTRIBUTE      ASA-NAC-Revalidation-Timer              91      integer
+ATTRIBUTE      ASA-NAC-Default-ACL                     92      string
+ATTRIBUTE      ASA-WebVPN-URL-Entry-Enable             93      integer
+ATTRIBUTE      ASA-WebVPN-File-Access-Enable           94      integer
+ATTRIBUTE      ASA-WebVPN-File-Server-Entry-Enable     95      integer
+ATTRIBUTE      ASA-WebVPN-File-Server-Browsing-Enable  96      integer
+ATTRIBUTE      ASA-WebVPN-Port-Forwarding-Enable       97      integer
+ATTRIBUTE      ASA-WebVPN-Port-Forwarding-Exchange-Proxy-Enable 98     integer
+ATTRIBUTE      ASA-WebVPN-Port-Forwarding-HTTP-Proxy   99      integer
+ATTRIBUTE      ASA-WebVPN-Citrix-Metaframe-Enable      101     integer
+ATTRIBUTE      ASA-WebVPN-Apply-ACL                    102     integer
+ATTRIBUTE      ASA-WebVPN-SSL-VPN-Client-Enable        103     integer
+ATTRIBUTE      ASA-WebVPN-SSL-VPN-Client-Required      104     integer
+ATTRIBUTE      ASA-WebVPN-SSL-VPN-Client-Keep-Installation 105 integer
+ATTRIBUTE      ASA-SVC-Keepalive                       107     integer
+ATTRIBUTE      ASA-WebVPN-SVC-Keepalive-Frequency      107     integer
+ATTRIBUTE      ASA-SVC-DPD-Interval-Client             108     integer
+ATTRIBUTE      ASA-WebVPN-SVC-Client-DPD-Frequency     108     integer
+ATTRIBUTE      ASA-SVC-DPD-Interval-Gateway            109     integer
+ATTRIBUTE      ASA-WebVPN-SVC-Gateway-DPD-Frequency    109     integer
+ATTRIBUTE      ASA-SVC-Rekey-Time                      110     integer
+ATTRIBUTE      ASA-WebVPN-SVC-Rekey-Time               110     integer
+ATTRIBUTE      ASA-WebVPN-SVC-Rekey-Method             111     integer
+ATTRIBUTE      ASA-WebVPN-SVC-Compression              112     integer
+ATTRIBUTE      ASA-WebVPN-Customization                113     string
+ATTRIBUTE      ASA-WebVPN-SSO-Server-Name              114     string
+ATTRIBUTE      ASA-WebVPN-Deny-Message                 116     string
+ATTRIBUTE      ASA-WebVPN-HTTP-Compression             120     integer
+ATTRIBUTE      ASA-WebVPN-Keepalive-Ignore             121     integer
+ATTRIBUTE      ASA-Extended-Authentication-On-Rekey    122     integer
+ATTRIBUTE      ASA-SVC-DTLS                            123     integer
+ATTRIBUTE      ASA-WebVPN-SVC-DTLS-Enable              123     integer
+ATTRIBUTE      ASA-WebVPN-Auto-HTTP-Signon             124     string
+ATTRIBUTE      ASA-SVC-MTU                             125     integer
+ATTRIBUTE      ASA-WebVPN-SVC-DTLS-MTU                 125     integer
+ATTRIBUTE      ASA-WebVPN-Hidden-Shares                126     integer
+ATTRIBUTE      ASA-SVC-Modules                         127     string
+ATTRIBUTE      ASA-SVC-Profiles                        128     string
+ATTRIBUTE      ASA-SVC-Ask                             131     integer
+ATTRIBUTE      ASA-SVC-Ask-Timeout                     132     integer
+ATTRIBUTE      ASA-IE-Proxy-PAC-URL                    133     string
+ATTRIBUTE      ASA-Strip-Realm                         135     integer
+ATTRIBUTE      ASA-Smart-Tunnel                        136     string
+ATTRIBUTE      ASA-WebVPN-Smart-Tunnel                 136     string
+ATTRIBUTE      ASA-WebVPN-ActiveX-Relay                137     integer
+ATTRIBUTE      ASA-Smart-Tunnel-Auto                   138     integer
+ATTRIBUTE      ASA-WebVPN-Smart-Tunnel-Auto-Start      138     integer
+ATTRIBUTE      ASA-Smart-Tunnel-Auto-Signon-Enable     139     string
+ATTRIBUTE      ASA-WebVPN-Smart-Tunnel-Auto-Sign-On    139     string
+ATTRIBUTE      ASA-VLAN                                140     integer
+ATTRIBUTE      ASA-NAC-Settings                        141     string
+ATTRIBUTE      ASA-Member-Of                           145     string
+ATTRIBUTE      ASA-TunnelGroupName                     146     string
+ATTRIBUTE      ASA-WebVPN-Idle-Timeout-Alert-Interval  148     integer
+ATTRIBUTE      ASA-WebVPN-Session-Timeout-Alert-Interval 149   integer
+ATTRIBUTE      ASA-ClientType                          150     integer
+ATTRIBUTE      ASA-SessionType                         151     integer
+ATTRIBUTE      ASA-SessionSubtype                      152     integer
+ATTRIBUTE      ASA-WebVPN-Download_Max-Size            157     integer
+ATTRIBUTE      ASA-WebVPN-Upload-Max-Size              158     integer
+ATTRIBUTE      ASA-WebVPN-Post-Max-Size                159     integer
+ATTRIBUTE      ASA-WebVPN-User-Storage                 160     string
+ATTRIBUTE      ASA-WebVPN-Storage-Objects              161     string
+ATTRIBUTE      ASA-WebVPN-Storage-Key                  162     string
+ATTRIBUTE      ASA-WebVPN-VDI                          163     string
+ATTRIBUTE      ASA-Address-Pools                       217     string
+ATTRIBUTE      ASA-IPv6-Address-Pools                  218     string
+ATTRIBUTE      ASA-IPv6-VPN-Filter                     219     string
+ATTRIBUTE      ASA-Privilege-Level                     220     integer
+ATTRIBUTE      ASA-WebVPN-UNIX-User-ID                 221     integer
+ATTRIBUTE      ASA-WebVPN-UNIX-Group-ID                222     integer
+ATTRIBUTE      ASA-WebVPN-Macro-Substitution-Value1    223     string
+ATTRIBUTE      ASA-WebVPN-Macro-Substitution-Value2    224     string
+ATTRIBUTE      ASA-WebVPNSmart-Card-Removal-Disconnect 225     integer
+ATTRIBUTE      ASA-WebVPN-Smart-Tunnel-Tunnel-Policy   227     string
+ATTRIBUTE      ASA-WebVPN-Home-Page-Use-Smart-Tunnel   228     integer
+
+VALUE  ASA-Authorization-Required      No                      0
+VALUE  ASA-Authorization-Required      Yes                     1
+
+VALUE  ASA-Authorization-Type          None                    0
+VALUE  ASA-Authorization-Type          Radius                  1
+VALUE  ASA-Authorization-Type          LDAP                    2
+
+VALUE  ASA-Cisco-IP-Phone-Bypass       Disabled                0
+VALUE  ASA-Cisco-IP-Phone-Bypass       Enabled                 1
+
+VALUE  ASA-Cisco-LEAP-Bypass           Disabled                0
+VALUE  ASA-Cisco-LEAP-Bypass           Enabled                 1
+
+VALUE  ASA-ClientType                  Cisco-VPN-Client-IKEv1  1
+VALUE  ASA-ClientType                  AnyConnect-Client-SSL-VPN 2
+VALUE  ASA-ClientType                  Clientless-SSL-VPN      3
+VALUE  ASA-ClientType                  Cut-Through-Proxy       4
+VALUE  ASA-ClientType                  L2TP/IPsec-SSL-VPN      5
+VALUE  ASA-ClientType                  AnyConnect-Client-IPSec-VPN-IKEv2 6
+
+VALUE  ASA-Extended-Authentication-On-Rekey Disabled           0
+VALUE  ASA-Extended-Authentication-On-Rekey Enabled            1
+
+VALUE  ASA-IE-Proxy-Bypass-Local       None                    0
+VALUE  ASA-IE-Proxy-Bypass-Local       Local                   1
+
+VALUE  ASA-IE-Proxy-Server-Policy      No-Modify               1
+VALUE  ASA-IE-Proxy-Server-Policy      No-Proxy                2
+VALUE  ASA-IE-Proxy-Server-Policy      Auto-detect             3
+VALUE  ASA-IE-Proxy-Server-Policy      Use-Concentrator-Setting 4
+
+VALUE  ASA-IKE-Keep-Alives             Disabled                0
+VALUE  ASA-IKE-Keep-Alives             Enabled                 1
+
+VALUE  ASA-Allow-Network-Extension-Mode Disabled               0
+VALUE  ASA-Allow-Network-Extension-Mode Enabled                1
+
+VALUE  ASA-Intercept-DHCP-Configure-Msg Disabled               0
+VALUE  ASA-Intercept-DHCP-Configure-Msg Enabled                1
+
+VALUE  ASA-IPsec-Allow-Passwd-Store    Disabled                0
+VALUE  ASA-IPsec-Allow-Passwd-Store    Enabled                 1
+
+VALUE  ASA-IPsec-Authentication        None                    0
+VALUE  ASA-IPsec-Authentication        RADIUS                  1
+VALUE  ASA-IPsec-Authentication        LDAP-Authorization-only 2
+VALUE  ASA-IPsec-Authentication        NT-Domain               3
+VALUE  ASA-IPsec-Authentication        SDI                     4
+VALUE  ASA-IPsec-Authentication        Internal                5
+VALUE  ASA-IPsec-Authentication        RADIUS-with-Expiry      6
+VALUE  ASA-IPsec-Authentication        Kerberos/Active-Directory 7
+
+VALUE  ASA-IPsec-Auth-On-Rekey         Disabled                0
+VALUE  ASA-IPsec-Auth-On-Rekey         Enabled                 1
+
+VALUE  ASA-IPsec-Backup-Servers        Use-Client-Configured-List 1
+VALUE  ASA-IPsec-Backup-Servers        Disable-and-clear-client-list 2
+VALUE  ASA-IPsec-Backup-Servers        Use-Backup-Server-List  3
+
+VALUE  ASA-IPsec-Client-Firewall-Filter-Optional Required      0
+VALUE  ASA-IPsec-Client-Firewall-Filter-Optional Optional      1
+
+VALUE  ASA-IPsec-IKE-Peer-ID-Check     Required                1
+VALUE  ASA-IPsec-IKE-Peer-ID-Check     If-Supported-By-Peer-Certificate 2
+VALUE  ASA-IPsec-IKE-Peer-ID-Check     Do-Not-Check            3
+
+VALUE  ASA-IPsec-IP-Compression        Disabled                0
+VALUE  ASA-IPsec-IP-Compression        Enabled                 1
+
+VALUE  ASA-IPsec-Mode-Config           Disabled                0
+VALUE  ASA-IPsec-Mode-Config           Enabled                 1
+
+VALUE  ASA-IPsec-Over-UDP              Disabled                0
+VALUE  ASA-IPsec-Over-UDP              Enabled                 1
+
+VALUE  ASA-IPsec-Required-Client-Firewall-Capability None      0
+VALUE  ASA-IPsec-Required-Client-Firewall-Capability Policy-Remotely-Defined 1
+VALUE  ASA-IPsec-Required-Client-Firewall-Capability Policy-Pushed 2
+VALUE  ASA-IPsec-Required-Client-Firewall-Capability Policy-from-Server 4
+
+VALUE  ASA-IPsec-Split-Tunneling-Policy No-Split-Tunneling     0
+VALUE  ASA-IPsec-Split-Tunneling-Policy Split-Tunneling        1
+VALUE  ASA-IPsec-Split-Tunneling-Policy Local-LAN-Permitted    2
+
+VALUE  ASA-IPsec-Tunnel-Type           LAN-to-LAN              1
+VALUE  ASA-IPsec-Tunnel-Type           Remote-Access           2
+
+VALUE  ASA-L2TP-MPPC-Compression       Disabled                0
+VALUE  ASA-L2TP-MPPC-Compression       Enabled                 1
+
+VALUE  ASA-NAC-Enable                  No                      0
+VALUE  ASA-NAC-Enable                  Yes                     1
+
+VALUE  ASA-Perfect-Forward-Secrecy-Enable No                   0
+VALUE  ASA-Perfect-Forward-Secrecy-Enable Yes                  1
+
+VALUE  ASA-PPTP-MPPC-Compression       Disabled                0
+VALUE  ASA-PPTP-MPPC-Compression       Enabled                 1
+
+VALUE  ASA-Required-Client-Firewall-Vendor-Code Cisco-CIC      1
+VALUE  ASA-Required-Client-Firewall-Vendor-Code Zone-Labs      2
+VALUE  ASA-Required-Client-Firewall-Vendor-Code NetworkICE     3
+VALUE  ASA-Required-Client-Firewall-Vendor-Code Sygate         4
+VALUE  ASA-Required-Client-Firewall-Vendor-Code Cisco-IPSA     5
+
+VALUE  ASA-Required-Individual-User-Auth Disabled              0
+VALUE  ASA-Required-Individual-User-Auth Enabled               1
+
+VALUE  ASA-Require-HW-Client-Auth      Disabled                0
+VALUE  ASA-Require-HW-Client-Auth      Enabled                 1
+
+VALUE  ASA-SessionSubtype              None                    0
+VALUE  ASA-SessionSubtype              Clientless              1
+VALUE  ASA-SessionSubtype              Client                  2
+VALUE  ASA-SessionSubtype              Client-Only             3
+
+VALUE  ASA-SessionType                 None                    0
+VALUE  ASA-SessionType                 AnyConnect-Client-SSL-VPN 1
+VALUE  ASA-SessionType                 AnyConnect-Client-IPSec-VPN/IKEv2 2
+VALUE  ASA-SessionType                 Clientless-SSL-VPN      3
+VALUE  ASA-SessionType                 Clientless-Email-Proxy  4
+VALUE  ASA-SessionType                 Cisco-VPN-Client/IKEv1  5
+VALUE  ASA-SessionType                 IKEv1-LAN-to-LAN        6
+VALUE  ASA-SessionType                 IKEv2-LAN-to-LAN        7
+VALUE  ASA-SessionType                 VPN-Load-Balancing      8
+
+VALUE  ASA-Smart-Tunnel-Auto           Disabled                0
+VALUE  ASA-Smart-Tunnel-Auto           Enabled                 1
+VALUE  ASA-Smart-Tunnel-Auto           AutoStart               2
+
+VALUE  ASA-Strip-Realm                 Disabled                0
+VALUE  ASA-Strip-Realm                 Enabled                 1
+
+VALUE  ASA-SVC-Ask                     Disabled                0
+VALUE  ASA-SVC-Ask                     Enabled                 1
+VALUE  ASA-SVC-Ask                     Enable-Default-Service  3
+VALUE  ASA-SVC-Ask                     Enable-Default-Clientless 5
+
+VALUE  ASA-SVC-DTLS                    FALSE                   0
+VALUE  ASA-SVC-DTLS                    TRUE                    1
+
+VALUE  ASA-Use-Client-Address          Disabled                0
+VALUE  ASA-Use-Client-Address          Enabled                 1
+
+VALUE  ASA-WebVPN-Apply-ACL            Disabled                0
+VALUE  ASA-WebVPN-Apply-ACL            Enabled                 1
+
+VALUE  ASA-WebVPN-Citrix-Metaframe-Enable Disabled             0
+VALUE  ASA-WebVPN-Citrix-Metaframe-Enable Enabled              1
+
+VALUE  ASA-WebVPN-File-Access-Enable   Disabled                0
+VALUE  ASA-WebVPN-File-Access-Enable   Enabled                 1
+
+VALUE  ASA-WebVPN-File-Server-Browsing-Enable Disabled         0
+VALUE  ASA-WebVPN-File-Server-Browsing-Enable Enabled          1
+
+VALUE  ASA-WebVPN-File-Server-Entry-Enable Disabled            0
+VALUE  ASA-WebVPN-File-Server-Entry-Enable Enabled             1
+
+VALUE  ASA-WebVPN-Hidden-Shares        None                    0
+VALUE  ASA-WebVPN-Hidden-Shares        Visible                 1
+
+VALUE  ASA-WebVPN-HTTP-Compression     Off                     0
+VALUE  ASA-WebVPN-HTTP-Compression     Deflate-Compression     1
+
+VALUE  ASA-WebVPN-Port-Forwarding-Enable Disabled              0
+VALUE  ASA-WebVPN-Port-Forwarding-Enable Enabled               1
+
+VALUE  ASA-WebVPN-Port-Forwarding-Exchange-Proxy-Enable Disabled 0
+VALUE  ASA-WebVPN-Port-Forwarding-Exchange-Proxy-Enable Enabled 1
+
+VALUE  ASA-WebVPN-Port-Forwarding-HTTP-Proxy Disabled          0
+VALUE  ASA-WebVPN-Port-Forwarding-HTTP-Proxy Enabled           1
+
+VALUE  ASA-WebVPNSmart-Card-Removal-Disconnect Disabled        0
+VALUE  ASA-WebVPNSmart-Card-Removal-Disconnect Enabled         1
+
+VALUE  ASA-WebVPN-Smart-Tunnel-Auto-Start Disabled             0
+VALUE  ASA-WebVPN-Smart-Tunnel-Auto-Start Enabled              1
+VALUE  ASA-WebVPN-Smart-Tunnel-Auto-Start AutoStart            2
+
+VALUE  ASA-WebVPN-SSL-VPN-Client-Enable Disabled               0
+VALUE  ASA-WebVPN-SSL-VPN-Client-Enable Enabled                1
+
+VALUE  ASA-WebVPN-SSL-VPN-Client-Keep-Installation Disabled    0
+VALUE  ASA-WebVPN-SSL-VPN-Client-Keep-Installation Enabled     1
+
+VALUE  ASA-WebVPN-SSL-VPN-Client-Required Disabled             0
+VALUE  ASA-WebVPN-SSL-VPN-Client-Required Enabled              1
+
+VALUE  ASA-WebVPN-SVC-DTLS-Enable      Disabled                0
+VALUE  ASA-WebVPN-SVC-DTLS-Enable      Enabled                 1
+
+VALUE  ASA-WebVPN-SVC-Rekey-Method     Off                     0
+VALUE  ASA-WebVPN-SVC-Rekey-Method     SSL                     1
+VALUE  ASA-WebVPN-SVC-Rekey-Method     New-Tunnel              2
+
+VALUE  ASA-WebVPN-SVC-Compression      Off                     0
+VALUE  ASA-WebVPN-SVC-Compression      Deflate-Compression     1
+
+VALUE  ASA-WebVPN-URL-Entry-Enable     Disabled                0
+VALUE  ASA-WebVPN-URL-Entry-Enable     Enabled                 1
+
+END-VENDOR      Cisco-ASA
index 733b077..f142f3d 100644 (file)
@@ -93,16 +93,16 @@ ATTRIBUTE   CVPN3000-Partition-Mobile-IP-Address    134     ipaddr
 ATTRIBUTE      CVPN3000-Partition-Mobile-IP-SPI        135     integer
 ATTRIBUTE      CVPN3000-Strip-Realm                    136     integer
 ATTRIBUTE      CVPN3000-Group-Name                     137     integer
-ATTRIBUTE      CPVN3000-Smart-Tunnel-Auto              138     integer
-ATTRIBUTE      CPVN3000-VLAN                           140     integer
-ATTRIBUTE      CPVN3000-NAC-Settings                   141     string
-ATTRIBUTE      CPVN3000-Member-Of                      145     string
-ATTRIBUTE      CPVN3000-Address-Pools                  217     string
-ATTRIBUTE      CPVN3000-IPv6-Address-Pools             218     string
-ATTRIBUTE      CPVN3000-IPv6-VPN-Filter                219     string
+ATTRIBUTE      CVPN3000-Smart-Tunnel-Auto              138     integer
+ATTRIBUTE      CVPN3000-VLAN                           140     integer
+ATTRIBUTE      CVPN3000-NAC-Settings                   141     string
+ATTRIBUTE      CVPN3000-Member-Of                      145     string
+ATTRIBUTE      CVPN3000-Address-Pools                  217     string
+ATTRIBUTE      CVPN3000-IPv6-Address-Pools             218     string
+ATTRIBUTE      CVPN3000-IPv6-VPN-Filter                219     string
 ATTRIBUTE      CVPN3000-Privilege-Level                220     integer
-ATTRIBUTE      CPVN3000-WebVPN-Macro-Value1            223     string
-ATTRIBUTE      CPVN3000-WebVPN-Macro-Value2            224     string
+ATTRIBUTE      CVPN3000-WebVPN-Macro-Value1            223     string
+ATTRIBUTE      CVPN3000-WebVPN-Macro-Value2            224     string
 
 VALUE  CVPN3000-Allow-Alpha-Only-Passwords Disallow            0
 VALUE  CVPN3000-Allow-Alpha-Only-Passwords Allow               1
@@ -208,8 +208,8 @@ VALUE       CVPN3000-IPSec-Over-UDP         ON                      1
 VALUE  CVPN3000-Strip-Realm            FALSE                   0
 VALUE  CVPN3000-Strip-Realm            TRUE                    1
 
-VALUE  CPVN3000-Smart-Tunnel-Auto      Disabled                0
-VALUE  CPVN3000-Smart-Tunnel-Auto      Enabled                 1
-VALUE  CPVN3000-Smart-Tunnel-Auto      Auto                    2
+VALUE  CVPN3000-Smart-Tunnel-Auto      Disabled                0
+VALUE  CVPN3000-Smart-Tunnel-Auto      Enabled                 1
+VALUE  CVPN3000-Smart-Tunnel-Auto      Auto                    2
 
 END-VENDOR     Cisco-VPN3000
index edf72f9..73632c5 100644 (file)
@@ -308,6 +308,7 @@ ATTRIBUTE   DHCP-End-Of-Options                     255     byte
 VALUE  DHCP-Opcode                     Client-Message          1
 VALUE  DHCP-Opcode                     Server-Message          2
 
+VALUE  DHCP-Message-Type               DHCP-Do-Not-Respond     0
 VALUE  DHCP-Message-Type               DHCP-Discover           1
 VALUE  DHCP-Message-Type               DHCP-Offer              2
 VALUE  DHCP-Message-Type               DHCP-Request            3
diff --git a/share/dictionary.equallogic b/share/dictionary.equallogic
new file mode 100644 (file)
index 0000000..0ae9546
--- /dev/null
@@ -0,0 +1,44 @@
+# -*- text -*-
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
+#
+#      Equallogic Dictionary
+#
+#      Equallogic was acquired by Dell in 2008.
+#
+
+VENDOR         Equallogic                      12740
+
+BEGIN-VENDOR   Equallogic
+
+ATTRIBUTE      Equallogic-Admin-Full-Name              1       string  # Optional
+ATTRIBUTE      Equallogic-Admin-Email                  2       string  # Optional
+ATTRIBUTE      Equallogic-Admin-Phone                  3       string  # Optional
+ATTRIBUTE      Equallogic-Admin-Mobile                 4       string  # Optional
+ATTRIBUTE      Equallogic-Poll-Interval                5       integer # Up to 6 numerals, default is 30 (seconds)
+ATTRIBUTE      Equallogic-EQL-Admin-Privilege          6       integer
+
+VALUE  Equallogic-EQL-Admin-Privilege          group-administrator     0
+VALUE  Equallogic-EQL-Admin-Privilege          pool-administrator      1
+VALUE  Equallogic-EQL-Admin-Privilege          pool-administrator-ro-entire-group      2
+VALUE  Equallogic-EQL-Admin-Privilege          volume-administrator    3
+
+# For read-only admin privileges set
+# Equallogic-EQL-Admin-Privilege to 0 and
+# Equallogic-Admin-Account-Type to RO
+
+ATTRIBUTE      Equallogic-Admin-Pool-Access            7       string  # Comma-separated list of pools
+
+# 'Pool1 25gb' sets the quota for Pool1 to 25GB
+# 'Pool1 500mb' sets a quota of 500MB.
+# 'Pool1 unlimited sets an unlimited quota to pool1
+# If no unit is specified, the default capacity unit is MB.
+
+ATTRIBUTE      Equallogic-Admin-Repl-Site-Access       8       string  # Comma-separated list of sites
+
+# Required if Equallogic-EQL-Admin-Privilege is 3
+# Used only if Equallogic-EQL-Admin-Privilege is 3
+
+ATTRIBUTE      Equallogic-Admin-Account-Type           9       string  # RO or RW
+
+END-VENDOR      Equallogic
+
diff --git a/share/dictionary.ericsson.packet.core.networks b/share/dictionary.ericsson.packet.core.networks
new file mode 100644 (file)
index 0000000..d15bfcb
--- /dev/null
@@ -0,0 +1,15 @@
+#
+# dictionary.ericsson.packet.core.networks
+#
+
+VENDOR         Ericsson-Packet-Core-Networks   10923
+
+#
+# Ericsson specific
+
+BEGIN-VENDOR   Ericsson-Packet-Core-Networks
+
+ATTRIBUTE      Suggested-Rule-Space                    30      string
+ATTRIBUTE      Suggested-Secondary-Rule-Space          31      string
+
+END-VENDOR      Ericsson-Packet-Core-Networks
index ee0db34..ccdf43d 100644 (file)
@@ -120,6 +120,7 @@ ATTRIBUTE   Packet-Dst-Port                         1087    integer
 ATTRIBUTE      Packet-Authentication-Vector            1088    octets
 ATTRIBUTE      Time-Of-Day                             1089    string
 ATTRIBUTE      Request-Processing-Stage                1090    string
+ATTRIBUTE      SHA2-Password                           1092    octets
 ATTRIBUTE      SHA-Password                            1093    octets
 ATTRIBUTE      SSHA-Password                           1094    octets
 ATTRIBUTE      SHA1-Password                           1093    octets
@@ -178,6 +179,7 @@ ATTRIBUTE   Home-Server-Pool                        1111    string
 
 ATTRIBUTE      FreeRADIUS-Client-IP-Address            1120    ipaddr
 ATTRIBUTE      FreeRADIUS-Client-IPv6-Address          1121    ipv6addr
+# The rest of the FreeRADIUS-Client-* attributes are at 1150...
 
 ATTRIBUTE      FreeRADIUS-Client-Require-MA            1122    integer
 
@@ -226,6 +228,7 @@ ATTRIBUTE   Cache-TTL                               1140    integer
 ATTRIBUTE      Cache-Status-Only                       1141    integer
 ATTRIBUTE      Cache-Merge                             1142    integer
 ATTRIBUTE      Cache-Entry-Hits                        1143    integer
+ATTRIBUTE      Cache-Read-Only                         1144    integer
 
 VALUE  Cache-Status-Only               no                      0
 VALUE  Cache-Status-Only               yes                     1
@@ -233,10 +236,8 @@ VALUE      Cache-Status-Only               yes                     1
 VALUE  Cache-Merge                     no                      0
 VALUE  Cache-Merge                     yes                     1
 
-#      More dynamic client attributes
-
-ATTRIBUTE      FreeRADIUS-Client-Src-IP-Address        1143    ipaddr
-ATTRIBUTE      FreeRADIUS-Client-Src-IPv6-Address      1144    ipv6addr
+VALUE  Cache-Read-Only                 no                      0
+VALUE  Cache-Read-Only                 yes                     1
 
 ATTRIBUTE      OTP-Challenge                           1145    string
 ATTRIBUTE      EAP-Session-Id                          1146    octets
@@ -251,6 +252,12 @@ VALUE      Chbind-Response-Code            failure                 3
 ATTRIBUTE      Acct-Input-Octets64                     1148    integer64
 ATTRIBUTE      Acct-Output-Octets64                    1149    integer64
 
+ATTRIBUTE      FreeRADIUS-Client-IP-Prefix             1150    ipv4prefix
+ATTRIBUTE      FreeRADIUS-Client-IPv6-Prefix           1151    ipv6prefix
+ATTRIBUTE      FreeRADIUS-Response-Delay               1152    integer
+ATTRIBUTE      FreeRADIUS-Client-Src-IP-Address        1153    ipaddr
+ATTRIBUTE      FreeRADIUS-Client-Src-IPv6-Address      1154    ipv6addr
+
 #
 #      Range:  1200-1279
 #              EAP-SIM (and other EAP type) weirdness.
@@ -284,6 +291,9 @@ ATTRIBUTE   EAP-Sim-KC1                             1212    octets
 ATTRIBUTE      EAP-Sim-KC2                             1213    octets
 ATTRIBUTE      EAP-Sim-KC3                             1214    octets
 
+ATTRIBUTE      EAP-Sim-Ki                              1215    octets
+ATTRIBUTE      EAP-Sim-Algo-Version                    1216    integer
+
 #
 #      Range:  1280 - 1535
 #              EAP-type specific attributes
@@ -357,6 +367,7 @@ ATTRIBUTE   EAP-Type-MS-CHAP-V2                     1306    octets
 #
 
 # these are PW_EAP_SIM_X + 1536
+ATTRIBUTE      EAP_Sim-Base                            1536    octets
 ATTRIBUTE      EAP-Sim-RAND                            1537    octets
 ATTRIBUTE      EAP-Sim-PADDING                         1542    octets
 ATTRIBUTE      EAP-Sim-NONCE_MT                        1543    octets
@@ -435,6 +446,17 @@ ATTRIBUTE  Tmp-Date-6                              1846    date
 ATTRIBUTE      Tmp-Date-7                              1847    date
 ATTRIBUTE      Tmp-Date-8                              1848    date
 ATTRIBUTE      Tmp-Date-9                              1849    date
+
+ATTRIBUTE      Tmp-Integer64-0                         1871    integer64
+ATTRIBUTE      Tmp-Integer64-1                         1872    integer64
+ATTRIBUTE      Tmp-Integer64-2                         1873    integer64
+ATTRIBUTE      Tmp-Integer64-3                         1874    integer64
+ATTRIBUTE      Tmp-Integer64-4                         1875    integer64
+ATTRIBUTE      Tmp-Integer64-5                         1876    integer64
+ATTRIBUTE      Tmp-Integer64-6                         1877    integer64
+ATTRIBUTE      Tmp-Integer64-7                         1878    integer64
+ATTRIBUTE      Tmp-Integer64-8                         1879    integer64
+ATTRIBUTE      Tmp-Integer64-9                         1880    integer64
 #
 #  These attributes shouldn't be used anywhere.  They are defined here
 #  only for casting of values in conditional expressions.
@@ -476,7 +498,9 @@ ATTRIBUTE   TLS-Cert-Issuer                         1912    string
 ATTRIBUTE      TLS-Cert-Subject                        1913    string
 ATTRIBUTE      TLS-Cert-Common-Name                    1914    string
 ATTRIBUTE      TLS-Cert-Subject-Alt-Name-Email         1915    string
-# 1916 - 1919: reserved for future cert attributes
+ATTRIBUTE      TLS-Cert-Subject-Alt-Name-Dns           1916    string
+ATTRIBUTE      TLS-Cert-Subject-Alt-Name-Upn           1917    string
+# 1918 - 1919: reserved for future cert attributes
 ATTRIBUTE      TLS-Client-Cert-Serial                  1920    string
 ATTRIBUTE      TLS-Client-Cert-Expiration              1921    string
 ATTRIBUTE      TLS-Client-Cert-Issuer                  1922    string
@@ -488,9 +512,11 @@ ATTRIBUTE  TLS-Client-Cert-X509v3-Extended-Key-Usage 1927  string
 ATTRIBUTE      TLS-Client-Cert-X509v3-Subject-Key-Identifier 1928      string
 ATTRIBUTE      TLS-Client-Cert-X509v3-Authority-Key-Identifier 1929    string
 ATTRIBUTE      TLS-Client-Cert-X509v3-Basic-Constraints 1930   string
-ATTRIBUTE      TLS-PSK-Identity                        1931    string
+ATTRIBUTE      TLS-Client-Cert-Subject-Alt-Name-Dns    1931    string
+ATTRIBUTE      TLS-Client-Cert-Subject-Alt-Name-Upn    1932    string
+ATTRIBUTE      TLS-PSK-Identity                        1933    string
 
-# 1932 - 1939: reserved for future cert attributes
+# 1934 - 1939: reserved for future cert attributes
 
 #
 #      Range:  1940-2099
index 75796b6..809a238 100644 (file)
@@ -14,9 +14,42 @@ VENDOR               H3C                             25506
 
 BEGIN-VENDOR   H3C
 
+ATTRIBUTE      H3C-Input-Peak-Rate                     1       integer
+ATTRIBUTE      H3C-Input-Average-Rate                  2       integer
+ATTRIBUTE      H3C-Input-Basic-Rate                    3       integer
+ATTRIBUTE      H3C-Remanent-Volume                     15      integer
+ATTRIBUTE      H3C-Command                             20      integer
+
+VALUE  H3C-Command                     Trigger-Request         1
+VALUE  H3C-Command                     Terminate-Request       2
+VALUE  H3C-Command                     SetPolicy               3
+VALUE  H3C-Command                     Result                  4
+VALUE  H3C-Command                     PortalClear             5
+
+ATTRIBUTE      H3C-Control-Identifier                  24      integer
+ATTRIBUTE      H3C-Result-Code                         25      integer
 ATTRIBUTE      H3C-Connect_Id                          26      integer
+ATTRIBUTE      H3C-Ftp-Directory                       28      string
+ATTRIBUTE      H3C-Exec-Privilege                      29      integer
+
+VALUE  H3C-Exec-Privilege              Visit                   0
+VALUE  H3C-Exec-Privilege              Monitor                 1
+VALUE  H3C-Exec-Privilege              System                  2
+VALUE  H3C-Exec-Privilege              Manage                  33
+
 ATTRIBUTE      H3C-NAS-Startup-Timestamp               59      integer
 ATTRIBUTE      H3C-Ip-Host-Addr                        60      string
+ATTRIBUTE      H3C-User-Notify                         61      string
+ATTRIBUTE      H3C-User-HeartBeat                      62      string
+ATTRIBUTE      H3C-User-Group                          140     string
+ATTRIBUTE      H3C-Security-Level                      141     integer
+ATTRIBUTE      H3C-Input-Interval-Octets               201     integer
+ATTRIBUTE      H3C-Output-Interval-Octets              202     integer
+ATTRIBUTE      H3C-Input-Interval-Packets              203     integer
+ATTRIBUTE      H3C-Output-Interval-Packets             204     integer
+ATTRIBUTE      H3C-Input-Interval-Gigawords            205     integer
+ATTRIBUTE      H3C-Output-Interval-Gigawords           206     integer
+ATTRIBUTE      H3C-Backup-NAS-IP                       207     ipaddr
 ATTRIBUTE      H3C-Product-ID                          255     string
 
 END-VENDOR     H3C
index 8b67fbf..566c77e 100644 (file)
@@ -1,10 +1,12 @@
 # -*- text -*-
-# Copyright (C) 2011 The FreeRADIUS Server project and contributors
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
 ##############################################################################
 #
 #      Packeteer VSAs, who followed the Cisco way of putting everything
 #      into one text string.
 #
+#      Packeteer was acquired by Blue Coat in 2008.
+#
 #      $Id$
 #
 ##############################################################################
@@ -17,5 +19,6 @@ VENDOR                Packeteer                       2334
 BEGIN-VENDOR   Packeteer
 
 ATTRIBUTE      Packeteer-AVPair                        1       string
+ATTRIBUTE      Packeteer-PC-AVPair                     2       string
 
 END-VENDOR     Packeteer
index 54d2cb9..6c5a4bd 100644 (file)
@@ -7,4 +7,4 @@
 #      $Id$
 #
 
-ATTRIBUTE      EAP-Key-Name                            102     string
+ATTRIBUTE      EAP-Key-Name                            102     octets
index 2e9f914..6c7f394 100644 (file)
@@ -6,4 +6,4 @@
 #
 #      $Id$
 #
-ATTRIBUTE      Chargeable-User-Identity                89      string
+ATTRIBUTE      Chargeable-User-Identity                89      octets
index e4d4d1b..4ce1920 100644 (file)
@@ -58,6 +58,6 @@ ATTRIBUTE     Access-Loop-Encapsulation               144     octets # 3
 #  If this attribute exists, it means that IFW has been performed
 #  for the subscribers session.
 #
-ATTRIBUTE      IWF-Session                             252     octets # 0
+ATTRIBUTE      IWF-Session                             254     octets # 0
 
 END-VENDOR ADSL-Forum
index 3da6b06..f366e33 100644 (file)
@@ -5,7 +5,7 @@
 #        http://www.ietf.org/rfc/rfc7055.txt
 #
 
-ATTRIBUTE      GSS-Acceptor-Service-Name      164   string
-ATTRIBUTE      GSS-Acceptor-Host-Name         165   string
-ATTRIBUTE      GSS-Acceptor-Service-Specifics 166   string
-ATTRIBUTE      GSS-Acceptor-Realm-Name        167   string
+ATTRIBUTE      GSS-Acceptor-Service-Name               164     string
+ATTRIBUTE      GSS-Acceptor-Host-Name                  165     string
+ATTRIBUTE      GSS-Acceptor-Service-Specifics          166     string
+ATTRIBUTE      GSS-Acceptor-Realm-Name                 167     string
index 2e57cab..bec5178 100644 (file)
@@ -1,9 +1,12 @@
 # -*- text -*-
-# Copyright (C) 2011 The FreeRADIUS Server project and contributors
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
 ##############################################################################
 #
 #      Symbol VSAs
 #
+#      Symbol Technologies has been acquired by Motorola in 2007.
+#      Some attributes remain in use by products after the acquisition.
+#
 #      $Id$
 #
 ##############################################################################
@@ -12,26 +15,35 @@ VENDOR              Symbol                          388
 BEGIN-VENDOR   Symbol
 
 ATTRIBUTE      Symbol-Admin-Role                       1       integer
+
 VALUE  Symbol-Admin-Role               Monitor                 1
 VALUE  Symbol-Admin-Role               Helpdesk                2
 VALUE  Symbol-Admin-Role               NetworkAdmin            4
 VALUE  Symbol-Admin-Role               SysAdmin                8
 VALUE  Symbol-Admin-Role               WebAdmin                16
+VALUE  Symbol-Admin-Role               Security                32
 VALUE  Symbol-Admin-Role               SuperUser               32768
 
 ATTRIBUTE      Symbol-Current-ESSID                    2       string
 ATTRIBUTE      Symbol-Allowed-ESSID                    3       string
 ATTRIBUTE      Symbol-WLAN-Index                       4       integer
 ATTRIBUTE      Symbol-QoS-Profile                      5       integer
+
+VALUE  Symbol-QoS-Profile              BestEffort              1
+VALUE  Symbol-QoS-Profile              Background              2
+VALUE  Symbol-QoS-Profile              Video                   3
+VALUE  Symbol-QoS-Profile              Voice                   4
+
 ATTRIBUTE      Symbol-Allowed-Radio                    6       string
-ATTRIBUTE      Symbol-Expiry-Date-Time                 7       string
-ATTRIBUTE      Symbol-Start-Date-Time                  8       string
+ATTRIBUTE      Symbol-Expiry-Date-Time                 7       string  # Format: MM/DD/YYYY-HH:MM
+ATTRIBUTE      Symbol-Start-Date-Time                  8       string  # Format: MM/DD/YYYY-HH:MM
 ATTRIBUTE      Symbol-Posture-Status                   9       string
-ATTRIBUTE      Symbol-Downlink-Limit                   10      string
-ATTRIBUTE      Symbol-Uplink-Limit                     11      string
+ATTRIBUTE      Symbol-Downlink-Limit                   10      string  # Format: 100-10000 (Kbps), 0 = Disabled
+ATTRIBUTE      Symbol-Uplink-Limit                     11      string  # Format: 100-10000 (Kbps), 0 = Disabled
 ATTRIBUTE      Symbol-User-Group                       12      string
 
 ATTRIBUTE      Symbol-Login-Source                     100     integer
+
 VALUE  Symbol-Login-Source             HTTP                    16
 VALUE  Symbol-Login-Source             SSH                     32
 VALUE  Symbol-Login-Source             Telnet                  64
index e6b5769..999d22c 100644 (file)
@@ -2,12 +2,16 @@
 #
 #       For TERENA VSA's
 #
+#      The reference for these values is:
+#       http://www.terena.org/activities/tf-emc2/oid.html
+#
 #       $Id$
 #
-VENDOR         TERENA                          25178
+VENDOR          TERENA                          25178
 
-BEGIN-VENDOR   TERENA
+BEGIN-VENDOR    TERENA
 
-ATTRIBUTE      Eduroam-SP-Country                      10      string
+ATTRIBUTE       Eduroam-SP-Country                      10      string
+ATTRIBUTE       Eduroam-Monitoring-Inflate              11      string
 
-END-VENDOR     TERENA
+END-VENDOR      TERENA
index 570c3ab..531d3f1 100644 (file)
@@ -404,5 +404,4 @@ ATTRIBUTE   WiMAX-vDHCP-RK-Lifetime                 77      integer
 
 # About 10 more attributes in 1.3
 
-
 END-VENDOR     WiMAX
index 8cf0754..01e41be 100644 (file)
@@ -23,7 +23,7 @@ ATTRIBUTE     Xylan-Auth-Group-Protocol               8       string
 ATTRIBUTE      Xylan-Asa-Access                        9       string
 ATTRIBUTE      Xylan-End-User-Profile                  10      integer
 ATTRIBUTE      Xylan-Access-Priv                       16      integer
-ATTRIBUTE      Xylan-Nms-Group                         20      string
+ATTRIBUTE      Xylan-Nms-Group                         20      string
 ATTRIBUTE      Xylan-Nms-First-Name                    21      string
 ATTRIBUTE      Xylan-Nms-Last-Name                     22      string
 ATTRIBUTE      Xylan-Nms-Description                   23      string
diff --git a/share/dictionary.zte b/share/dictionary.zte
new file mode 100644 (file)
index 0000000..a826c0f
--- /dev/null
@@ -0,0 +1,71 @@
+# -*- text -*-
+# Copyright (C) 2014 The FreeRADIUS Server project and contributors
+#
+#       For ZTE.
+#
+#       $Id$
+#
+# QoS attributes Mostly derived from:
+# http://wwwen.zte.com.cn/en/products/bearer/201308/P020130828522349526032.pdf
+#
+VENDOR         ZTE                             3902
+BEGIN-VENDOR   ZTE
+
+ATTRIBUTE      ZTE-Client-DNS-Pri                      1       integer
+ATTRIBUTE      ZTE-Client-DNS-Sec                      2       integer
+ATTRIBUTE      ZTE-Context-Name                        4       string
+ATTRIBUTE      ZTE-Tunnel-Max-Sessions                 21      integer
+ATTRIBUTE      ZTE-Tunnel-Max-Tunnels                  22      integer
+ATTRIBUTE      ZTE-Tunnel-Window                       24      integer
+ATTRIBUTE      ZTE-Tunnel-Retransmit                   25      integer
+ATTRIBUTE      ZTE-Tunnel-Cmd-Timeout                  26      integer
+ATTRIBUTE      ZTE-PPPOE-URL                           27      string
+ATTRIBUTE      ZTE-PPPOE-MOTM                          28      string
+ATTRIBUTE      ZTE-Tunnel-Algorithm                    31      integer
+ATTRIBUTE      ZTE-Tunnel-Deadtime                     32      integer
+ATTRIBUTE      ZTE-Mcast-Send                          33      integer
+ATTRIBUTE      ZTE-Mcast-Receive                       34      integer
+ATTRIBUTE      ZTE-Mcast-MaxGroups                     35      integer
+ATTRIBUTE      ZTE-Access-Type                         74      integer
+
+ATTRIBUTE      ZTE-QoS-Type                            81      integer
+ATTRIBUTE      ZTE-QoS-Profile-Down                    82      string
+ATTRIBUTE      ZTE-Rate-Ctrl-SCR-Down                  83      integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Down                84      integer
+ATTRIBUTE      ZTE-Rate-Ctrl-PCR                       86      integer
+ATTRIBUTE      ZTE-TCP-Syn-Rate                        88      integer
+ATTRIBUTE      ZTE-Rate-Ctrl-SCR-Up                    89      integer
+ATTRIBUTE      ZTE-Priority-Level                      90      integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Up                  91      integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Max-Down            92      integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Max-Up              93      integer
+ATTRIBUTE      ZTE-QOS-Profile-Up                      94      string
+
+ATTRIBUTE      ZTE-TCP-Limit-Num                       95      integer
+ATTRIBUTE      ZTE-TCP-Limit-Mode                      96      integer
+ATTRIBUTE      ZTE-IGMP-Service-Profile-Num            97      integer
+ATTRIBUTE      ZTE-PPP-Sservice-Type                   101     integer
+ATTRIBUTE      ZTE-Access-Domain                       151     string
+ATTRIBUTE      ZTE-VPN-ID                              190     string
+
+# 0 (lowest) - 15 (highest) privilege level
+#ATTRIBUTE     ZTE-SW-Privilege                        191     integer
+
+ATTRIBUTE      ZTE_Rate-Bust-DPIR                      191     integer
+ATTRIBUTE      ZTE_Rate-Bust-UPIR                      192     integer
+
+ATTRIBUTE      ZTE-Rate-Ctrl-PBS-Down                  202     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-PBS-Up                    203     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-SCR-Up-v6                 228     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Up-v6               229     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Max-Up-v6           230     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-PBS-Up-v6                 231     integer
+ATTRIBUTE      ZTE-QoS-Profile-Up-v6                   232     string
+
+ATTRIBUTE      ZTE-Rate-Ctrl-SCR-Down-v6               233     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Down-v6             234     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-Burst-Max-Down-v6         235     integer
+ATTRIBUTE      ZTE-Rate-Ctrl-PBS-Down-v6               236     integer
+ATTRIBUTE      ZTE-QoS-Profile-Down-v6                 237     string
+
+END-VENDOR      ZTE
index 3e8b738..b3c1025 100644 (file)
@@ -3,6 +3,7 @@ autoconf.h
 autoconf.sed
 
 # Dynamically generated headers
+attributes.h
 missing.h
 tls.h
 features.h
index a2c4ec5..1ce73e0 100644 (file)
@@ -2,11 +2,36 @@
 # Version:     $Id$
 #
 
-HEADERS        = conf.h conffile.h detail.h event.h features.h hash.h heap.h \
-       libradius.h md4.h md5.h missing.h modcall.h modules.h \
-       packet.h rad_assert.h radius.h radiusd.h radpaths.h \
-       radutmp.h realms.h sha1.h stats.h sysutmp.h token.h \
-       udpfromto.h base64.h map.h
+HEADERS        = \
+       attributes.h \
+       build.h \
+       conf.h \
+       conffile.h \
+       detail.h \
+       event.h \
+       features.h \
+       hash.h \
+       heap.h \
+       libradius.h \
+       md4.h \
+       md5.h \
+       missing.h \
+       modcall.h \
+       modules.h \
+       packet.h \
+       rad_assert.h \
+       radius.h \
+       radiusd.h \
+       radpaths.h \
+       radutmp.h \
+       realms.h \
+       sha1.h \
+       stats.h \
+       sysutmp.h \
+       token.h \
+       udpfromto.h \
+       base64.h \
+       map.h
 
 #
 #  Build dynamic headers by substituting various values from autoconf.h, these
@@ -15,7 +40,7 @@ HEADERS       = conf.h conffile.h detail.h event.h features.h hash.h heap.h \
 #
 
 HEADERS_DY = src/include/features.h src/include/missing.h src/include/tls.h \
-       src/include/radpaths.h
+       src/include/radpaths.h src/include/attributes.h
 
 src/include/autoconf.sed: src/include/autoconf.h
        @grep ^#define $< | sed 's,/\*\*/,1,;' | awk '{print "\
@@ -24,6 +49,12 @@ src/include/autoconf.sed: src/include/autoconf.h
        s,defined(" $$2 ")," $$3 ",g;\
        s," $$2 ","$$3 ",g;"}' > $@
 
+src/include/radius.h: | src/include/attributes.h
+
+src/include/attributes.h: share/dictionary.freeradius.internal
+       @$(ECHO) HEADER $@
+       @grep ^ATTRIBUTE $<  | awk '{print "PW_"$$2 " " $$3}' | tr 'a-z' 'A-Z' | tr -- - _ | sed 's/^/#define /' > $@
+
 src/freeradius-devel/features.h: src/include/features.h src/freeradius-devel
 
 src/include/features.h: src/include/features-h src/include/autoconf.h
index 7eb40d9..667efaa 100644 (file)
@@ -3,6 +3,10 @@
 /* Define if building universal (internal helper macro) */
 #undef AC_APPLE_UNIVERSAL_BUILD
 
+/* Define if your processor stores words with the most significant byte first
+   */
+#undef BIG_ENDIAN
+
 /* BSD-Style get*byaddr_r */
 #undef BSDSTYLE
 
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #undef HAVE_ARPA_INET_H
 
+/* Define if the compiler supports __builtin_choose_expr */
+#undef HAVE_BUILTIN_CHOOSE_EXPR
+
+/* Define if the compiler supports __builtin_types_compatible_p */
+#undef HAVE_BUILTIN_TYPES_COMPATIBLE_P
+
 /* Define to 1 if you have the `closefrom' function. */
 #undef HAVE_CLOSEFROM
 
+/* Define to 1 if you have the `collectdclient' library (-lcollectdclient). */
+#undef HAVE_COLLECTDC_H
+
 /* Do we have the crypt function */
 #undef HAVE_CRYPT
 
 /* Define to 1 if you have the <errno.h> header file. */
 #undef HAVE_ERRNO_H
 
+/* define this if we have <execinfo.h> and symbols */
+#undef HAVE_EXECINFO
+
 /* Define to 1 if you have the `fcntl' function. */
 #undef HAVE_FCNTL
 
 /* Define to 1 if you have the <fcntl.h> header file. */
 #undef HAVE_FCNTL_H
 
+/* Define to 1 if you have the <features.h> header file. */
+#undef HAVE_FEATURES_H
+
 /* Define to 1 if you have the <fnmatch.h> header file. */
 #undef HAVE_FNMATCH_H
 
 /* Define to 1 if you have the `ws2_32' library (-lws2_32). */
 #undef HAVE_LIBWS2_32
 
+/* Define to 1 if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+
 /* Define to 1 if you have the `localtime_r' function. */
 #undef HAVE_LOCALTIME_R
 
 /* Define to 1 if you have the <malloc.h> header file. */
 #undef HAVE_MALLOC_H
 
+/* Define to 1 if you have the `mallopt' function. */
+#undef HAVE_MALLOPT
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
 /* Define to 1 if you have the <openssl/ssl.h> header file. */
 #undef HAVE_OPENSSL_SSL_H
 
-/* Define to 1 if you have the function pcap_dump_fopen. */
+/* Define to 1 if you have the `pcap_activate' function. */
+#undef HAVE_PCAP_ACTIVATE
+
+/* Define to 1 if you have the `pcap_create' function. */
+#undef HAVE_PCAP_CREATE
+
+/* Define to 1 if you have the `pcap_dump_fopen' function. */
 #undef HAVE_PCAP_DUMP_FOPEN
 
-/* Define to 1 if you have the function pcap_fopen_offline. */
+/* Define to 1 if you have the `pcap_fopen_offline' function. */
 #undef HAVE_PCAP_FOPEN_OFFLINE
 
 /* Define to 1 if you have the <pcap.h> header file. */
 /* Define to 1 if you have the <sys/wait.h> header file. */
 #undef HAVE_SYS_WAIT_H
 
-/* Define if the compiler supports __thread */
-#undef HAVE_THREAD_TLS
+/* Define to 1 if you have the function talloc_set_memlimit. */
+#undef HAVE_TALLOC_SET_MEMLIMIT
+
+/* 128 bit unsigned integer */
+#undef HAVE_UINT128_T
 
 /* Define to 1 if you have the <unistd.h> header file. */
 #undef HAVE_UNISTD_H
 /* Define to 1 if you have the <winsock.h> header file. */
 #undef HAVE_WINSOCK_H
 
+/* compiler specific 128 bit unsigned integer */
+#undef HAVE___UINT128_T
+
+/* Define if your processor stores words with the least significant byte first
+   */
+#undef LITTLE_ENDIAN
+
 /* define if you have OSFC2 authentication */
 #undef OSFC2
 
 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 #undef TIME_WITH_SYS_TIME
 
+/* Define if the compiler supports a thread local storage class */
+#undef TLS_STORAGE_CLASS
+
 /* Enable extensions on AIX 3, Interix.  */
 #ifndef _ALL_SOURCE
 # undef _ALL_SOURCE
 /* Define to `int' if <sys/types.h> does not define. */
 #undef pid_t
 
+/* signal action callback function */
+#undef sig_t
+
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 #undef size_t
 
 /* uint32_t should be the canonical 'network integer' */
 #undef uint32_t
 
+/* uint64_t is required for larger counters */
+#undef uint64_t
+
 /* uint8_t should be the canonical 'octet' for network traffic */
 #undef uint8_t
 
index 332d05a..e12d457 100644 (file)
@@ -31,8 +31,8 @@ RCSIDH(base64_h, "$Id$")
 
 int fr_isbase64(char c);
 
-size_t fr_base64_encode(uint8_t const *in, size_t inlen, char *out, size_t outlen);
+size_t fr_base64_encode(char *out, size_t outlen, uint8_t const *in, size_t inlen);
 
-ssize_t fr_base64_decode(char const *in, size_t inlen, uint8_t *out, size_t outlen);
+ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inlen);
 
 #endif /* _FR_BASE64_H */
index f59d03d..506a21b 100644 (file)
@@ -20,6 +20,7 @@ sbindir=@sbindir@
 mandir=@mandir@
 logdir=@logdir@
 raddbdir=@raddbdir@
+dictdir=@dictdir@
 radacctdir=@radacctdir@
 datarootdir=@datarootdir@
 
@@ -31,5 +32,6 @@ cat <<EOF > radpaths.h
 #define RUNDIR         "@localstatedir@/run"
 #define SBINDIR                "@sbindir@"
 #define RADIR          "@radacctdir@"
+#define DICTDIR                "@dictdir@"
 EOF
 
index 403965d..66c3087 100644 (file)
@@ -5,38 +5,71 @@
  *
  * @copyright 2013 The FreeRADIUS server project
  */
+#ifndef _BUILD_H
+#define _BUILD_H
 #ifdef __cplusplus
 extern "C" {
 #endif
+#include <freeradius-devel/autoconf.h> /* Needed for endian macros */
 
 /*
  *     The ubiquitous stringify macros
  */
 #define XSTRINGIFY(x) #x
 #define STRINGIFY(x) XSTRINGIFY(x)
+#define JOINSTR(x,y) XSTRINGIFY(x ## y)
 
 /*
- *     Macros for controlling warnings in GCC >= 4.2 and clang >= 2.8
+ *     HEX concatenation macros
+ */
+#ifndef HEXIFY
+#  define XHEXIFY4(b1,b2,b3,b4)        (0x ## b1 ## b2 ## b3 ## b4)
+#  define HEXIFY4(b1,b2,b3,b4) XHEXIFY4(b1, b2, b3, b4)
+
+#  define XHEXIFY3(b1,b2,b3)   (0x ## b1 ## b2 ## b3)
+#  define HEXIFY3(b1,b2,b3)    XHEXIFY3(b1, b2, b3)
+
+#  define XHEXIFY2(b1,b2)      (0x ## b1 ## b2)
+#  define HEXIFY2(b1,b2)       XHEXIFY2(b1, b2)
+
+#  define XHEXIFY(b1)          (0x ## b1)
+#  define HEXIFY(b1)           XHEXIFY(b1)
+#endif
+
+/*
+ *     Only use GCC __attribute__ if were building with a GCClike
+ *     compiler.
+ */
+#ifdef __GNUC__
+#  define CC_HINT(_x) __attribute__ ((_x))
+#else
+#  define CC_HINT(_x)
+#endif
+
+/*
+ *     Macros to add pragmas
  */
-#define DIAG_JOINSTR(x,y) XSTRINGIFY(x ## y)
-#define DIAG_DO_PRAGMA(x) _Pragma (#x)
+#define PRAGMA(_x) _Pragma(#_x)
 
+/*
+ *     Macros for controlling warnings in GCC >= 4.2 and clang >= 2.8
+ */
 #if defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
-#  define DIAG_PRAGMA(x) DIAG_DO_PRAGMA(GCC diagnostic x)
+#  define DIAG_PRAGMA(_x) PRAGMA(GCC diagnostic _x)
 #  if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
-#    define DIAG_OFF(x) DIAG_PRAGMA(push) DIAG_PRAGMA(ignored DIAG_JOINSTR(-W,x))
-#    define DIAG_ON(x) DIAG_PRAGMA(pop)
+#    define DIAG_OFF(_x) DIAG_PRAGMA(push) DIAG_PRAGMA(ignored JOINSTR(-W,_x))
+#    define DIAG_ON(_x) DIAG_PRAGMA(pop)
 #  else
-#    define DIAG_OFF(x) DIAG_PRAGMA(ignored DIAG_JOINSTR(-W,x))
-#    define DIAG_ON(x)  DIAG_PRAGMA(warning DIAG_JOINSTR(-W,x))
+#    define DIAG_OFF(_x) DIAG_PRAGMA(ignored JOINSTR(-W,_x))
+#    define DIAG_ON(_x)  DIAG_PRAGMA(warning JOINSTR(-W,_x))
 #  endif
 #elif defined(__clang__) && ((__clang_major__ * 100) + __clang_minor__ >= 208)
-#  define DIAG_PRAGMA(x) DIAG_DO_PRAGMA(clang diagnostic x)
-#  define DIAG_OFF(x) DIAG_PRAGMA(push) DIAG_PRAGMA(ignored DIAG_JOINSTR(-W,x))
-#  define DIAG_ON(x) DIAG_PRAGMA(pop)
+#  define DIAG_PRAGMA(_x) PRAGMA(clang diagnostic _x)
+#  define DIAG_OFF(_x) DIAG_PRAGMA(push) DIAG_PRAGMA(ignored JOINSTR(-W,_x))
+#  define DIAG_ON(_x) DIAG_PRAGMA(pop)
 #else
-#  define DIAG_OFF(x)
-#  define DIAG_ON(x)
+#  define DIAG_OFF(_x)
+#  define DIAG_ON(_x)
 #endif
 
 /*
@@ -52,18 +85,39 @@ extern "C" {
 
 #if defined(__GNUC__)
 /* force inclusion of ident keywords in the face of optimization */
-#define RCSID(id) static char const rcsid[] __attribute__ ((used)) = id;
-#define RCSIDH(h, id) static char const rcsid_ ## h [] __attribute__ ((used)) = id;
+#  define RCSID(id) static char const rcsid[] __attribute__ ((used)) = id;
+#  define RCSIDH(h, id) static char const rcsid_ ## h [] __attribute__ ((used)) = id;
 #elif defined(__SUNPRO_C)
 /* put ident keyword into comment section (nicer than gcc way) */
-#define DO_PRAGMA(x) _Pragma(#x)
-#define RCSID(id) DO_PRAGMA(sun ident id)
-#define RCSIDH(h, id) DO_PRAGMA(sun ident id)
+#  define RCSID(id) PRAGMA(sun ident id)
+#  define RCSIDH(h, id) PRAGMA(sun ident id)
 #else
-#define RCSID(id)
-#define RCSIDH(h, id)
+#  define RCSID(id)
+#  define RCSIDH(h, id)
+#endif
+
+/*
+ *     Try and determine endianness of the target system.
+ *
+ *     Other projects seem to use endian.h and variants, but these are
+ *     in non standard locations, and may mess up cross compiling.
+ *
+ *     Here at least the endianess can be set explicitly with
+ *     -DLITTLE_ENDIAN or -DBIG_ENDIAN.
+ */
+#if !defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN)
+#  if defined(__LITTLE_ENDIAN__) || \
+      (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
+#    define LITTLE_ENDIAN 1
+#  elif defined(__BIG_ENDIAN__) || \
+      (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
+#    define BIG_ENDIAN 1
+#  else
+#    error Failed determining endianness of system
+#  endif
 #endif
 
 #ifdef __cplusplus
 }
 #endif
+#endif /* _BUILD_H */
index f16ba26..b8cbe87 100644 (file)
@@ -12,6 +12,7 @@ RCSIDH(conffile_h, "$Id$")
 
 #include <stddef.h>
 #include <freeradius-devel/token.h>
+#include <sys/time.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -25,36 +26,132 @@ typedef struct conf_pair CONF_PAIR;
 typedef struct conf_part CONF_SECTION;
 typedef struct conf_data CONF_DATA;
 
+
+typedef void conf_type_mismatch;
+typedef void conf_type_invalid;
+
+#if defined(HAVE_BUILTIN_CHOOSE_EXPR) && defined(HAVE_BUILTIN_TYPES_COMPATIBLE_P)
+/*
+ * Dumb hack for GCC which explodes with lots of errors masking the real
+ * error cause, if we don't use typdefs for these structures.
+ */
+typedef struct timeval _timeval_t;
+
+/*
+ * Validation macro to check the type of the pointer or offset passed in
+ * matches the type of the configuration item.
+ */
+#  define FR_CONF_TYPE_CHECK(_t, _ct, _p) \
+       __builtin_choose_expr((_t == PW_TYPE_STRING),\
+               __builtin_choose_expr(__builtin_types_compatible_p(char const **, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_BOOLEAN),\
+               __builtin_choose_expr(__builtin_types_compatible_p(bool *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_SUBSECTION),\
+               NULL,\
+       __builtin_choose_expr((_t == PW_TYPE_INTEGER),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint32_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IPV4_ADDR),\
+               __builtin_choose_expr(__builtin_types_compatible_p(fr_ipaddr_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_DATE),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint32_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_ABINARY),\
+               __builtin_choose_expr(__builtin_types_compatible_p(size_t[32/sizeof(size_t)], _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_OCTETS),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint8_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IFID),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint8_t[8], _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IPV6_ADDR),\
+               __builtin_choose_expr(__builtin_types_compatible_p(fr_ipaddr_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IPV6_PREFIX),\
+               __builtin_choose_expr(__builtin_types_compatible_p(fr_ipaddr_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_BYTE),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint8_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_SHORT),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint16_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_ETHERNET),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint8_t[6], _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_SIGNED),\
+               __builtin_choose_expr(__builtin_types_compatible_p(int32_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IP_ADDR),\
+               __builtin_choose_expr(__builtin_types_compatible_p(fr_ipaddr_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_INTEGER64),\
+               __builtin_choose_expr(__builtin_types_compatible_p(uint64_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IPV4_PREFIX),\
+               __builtin_choose_expr(__builtin_types_compatible_p(fr_ipaddr_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_TIMEVAL),\
+               __builtin_choose_expr(__builtin_types_compatible_p(_timeval_t *, _ct), _p, (conf_type_mismatch) 0),\
+       __builtin_choose_expr((_t == PW_TYPE_IP_PREFIX),\
+               __builtin_choose_expr(__builtin_types_compatible_p(fr_ipaddr_t *, _ct), _p, (conf_type_mismatch) 0),\
+               (conf_type_invalid) 0\
+       ))))))))))))))))))))
+
+#  define FR_CONF_OFFSET(_t, _s, _f)   _t, FR_CONF_TYPE_CHECK(((_t) & 0xff), __typeof__(&(((_s *)NULL)->_f)), offsetof(_s, _f)), NULL
+#  define FR_CONF_POINTER(_t, _p)      _t, 0, FR_CONF_TYPE_CHECK(((_t) & 0xff), __typeof__(_p), _p)
+#  define FR_ITEM_POINTER(_t, _p)      _t, FR_CONF_TYPE_CHECK(((_t) & 0xff), __typeof__(_p), _p)
+#else
+#  define FR_CONF_OFFSET(_t, _s, _f)   _t, offsetof(_s, _f), NULL
+#  define FR_CONF_POINTER(_t, _p)      _t, 0, _p
+#  define FR_ITEM_POINTER(_t, _p)      _t, _p
+#endif
+
 /*
  *  Instead of putting the information into a configuration structure,
  *  the configuration file routines MAY just parse it directly into
  *  user-supplied variables.
  */
-#define PW_TYPE_STRING_PTR     100
-#define PW_TYPE_BOOLEAN                101
 #define PW_TYPE_SUBSECTION     102
-#define PW_TYPE_FILE_INPUT     103
-#define PW_TYPE_FILE_OUTPUT    104
-#define PW_TYPE_DEPRECATED     (1 << 10)
-#define PW_TYPE_REQUIRED       (1 << 11)
-#define PW_TYPE_ATTRIBUTE      (1 << 12)
+
+/*
+ * Configuration type flags, these modify the processing of config
+ * items.
+ */
+#define PW_TYPE_DEPRECATED     (1 << 10)       //!< CONF_PAIR is deprecated, the server will refuse to start
+                                               //!< if it finds a CONFIG_ITEM with this flag.
+#define PW_TYPE_REQUIRED       (1 << 11)       //!< CONF_PAIR is required, server will not start without this
+                                               //!< config item.
+#define PW_TYPE_ATTRIBUTE      (1 << 12)       //!< CONF_PAIR value must exist in the dictionary as an attribute.
+#define PW_TYPE_SECRET         (1 << 13)       //!< don't print it when debug_flag==2.
+
+/*
+ * File i/o types have a base type of string, so they're validate
+ * correctly by the config parser.
+ */
+#define PW_TYPE_FILE_INPUT     ((1 << 14) | PW_TYPE_STRING)
+#define PW_TYPE_FILE_OUTPUT    ((1 << 15) | PW_TYPE_STRING)
+
+#define FR_INTEGER_COND_CHECK(_name, _var, _cond, _new)\
+do {\
+       if (!(_cond)) {\
+               WARN("WARNING: Ignoring \"" _name " = %i\", forcing to \"" _name " = %i\"", _var, _new);\
+               _var = _new;\
+       }\
+} while (0)
+
+#define FR_INTEGER_BOUND_CHECK(_name, _var, _op, _bound) FR_INTEGER_COND_CHECK(_name, _var, (_var _op _bound), _bound)
+
+#define FR_TIMEVAL_BOUND_CHECK(_name, _var, _op, _bound_sec, _bound_usec)\
+do {\
+       struct timeval _bound = {_bound_sec, _bound_usec};\
+       if (!timercmp(_var, &_bound, _op)) {\
+               WARN("WARNING: Ignoring \"" _name " = %d.%.06d\", forcing to \"" _name " = %d.%06d\"",\
+                       (int)(_var)->tv_sec, (int)(_var)->tv_usec,\
+                       (int)_bound.tv_sec, (int)_bound.tv_usec);\
+               *_var = _bound;\
+       }\
+} while (0)
 
 typedef struct CONF_PARSER {
-  char const *name;
-  int type;                    /* PW_TYPE_STRING, etc. */
-  size_t offset;               /* relative pointer within "base" */
-  void *data;                  /* absolute pointer if base is NULL */
-  const void *dflt;            /* default as it would appear in radiusd.conf */
+       char const      *name;
+       int             type;                   //!< PW_TYPE_STRING, etc.
+       size_t          offset;                 //!< Relative pointer within "base".
+       void            *data;                  //!< Absolute pointer if base is NULL.
+       const void      *dflt;                  //!< Default as it would appear in radiusd.conf.
 } CONF_PARSER;
 
-CONF_SECTION   *cf_section_alloc(CONF_SECTION *parent, char const *name1,
-                                 char const *name2);
-int            cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp,
-                               char const *value);
-int            cf_item_parse(CONF_SECTION *cs, char const *name,
-                             int type, void *data, char const *dflt);
-int            cf_section_parse(CONF_SECTION *, void *base,
-                                CONF_PARSER const *variables);
+CONF_SECTION   *cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2);
+int            cf_pair_replace(CONF_SECTION *cs, CONF_PAIR *cp, char const *value);
+int            cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char const *dflt);
+int            cf_section_parse(CONF_SECTION *, void *base, CONF_PARSER const *variables);
 const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs);
 CONF_SECTION   *cf_file_read(char const *file);
 void           cf_file_free(CONF_SECTION *cs);
@@ -80,6 +177,7 @@ FR_TOKEN cf_pair_value_type(CONF_PAIR const *pair);
 VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair);
 char const *cf_section_name1(CONF_SECTION const *);
 char const *cf_section_name2(CONF_SECTION const *);
+FR_TOKEN cf_section_name2_type(CONF_SECTION const *cs);
 int dump_config(CONF_SECTION const *cs);
 CONF_SECTION *cf_subsection_find_next(CONF_SECTION const *section,
                                      CONF_SECTION const *subsection,
@@ -99,48 +197,19 @@ CONF_PAIR *cf_itemtopair(CONF_ITEM const *item);
 CONF_SECTION *cf_itemtosection(CONF_ITEM const *item);
 CONF_ITEM *cf_pairtoitem(CONF_PAIR const *cp);
 CONF_ITEM *cf_sectiontoitem(CONF_SECTION const *cs);
-int cf_section_template(CONF_SECTION *cs, CONF_SECTION *template);
-void cf_log_err(CONF_ITEM const *ci, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
-void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
-void cf_log_err_cp(CONF_PAIR const *cp, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
 
-void cf_log_info(CONF_SECTION const *cs, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
-void cf_log_module(CONF_SECTION const *cs, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
+void cf_log_err(CONF_ITEM const *ci, char const *fmt, ...)             CC_HINT(format (printf, 2, 3));
+void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt, ...)       CC_HINT(format (printf, 2, 3));
+void cf_log_err_cp(CONF_PAIR const *cp, char const *fmt, ...)          CC_HINT(format (printf, 2, 3));
+void cf_log_info(CONF_SECTION const *cs, char const *fmt, ...)         CC_HINT(format (printf, 2, 3));
+void cf_log_module(CONF_SECTION const *cs, char const *fmt, ...)       CC_HINT(format (printf, 2, 3));
+
 CONF_ITEM *cf_reference_item(CONF_SECTION const *parentcs,
                             CONF_SECTION *outercs,
                             char const *ptr);
 
-extern int cf_pair2xml(FILE *fp, CONF_PAIR const *cp);
-extern int cf_section2xml(FILE *fp, CONF_SECTION const *cs);
-extern int cf_pair2file(FILE *fp, CONF_PAIR const *cp);
-extern int cf_section2file(FILE *fp, CONF_SECTION const *cs);
 extern CONF_SECTION *root_config;
 
-/*
- *     Big magic.
- */
-int cf_section_migrate(CONF_SECTION *dst, CONF_SECTION *src);
-
 #ifdef __cplusplus
 }
 #endif
index 3606ad4..5e22116 100644 (file)
@@ -76,14 +76,13 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *cs,
                                              fr_connection_create_t c,
                                              fr_connection_alive_t a,
                                              fr_connection_delete_t d,
-                                             char *prefix);
+                                             char const *prefix);
 void fr_connection_pool_delete(fr_connection_pool_t *pool);
 
-int fr_connection_check(fr_connection_pool_t *pool, void *conn);
 void *fr_connection_get(fr_connection_pool_t *pool);
+int fr_connection_get_num(fr_connection_pool_t *pool);
 void fr_connection_release(fr_connection_pool_t *pool, void *conn);
 void *fr_connection_reconnect(fr_connection_pool_t *pool, void *conn);
-int fr_connection_add(fr_connection_pool_t *pool, void *conn);
 int fr_connection_del(fr_connection_pool_t *pool, void *conn);
 
 #ifdef __cplusplus
index 1699ea0..920a09c 100644 (file)
@@ -24,22 +24,42 @@ typedef enum detail_state_t {
   STATE_REPLIED
 } detail_state_t;
 
+/*
+ *     Allow people to revert to the old behavior if desired.
+ *     Also, use the old code if we don't have threads.
+ *     FIXME: delete the old (crappy) code, and enable the new
+ *     code to work without threads.  One thing at a time...
+ */
+#ifndef WITHOUT_DETAIL_THREAD
+#ifdef HAVE_PTHREAD_H
+#define WITH_DETAIL_THREAD (1)
+#endif
+#endif
+
 typedef struct listen_detail_t {
        fr_event_t      *ev;    /* has to be first entry (ugh) */
        int             delay_time;
-       char            *filename;
-       char            *filename_work;
+       char const      *filename;
+       char const      *filename_work;
        VALUE_PAIR      *vps;
+       int             work_fd;
+#ifdef WITH_DETAIL_THREAD
+       int             master_pipe[2];
+       int             child_pipe[2];
+       pthread_t       pthread_id;
+#endif
        FILE            *fp;
        off_t           offset;
        detail_state_t  state;
        time_t          timestamp;
        time_t          running;
        fr_ipaddr_t     client_ip;
-       int             load_factor; /* 1..100 */
+
+       uint32_t        load_factor; /* 1..100 */
+       uint32_t        poll_interval;
+       uint32_t        retry_interval;
+
        int             signal;
-       int             poll_interval;
-       int             retry_interval;
        int             packets;
        int             tries;
        bool            one_shot;
index f360475..654f135 100644 (file)
@@ -39,6 +39,8 @@ int fr_dhcp_send(RADIUS_PACKET *packet);
 
 int fr_dhcp_add_arp_entry(int fd, char const *interface, VALUE_PAIR *hwvp, VALUE_PAIR *clvp);
 
+int8_t fr_dhcp_attr_cmp(void const *a, void const *b);
+ssize_t fr_dhcp_encode_option(uint8_t *out, size_t outlen, TALLOC_CTX *ctx, vp_cursor_t *cursor);
 int fr_dhcp_encode(RADIUS_PACKET *packet);
 ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
                               uint8_t const *data, size_t len, VALUE_PAIR **head);
index 9e567f2..a29c956 100644 (file)
@@ -37,15 +37,15 @@ typedef     void (*fr_event_callback_t)(void *);
 typedef        void (*fr_event_status_t)(struct timeval *);
 typedef void (*fr_event_fd_handler_t)(fr_event_list_t *el, int sock, void *ctx);
 
-fr_event_list_t *fr_event_list_create(fr_event_status_t status);
-void fr_event_list_free(fr_event_list_t *el);
+fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status);
 
+int fr_event_list_num_fds(fr_event_list_t *el);
 int fr_event_list_num_elements(fr_event_list_t *el);
 
 int fr_event_insert(fr_event_list_t *el,
                    fr_event_callback_t callback,
-                   void *ctx, struct timeval *when, fr_event_t **ev_p);
-int fr_event_delete(fr_event_list_t *el, fr_event_t **ev_p);
+                   void *ctx, struct timeval *when, fr_event_t **parent);
+int fr_event_delete(fr_event_list_t *el, fr_event_t **parent);
 
 int fr_event_run(fr_event_list_t *el, struct timeval *when);
 
@@ -56,6 +56,7 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
 int fr_event_fd_delete(fr_event_list_t *el, int type, int fd);
 int fr_event_loop(fr_event_list_t *el);
 void fr_event_loop_exit(fr_event_list_t *el, int code);
+bool fr_event_loop_exiting(fr_event_list_t *el);
 
 #ifdef __cplusplus
 }
index aab0aec..f24a52d 100644 (file)
@@ -6,60 +6,62 @@
  *     into individual modules.
  */
 #ifndef WITHOUT_PROXY
-#define WITH_PROXY (1)
+#  define WITH_PROXY (1)
+#else
+#  define WITHOUT_COA (1)
 #endif
 
 #ifndef WITHOUT_UNLANG
-#define WITH_UNLANG (1)
+#  define WITH_UNLANG (1)
 #endif
 
 #ifndef WITHOUT_ACCOUNTING
-#define WITH_ACCOUNTING (1)
+#  define WITH_ACCOUNTING (1)
 #endif
 
 #ifdef WITH_ACCOUNTING
-#ifndef WITHOUT_DETAIL
-#define WITH_DETAIL (1)
-#endif
+#  ifndef WITHOUT_DETAIL
+#    define WITH_DETAIL (1)
+#  endif
 #endif
 
 #ifdef WITH_ACCOUNTING
-#ifndef WITHOUT_SESSION_MGMT
-#define WITH_SESSION_MGMT (1)
-#endif
+#  ifndef WITHOUT_SESSION_MGMT
+#    define WITH_SESSION_MGMT (1)
+#  endif
 #endif
 
 #ifndef WITHOUT_DYNAMIC_CLIENTS
-#define WITH_DYNAMIC_CLIENTS (1)
+#  define WITH_DYNAMIC_CLIENTS (1)
 #endif
 
 #ifndef WITHOUT_STATS
-#define WITH_STATS
+#  define WITH_STATS
 #endif
 
 #ifndef WITHOUT_COMMAND_SOCKET
-#ifdef HAVE_SYS_UN_H
-#define WITH_COMMAND_SOCKET (1)
-#endif
+#  ifdef HAVE_SYS_UN_H
+#    define WITH_COMMAND_SOCKET (1)
+#  endif
 #endif
 
 #ifndef WITHOUT_COA
-#define WITH_COA (1)
-#ifndef WITH_PROXY
-#error WITH_COA requires WITH_PROXY
-#endif
+#  define WITH_COA (1)
+#  ifndef WITH_PROXY
+#    error WITH_COA requires WITH_PROXY
+#  endif
 #endif
 
 #ifdef WITHOUT_TLS
-#ifndef HAVE_OPENSSL_SSL_H
-#error TLS requires OpenSSL
-#endif
+#  ifndef HAVE_OPENSSL_SSL_H
+#    error TLS requires OpenSSL
+#  endif
 #else
-#ifdef HAVE_OPENSSL_SSL_H
-#ifndef WITH_TLS
-#ifndef NO_OPENSSL
-#define WITH_TLS (1)
-#endif
-#endif
-#endif
+#  ifdef HAVE_OPENSSL_SSL_H
+#    ifndef WITH_TLS
+#      ifndef NO_OPENSSL
+#        define WITH_TLS (1)
+#      endif
+#    endif
+#  endif
 #endif
index a48509b..54259d7 100644 (file)
@@ -38,13 +38,6 @@ uint32_t fr_hash(void const *, size_t);
 uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash);
 uint32_t fr_hash_string(char const *p);
 
-/*
- *     If you need fewer than 32-bits of hash, use this macro to get
- *     the number of bits in the hash you need.  The upper bits of the
- *     hash will be set to zero.
- */
-uint32_t fr_hash_fold(uint32_t hash, int bits);
-
 typedef struct fr_hash_table_t fr_hash_table_t;
 typedef void (*fr_hash_table_free_t)(void *);
 typedef uint32_t (*fr_hash_table_hash_t)(void const *);
index ed920c6..97ac64b 100644 (file)
  * @file libradius.h
  * @brief Structures and prototypes for the radius library.
  *
- * @copyright 1999-2008 The FreeRADIUS server project
+ * @copyright 1999-2014 The FreeRADIUS server project
  */
 RCSIDH(libradius_h, "$Id$")
 
-#include <freeradius-devel/missing.h>
-
-#include <talloc.h>
+/*
+ *  Compiler hinting macros.  Included here for 3rd party consumers
+ *  of libradius.h.
+ */
+#include <freeradius-devel/build.h>
 
 /*
  *  Let any external program building against the library know what
@@ -35,10 +37,45 @@ RCSIDH(libradius_h, "$Id$")
  */
 #include <freeradius-devel/features.h>
 
+#ifdef WITHOUT_VERSION_CHECK
+#  define RADIUSD_MAGIC_NUMBER ((uint64_t) (0xf4ee4ad3f4ee4ad3))
+#  define MAGIC_PREFIX(_x)     ((uint8_t) 0x00)
+#  define MAGIC_VERSION(_x)    ((uint32_t) 0x00000000)
+#  define MAGIC_COMMIT(_x)     ((uint32_t) 0x00000000)
+#else
+#  ifdef RADIUSD_VERSION_COMMIT
+#    define RADIUSD_MAGIC_NUMBER ((uint64_t) HEXIFY4(f4, RADIUSD_VERSION, RADIUSD_VERSION_COMMIT, 0))
+#  else
+#    define RADIUSD_MAGIC_NUMBER ((uint64_t) HEXIFY3(f4, RADIUSD_VERSION, 00000000))
+#  endif
+#  define MAGIC_PREFIX(_x)     ((uint8_t) (_x >> 56))
+#  define MAGIC_VERSION(_x)    ((uint32_t) ((_x >> 32) & 0x00ffffff))
+#  define MAGIC_COMMIT(_x)     ((uint32_t) (_x & 0xffffffff))
+#endif
+
+/*
+ *  Talloc memory allocation is used in preference to malloc throughout
+ *  the libraries and server.
+ */
+#include <talloc.h>
+
+/*
+ *  Defines signatures for any missing functions.
+ */
+#include <freeradius-devel/missing.h>
+
+/*
+ *  Include system headers.
+ */
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <signal.h>
+
+#ifdef HAVE_LIMITS_H
+#  include <limits.h>
+#endif
 
 #include <freeradius-devel/threads.h>
 #include <freeradius-devel/radius.h>
@@ -46,9 +83,9 @@ RCSIDH(libradius_h, "$Id$")
 #include <freeradius-devel/hash.h>
 
 #ifdef SIZEOF_UNSIGNED_INT
-#if SIZEOF_UNSIGNED_INT != 4
-#error FATAL: sizeof(unsigned int) != 4
-#endif
+#  if SIZEOF_UNSIGNED_INT != 4
+#    error FATAL: sizeof(unsigned int) != 4
+#  endif
 #endif
 
 /*
@@ -62,24 +99,25 @@ extern "C" {
 #endif
 
 #if defined(WITH_VERIFY_PTR)
-/*
- *     Requires typeof(), which is in most modern C compilers.
- */
+#  define FREE_MAGIC (0xF4EEF4EE)
 
 /*
-#define VERIFY_VP(_x) do { (void) talloc_get_type_abort(_x, VALUE_PAIR); \
-                       if (_x->da) { \
-                               (void) talloc_get_type_abort(_x->da, DICT_ATTR); \
-                       } \
-                     } while (0)
-*/
-#define FREE_MAGIC (0xF4EEF4EE)
-
-#define VERIFY_VP(_x) (void) talloc_get_type_abort(_x, VALUE_PAIR)
-#define VERIFY_PACKET(_x) (void) talloc_get_type_abort(_x, RADIUS_PACKET)
+ * @FIXME
+ *  Add if (_x->da) (void) talloc_get_type_abort(_x->da, DICT_ATTR);
+ *  to the macro below when dictionaries are talloced.
+ */
+#  define VERIFY_VP(_x)                fr_verify_vp(_x)
+#  define VERIFY_LIST(_x)      fr_verify_list(NULL, _x)
+#  define VERIFY_PACKET(_x)    (void) talloc_get_type_abort(_x, RADIUS_PACKET)
 #else
-#define VERIFY_VP(_x)
-#define VERIFY_PACKET(_x)
+/*
+ *  Even if were building without WITH_VERIFY_PTR
+ *  the pointer must not be NULL when these various macros are used
+ *  so we can add some sneaky soft asserts.
+ */
+#  define VERIFY_VP(_x)                fr_assert(_x)
+#  define VERIFY_LIST(_x)      fr_assert(_x)
+#  define VERIFY_PACKET(_x)    fr_assert(_x)
 #endif
 
 #define AUTH_VECTOR_LEN                16
@@ -101,22 +139,25 @@ extern "C" {
 
 #define TAG_VALID(x)           ((x) > 0 && (x) < 0x20)
 #define TAG_VALID_ZERO(x)      ((x) < 0x20)
-#define TAG_ANY                        -128    /* minimum signed char */
-#define TAG_UNUSED             0
+#define TAG_ANY                        INT8_MIN
+#define TAG_NONE               0
+/** Check if tags are equal
+ *
+ * @param _s tag were matching on.
+ * @param _a tag belonging to the attribute were checking.
+ */
+#define TAG_EQ(_s, _a) ((_s == _a) || (_s == TAG_ANY) || ((_s == TAG_NONE) && (_a == TAG_ANY)))
+
+#define NUM_ANY                        INT_MIN
+#define NUM_JOIN               (INT_MIN + 1)
+#define NUM_COUNT              (INT_MIN + 2)
 
 #define PAD(_x, _y)            (_y - ((_x) % _y))
 
-#if defined(__GNUC__)
-# define PRINTF_LIKE(n) __attribute__ ((format(printf, n, n+1)))
-# define NEVER_RETURNS __attribute__ ((noreturn))
-# define UNUSED __attribute__ ((unused))
-# define BLANK_FORMAT " "      /* GCC_LINT whines about empty formats */
-#else
-# define PRINTF_LIKE(n)        /* ignore */
-# define NEVER_RETURNS /* ignore */
-# define UNUSED /* ignore */
-# define BLANK_FORMAT ""
-#endif
+#define PRINTF_LIKE(n)         CC_HINT(format(printf, n, n+1))
+#define NEVER_RETURNS          CC_HINT(noreturn)
+#define UNUSED                 CC_HINT(unused)
+#define BLANK_FORMAT           " "     /* GCC_LINT whines about empty formats */
 
 typedef struct attr_flags {
        unsigned int    is_unknown : 1;                         //!< Attribute number or vendor is unknown.
@@ -186,7 +227,7 @@ typedef struct dict_vendor {
 
 /** Union containing all data types supported by the server
  *
- * This union contains all data types that can be represented with VALUE_PAIRs. It may also be used in other parts
+ * This union contains all data types that can be represented by VALUE_PAIRs. It may also be used in other parts
  * of the server where values of different types need to be stored.
  *
  * PW_TYPE should be an enumeration of the values in this union.
@@ -194,17 +235,26 @@ typedef struct dict_vendor {
 typedef union value_data {
        char const              *strvalue;                      //!< Pointer to UTF-8 string.
        uint8_t const           *octets;                        //!< Pointer to binary string.
+       uint32_t                integer;                        //!< 32bit unsigned integer.
        struct in_addr          ipaddr;                         //!< IPv4 Address.
-       struct in6_addr         ipv6addr;                       //!< IPv6 Address.
        uint32_t                date;                           //!< Date (32bit Unix timestamp).
-       uint32_t                integer;                        //!< 32bit unsigned integer.
+       size_t                  filter[32/sizeof(size_t)];      //!< Ascend binary format a packed data
+                                                               //!< structure.
+
+       uint8_t                 ifid[8];                        //!< IPv6 interface ID (should be struct?).
+       struct in6_addr         ipv6addr;                       //!< IPv6 Address.
+       uint8_t                 ipv6prefix[18];                 //!< IPv6 prefix (should be struct?).
+
+       uint8_t                 byte;                           //!< 8bit unsigned integer.
+       uint16_t                ushort;                         //!< 16bit unsigned integer.
+
+       uint8_t                 ether[6];                       //!< Ethernet (MAC) address.
+
        int32_t                 sinteger;                       //!< 32bit signed integer.
        uint64_t                integer64;                      //!< 64bit unsigned integer.
-       size_t                  filter[32/sizeof(size_t)];      //!< 64bit signed integer.
-       uint8_t                 ifid[8]; /* struct? */          //!< IPv6 interface ID.
-       uint8_t                 ipv6prefix[18]; /* struct? */   //!< IPv6 prefix.
-       uint8_t                 ipv4prefix[6]; /* struct? */    //!< IPv4 prefix.
-       uint8_t                 ether[6];                       //!< Ethernet (MAC) address.
+
+       uint8_t                 ipv4prefix[6];                  //!< IPv4 prefix (should be struct?).
+
        uint8_t                 *tlv;                           //!< Nested TLV (should go away).
        void const              *ptr;                           //!< generic pointer.
 } value_data_t;
@@ -226,8 +276,7 @@ typedef enum value_type {
 
 /** Stores an attribute, a value and various bits of other data
  *
- * VALUE_PAIRs are the main data structure used in the server, they specify an attribute, it's children and
- * it's siblings.
+ * VALUE_PAIRs are the main data structure used in the server
  *
  * They also specify what behaviour should be used when the attribute is merged into a new list/tree.
  */
@@ -268,7 +317,7 @@ typedef struct value_pair {
 typedef struct vp_cursor {
        VALUE_PAIR      **first;
        VALUE_PAIR      *found;                                 //!< pairfind marker.
-       VALUE_PAIR      *last;                                  //!< Temporary only used for pairinsert
+       VALUE_PAIR      *last;                                  //!< Temporary only used for fr_cursor_insert
        VALUE_PAIR      *current;                               //!< The current attribute.
        VALUE_PAIR      *next;                                  //!< Next attribute to process.
 } vp_cursor_t;
@@ -286,20 +335,22 @@ typedef struct value_pair_raw {
        FR_TOKEN op;                                            //!< Operator.
 } VALUE_PAIR_RAW;
 
-#define vp_strvalue   data.strvalue
-#define vp_octets     data.octets
-#define vp_ipv6addr   data.ipv6addr
-#define vp_ifid       data.ifid
-#define vp_ipv6prefix data.ipv6prefix
-#define vp_ipv4prefix data.ipv4prefix
-#define vp_filter     data.filter
-#define vp_ether      data.ether
-#define vp_signed     data.sinteger
-#define vp_tlv       data.tlv
-#define vp_integer64  data.integer64
-#define vp_ipaddr     data.ipaddr.s_addr
-#define vp_date       data.date
-#define vp_integer    data.integer
+#define vp_strvalue    data.strvalue
+#define vp_integer     data.integer
+#define vp_ipaddr      data.ipaddr.s_addr
+#define vp_date                data.date
+#define vp_filter      data.filter
+#define vp_octets      data.octets
+#define vp_ifid                data.ifid
+#define vp_ipv6addr    data.ipv6addr
+#define vp_ipv6prefix  data.ipv6prefix
+#define vp_byte                data.byte
+#define vp_short       data.ushort
+#define vp_ether       data.ether
+#define vp_signed      data.sinteger
+#define vp_integer64   data.integer64
+#define vp_ipv4prefix  data.ipv4prefix
+#define vp_tlv         data.tlv
 
 typedef struct fr_ipaddr_t {
        int             af;     /* address family */
@@ -307,6 +358,7 @@ typedef struct fr_ipaddr_t {
                struct in_addr  ip4addr;
                struct in6_addr ip6addr; /* maybe defined in missing.h */
        } ipaddr;
+       uint8_t         prefix;
        uint32_t        scope;  /* for IPv6 */
 } fr_ipaddr_t;
 
@@ -335,23 +387,50 @@ typedef struct radius_packet {
        ssize_t                 offset;
 #ifdef WITH_TCP
        size_t                  partial;
+       int                     proto;
 #endif
 } RADIUS_PACKET;
 
+typedef enum {
+       DECODE_FAIL_NONE = 0,
+       DECODE_FAIL_MIN_LENGTH_PACKET,
+       DECODE_FAIL_MIN_LENGTH_FIELD,
+       DECODE_FAIL_MIN_LENGTH_MISMATCH,
+       DECODE_FAIL_HEADER_OVERFLOW,
+       DECODE_FAIL_UNKNOWN_PACKET_CODE,
+       DECODE_FAIL_INVALID_ATTRIBUTE,
+       DECODE_FAIL_ATTRIBUTE_TOO_SHORT,
+       DECODE_FAIL_ATTRIBUTE_OVERFLOW,
+       DECODE_FAIL_MA_INVALID_LENGTH,
+       DECODE_FAIL_ATTRIBUTE_UNDERFLOW,
+       DECODE_FAIL_TOO_MANY_ATTRIBUTES,
+       DECODE_FAIL_MA_MISSING,
+       DECODE_FAIL_MAX
+} decode_fail_t;
+
+/*
+ *     Version check.
+ */
+int            fr_check_lib_magic(uint64_t magic);
+
 /*
  *     Printing functions.
  */
 int            fr_utf8_char(uint8_t const *str);
 size_t         fr_print_string(char const *in, size_t inlen,
                                 char *out, size_t outlen);
+size_t         fr_print_string_len(char const *in, size_t inlen);
+
+#define                is_truncated(_ret, _max) ((_ret) >= (_max))
+#define                truncate_len(_ret, _max) (((_ret) >= (_max)) ? ((_max) - 1) : _ret)
 size_t         vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t quote);
-char           *vp_aprinttype(TALLOC_CTX *ctx, PW_TYPE type);
-char           *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp);
 size_t         vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp);
-size_t         vp_print_name(char *out, size_t outlen, unsigned int attr, unsigned int vendor);
 size_t         vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp);
 void           vp_print(FILE *, VALUE_PAIR const *);
 void           vp_printlist(FILE *, VALUE_PAIR const *);
+char           *vp_aprint_type(TALLOC_CTX *ctx, PW_TYPE type);
+char           *vp_aprint_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp);
+char           *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp);
 #define                fprint_attr_val vp_print
 
 /*
@@ -362,7 +441,7 @@ int         str2argv(char *str, char **argv, int max_argc);
 int            dict_str2oid(char const *ptr, unsigned int *pattr,
                             unsigned int *pvendor, int tlv_depth);
 int            dict_addvendor(char const *name, unsigned int value);
-int            dict_addattr(char const *name, int attr, unsigned int vendor, int type, ATTR_FLAGS flags);
+int            dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type, ATTR_FLAGS flags);
 int            dict_addvalue(char const *namestr, char const *attrstr, int value);
 int            dict_init(char const *dir, char const *fn);
 void           dict_free(void);
@@ -373,6 +452,7 @@ DICT_ATTR const     *dict_attrunknown(unsigned int attr, unsigned int vendor, int vp
 DICT_ATTR const        *dict_attrunknownbyname(char const *attribute, int vp_free);
 DICT_ATTR const        *dict_attrbyvalue(unsigned int attr, unsigned int vendor);
 DICT_ATTR const        *dict_attrbyname(char const *attr);
+DICT_ATTR const *dict_attrbytagged_name(char const *name);
 DICT_ATTR const        *dict_attrbytype(unsigned int attr, unsigned int vendor,
                                 PW_TYPE type);
 DICT_ATTR const        *dict_attrbyparent(DICT_ATTR const *parent, unsigned int attr,
@@ -406,10 +486,9 @@ void fr_hmac_sha1(uint8_t const *text, size_t text_len, uint8_t const *key, size
 
 /* radius.c */
 int            rad_send(RADIUS_PACKET *, RADIUS_PACKET const *, char const *secret);
-int            rad_packet_ok(RADIUS_PACKET *packet, int flags);
+bool           rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason);
 RADIUS_PACKET  *rad_recv(int fd, int flags);
-ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
-                       int *code);
+ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code);
 void           rad_recv_discard(int sockfd);
 int            rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                           char const *secret);
@@ -422,6 +501,8 @@ int         rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
 int rad_digest_cmp(uint8_t const *a, uint8_t const *b, size_t length);
 RADIUS_PACKET  *rad_alloc(TALLOC_CTX *ctx, int newvector);
 RADIUS_PACKET  *rad_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *);
+RADIUS_PACKET *rad_copy_packet(TALLOC_CTX *ctx, RADIUS_PACKET const *in);
+
 void           rad_free(RADIUS_PACKET **);
 int            rad_pwencode(char *encpw, size_t *len, char const *secret,
                             uint8_t const *vector);
@@ -436,42 +517,49 @@ int               rad_tunnel_pwdecode(uint8_t *encpw, size_t *len,
 int            rad_chap_encode(RADIUS_PACKET *packet, uint8_t *output,
                                int id, VALUE_PAIR *password);
 
-int rad_attr_ok(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
-               DICT_ATTR *da,
-               uint8_t const *data, size_t length);
-int rad_tlv_ok(uint8_t const *data, size_t length,
-              size_t dv_type, size_t dv_length);
-
-ssize_t        rad_attr2vp(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
-                   char const *secret,
-                   uint8_t const *data, size_t length,
-                   VALUE_PAIR **pvp);
-
-ssize_t  rad_data2vp(unsigned int attribute, unsigned int vendor,
-                    uint8_t const *data, size_t length,
-                    VALUE_PAIR **pvp);
-
-ssize_t rad_vp2data(VALUE_PAIR const *vp, uint8_t *out, size_t outlen);
-
-int rad_vp2extended(RADIUS_PACKET const *packet,
-                   RADIUS_PACKET const *original,
-                   char const *secret, VALUE_PAIR const **pvp,
-                   uint8_t *ptr, size_t room);
-int rad_vp2wimax(RADIUS_PACKET const *packet,
-                RADIUS_PACKET const *original,
-                char const *secret, VALUE_PAIR const **pvp,
-                uint8_t *ptr, size_t room);
-int rad_vp2vsa(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
-              char const *secret, VALUE_PAIR const **pvp, uint8_t *start,
-              size_t room);
-int rad_vp2rfc(RADIUS_PACKET const *packet,
-              RADIUS_PACKET const *original,
-              char const *secret, VALUE_PAIR const **pvp,
-              uint8_t *ptr, size_t room);
-
-int rad_vp2attr(RADIUS_PACKET const *packet,
-               RADIUS_PACKET const *original, char const *secret,
-               VALUE_PAIR const **pvp, uint8_t *ptr, size_t room);
+int            rad_attr_ok(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
+                           DICT_ATTR *da, uint8_t const *data, size_t length);
+int            rad_tlv_ok(uint8_t const *data, size_t length,
+                          size_t dv_type, size_t dv_length);
+
+ssize_t                data2vp(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                       char const *secret,
+                       DICT_ATTR const *da, uint8_t const *start,
+                       size_t const attrlen, size_t const packetlen,
+                       VALUE_PAIR **pvp);
+
+ssize_t                rad_attr2vp(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
+                           char const *secret,
+                           uint8_t const *data, size_t length,
+                           VALUE_PAIR **pvp);
+
+ssize_t                rad_data2vp(unsigned int attribute, unsigned int vendor,
+                           uint8_t const *data, size_t length,
+                           VALUE_PAIR **pvp);
+
+ssize_t                rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp);
+
+int            rad_vp2extended(RADIUS_PACKET const *packet,
+                               RADIUS_PACKET const *original,
+                               char const *secret, VALUE_PAIR const **pvp,
+                               uint8_t *ptr, size_t room);
+int            rad_vp2wimax(RADIUS_PACKET const *packet,
+                            RADIUS_PACKET const *original,
+                            char const *secret, VALUE_PAIR const **pvp,
+                            uint8_t *ptr, size_t room);
+
+int            rad_vp2vsa(RADIUS_PACKET const *packet, RADIUS_PACKET const *original,
+                          char const *secret, VALUE_PAIR const **pvp, uint8_t *start,
+                          size_t room);
+
+int            rad_vp2rfc(RADIUS_PACKET const *packet,
+                          RADIUS_PACKET const *original,
+                          char const *secret, VALUE_PAIR const **pvp,
+                          uint8_t *ptr, size_t room);
+
+int            rad_vp2attr(RADIUS_PACKET const *packet,
+                           RADIUS_PACKET const *original, char const *secret,
+                           VALUE_PAIR const **pvp, uint8_t *ptr, size_t room);
 
 /* valuepair.c */
 VALUE_PAIR     *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da);
@@ -479,90 +567,101 @@ VALUE_PAIR       *paircreate(TALLOC_CTX *ctx, unsigned int attr, unsigned int vendor);
 int            pair2unknown(VALUE_PAIR *vp);
 void           pairfree(VALUE_PAIR **);
 VALUE_PAIR     *pairfind(VALUE_PAIR *, unsigned int attr, unsigned int vendor, int8_t tag);
-
-#define                paircursor(_x, _y)      paircursorc(_x,(VALUE_PAIR const * const *) _y)
-VALUE_PAIR     *paircursorc(vp_cursor_t *cursor, VALUE_PAIR const * const *node);
-VALUE_PAIR     *pairfirst(vp_cursor_t *cursor);
-VALUE_PAIR     *pairfindnext(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag);
-VALUE_PAIR     *pairnext(vp_cursor_t *cursor);
-VALUE_PAIR     *pairlast(vp_cursor_t *cursor);
-VALUE_PAIR     *paircurrent(vp_cursor_t *cursor);
-void           pairinsert(vp_cursor_t *cursor, VALUE_PAIR *vp);
-VALUE_PAIR     *pairremove(vp_cursor_t *cursor);
+VALUE_PAIR     *pairfind_da(VALUE_PAIR *, DICT_ATTR const *da, int8_t tag);
+
+#define                fr_cursor_init(_x, _y)  _fr_cursor_init(_x,(VALUE_PAIR const * const *) _y)
+VALUE_PAIR     *_fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR const * const *node);
+void           fr_cursor_copy(vp_cursor_t *out, vp_cursor_t *in);
+VALUE_PAIR     *fr_cursor_first(vp_cursor_t *cursor);
+VALUE_PAIR     *fr_cursor_last(vp_cursor_t *cursor);
+VALUE_PAIR     *fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag);
+
+VALUE_PAIR     *fr_cursor_next_by_da(vp_cursor_t *cursor, DICT_ATTR const *da, int8_t tag)
+               CC_HINT(nonnull);
+
+VALUE_PAIR     *fr_cursor_next(vp_cursor_t *cursor);
+VALUE_PAIR     *fr_cursor_current(vp_cursor_t *cursor);
+void           fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp);
+VALUE_PAIR     *fr_cursor_remove(vp_cursor_t *cursor);
+VALUE_PAIR     *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new);
 void           pairdelete(VALUE_PAIR **, unsigned int attr, unsigned int vendor, int8_t tag);
 void           pairadd(VALUE_PAIR **, VALUE_PAIR *);
 void           pairreplace(VALUE_PAIR **first, VALUE_PAIR *add);
-int            paircmp(VALUE_PAIR *check, VALUE_PAIR *data);
-int            paircmp_op(VALUE_PAIR const *one, FR_TOKEN op, VALUE_PAIR const *two);
-void           pairsort(VALUE_PAIR **vps, bool with_tag);
-bool           pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list);
+int8_t         paircmp_value(VALUE_PAIR const *a, VALUE_PAIR const *b);
+int8_t         paircmp_op(VALUE_PAIR const *a, FR_TOKEN op, VALUE_PAIR const *b);
+int8_t         paircmp(VALUE_PAIR *a, VALUE_PAIR *b);
+int8_t         pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b);
+
+typedef int8_t (*fr_cmp_t)(void const *a, void const *b);
+int8_t         attrcmp(void const *a, void const *b);
+int8_t         attrtagcmp(void const *a, void const *b);
+void           pairsort(VALUE_PAIR **vps, fr_cmp_t cmp);
+void           pairvalidate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2]);
+bool           pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list);
+bool           pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list);
 VALUE_PAIR     *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp);
 VALUE_PAIR     *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR const *vp);
 VALUE_PAIR     *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from);
 VALUE_PAIR     *paircopy2(TALLOC_CTX *ctx, VALUE_PAIR *from, unsigned int attr, unsigned int vendor, int8_t tag);
+VALUE_PAIR     *pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *from);
 void           pairmemcpy(VALUE_PAIR *vp, uint8_t const * src, size_t len);
-void           pairmemsteal(VALUE_PAIR *vp, uint8_t *src);
-void           pairstrsteal(VALUE_PAIR *vp, char *src);
+void           pairmemsteal(VALUE_PAIR *vp, uint8_t const *src);
+void           pairstrsteal(VALUE_PAIR *vp, char const *src);
 void           pairstrcpy(VALUE_PAIR *vp, char const * src);
-void           pairsprintf(VALUE_PAIR *vp, char const * fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
+void           pairstrncpy(VALUE_PAIR *vp, char const * src, size_t len);
+void           pairsprintf(VALUE_PAIR *vp, char const * fmt, ...) CC_HINT(format (printf, 2, 3));
 void           pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from);
 void           pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from,
-                                          unsigned int attr, unsigned int vendor, int8_t tag);
-bool           pairparsevalue(VALUE_PAIR *vp, char const *value);
+                          unsigned int attr, unsigned int vendor, int8_t tag);
+VALUE_PAIR     *pairmake_ip(TALLOC_CTX *ctx, char const *value,
+                            DICT_ATTR *ipv4, DICT_ATTR *ipv6, DICT_ATTR *ipv4_prefix, DICT_ATTR *ipv6_prefix);
+int            pairparsevalue(VALUE_PAIR *vp, char const *value, size_t len);
 VALUE_PAIR     *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps, char const *attribute, char const *value, FR_TOKEN op);
 int            pairmark_xlat(VALUE_PAIR *vp, char const *value);
 FR_TOKEN       pairread(char const **ptr, VALUE_PAIR_RAW *raw);
 FR_TOKEN       userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **head);
-VALUE_PAIR     *readvp2(TALLOC_CTX *ctx, FILE *fp, int *pfiledone, char const *errprefix);
+int            readvp2(VALUE_PAIR **out, TALLOC_CTX *ctx, FILE *fp, bool *pfiledone);
 
 /*
  *     Error functions.
  */
-#ifdef _LIBRADIUS
-void           fr_strerror_printf(char const *, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 1, 2)))
-#endif
-;
-#endif
-void           fr_perror(char const *, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 1, 2)))
-#endif
-;
+void           fr_strerror_printf(char const *, ...) CC_HINT(format (printf, 1, 2));
+void           fr_perror(char const *, ...) CC_HINT(format (printf, 1, 2));
+
 extern bool fr_assert_cond(char const *file, int line, char const *expr, bool cond);
 #define fr_assert(_x) fr_assert_cond(__FILE__,  __LINE__, #_x, (_x))
 
-extern void _fr_exit(char const *file, int line, int status);
+extern void NEVER_RETURNS _fr_exit(char const *file, int line, int status);
 #define fr_exit(_x) _fr_exit(__FILE__,  __LINE__, (_x))
 
-extern void _fr_exit_now(char const *file, int line, int status);
+extern void NEVER_RETURNS _fr_exit_now(char const *file, int line, int status);
 #define fr_exit_now(_x) _fr_exit_now(__FILE__,  __LINE__, (_x))
 
 extern char const *fr_strerror(void);
+extern char const *fr_syserror(int num);
 extern bool    fr_dns_lookups; /* do IP -> hostname lookups? */
 extern bool    fr_hostname_lookups; /* do hostname -> IP lookups? */
 extern int     fr_debug_flag;  /* 0 = no debugging information */
-extern int     fr_max_attributes; /* per incoming packet */
+extern uint32_t        fr_max_attributes; /* per incoming packet */
 #define        FR_MAX_PACKET_CODE (52)
 extern char const *fr_packet_codes[FR_MAX_PACKET_CODE];
+#define is_radius_code(_x) ((_x > 0) && (_x < FR_MAX_PACKET_CODE))
 extern FILE    *fr_log_fp;
 extern void rad_print_hex(RADIUS_PACKET *packet);
-void           fr_printf_log(char const *, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 1, 2)))
-#endif
-;
+void           fr_printf_log(char const *, ...) CC_HINT(format (printf, 1, 2));
 
 /*
  *     Several handy miscellaneous functions.
  */
-void           fr_debug_break(void);
+int            fr_set_signal(int sig, sig_t func);
+TALLOC_CTX     *fr_autofree_ctx(void);
+char const     *fr_inet_ntop(int af, void const *src);
 char const     *ip_ntoa(char *, uint32_t);
+int            fr_pton4(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve, bool fallback);
+int            fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve, bool fallback);
+int            fr_pton(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve);
+bool           is_wildcard(fr_ipaddr_t *addr);
+int            fr_ntop(char *out, size_t outlen, fr_ipaddr_t *addr);
 char           *ifid_ntoa(char *buffer, size_t size, uint8_t const *ifid);
 uint8_t                *ifid_aton(char const *ifid_str, uint8_t *ifid);
 int            rad_lockfd(int fd, int lock_len);
@@ -571,30 +670,36 @@ int               rad_unlockfd(int fd, int lock_len);
 size_t         fr_bin2hex(char *hex, uint8_t const *bin, size_t inlen);
 size_t         fr_hex2bin(uint8_t *bin, char const *hex, size_t outlen);
 uint32_t       fr_strtoul(char const *value, char **end);
-bool           fr_whitespace_check(char const *value);
+bool           is_whitespace(char const *value);
+bool           is_integer(char const *value);
+bool           is_zero(char const *value);
 
 int            fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b);
 
-int            ip_ptonx(char const *src, fr_ipaddr_t *dst);
-int            ip_hton(char const *src, int af, fr_ipaddr_t *dst);
+int            ip_hton(fr_ipaddr_t *out, int af, char const *hostname, bool fallback);
 char const     *ip_ntoh(fr_ipaddr_t const *src, char *dst, size_t cnt);
-int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, int port,
-                      struct sockaddr_storage *sa, socklen_t *salen);
-int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
-                      fr_ipaddr_t *ipaddr, int * port);
+struct in_addr fr_inaddr_mask(struct in_addr const *ipaddr, uint8_t prefix);
+struct in6_addr        fr_in6addr_mask(struct in6_addr const *ipaddr, uint8_t prefix);
+void           fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix);
+int            fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, uint16_t port,
+                                  struct sockaddr_storage *sa, socklen_t *salen);
+int            fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
+                                  fr_ipaddr_t *ipaddr, uint16_t *port);
 ssize_t                fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inlen);
+size_t         fr_prints_uint128(char *out, size_t outlen, uint128_t const num);
 int64_t                fr_pow(int32_t base, uint8_t exp);
 int            fr_get_time(char const *date_str, time_t *date);
-
+int8_t         fr_pointer_cmp(void const *a, void const *b);
+void           fr_quick_sort(void const *to_sort[], int min_idx, int max_idx, fr_cmp_t cmp);
 /*
  *     Define TALLOC_DEBUG to check overflows with talloc.
  *     we can't use valgrind, because the memory used by
  *     talloc is valid memory... just not for us.
  */
 #ifdef TALLOC_DEBUG
-void fr_talloc_verify_cb(const void *ptr, int depth,
-                        int max_depth, int is_ref,
-                        void *private_data);
+void           fr_talloc_verify_cb(const void *ptr, int depth,
+                                   int max_depth, int is_ref,
+                                   void *private_data);
 #define VERIFY_ALL_TALLOC talloc_report_depth_cb(NULL, 0, -1, fr_talloc_verify_cb, NULL)
 #else
 #define VERIFY_ALL_TALLOC
@@ -602,8 +707,8 @@ void fr_talloc_verify_cb(const void *ptr, int depth,
 
 #ifdef WITH_ASCEND_BINARY
 /* filters.c */
-int            ascend_parse_filter(VALUE_PAIR *pair);
-void           print_abinary(VALUE_PAIR const *vp, char *buffer, size_t len, int8_t quote);
+int            ascend_parse_filter(VALUE_PAIR *vp, char const *value, size_t len);
+void           print_abinary(char *out, size_t outlen, VALUE_PAIR const *vp,  int8_t quote);
 #endif /*WITH_ASCEND_BINARY*/
 
 /* random numbers in isaac.c */
@@ -611,44 +716,93 @@ void              print_abinary(VALUE_PAIR const *vp, char *buffer, size_t len, int8_t quote
 typedef struct fr_randctx {
        uint32_t randcnt;
        uint32_t randrsl[256];
-       uint32_t randmem[256];
+       uint32_t randmem[256];
        uint32_t randa;
        uint32_t randb;
        uint32_t randc;
 } fr_randctx;
 
-void fr_isaac(fr_randctx *ctx);
-void fr_randinit(fr_randctx *ctx, int flag);
-uint32_t fr_rand(void);        /* like rand(), but better. */
-void fr_rand_seed(void const *, size_t ); /* seed the random pool */
+void           fr_isaac(fr_randctx *ctx);
+void           fr_randinit(fr_randctx *ctx, int flag);
+uint32_t       fr_rand(void);  /* like rand(), but better. */
+void           fr_rand_seed(void const *, size_t ); /* seed the random pool */
 
 
 /* crypt wrapper from crypt.c */
-int fr_crypt_check(char const *key, char const *salt);
+int            fr_crypt_check(char const *key, char const *salt);
+
+/* cbuff.c */
+typedef struct fr_cbuff fr_cbuff_t;
+
+fr_cbuff_t     *fr_cbuff_alloc(TALLOC_CTX *ctx, uint32_t size, bool lock);
+void           fr_cbuff_rp_insert(fr_cbuff_t *cbuff, void *obj);
+void           *fr_cbuff_rp_next(fr_cbuff_t *cbuff, TALLOC_CTX *ctx);
+
+/* debug.c */
+
+/** Optional callback passed to fr_fault_setup
+ *
+ * Allows optional logic to be run before calling the main fault handler.
+ *
+ * If the callback returns < 0, the main fault handler will not be called.
+ *
+ * @param signum signal raised.
+ * @return 0 on success < 0 on failure.
+ */
+typedef int (*fr_fault_cb_t)(int signum);
+typedef struct fr_bt_marker fr_bt_marker_t;
+
+void           fr_debug_break(void);
+void           backtrace_print(fr_cbuff_t *cbuff, void *obj);
+fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj);
+
+typedef void (*fr_fault_log_t)(char const *msg, ...) CC_HINT(format (printf, 1, 2));
+
+void           fr_panic_on_free(TALLOC_CTX *ctx);
+int            fr_set_dumpable_init(void);
+int            fr_set_dumpable(bool allow_core_dumps);
+int            fr_log_talloc_report(TALLOC_CTX *ctx);
+void           fr_fault(int sig);
+int            fr_fault_setup(char const *cmd, char const *program);
+void           fr_fault_set_cb(fr_fault_cb_t func);
+void           fr_fault_set_log_fn(fr_fault_log_t func);
+void           fr_fault_set_log_fd(int fd);
+
+#ifdef WITH_VERIFY_PTR
+void           fr_verify_vp(VALUE_PAIR const *vp);
+void           fr_verify_list(TALLOC_CTX *expected, VALUE_PAIR *vps);
+#endif
 
 /* rbtree.c */
 typedef struct rbtree_t rbtree_t;
 typedef struct rbnode_t rbnode_t;
 
+/* callback order for walking  */
+typedef enum {
+       RBTREE_PRE_ORDER,
+       RBTREE_IN_ORDER,
+       RBTREE_POST_ORDER,
+       RBTREE_DELETE_ORDER
+} rb_order_t;
+
 #define RBTREE_FLAG_NONE    (0)
 #define RBTREE_FLAG_REPLACE (1 << 0)
 #define RBTREE_FLAG_LOCK    (1 << 1)
-rbtree_t       *rbtree_create(int (*Compare)(void const *, void const *),
-                             void (*freeNode)(void *),
-                             int flags);
+
+typedef int (*rb_comparator_t)(void const *ctx, void const *data);
+typedef int (*rb_walker_t)(void *ctx, void *data);
+typedef void (*rb_free_t)(void *data);
+
+rbtree_t       *rbtree_create(rb_comparator_t compare, rb_free_t node_free, int flags);
 void           rbtree_free(rbtree_t *tree);
-bool           rbtree_insert(rbtree_t *tree, void *Data);
-rbnode_t       *rbtree_insertnode(rbtree_t *tree, void *Data);
-void           rbtree_delete(rbtree_t *tree, rbnode_t *Z);
+bool           rbtree_insert(rbtree_t *tree, void *data);
+rbnode_t       *rbtree_insert_node(rbtree_t *tree, void *data);
+void           rbtree_delete(rbtree_t *tree, rbnode_t *z);
 bool           rbtree_deletebydata(rbtree_t *tree, void const *data);
-rbnode_t       *rbtree_find(rbtree_t *tree, void const *Data);
-void          *rbtree_finddata(rbtree_t *tree, void const *Data);
-int            rbtree_num_elements(rbtree_t *tree);
-void          *rbtree_min(rbtree_t *tree);
-void          *rbtree_node2data(rbtree_t *tree, rbnode_t *node);
-
-/* callback order for walking  */
-typedef enum { PreOrder, InOrder, PostOrder, DeleteOrder } RBTREE_ORDER;
+rbnode_t       *rbtree_find(rbtree_t *tree, void const *data);
+void           *rbtree_finddata(rbtree_t *tree, void const *data);
+uint32_t       rbtree_num_elements(rbtree_t *tree);
+void           *rbtree_node2data(rbtree_t *tree, rbnode_t *node);
 
 /*
  *     The callback should be declared as:
@@ -656,49 +810,29 @@ typedef enum { PreOrder, InOrder, PostOrder, DeleteOrder } RBTREE_ORDER;
  *
  *     The "context" is some user-defined context.
  *     The "data" is the pointer to the user data in the node,
- *       NOT the node itself.
+ *     NOT the node itself.
  *
  *     It should return 0 if all is OK, and !0 for any error.
  *     The walking will stop on any error.
  *
- *     Except with DeleteOrder, where the callback should return <0 for
+ *     Except with RBTREE_DELETE_ORDER, where the callback should return <0 for
  *     errors, and may return 1 to delete the current node and halt,
  *     or 2 to delete the current node and continue.  This may be
  *     used to batch-delete select nodes from a locked rbtree.
  */
-int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order, int (*callback)(void *, void *), void *context);
-
-/*
- *     Find a matching data item in an rbtree and, if one is found,
- *     perform a callback on it.
- *
- *     The callback is similar to rbtree_walk above, except that a
- *     positive return code from the callback will cause the found node
- *     to be deleted from the tree.  If the tree was created with
- *     RBTREE_FLAG_LOCK, then the entire find/callback/delete/rebalance
- *     sequence happens while the lock is held.
- *
- *     Note that the callback MUST NOT alter any of the data which
- *     is used as the rbtree key, nor attempt to alter the rest of
- *     the rbtree in any way.
- *
- *     Returns a pointer to the user data in the found node, or NULL if the
- *     item was not found, or NULL if the item was deleted and the tree was
- *     created with a freeNode garbage collection routine.
- */
-void *rbtree_callbydata(rbtree_t *tree, void const *Data, int (*callback)(void *, void *), void *context);
+int            rbtree_walk(rbtree_t *tree, rb_order_t order, rb_walker_t compare, void *context);
 
 /*
  *     FIFOs
  */
-typedef struct fr_fifo_t fr_fifo_t;
+typedef struct fr_fifo_t fr_fifo_t;
 typedef void (*fr_fifo_free_t)(void *);
-fr_fifo_t *fr_fifo_create(int max_entries, fr_fifo_free_t freeNode);
-void fr_fifo_free(fr_fifo_t *fi);
-int fr_fifo_push(fr_fifo_t *fi, void *data);
-void *fr_fifo_pop(fr_fifo_t *fi);
-void *fr_fifo_peek(fr_fifo_t *fi);
-int fr_fifo_num_elements(fr_fifo_t *fi);
+fr_fifo_t      *fr_fifo_create(int max_entries, fr_fifo_free_t freeNode);
+void           fr_fifo_free(fr_fifo_t *fi);
+int            fr_fifo_push(fr_fifo_t *fi, void *data);
+void           *fr_fifo_pop(fr_fifo_t *fi);
+void           *fr_fifo_peek(fr_fifo_t *fi);
+int            fr_fifo_num_elements(fr_fifo_t *fi);
 
 #ifdef __cplusplus
 }
@@ -707,7 +841,7 @@ int fr_fifo_num_elements(fr_fifo_t *fi);
 #include <freeradius-devel/packet.h>
 
 #ifdef WITH_TCP
-#include <freeradius-devel/tcp.h>
+#  include <freeradius-devel/tcp.h>
 #endif
 
 #endif /*LIBRADIUS_H*/
index 0a02b5b..1b3929f 100644 (file)
@@ -40,8 +40,8 @@ typedef enum log_type {
        L_DBG = 16,             //!< Only displayed when debugging is enabled.
        L_DBG_WARN = 17,        //!< Warning only displayed when debugging is enabled.
        L_DBG_ERR = 18,         //!< Error only displayed when debugging is enabled.
-       L_DBG_WARN2 = 19,       //!< Less severe warning only displayed when debugging is enabled.
-       L_DBG_ERR2 = 20         //!< Less severe error only displayed when debugging is enabled.
+       L_DBG_WARN_REQ = 19,    //!< Less severe warning only displayed when debugging is enabled.
+       L_DBG_ERR_REQ = 20      //!< Less severe error only displayed when debugging is enabled.
 } log_type_t;
 
 typedef enum log_debug {
@@ -66,33 +66,55 @@ typedef struct fr_log_t {
        int             colourise;      //!< Prefix log messages with VT100 escape codes to change text
                                        //!< colour.
        int             fd;             //!< File descriptor to write messages to.
-       log_dst_t       dest;           //!< Log destination.
-       char            *file;          //!< Path to log file.
-       char            *debug_file;    //!< Path to debug log file.
+       log_dst_t       dst;            //!< Log destination.
+       char const      *file;          //!< Path to log file.
+       char const      *debug_file;    //!< Path to debug log file.
 } fr_log_t;
 
-typedef                void (*radlog_func_t)(log_type_t lvl, log_debug_t priority, REQUEST *, char const *, ...);
+typedef                void (*radlog_func_t)(log_type_t lvl, log_debug_t priority, REQUEST *, char const *, va_list ap);
 
 extern FR_NAME_NUMBER const syslog_str2fac[];
 extern FR_NAME_NUMBER const log_str2dst[];
 extern fr_log_t default_log;
 
-int            vradlog(log_type_t lvl, char const *fmt, va_list ap);
-int            radlog(log_type_t lvl, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
-void           vp_listdebug(VALUE_PAIR *vp);
-bool           radlog_debug_enabled(log_type_t type, log_debug_t lvl, REQUEST *request);
-void           radlog_request(log_type_t lvl, log_debug_t priority, REQUEST *request, char const *msg, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 4, 5)))
-#endif
-;
+int    radlog_init(fr_log_t *log, bool daemonize);
+
+void   vp_listdebug(VALUE_PAIR *vp);
+
+int    vradlog(log_type_t lvl, char const *fmt, va_list ap)
+       CC_HINT(format (printf, 2, 0)) CC_HINT(nonnull);
+int    radlog(log_type_t lvl, char const *fmt, ...)
+       CC_HINT(format (printf, 2, 3)) CC_HINT(nonnull (2));
+
+bool   debug_enabled(log_type_t type, log_debug_t lvl);
+
+bool   rate_limit_enabled(void);
+
+bool   radlog_debug_enabled(log_type_t type, log_debug_t lvl, REQUEST *request)
+       CC_HINT(nonnull);
 
-void log_talloc(char const *message);
-void log_talloc_report(TALLOC_CTX *ctx);
+void   vradlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, va_list ap)
+       CC_HINT(format (printf, 4, 0)) CC_HINT(nonnull (3, 4));
+
+void   radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, ...)
+       CC_HINT(format (printf, 4, 5)) CC_HINT(nonnull (3, 4));
+
+void   radlog_request_error(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, ...)
+       CC_HINT(format (printf, 4, 5)) CC_HINT(nonnull (3, 4));
+
+void   radlog_request_marker(log_type_t type, log_debug_t lvl, REQUEST *request,
+                             char const *fmt, size_t indent, char const *error)
+       CC_HINT(nonnull);
+
+/*
+ *     Multiple threads logging to one or more files.
+ */
+typedef struct fr_logfile_t fr_logfile_t;
+
+fr_logfile_t *fr_logfile_init(TALLOC_CTX *ctx);
+int fr_logfile_open(fr_logfile_t *lf, char const *filename, mode_t permissions);
+int fr_logfile_close(fr_logfile_t *lf, int fd);
+int fr_logfile_unlock(fr_logfile_t *lf, int fd);
 
 /*
  *     Logging macros.
@@ -115,10 +137,10 @@ void log_talloc_report(TALLOC_CTX *ctx);
  */
 #define _SL(_l, _p, _f, ...)   if (debug_flag >= _p) radlog(_l, _f, ## __VA_ARGS__)
 
-#define DEBUG_ENABLED          radlog_debug_enabled(L_DBG, L_DBG_LVL_1, NULL)
-#define DEBUG_ENABLED2         radlog_debug_enabled(L_DBG, L_DBG_LVL_2, NULL)
-#define DEBUG_ENABLED3         radlog_debug_enabled(L_DBG, L_DBG_LVL_3, NULL)
-#define DEBUG_ENABLED4         radlog_debug_enabled(L_DBG, L_DBG_LVL_MAX, NULL)
+#define DEBUG_ENABLED          debug_enabled(L_DBG, L_DBG_LVL_1)
+#define DEBUG_ENABLED2         debug_enabled(L_DBG, L_DBG_LVL_2)
+#define DEBUG_ENABLED3         debug_enabled(L_DBG, L_DBG_LVL_3)
+#define DEBUG_ENABLED4         debug_enabled(L_DBG, L_DBG_LVL_MAX)
 
 #define AUTH(fmt, ...)         _SL(L_AUTH, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
 #define ACCT(fmt, ...)         _SL(L_ACCT, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
@@ -130,56 +152,80 @@ void log_talloc_report(TALLOC_CTX *ctx);
 #define DEBUG4(fmt, ...)       _SL(L_DBG, L_DBG_LVL_MAX, fmt, ## __VA_ARGS__)
 
 #define INFO(fmt, ...)         _SL(L_INFO, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define DEBUGI(fmt, ...)       _SL(L_INFO, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-
 #define WARN(fmt, ...)         _SL(L_WARN, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define WDEBUG(fmt, ...)       _SL(L_DBG_WARN, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-#define WDEBUG2(fmt, ...)      _SL(L_DBG_WARN, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
-
 #define ERROR(fmt, ...)                _SL(L_ERR, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define EDEBUG(fmt, ...)       _SL(L_DBG_ERR, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-#define EDEBUG2(fmt, ...)      _SL(L_DBG_ERR, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
 
 /*
  *     Log request driven messages which including elements from the current request, like section and module
  *
  *     If a REQUEST * is available, these functions should be used.
  */
-#define _RL(_l, _p, _f, ...)   if (request && request->radlog) request->radlog(_l, _p, request, _f, ## __VA_ARGS__)
-#define _RM(_l, _p, _f, ...)   do { \
-                                       if(request) { \
-                                               module_failure_msg(request, _f, ## __VA_ARGS__); \
-                                               _RL(_l, _p, _f, ## __VA_ARGS__); \
-                                       } \
-                               } while(0)
+#define _RLOG(_l, _p, _f, ...)         radlog_request(_l, _p, request, _f, ## __VA_ARGS__)
+#define _RMOD(_l, _p, _f, ...)         radlog_request_error(_l, _p, request, _f, ## __VA_ARGS__)
+#define _RMKR(_l, _p, _m, _i, _e)      radlog_request_marker(_l, _p, request, _m, _i, _e)
 
 #define RDEBUG_ENABLED         radlog_debug_enabled(L_DBG, L_DBG_LVL_1, request)
 #define RDEBUG_ENABLED2                radlog_debug_enabled(L_DBG, L_DBG_LVL_2, request)
 #define RDEBUG_ENABLED3                radlog_debug_enabled(L_DBG, L_DBG_LVL_3, request)
 #define RDEBUG_ENABLED4                radlog_debug_enabled(L_DBG, L_DBG_LVL_MAX, request)
 
-#define RAUTH(fmt, ...)                _RL(L_AUTH, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define RACCT(fmt, ...)                _RL(L_ACCT, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define RPROXY(fmt, ...)       _RL(L_PROXY, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+#define RAUTH(fmt, ...)                _RLOG(L_AUTH, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+#define RACCT(fmt, ...)                _RLOG(L_ACCT, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+#define RPROXY(fmt, ...)       _RLOG(L_PROXY, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+
+#define RDEBUGX(_l, fmt, ...)  _RLOG(L_DBG, _l, fmt, ## __VA_ARGS__)
+#define RDEBUG(fmt, ...)       _RLOG(L_DBG, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      _RLOG(L_DBG, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
+#define RDEBUG3(fmt, ...)      _RLOG(L_DBG, L_DBG_LVL_3, fmt, ## __VA_ARGS__)
+#define RDEBUG4(fmt, ...)      _RLOG(L_DBG, L_DBG_LVL_MAX, fmt, ## __VA_ARGS__)
+
+#define RINFO(fmt, ...)                _RLOG(L_INFO, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+#define RIDEBUG(fmt, ...)      _RLOG(L_INFO, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
+#define RIDEBUG2(fmt, ...)     _RLOG(L_INFO, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
+
+#define RWARN(fmt, ...)                _RLOG(L_DBG_WARN, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+#define RWDEBUG(fmt, ...)      _RLOG(L_DBG_WARN, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
+#define RWDEBUG2(fmt, ...)     _RLOG(L_DBG_WARN, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
 
-#define RDEBUG(fmt, ...)       _RL(L_DBG, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-#define RDEBUG2(fmt, ...)      _RL(L_DBG, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
-#define RDEBUG3(fmt, ...)      _RL(L_DBG, L_DBG_LVL_3, fmt, ## __VA_ARGS__)
-#define RDEBUG4(fmt, ...)      _RL(L_DBG, L_DBG_LVL_MAX, fmt, ## __VA_ARGS__)
+#define RERROR(fmt, ...)       _RMOD(L_DBG_ERR, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
+#define REDEBUG(fmt, ...)      _RMOD(L_DBG_ERR, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
+#define REDEBUG2(fmt, ...)     _RMOD(L_DBG_ERR, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
+#define REDEBUG3(fmt, ...)     _RMOD(L_DBG_ERR, L_DBG_LVL_3, fmt, ## __VA_ARGS__)
+#define REDEBUG4(fmt, ...)     _RMOD(L_DBG_ERR, L_DBG_LVL_MAX, fmt, ## __VA_ARGS__)
 
-#define RINFO(fmt, ...)                _RL(L_INFO, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define RIDEBUG(fmt, ...)      _RL(L_INFO, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-#define RIDEBUG2(fmt, ...)     _RL(L_INFO, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
+#define RINDENT()              (request->log.indent++)
+#define REXDENT()              (request->log.indent--)
 
-#define RWARN(fmt, ...)                _RL(L_DBG_WARN, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define RWDEBUG(fmt, ...)      _RL(L_DBG_WARN, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-#define RWDEBUG2(fmt, ...)     _RL(L_DBG_WARN, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
+/*
+ * Output string with error marker, showing where format error occurred.
+ *
+ @verbatim
+   my pet kitty
+      ^ kitties are not pets, are nature devouring hell beasts
+ @endverbatim
+ *
+ * @param _m string to mark e.g. "my pet kitty".
+ * @param _i index e.g. 3 (starts from 0).
+ * @param _e error e.g. "kitties are not pets, are nature devouring hell beasts".
+ */
+#define REMARKER(_m, _i, _e)   _RMKR(L_DBG_ERR, L_DBG_LVL_1, _m, _i, _e)
+#define RDMARKER(_m, _i, _e)   _RMKR(L_DBG, L_DBG_LVL_1, _m, _i, _e)
 
-#define RERROR(fmt, ...)       _RM(L_DBG_ERR, L_DBG_LVL_OFF, fmt, ## __VA_ARGS__)
-#define REDEBUG(fmt, ...)      _RM(L_DBG_ERR, L_DBG_LVL_1, fmt, ## __VA_ARGS__)
-#define REDEBUG2(fmt, ...)     _RM(L_DBG_ERR, L_DBG_LVL_2, fmt, ## __VA_ARGS__)
-#define REDEBUG3(fmt, ...)     _RM(L_DBG_ERR, L_DBG_LVL_3, fmt, ## __VA_ARGS__)
-#define REDEBUG4(fmt, ...)     _RM(L_DBG_ERR, L_DBG_LVL_MAX, fmt, ## __VA_ARGS__)
+/*
+ *     Rate limit messages.
+ */
+#define RATE_LIMIT_ENABLED rate_limit_enabled()
+#define RATE_LIMIT(_x) \
+do {\
+       if (RATE_LIMIT_ENABLED) {\
+               static time_t _last_complained = 0;\
+               time_t _now = time(NULL);\
+               if (_now != _last_complained) {\
+                       _last_complained = _now;\
+                       _x;\
+               }\
+       } else _x;\
+} while (0)
 
 #ifdef __cplusplus
 }
index bd2f199..37d873f 100644 (file)
@@ -28,6 +28,27 @@ RCSIDH(map_h, "$Id$")
 
 #include <freeradius-devel/conffile.h>
 
+#ifdef HAVE_PCREPOSIX_H
+#  include <pcreposix.h>
+#else
+#  ifdef HAVE_REGEX_H
+#    include <regex.h>
+
+/*
+ *  For POSIX Regular expressions.
+ *  (0) Means no extended regular expressions.
+ *  REG_EXTENDED means use extended regular expressions.
+ */
+#    ifndef REG_EXTENDED
+#      define REG_EXTENDED (0)
+#    endif
+
+#    ifndef REG_NOSUB
+#      define REG_NOSUB (0)
+#    endif
+#  endif
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -78,15 +99,36 @@ typedef enum vpt_type {
        VPT_TYPE_LIST,                  //!< Is a list.
        VPT_TYPE_REGEX,                 //!< Is a regex.
        VPT_TYPE_EXEC,                  //!< Needs to be executed.
-       VPT_TYPE_DATA                   //!< is a value_data_t
+       VPT_TYPE_DATA,                  //!< is a value_data_t
+       VPT_TYPE_XLAT_STRUCT,           //!< pre-parsed xlat_exp_t
+       VPT_TYPE_REGEX_STRUCT,          //!< pre-parsed regex_t
+       VPT_TYPE_NULL                   //!< VPT has no value
 } vpt_type_t;
 
 extern const FR_NAME_NUMBER vpt_types[];
 
+typedef struct xlat_exp xlat_exp_t;
+
 /** A pre-parsed template attribute
  *
- *  Value pair template, used when processing various mappings sections
- *  to create a real valuepair later.
+ * Is used as both the RHS and LHS of a map (both update, and conditional types)
+ *
+ * When used on the LHS it describes an attribute to create and should be one of these types:
+ * - VPT_TYPE_ATTR
+ * - VPT_TYPE_LIST
+ *
+ * When used on the RHS it describes the value to assign to the attribute being created and
+ * should be one of these types:
+ * - VPT_TYPE_LITERAL
+ * - VPT_TYPE_XLAT
+ * - VPT_TYPE_ATTR
+ * - VPT_TYPE_LIST
+ * - VPT_TYPE_EXEC
+ * - VPT_TYPE_DATA
+ * - VPT_TYPE_XLAT_STRUCT (pre-parsed xlat)
+ *
+ * When used as part of a condition it may be any of the RHS side types, as well as:
+ * - VPT_TYPE_REGEX_STRUCT (pre-parsed regex)
  *
  * @see value_pair_map_t
  */
@@ -97,14 +139,48 @@ typedef struct value_pair_tmpl_t {
                                         //!< attribute, just the string id for
                                         //!< the attribute.
 
-       request_refs_t          request; //!< Request to search or insert in.
-       pair_lists_t            list;    //!< List to search or insert in.
-
-       DICT_ATTR const         *da;     //!< Resolved dictionary attribute.
-       value_data_t const      *vpd;    //!< actual data
-       size_t                  length;  //!< of the vpd data
+       /*
+        * @todo This should be moved into the union, but some code currently
+        * uses value_pair_tmpl_t's to describe both the value and the attribute.
+        * This is wrong, and the code that does this should be converted to use
+        * maps.
+        */
+       struct {
+               request_refs_t          request; //!< Request to search or insert in.
+               pair_lists_t            list;    //!< List to search or insert in.
+
+               DICT_ATTR const         *da;     //!< Resolved dictionary attribute.
+               int                     num;     //!< for array references
+               int8_t                  tag;     //!< for tag references
+       } attribute;
+
+       union {
+               struct {
+                       value_data_t const      *value;         //!< actual data
+                       size_t                  length;         //!< of the vpd data
+               } literal;
+               xlat_exp_t      *xlat;   //!< pre-parsed xlat_exp_t
+               struct {
+                       regex_t                 *comp;          //!< pre-parsed regex_t
+                       bool                    iflag;          //!< Case insensitive
+               } preg;
+       } data;
 } value_pair_tmpl_t;
 
+#define vpt_request    attribute.request
+#define vpt_list       attribute.list
+#define vpt_da         attribute.da
+#define vpt_num                attribute.num
+#define vpt_tag                attribute.tag
+
+#define vpt_xlat       data.xlat
+
+#define vpt_preg       data.preg.comp
+#define vpt_iflag      data.preg.iflag
+
+#define vpt_value      data.literal.value
+#define vpt_length     data.literal.length
+
 /** Value pair map
  *
  * Value pair maps contain a pair of templates, that describe a src attribute
@@ -132,14 +208,17 @@ typedef struct value_pair_map {
 } value_pair_map_t;
 
 void radius_tmplfree(value_pair_tmpl_t **tmpl);
-int radius_parse_attr(char const *name, value_pair_tmpl_t *vpt,
+int radius_parse_attr(value_pair_tmpl_t *vpt, char const *name,
                      request_refs_t request_def,
                      pair_lists_t list_def);
 value_pair_tmpl_t *radius_attr2tmpl(TALLOC_CTX *ctx, char const *name,
                                    request_refs_t request_def,
                                    pair_lists_t list_def);
 
-value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type);
+value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type,
+                                  request_refs_t request_def,
+                                  pair_lists_t list_def);
+bool radius_cast_tmpl(value_pair_tmpl_t *vpt, DICT_ATTR const *da);
 size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt);
 int radius_attrmap(CONF_SECTION *cs, value_pair_map_t **head,
                   pair_lists_t dst_list_def, pair_lists_t src_list_def,
index 006dcf2..d70d606 100644 (file)
@@ -25,7 +25,7 @@ RCSIDH(md4_h, "$Id$")
 
 #include <string.h>
 
-#ifdef WITH_OPENSSL_MD4
+#ifdef HAVE_OPENSSL_MD4_H
 #include <openssl/md4.h>
 #endif
 
@@ -35,7 +35,7 @@ extern "C" {
 
 void fr_md4_calc (unsigned char *, unsigned char const *, unsigned int);
 
-#ifndef WITH_OPENSSL_MD4
+#ifndef HAVE_OPENSSL_MD4_H
 /*  The below was retrieved from
  *  http://www.openbsd.org/cgi-bin/cvsweb/src/include/md4.h?rev=1.12
  *  With the following changes: uint64_t => uint32_t[2]
@@ -83,7 +83,7 @@ void   fr_MD4Transform(uint32_t [4], uint8_t const [MD4_BLOCK_LENGTH])
 /*             __attribute__((__bounded__(__minbytes__,1,4)))
                __attribute__((__bounded__(__minbytes__,2,MD4_BLOCK_LENGTH)))*/;
 /*__END_DECLS*/
-#else  /* WITH_OPENSSL_MD4 */
+#else  /* HAVE_OPENSSL_MD4_H */
 USES_APPLE_DEPRECATED_API
 #define FR_MD4_CTX     MD4_CTX
 #define fr_MD4Init     MD4_Init
index dc902d8..029619e 100644 (file)
@@ -26,7 +26,7 @@ RCSIDH(md5_h, "$Id$")
 
 #include <string.h>
 
-#ifdef WITH_OPENSSL_MD5
+#ifdef HAVE_OPENSSL_MD5_H
 #include <openssl/md5.h>
 #endif
 
@@ -34,7 +34,7 @@ RCSIDH(md5_h, "$Id$")
 extern "C" {
 #endif
 
-#ifndef WITH_OPENSSL_MD5
+#ifndef HAVE_OPENSSL_MD5_H
 /*  The below was retrieved from
  *  http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/crypto/md5.h?rev=1.1
  *  With the following changes: uint64_t => uint32_t[2]
@@ -76,7 +76,7 @@ void   fr_MD5Transform(uint32_t [4], uint8_t const [MD5_BLOCK_LENGTH])
 /*             __attribute__((__bounded__(__minbytes__,2,MD5_BLOCK_LENGTH)))*/;
 /* __END_DECLS */
 
-#else  /* WITH_OPENSSL_HASH */
+#else  /* HAVE_OPENSSL_MD5_H */
 
 USES_APPLE_DEPRECATED_API
 #define FR_MD5_CTX     MD5_CTX
index 0ab99a7..b4dc2bf 100644 (file)
@@ -385,12 +385,44 @@ int gettimeofday (struct timeval *tv, void *tz);
 void timeval2ntp(struct  timeval const *tv, uint8_t *ntp);
 void ntp2timeval(struct timeval *tv, char const *ntp);
 
-#define ntohll(x) (((uint64_t)(ntohl((uint32_t)((x << 32) >> 32))) << 32) | \
-                  (uint64_t)ntohl(((uint32_t)(x >> 32))))
+/*
+ *     This is really hacky. Any code needing to perform operations on 128bit integers,
+ *     or return 128BIT integers should check for HAVE_128BIT_INTEGERS.
+ */
+#ifndef HAVE_UINT128_T
+#  ifdef HAVE___UINT128_T
+#    define HAVE_128BIT_INTEGERS
+#    define uint128_t __uint128_t
+#    define int128_t __int128_t
+#  else
+typedef struct uint128_t { uint8_t v[16]; } uint128_t;
+typedef struct int128_t { uint8_t v[16]; } int128_t;
+#  endif
+#else
+#  define HAVE_128BIT_INTEGERS
+#endif
+
+/* abcd efgh -> dcba hgfe -> hgfe dcba */
+#ifdef LITTLE_ENDIAN
+#  define ntohll(x) (((uint64_t)ntohl((uint32_t)(x >> 32))) | (((uint64_t)ntohl(((uint32_t) x)) << 32)))
+
+#  ifdef HAVE_128BIT_INTEGERS
+#    define ntohlll(x) (((uint128_t)ntohll((uint64_t)(x >> 64))) | (((uint128_t)ntohll(((uint64_t) x)) << 64)))
+#  else
+uint128_t ntohlll(uint128_t num);
+#  endif
+#else
+#  define ntohll(x) (x)
+#  define ntohlll(x) (x)
+#endif
 
 #define htonll(x) ntohll(x)
+#define htonlll(x) htohlll(x)
 
 #ifdef __cplusplus
 }
 #endif
+
+char *talloc_typed_strdup(const void *t, const char *p);
+char *talloc_typed_asprintf(const void *t, const char *fmt, ...) CC_HINT(format (printf, 2, 3));
 #endif /* _FR_MISSING_H */
index 7a34eda..f14a1b5 100644 (file)
@@ -31,7 +31,7 @@ modcallable *compile_modgroup(modcallable *parent,
 /* Create a single modcallable node that references a module instance. This
  * may be a CONF_SECTION containing action specifiers like "notfound = return"
  * or a simple CONF_PAIR, in which case the default actions are used. */
-modcallable *compile_modsingle(modcallable *parent, rlm_components_t component, CONF_ITEM *ci,
+modcallable *compile_modsingle(modcallable **parent, rlm_components_t component, CONF_ITEM *ci,
                               char const **modname);
 
 /*
@@ -39,13 +39,14 @@ modcallable *compile_modsingle(modcallable *parent, rlm_components_t component,
  */
 bool modcall_pass2(modcallable *mc);
 
-/* Add an entry to the end of a modgroup, creating it first if necessary */
-void add_to_modcallable(modcallable **parent, modcallable *this,
-                       rlm_components_t component, char const *name);
+/* Add an entry to the end of a modgroup */
+void add_to_modcallable(modcallable *parent, modcallable *this);
 
 /* Free a tree returned by compile_modgroup or compile_modsingle */
 void modcallable_free(modcallable **pc);
 
+void modcall_debug(modcallable *mc, int depth);
+
 #ifdef __cplusplus
 }
 #endif
index 5e3e8f0..0d8675a 100644 (file)
@@ -49,7 +49,8 @@ typedef struct module_instance_t {
        pthread_mutex_t         *mutex;
 #endif
        CONF_SECTION            *cs;
-       int                     dead;
+       bool                    force;
+       rlm_rcode_t             code;
        fr_module_hup_t         *mh;
 } module_instance_t;
 
index 5d2c610..3b283d5 100644 (file)
 RCSIDH(modules_h, "$Id$")
 
 #include <freeradius-devel/conffile.h>
+#include <freeradius-devel/features.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/** Return codes indicating the result of the module call
- *
- * All module functions must return one of the codes listed below (apart from
- * RLM_MODULE_NUMCODES, which is used to check for validity).
- */
-typedef enum rlm_rcodes {
-       RLM_MODULE_REJECT = 0,  //!< Immediately reject the request.
-       RLM_MODULE_FAIL,        //!< Module failed, don't reply.
-       RLM_MODULE_OK,          //!< The module is OK, continue.
-       RLM_MODULE_HANDLED,     //!< The module handled the request, so stop.
-       RLM_MODULE_INVALID,     //!< The module considers the request invalid.
-       RLM_MODULE_USERLOCK,    //!< Reject the request (user is locked out).
-       RLM_MODULE_NOTFOUND,    //!< User not found.
-       RLM_MODULE_NOOP,        //!< Module succeeded without doing anything.
-       RLM_MODULE_UPDATED,     //!< OK (pairs modified).
-       RLM_MODULE_NUMCODES,    //!< How many valid return codes there are.
-       RLM_MODULE_UNKNOWN      //!< Error resolving rcode (should not be
-                               //!< returned by modules).
-} rlm_rcode_t;
-
 /** The different section components of the server
  *
  * Used as indexes in the methods array in the module_t struct.
@@ -95,8 +76,8 @@ extern const section_type_value_t section_type_value[];
 #define RLM_TYPE_THREAD_UNSAFE (1 << 0)        //!< Module is not threadsafe.
                                                //!< Server will protect calls
                                                //!< with mutex.
-#define RLM_TYPE_CHECK_CONFIG_SAFE (1 << 1)    //!< Instantiate module on -C.
-                                               //!< Module will be
+#define RLM_TYPE_CHECK_CONFIG_UNSAFE (1 << 1)  //!< Don't instantiate module on -C.
+                                               //!< Module will NOT be
                                                //!< instantiated if the server
                                                //!< is started in config
                                                //!< check mode.
@@ -105,8 +86,9 @@ extern const section_type_value_t section_type_value[];
                                                //!< new instance, and then
                                                //!< destroy old instance.
 
-#define RLM_MODULE_MAGIC_NUMBER ((uint32_t) (0xf4ee4ad3))
-#define RLM_MODULE_INIT RLM_MODULE_MAGIC_NUMBER
+
+/* Stop people using different module/library/server versions together */
+#define RLM_MODULE_INIT RADIUSD_MAGIC_NUMBER
 
 /** Module section callback
  *
@@ -151,7 +133,7 @@ typedef int (*detach_t)(void *instance);
  * within the module to different sections.
  */
 typedef struct module_t {
-       uint32_t                magic;                          //!< Used to validate module struct.
+       uint64_t                magic;                          //!< Used to validate module struct.
        char const              *name;                          //!< The name of the module (without rlm_ prefix).
        int                     type;                           //!< One or more of the RLM_TYPE_* constants.
        size_t                  inst_size;                      //!< Size of the instance data
@@ -164,9 +146,9 @@ typedef struct module_t {
 
 } module_t;
 
-int setup_modules(int, CONF_SECTION *);
-int detach_modules(void);
-int module_hup(CONF_SECTION *modules);
+int modules_init(CONF_SECTION *);
+int modules_free(void);
+int modules_hup(CONF_SECTION *modules);
 rlm_rcode_t process_authorize(int type, REQUEST *request);
 rlm_rcode_t process_authenticate(int type, REQUEST *request);
 rlm_rcode_t module_preacct(REQUEST *request);
index d831814..d140b7d 100644 (file)
@@ -34,7 +34,7 @@ int fr_packet_cmp(RADIUS_PACKET const *a, RADIUS_PACKET const *b);
 int fr_inaddr_any(fr_ipaddr_t *ipaddr);
 void fr_request_from_reply(RADIUS_PACKET *request,
                             RADIUS_PACKET const *reply);
-int fr_socket(fr_ipaddr_t *ipaddr, int port);
+int fr_socket(fr_ipaddr_t *ipaddr, uint16_t port);
 int fr_nonblock(int fd);
 
 typedef struct fr_packet_list_t fr_packet_list_t;
@@ -50,24 +50,23 @@ RADIUS_PACKET **fr_packet_list_find_byreply(fr_packet_list_t *pl,
                                              RADIUS_PACKET *reply);
 bool fr_packet_list_yank(fr_packet_list_t *pl,
                         RADIUS_PACKET *request);
-int fr_packet_list_num_elements(fr_packet_list_t *pl);
+uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl);
 bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
                            RADIUS_PACKET **request_p, void **pctx);
 bool fr_packet_list_id_free(fr_packet_list_t *pl,
                            RADIUS_PACKET *request, bool yank);
 bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
-                             fr_ipaddr_t *dst_ipaddr, int dst_port,
+                             fr_ipaddr_t *dst_ipaddr, uint16_t dst_port,
                              void *ctx);
 bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd);
 bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd);
 bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd);
-int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx,
-                         fr_hash_table_walk_t callback);
+int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx, rb_walker_t callback);
 int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set);
 RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set);
 
-int fr_packet_list_num_incoming(fr_packet_list_t *pl);
-int fr_packet_list_num_outgoing(fr_packet_list_t *pl);
+uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl);
+uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl);
 
 /*
  *     "find" returns a pointer to the RADIUS_PACKET* member in the
index 4adff2a..6aa858a 100644 (file)
@@ -66,13 +66,13 @@ typedef enum fr_cond_type_t {
 struct fr_cond_t {
        fr_cond_type_t  type;
 
+       CONF_ITEM const *ci;
        union {
                value_pair_map_t *map;
                value_pair_tmpl_t *vpt;
                fr_cond_t       *child;
        } data;
 
-       int             regex_i;
        int             negate;
        int             pass2_fixup;
 
@@ -106,7 +106,6 @@ bool fr_condition_walk(fr_cond_t *head, bool (*callback)(void *, fr_cond_t *), v
 /*
  *     In xlat.c for now
  */
-typedef struct xlat_exp xlat_exp_t;
 ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
                      char const **error);
 size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node);
diff --git a/src/include/pcap.h b/src/include/pcap.h
new file mode 100644 (file)
index 0000000..005b481
--- /dev/null
@@ -0,0 +1,187 @@
+#ifndef FR_PCAP_H
+#define FR_PCAP_H
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file include/pcap.h
+ * @brief Prototypes and constants for PCAP functions.
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#include <freeradius-devel/libradius.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <pcap.h>
+
+/*
+ *     Length of a DEC/Intel/Xerox or 802.3 Ethernet header.
+ *     Note that some compilers may pad "struct ether_header" to
+ *     a multiple of 4 *bytes, for example, so "sizeof (struct
+ *     ether_header)" may not give the right answer.
+ *
+ *     6 Byte SRC, 6 Byte DST, 2 Byte Ether type, 4 Byte CVID, 4 Byte SVID
+ */
+#define ETHER_HDRLEN   22
+#define IP_HDRLEN      60
+
+/*
+ *     RADIUS packet length.
+ *     RFC 2865, Section 3., subsection 'length' says:
+ *     " ... and maximum length is 4096."
+ */
+#define MAX_RADIUS_LEN 4096
+#define MIN_RADIUS_LEN 20
+#define SNAPLEN ETHER_HDRLEN + IP_HDRLEN + sizeof(struct udp_header) + MAX_RADIUS_LEN
+#define PCAP_BUFFER_DEFAULT (10000)
+/*
+ *     It's unclear why this differs between platforms
+ */
+#ifndef __linux__
+#  define PCAP_NONBLOCK_TIMEOUT (0)
+#else
+#  define PCAP_NONBLOCK_TIMEOUT (-1)
+#endif
+
+#ifndef BIOCIMMEDIATE
+#define BIOCIMMEDIATE (2147762800)
+#endif
+
+/*
+ *     Older versions of libpcap don't define this
+ */
+#ifndef PCAP_NETMASK_UNKNOWN
+#  define PCAP_NETMASK_UNKNOWN 0
+#endif
+
+/*
+ *     The number of bytes in an ethernet (MAC) address.
+ */
+#define ETHER_ADDR_LEN         6
+
+/*
+ *     Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
+ */
+struct  ethernet_header {
+       uint8_t         ether_dst[ETHER_ADDR_LEN];
+       uint8_t         ether_src[ETHER_ADDR_LEN];
+       uint16_t        ether_type;
+};
+
+/*
+ *     Structure of an internet header, naked of options.
+ */
+
+#define IP_V(ip)       (((ip)->ip_vhl & 0xf0) >> 4)
+#define IP_HL(ip)       ((ip)->ip_vhl & 0x0f)
+
+#define        I_DF            0x4000          //!< Dont fragment flag.
+#define IP_MF          0x2000          //!< More fragments flag.
+#define IP_OFFMASK     0x1fff          //!< Mask for fragmenting bits.
+
+typedef struct ip_header {
+       uint8_t         ip_vhl;         //!< Header length, version.
+
+       uint8_t         ip_tos;         //!< Type of service.
+       uint16_t        ip_len;         //!< Total length.
+       uint16_t        ip_id;          //!< identification.
+       uint16_t        ip_off;         //!< Fragment offset field.
+
+       uint8_t         ip_ttl;         //!< Time To Live.
+       uint8_t         ip_p;           //!< Protocol.
+       uint16_t        ip_sum;         //!< Checksum.
+       struct in_addr  ip_src, ip_dst; //!< Src and Dst address
+} ip_header_t;
+
+typedef struct ip_header6 {
+       uint32_t        ip_vtcfl;       //!< Version, traffic class, flow label.
+       uint16_t        ip_len;         //!< Payload length
+
+       uint8_t         ip_next;        //!< Next header (protocol)
+       uint8_t         ip_hopl;        //!< IP Hop Limit
+
+       struct in6_addr ip_src, ip_dst; //!< Src and Dst address
+} ip_header6_t;
+
+/*
+ *     UDP protocol header.
+ *     Per RFC 768, September, 1981.
+ */
+typedef struct udp_header {
+       uint16_t        src;            //!< Source port.
+       uint16_t        dst;            //!< Destination port.
+       uint16_t        len;            //!< UDP length.
+       uint16_t        checksum;       //!< UDP checksum.
+} udp_header_t;
+
+typedef struct radius_packet_t {
+       uint8_t         code;
+       uint8_t         id;
+       uint8_t         length[2];
+       uint8_t         vector[AUTH_VECTOR_LEN];
+       uint8_t         data[1];
+} radius_packet_t;
+
+#define AUTH_HDR_LEN 20
+
+typedef enum {
+       PCAP_INVALID = 0,
+       PCAP_INTERFACE_IN,
+       PCAP_FILE_IN,
+       PCAP_STDIO_IN,
+       PCAP_INTERFACE_OUT,
+       PCAP_FILE_OUT,
+       PCAP_STDIO_OUT
+} fr_pcap_type_t;
+
+extern const FR_NAME_NUMBER pcap_types[];
+
+/*
+ *     Internal pcap structures
+ */
+typedef struct fr_pcap fr_pcap_t;
+struct fr_pcap {
+       char                    errbuf[PCAP_ERRBUF_SIZE];       //!< Last error on this interface.
+       fr_pcap_type_t          type;                           //!< What type of handle this is.
+       char                    *name;                          //!< Name of file or interface.
+       bool                    promiscuous;                    //!< Whether the interface is in promiscuous mode.
+                                                               //!< Only valid for live capture handles.
+       int                     buffer_pkts;                    //!< How big to make the PCAP ring buffer.
+                                                               //!< Actual buffer size is SNAPLEN * buffer.
+                                                               //!< Only valid for live capture handles.
+
+       pcap_t                  *handle;                        //!< libpcap handle.
+       pcap_dumper_t           *dumper;                        //!< libpcap dumper handle.
+
+       int                     link_type;                      //!< Link layer type.
+
+       int                     fd;                             //!< Selectable file descriptor we feed to select.
+       struct pcap_stat        pstats;                         //!< The last set of pcap stats for this handle.
+
+       fr_pcap_t               *next;                          //!< Next handle in collection.
+};
+
+
+fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type);
+int fr_pcap_open(fr_pcap_t *handle);
+int fr_pcap_apply_filter(fr_pcap_t *handle, char const *expression);
+char *fr_pcap_device_names(TALLOC_CTX *ctx, fr_pcap_t *handle, char c);
+ssize_t fr_pcap_link_layer_offset(uint8_t const *data, size_t len, int link_type);
+uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum,
+                        struct in_addr const src_addr, struct in_addr const dst_addr);
+#endif
+
index 263b2c0..f09ea4a 100644 (file)
@@ -35,10 +35,10 @@ typedef int (*rad_listen_parse_t)(CONF_SECTION *, rad_listen_t *);
 typedef void (*rad_listen_free_t)(rad_listen_t *);
 
 typedef struct fr_protocol_t {
-       uint32_t        magic;  //!< Used to validate loaded library
-       char const      *name;  //!< The name of the protocol
-       size_t          inst_size;
-       CONF_PARSER     *proto_config;
+       uint64_t                magic;  //!< Used to validate loaded library
+       char const              *name;  //!< The name of the protocol
+       size_t                  inst_size;
+       CONF_PARSER             *proto_config;
 
        rad_listen_parse_t      parse;
        rad_listen_free_t       free;
diff --git a/src/include/radclient.h b/src/include/radclient.h
new file mode 100644 (file)
index 0000000..1764225
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ *   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
+ */
+#ifndef _RADCLIENT_H
+#define _RADCLIENT_H
+/*
+ * $Id$
+ *
+ * @file radclient.h
+ * @brief Structures for the radclient utility.
+ *
+ * @copyright 2014  The FreeRADIUS server project
+ */
+#include <freeradius-devel/libradius.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ *     Logging macros
+ */
+ #undef DEBUG
+#define DEBUG(fmt, ...)                if (do_output && (fr_debug_flag > 0)) fprintf(fr_log_fp, fmt "\n", ## __VA_ARGS__)
+#undef DEBUG2
+#define DEBUG2(fmt, ...)       if (do_output && (fr_debug_flag > 1)) fprintf(fr_log_fp, fmt "\n", ## __VA_ARGS__)
+
+
+#define ERROR(fmt, ...)                if (do_output) fr_perror("radclient: " fmt, ## __VA_ARGS__)
+
+#define RDEBUG_ENABLED()       (do_output && (fr_debug_flag > 0))
+#define RDEBUG_ENABLED2()      (do_output && (fr_debug_flag > 1))
+
+#define REDEBUG(fmt, ...)      if (do_output) fr_perror("(%" PRIu64 ") " fmt , request->num, ## __VA_ARGS__)
+#define RDEBUG(fmt, ...)       if (do_output && (fr_debug_flag > 0)) fprintf(fr_log_fp, "(%" PRIu64 ") " fmt "\n", request->num, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      if (do_output && (fr_debug_flag > 1)) fprintf(fr_log_fp, "(%" PRIu64 ") " fmt "\n", request->num, ## __VA_ARGS__)
+
+typedef struct rc_stats {
+       uint64_t accepted;              //!< Requests to which we received a accept
+       uint64_t rejected;              //!< Requests to which we received a reject
+       uint64_t lost;                  //!< Requests to which we received no response
+       uint64_t passed;                //!< Requests which passed a filter
+       uint64_t failed;                //!< Requests which failed a fitler
+} rc_stats_t;
+
+typedef struct rc_file_pair {
+       char const *packets;            //!< The file containing the request packet
+       char const *filters;            //!< The file containing the definition of the
+                                       //!< packet we want to match.
+} rc_file_pair_t;
+
+typedef struct rc_request rc_request_t;
+
+struct rc_request {
+       uint64_t        num;            //!< The number (within the file) of the request were reading.
+
+       rc_request_t    *prev;
+       rc_request_t    *next;
+
+       rc_file_pair_t  *files;         //!< Request and response file names.
+
+       char            password[256];
+       time_t          timestamp;
+
+       RADIUS_PACKET   *packet;        //!< The outgoing request.
+       PW_CODE         packet_code;    //!< The code in the outgoing request.
+       RADIUS_PACKET   *reply;         //!< The incoming response.
+       VALUE_PAIR      *filter;        //!< If the reply passes the filter, then the request passes.
+       PW_CODE         filter_code;    //!< Expected code of the response packet.
+
+       int             resend;
+       int             tries;
+       bool            done;           //!< Whether the request is complete.
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _RADCLIENT_H */
index d88e332..7a9158b 100644 (file)
@@ -6,56 +6,62 @@
  */
 
 typedef enum {
-       PW_TYPE_INVALID = 0,    //!< Invalid (uninitialised) attribute type.
-       PW_TYPE_STRING,         //!< String of printable characters.
-       PW_TYPE_INTEGER,        //!< 32 Bit unsigned integer.
-       PW_TYPE_IPADDR,         //!< 32 Bit IPv4 Address.
-       PW_TYPE_DATE,           //!< 32 Bit Unix timestamp.
-       PW_TYPE_ABINARY,        //!< Ascend binary format a packed data
-                               //!< structure.
-       PW_TYPE_OCTETS,         //!< Raw octets.
-       PW_TYPE_IFID,           //!< Interface ID.
-       PW_TYPE_IPV6ADDR,       //!< 128 Bit IPv6 Address.
-       PW_TYPE_IPV6PREFIX,     //!< IPv6 Prefix.
-       PW_TYPE_BYTE,           //!< 8 Bit unsigned integer.
-       PW_TYPE_SHORT,          //!< 16 Bit unsigned integer.
-       PW_TYPE_ETHERNET,       //!< 48 Bit Mac-Address.
-       PW_TYPE_SIGNED,         //!< 32 Bit signed integer.
-       PW_TYPE_COMBO_IP,       //!< WiMAX IPv4 or IPv6 address depending
-                               //!< on length.
-       PW_TYPE_TLV,            //!< Contains nested attributes.
-       PW_TYPE_EXTENDED,       //!< Extended attribute space attribute.
-       PW_TYPE_LONG_EXTENDED,  //!< Long extended attribute space attribute.
-       PW_TYPE_EVS,            //!< Extended attribute, vendor specific.
-       PW_TYPE_INTEGER64,      //!< 64 Bit unsigned integer.
-       PW_TYPE_IPV4PREFIX,     //!< IPv4 Prefix.
-       PW_TYPE_VSA,            //!< Vendor-Specific, for attribute 26
-       PW_TYPE_MAX             //!< Number of defined data types.
+       PW_TYPE_INVALID = 0,                    //!< Invalid (uninitialised) attribute type.
+       PW_TYPE_STRING,                         //!< String of printable characters.
+       PW_TYPE_INTEGER,                        //!< 32 Bit unsigned integer.
+       PW_TYPE_IPV4_ADDR,                      //!< 32 Bit IPv4 Address.
+       PW_TYPE_DATE,                           //!< 32 Bit Unix timestamp.
+       PW_TYPE_ABINARY,                        //!< Ascend binary format a packed data structure.
+       PW_TYPE_OCTETS,                         //!< Raw octets.
+       PW_TYPE_IFID,                           //!< Interface ID.
+       PW_TYPE_IPV6_ADDR,                      //!< 128 Bit IPv6 Address.
+       PW_TYPE_IPV6_PREFIX,                    //!< IPv6 Prefix.
+       PW_TYPE_BYTE,                           //!< 8 Bit unsigned integer.
+       PW_TYPE_SHORT,                          //!< 16 Bit unsigned integer.
+       PW_TYPE_ETHERNET,                       //!< 48 Bit Mac-Address.
+       PW_TYPE_SIGNED,                         //!< 32 Bit signed integer.
+       PW_TYPE_IP_ADDR,                        //!< WiMAX IPv4 or IPv6 address depending on length.
+       PW_TYPE_TLV,                            //!< Contains nested attributes.
+       PW_TYPE_EXTENDED,                       //!< Extended attribute space attribute.
+       PW_TYPE_LONG_EXTENDED,                  //!< Long extended attribute space attribute.
+       PW_TYPE_EVS,                            //!< Extended attribute, vendor specific.
+       PW_TYPE_INTEGER64,                      //!< 64 Bit unsigned integer.
+       PW_TYPE_IPV4_PREFIX,                    //!< IPv4 Prefix.
+       PW_TYPE_VSA,                            //!< Vendor-Specific, for RADIUS attribute 26.
+       PW_TYPE_TIMEVAL,                        //!< Time value (struct timeval), only for config items.
+       PW_TYPE_BOOLEAN,                        //!< A truth value.
+       PW_TYPE_IP_PREFIX,                      //!< WiMAX IPv4 or IPv6 address prefix depending on length.
+       PW_TYPE_MAX                             //!< Number of defined data types.
 } PW_TYPE;
 
-#define PW_AUTHENTICATION_REQUEST      1
-#define PW_AUTHENTICATION_ACK          2
-#define PW_AUTHENTICATION_REJECT       3
-#define PW_ACCOUNTING_REQUEST          4
-#define PW_ACCOUNTING_RESPONSE         5
-#define PW_ACCOUNTING_STATUS           6
-#define PW_PASSWORD_REQUEST            7
-#define PW_PASSWORD_ACK                        8
-#define PW_PASSWORD_REJECT             9
-#define PW_ACCOUNTING_MESSAGE          10
-#define PW_ACCESS_CHALLENGE            11
-#define PW_STATUS_SERVER               12
-#define PW_STATUS_CLIENT               13
-#define PW_DISCONNECT_REQUEST          40
-#define PW_DISCONNECT_ACK              41
-#define PW_DISCONNECT_NAK              42
-#define PW_COA_REQUEST                 43
-#define PW_COA_ACK                     44
-#define PW_COA_NAK                     45
+typedef enum {
+       PW_CODE_UNDEFINED               = 0,    //!< Packet code has not been set
+       PW_CODE_AUTHENTICATION_REQUEST  = 1,    //!< RFC2865 - Access-Request
+       PW_CODE_AUTHENTICATION_ACK      = 2,    //!< RFC2865 - Access-Accept
+       PW_CODE_AUTHENTICATION_REJECT   = 3,    //!< RFC2865 - Access-Reject
+       PW_CODE_ACCOUNTING_REQUEST      = 4,    //!< RFC2866 - Accounting-Request
+       PW_CODE_ACCOUNTING_RESPONSE     = 5,    //!< RFC2866 - Accounting-Response
+       PW_CODE_ACCOUNTING_STATUS       = 6,    //!< RFC3575 - Reserved
+       PW_CODE_PASSWORD_REQUEST        = 7,    //!< RFC3575 - Reserved
+       PW_CODE_PASSWORD_ACK            = 8,    //!< RFC3575 - Reserved
+       PW_CODE_PASSWORD_REJECT         = 9,    //!< RFC3575 - Reserved
+       PW_CODE_ACCOUNTING_MESSAGE      = 10,   //!< RFC3575 - Reserved
+       PW_CODE_ACCESS_CHALLENGE        = 11,   //!< RFC2865 - Access-Challenge
+       PW_CODE_STATUS_SERVER           = 12,   //!< RFC2865/RFC5997 - Status Server (request)
+       PW_CODE_STATUS_CLIENT           = 13,   //!< RFC2865/RFC5997 - Status Server (response)
+       PW_CODE_DISCONNECT_REQUEST      = 40,   //!< RFC3575/RFC5176 - Disconnect-Request
+       PW_CODE_DISCONNECT_ACK          = 41,   //!< RFC3575/RFC5176 - Disconnect-Ack (positive)
+       PW_CODE_DISCONNECT_NAK          = 42,   //!< RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
+       PW_CODE_COA_REQUEST             = 43,   //!< RFC3575/RFC5176 - CoA-Request
+       PW_CODE_COA_ACK                 = 44,   //!< RFC3575/RFC5176 - CoA-Ack (positive)
+       PW_CODE_COA_NAK                 = 45,   //!< RFC3575/RFC5176 - CoA-Nak (not willing to perform)
+       PW_CODE_MAX                     = 255,  //!< Maximum possible code
+} PW_CODE;
 
 #define PW_AUTH_UDP_PORT               1812
 #define PW_ACCT_UDP_PORT               1813
 #define PW_POD_UDP_PORT                        1700
+#define PW_RADIUS_TLS_PORT             2083
 #define PW_COA_UDP_PORT                        3799
 
 #define        PW_USER_NAME                    1
@@ -138,118 +144,10 @@ typedef enum {
 #define PW_DIGEST_RESPONSE             206
 #define PW_DIGEST_ATTRIBUTES           207
 
-#define PW_FALL_THROUGH                        500
-#define PW_RELAX_FILTER                        501
-#define PW_EXEC_PROGRAM                        502
-#define PW_EXEC_PROGRAM_WAIT           503
-
-#define PW_AUTH_TYPE                   1000
-#define PW_PREFIX                      1003
-#define PW_SUFFIX                      1004
-#define PW_GROUP                       1005
-#define PW_CRYPT_PASSWORD              1006
-#define PW_CONNECT_RATE                        1007
-#define PW_ADD_PREFIX                  1008
-#define PW_ADD_SUFFIX                  1009
-#define PW_EXPIRATION                  1010
-#define PW_AUTZ_TYPE                   1011
-#define PW_ACCT_TYPE                   1012
-#define PW_SESSION_TYPE                        1013
-#define PW_POST_AUTH_TYPE              1014
-#define PW_PRE_PROXY_TYPE              1015
-#define PW_POST_PROXY_TYPE             1016
-#define PW_PRE_ACCT_TYPE               1017
-#define PW_EAP_TYPE                    1018
-#define PW_EAP_TLS_REQUIRE_CLIENT_CERT 1019
-#define PW_EAP_MD5_PASSWORD            1022
-#define PW_CLIENT_SHORTNAME            1024
-#define PW_LOAD_BALANCE_KEY            1025
-#define PW_RAW_ATTRIBUTE               1026
-#define PW_TNC_VLAN_ACCESS             1027
-#define PW_TNC_VLAN_ISOLATE            1028
-#define PW_USER_CATEGORY               1029
-#define PW_GROUP_NAME                  1030
-#define PW_HUNTGROUP_NAME              1031
-#define PW_SIMULTANEOUS_USE            1034
-#define PW_STRIP_USER_NAME             1035
-#define PW_HINT                                1040
-#define PAM_AUTH_ATTR                  1041
-#define PW_LOGIN_TIME                  1042
-#define PW_STRIPPED_USER_NAME          1043
-#define PW_CURRENT_TIME                        1044
-#define PW_REALM                       1045
-#define PW_NO_SUCH_ATTRIBUTE           1046
-#define PW_PACKET_TYPE                 1047
-#define PW_PROXY_TO_REALM              1048
-#define PW_REPLICATE_TO_REALM          1049
-#define PW_ACCT_SESSION_START_TIME     1050
-#define PW_ACCT_UNIQUE_SESSION_ID      1051
-#define PW_CLIENT_IP_ADDRESS           1052
-#define PW_LDAP_USERDN                 1053
-#define PW_NS_MTA_MD5_PASSWORD         1054
-#define PW_SQL_USER_NAME               1055
-#define PW_LM_PASSWORD                 1057
-#define PW_NT_PASSWORD                 1058
-#define PW_SMB_ACCOUNT_CTRL            1059
-#define PW_SMB_ACCOUNT_CTRL_TEXT       1061
-#define PW_USER_PROFILE                        1062
-#define PW_DIGEST_REALM                        1063
-#define PW_DIGEST_NONCE                        1064
-#define PW_DIGEST_METHOD               1065
-#define PW_DIGEST_URI                  1066
-#define PW_DIGEST_QOP                  1067
-#define PW_DIGEST_ALGORITHM            1068
-#define PW_DIGEST_BODY_DIGEST          1069
-#define PW_DIGEST_CNONCE               1070
-#define PW_DIGEST_NONCE_COUNT          1071
-#define PW_DIGEST_USER_NAME            1072
-#define PW_POOL_NAME                   1073
-#define PW_LDAP_GROUP                  1074
-#define PW_MODULE_SUCCESS_MESSAGE      1075
-#define PW_MODULE_FAILURE_MESSAGE      1076
-#if 0 /* no longer used */
-#define PW_X99_FAST                    1077
-#endif
-#define PW_REWRITE_RULE                        1078
-#define PW_SQL_GROUP                   1079
-#define PW_RESPONSE_PACKET_TYPE                1080
-#define PW_DIGEST_HA1                  1081
-#define PW_MS_CHAP_USE_NTLM_AUTH       1082
-#define PW_MS_CHAP_USER_NAME           1083
-#define PW_PACKET_SRC_IP_ADDRESS       1084
-#define PW_PACKET_DST_IP_ADDRESS       1085
-#define PW_PACKET_SRC_PORT             1086
-#define PW_PACKET_DST_PORT             1087
-#define PW_PACKET_AUTHENTICATION_VECTOR        1088
-#define PW_TIME_OF_DAY                 1089
-#define PW_REQUEST_PROCESSING_STAGE    1090
-
-#define PW_SHA_PASSWORD                        1093
-#define PW_SSHA_PASSWORD               1094
-#define PW_MD5_PASSWORD                        1095
-#define PW_SMD5_PASSWORD               1096
-
-#define PW_PACKET_SRC_IPV6_ADDRESS     1097
-#define PW_PACKET_DST_IPV6_ADDRESS     1098
-#define PW_VIRTUAL_SERVER              1099
-#define PW_CLEARTEXT_PASSWORD          1100
-#define PW_PASSWORD_WITH_HEADER                1101
-#define PW_SEND_COA_REQUEST            1107
-#define PW_MODULE_RETURN_CODE          1108
-#define PW_PACKET_ORIGINAL_TIMESTAMP   1109
-#define PW_HOME_SERVER_POOL            1111
-#define PW_FREERADIUS_CLIENT_IP_ADDRESS                1120
-#define PW_FREERADIUS_CLIENT_IPV6_ADDRESS      1121
-#define PW_RECV_COA_TYPE               1131
-#define PW_SEND_COA_TYPE               1132
-#define PW_MSCHAP_PASSWORD             1133
-#define PW_PACKET_TRANSMIT_COUNTER     1134
-#define PW_CACHED_SESSION_POLICY       1135
-#define PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS    1143
-#define PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS  1144
-
-#define PW_OTP_CHALLENGE               1145
-#define PW_EAP_SESSION_ID              1146
+/*
+ *     All internal attributes are now defined in this file.
+ */
+#include <freeradius-devel/attributes.h>
 
 #define PW_CHBIND_RESPONSE_CODE                1147
 /*
index 6e57da9..aa1f400 100644 (file)
@@ -34,7 +34,7 @@ RCSIDH(radiusd_h, "$Id$")
 #include <freeradius-devel/connection.h>
 #include <freeradius-devel/map.h>
 
-typedef struct request REQUEST;
+typedef struct rad_request REQUEST;
 
 #include <freeradius-devel/log.h>
 
@@ -44,27 +44,6 @@ typedef struct request REQUEST;
 #  include <sys/wait.h>
 #endif
 
-#ifdef HAVE_PCREPOSIX_H
-#  include <pcreposix.h>
-#else
-#  ifdef HAVE_REGEX_H
-#    include <regex.h>
-
-/*
- *  For POSIX Regular expressions.
- *  (0) Means no extended regular expressions.
- *  REG_EXTENDED means use extended regular expressions.
- */
-#    ifndef REG_EXTENDED
-#      define REG_EXTENDED (0)
-#    endif
-
-#    ifndef REG_NOSUB
-#      define REG_NOSUB (0)
-#    endif
-#  endif
-#endif
-
 #ifndef NDEBUG
 #  define REQUEST_MAGIC (0xdeadbeef)
 #endif
@@ -83,10 +62,6 @@ typedef struct request REQUEST;
 #include <freeradius-devel/stats.h>
 #include <freeradius-devel/realms.h>
 
-#ifdef WITH_COMMAND_SOCKET
-#  define PW_RADMIN_PORT 18120
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -99,15 +74,14 @@ typedef struct request_data_t request_data_t;
 typedef struct radclient {
        fr_ipaddr_t             ipaddr;
        fr_ipaddr_t             src_ipaddr;
-       int                     prefix;
        char const              *longname;
        char const              *secret;
        char const              *shortname;
        bool                    message_authenticator;
-       char                    *nas_type;
-       char                    *login;
-       char                    *password;
-       char                    *server;
+       char const              *nas_type;
+       char const              *login;
+       char const              *password;
+       char const              *server;
        int                     number; /* internal use only */
        CONF_SECTION const      *cs;
 #ifdef WITH_STATS
@@ -121,6 +95,8 @@ typedef struct radclient {
 #endif
 #endif
 
+       struct timeval          response_window;
+
        int                     proto;
 #ifdef WITH_TCP
        fr_socket_limit_t       limit;
@@ -130,17 +106,17 @@ typedef struct radclient {
 #endif
 
 #ifdef WITH_DYNAMIC_CLIENTS
-       int                     lifetime;
-       int                     dynamic; /* was dynamically defined */
+       uint32_t                lifetime;
+       uint32_t                dynamic; /* was dynamically defined */
        time_t                  created;
        time_t                  last_new_client;
-       char                    *client_server;
+       char const              *client_server;
        bool                    rate_limit;
 #endif
 
 #ifdef WITH_COA
-       char                    *coa_name;
-       home_server             *coa_server;
+       char const              *coa_name;
+       home_server_t           *coa_server;
        home_pool_t             *coa_pool;
 #endif
 } RADCLIENT;
@@ -177,6 +153,26 @@ typedef enum RAD_LISTEN_TYPE {
        RAD_LISTEN_MAX
 } RAD_LISTEN_TYPE;
 
+/** Return codes indicating the result of the module call
+ *
+ * All module functions must return one of the codes listed below (apart from
+ * RLM_MODULE_NUMCODES, which is used to check for validity).
+ */
+typedef enum rlm_rcodes {
+       RLM_MODULE_REJECT = 0,  //!< Immediately reject the request.
+       RLM_MODULE_FAIL,        //!< Module failed, don't reply.
+       RLM_MODULE_OK,          //!< The module is OK, continue.
+       RLM_MODULE_HANDLED,     //!< The module handled the request, so stop.
+       RLM_MODULE_INVALID,     //!< The module considers the request invalid.
+       RLM_MODULE_USERLOCK,    //!< Reject the request (user is locked out).
+       RLM_MODULE_NOTFOUND,    //!< User not found.
+       RLM_MODULE_NOOP,        //!< Module succeeded without doing anything.
+       RLM_MODULE_UPDATED,     //!< OK (pairs modified).
+       RLM_MODULE_NUMCODES,    //!< How many valid return codes there are.
+       RLM_MODULE_UNKNOWN      //!< Error resolving rcode (should not be
+                               //!< returned by modules).
+} rlm_rcode_t;
+extern const FR_NAME_NUMBER modreturn_table[];
 
 /*
  *     For listening on multiple IP's and ports.
@@ -198,13 +194,27 @@ typedef           int (*RAD_REQUEST_FUNP)(REQUEST *);
 #define VERIFY_REQUEST(_x)
 #endif
 
-struct request {
+typedef enum {
+       REQUEST_ACTIVE = 1,
+       REQUEST_STOP_PROCESSING,
+       REQUEST_COUNTED
+} rad_master_state_t;
+#define REQUEST_MASTER_NUM_STATES (REQUEST_COUNTED + 1)
+
+typedef enum {
+       REQUEST_QUEUED = 1,
+       REQUEST_RUNNING,
+       REQUEST_PROXIED,
+       REQUEST_RESPONSE_DELAY,
+       REQUEST_CLEANUP_DELAY,
+       REQUEST_DONE
+} rad_child_state_t;
+#define REQUEST_CHILD_NUM_STATES (REQUEST_DONE + 1)
+
+struct rad_request {
 #ifndef NDEBUG
-       uint32_t                magic;          //!< Magic number used to
-                                               //!< detect memory corruption,
-                                               //!< or request structs that
-                                               //!< have not been properly
-                                               //!< initialised.
+       uint32_t                magic;          //!< Magic number used to detect memory corruption,
+                                               //!< or request structs that have not been properly initialised.
 #endif
        RADIUS_PACKET           *packet;        //!< Incoming request.
 #ifdef WITH_PROXY
@@ -214,121 +224,93 @@ struct request {
 #ifdef WITH_PROXY
        RADIUS_PACKET           *proxy_reply;   //!< Incoming response.
 #endif
-       VALUE_PAIR              *config_items;  //!< VALUE_PAIR s used to set
-                                               //!< per request parameters for
-                                               //!< modules and the server
-                                               //!< core at runtime.
+       VALUE_PAIR              *config_items;  //!< VALUE_PAIRs used to set per request parameters
+                                               //!< for modules and the server core at runtime.
        VALUE_PAIR              *username;      //!< Cached username VALUE_PAIR.
        VALUE_PAIR              *password;      //!< Cached password VALUE_PAIR.
 
-       fr_request_process_t    process;        //!< The function to call to
-                                               //!< move the request through
-                                               //!< the state machine.
+       fr_request_process_t    process;        //!< The function to call to move the request through the state machine.
 
-       RAD_REQUEST_FUNP        handle;         //!< The function to call to
-                                               //!< move the request through
-                                               //!< the various server
-                                               //!< configuration sections.
+       RAD_REQUEST_FUNP        handle;         //!< The function to call to move the request through the
+                                               //!< various server configuration sections.
 
-       struct main_config_t    *root;          //!< Pointer to the main config
-                                               //!< hack to try and deal with
-                                               //!< hup.
+       struct main_config_t    *root;          //!< Pointer to the main config hack to try and deal with hup.
 
        request_data_t          *data;          //!< Request metadata.
 
-       RADCLIENT               *client;        //!< The client that originally
-                                               //!< sent us the request.
+       RADCLIENT               *client;        //!< The client that originally sent us the request.
 
 #ifdef HAVE_PTHREAD_H
-       pthread_t               child_pid;      //!< Current thread handling
-                                               //!< the request.
+       pthread_t               child_pid;      //!< Current thread handling the request.
 #endif
-       time_t                  timestamp;      //!< When the request was
-                                               //!< received.
-       unsigned int            number;         //!< Monotonically increasing
-                                               //!< request number. Reset on
-                                               //!< server restart.
+       time_t                  timestamp;      //!< When the request was received.
+       unsigned int            number;         //!< Monotonically increasing request number. Reset on server restart.
 
-       rad_listen_t            *listener;      //!< The listener that received
-                                               //!< the request.
+       rad_listen_t            *listener;      //!< The listener that received the request.
 #ifdef WITH_PROXY
-       rad_listen_t            *proxy_listener;//!< Listener for outgoing
-                                               //!< requests.
+       rad_listen_t            *proxy_listener;//!< Listener for outgoing requests.
 #endif
 
+       rlm_rcode_t             rcode;          //!< Last rcode returned by a module
 
-       int                  simul_max; //!< Maximum number of
-                                               //!< concurrent sessions for
-                                               //!< this user.
+       int                     simul_max;      //!< Maximum number of concurrent sessions for this user.
 #ifdef WITH_SESSION_MGMT
-       int                  simul_count;       //!< The current number of
-                                               //!< sessions for this user.
-       int                  simul_mpp;         //!< WEIRD: 1 is false,
-                                               //!< 2 is true.
+       int                     simul_count;    //!< The current number of sessions for this user.
+       int                     simul_mpp;      //!< WEIRD: 1 is false, 2 is true.
 #endif
 
-       log_debug_t             options;        //!< Request options, currently
-                                               //!< just holds the debug level
-                                               //!< for the request.
-
-       char const              *module;        //!< Module the request is
-                                               //!< currently being processed
-                                               //!< by.
-       char const              *component;     //!< Section the request is
-                                               //!< in.
+       char const              *module;        //!< Module the request is currently being processed by.
+       char const              *component;     //!< Section the request is in.
 
        int                     delay;
 
-       int                     master_state;
-       int                     child_state;
+       rad_master_state_t      master_state;
+       rad_child_state_t       child_state;
        RAD_LISTEN_TYPE         priority;
 
+       int                     response_delay;
        int                     timer_action;
        fr_event_t              *ev;
 
-       int                     in_request_hash;
+       bool                    in_request_hash;
 #ifdef WITH_PROXY
-       int                     in_proxy_hash;
+       bool                    in_proxy_hash;
 
-       home_server             *home_server;
-       home_pool_t             *home_pool; /* for dynamic failover */
+       home_server_t           *home_server;
+       home_pool_t             *home_pool;     //!< For dynamic failover
 
        struct timeval          proxy_retransmit;
 
-       int                     num_proxied_requests;
-       int                     num_proxied_responses;
+       uint32_t                num_proxied_requests;
+       uint32_t                num_proxied_responses;
 #endif
 
        char const              *server;
        REQUEST                 *parent;
-       radlog_func_t           radlog;         //!< Function to call to output
-                                               //!< log messages about this
+
+       struct {
+               radlog_func_t   func;           //!< Function to call to output log messages about this
                                                //!< request.
+
+               log_debug_t     lvl;            //!< Request options, currently just holds the debug level or
+                                               //!< the request.
+
+               uint8_t         indent;         //!< By how much to indent log messages. uin8_t so it's obvious
+                                               //!< when a request has been exdented too much.
+       } log;
+
 #ifdef WITH_COA
-       REQUEST                 *coa;           //!< CoA request originated
-                                               //!< by this request.
-       int                     num_coa_requests;//!< Counter for number of
-                                               //!< requests sent including
+       REQUEST                 *coa;           //!< CoA request originated by this request.
+       uint32_t                num_coa_requests;//!< Counter for number of requests sent including
                                                //!< retransmits.
 #endif
 };                             /* REQUEST typedef */
 
-#define RAD_REQUEST_OPTION_NONE            (0)
-#define RAD_REQUEST_OPTION_DEBUG          (1)
-#define RAD_REQUEST_OPTION_DEBUG2        (2)
-#define RAD_REQUEST_OPTION_DEBUG3        (3)
-#define RAD_REQUEST_OPTION_DEBUG4        (4)
-
-#define REQUEST_ACTIVE                 (1)
-#define REQUEST_STOP_PROCESSING (2)
-#define REQUEST_COUNTED                (3)
-
-#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)
+#define RAD_REQUEST_OPTION_NONE                (0)
+#define RAD_REQUEST_OPTION_DEBUG       (1)
+#define RAD_REQUEST_OPTION_DEBUG2      (2)
+#define RAD_REQUEST_OPTION_DEBUG3      (3)
+#define RAD_REQUEST_OPTION_DEBUG4      (4)
 
 typedef struct radclient_list RADCLIENT_LIST;
 
@@ -354,7 +336,7 @@ struct rad_listen_t {
 #endif
        bool            nodup;
        bool            synchronous;
-       int             workers;
+       uint32_t        workers;
 
 #ifdef WITH_TLS
        fr_tls_server_conf_t *tls;
@@ -382,26 +364,26 @@ typedef struct listen_socket_t {
         *      For normal sockets.
         */
        fr_ipaddr_t     my_ipaddr;
-       int             my_port;
+       uint16_t        my_port;
 
        char const      *interface;
 #ifdef SO_BROADCAST
        int             broadcast;
 #endif
        time_t          rate_time;
-       int             rate_pps_old;
-       int             rate_pps_now;
-       int             max_rate;
+       uint32_t        rate_pps_old;
+       uint32_t        rate_pps_now;
+       uint32_t        max_rate;
 
        /* for outgoing sockets */
-       home_server     *home;
+       home_server_t   *home;
        fr_ipaddr_t     other_ipaddr;
-       int             other_port;
+       uint16_t        other_port;
 
        int             proto;
 
 #ifdef WITH_TCP
-       /* for a proxy connecting to home servers */
+       /* for a proxy connecting to home servers */
        time_t          last_packet;
        time_t          opened;
        fr_event_t      *ev;
@@ -420,6 +402,7 @@ typedef struct listen_socket_t {
        VALUE_PAIR      *certs;
        pthread_mutex_t mutex;
        uint8_t         *data;
+       size_t          partial;
 #endif
 
        RADCLIENT_LIST  *clients;
@@ -433,26 +416,26 @@ typedef struct listen_socket_t {
 typedef struct main_config_t {
        struct main_config *next;
        fr_ipaddr_t     myip;   /* from the command-line only */
-       int             port;   /* from the command-line only */
+       uint16_t        port;   /* from the command-line only */
        bool            log_auth;
        bool            log_auth_badpass;
        bool            log_auth_goodpass;
        bool            allow_core_dumps;
-       int             debug_level;
+       uint32_t        debug_level;
+       bool            daemonize;
 #ifdef WITH_PROXY
        bool            proxy_requests;
 #endif
-       int             reject_delay;
+       uint32_t        reject_delay;
        bool            status_server;
-       int             max_request_time;
-       int             cleanup_delay;
-       int             max_requests;
-#ifdef DELETE_BLOCKED_REQUESTS
-       int             kill_unresponsive_children;
-#endif
-       char            *log_file;
+       char const      *allow_vulnerable_openssl;
+
+       uint32_t        max_request_time;
+       uint32_t        cleanup_delay;
+       uint32_t        max_requests;
+       char const      *log_file;
        char const      *dictionary_dir;
-       char            *checkrad;
+       char const      *checkrad;
        char const      *pid_file;
        rad_listen_t    *listen;
        int             syslog_facility;
@@ -460,17 +443,21 @@ typedef struct main_config_t {
        char const      *name;
        char const      *auth_badpass_msg;
        char const      *auth_goodpass_msg;
-       int             debug_memory;
+       bool            debug_memory;
+       bool            memory_report;
+       char const      *panic_action;
+       char const      *denied_msg;
+       struct timeval  init_delay; /* initial request processing delay */
 } MAIN_CONFIG_T;
 
 #define SECONDS_PER_DAY                86400
 #define MAX_REQUEST_TIME       30
 #define CLEANUP_DELAY          5
 #define MAX_REQUESTS           256
-#define RETRY_DELAY         5
-#define RETRY_COUNT         3
-#define DEAD_TIME             120
-#define EXEC_TIMEOUT          10
+#define RETRY_DELAY            5
+#define RETRY_COUNT            3
+#define DEAD_TIME              120
+#define EXEC_TIMEOUT           10
 
 /* for paircompare_register */
 typedef int (*RAD_COMPARE_FUNC)(void *instance, REQUEST *,VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR *, VALUE_PAIR **);
@@ -481,14 +468,12 @@ typedef enum request_fail {
        REQUEST_FAIL_DECODE,            //!< Rad_decode didn't like it.
        REQUEST_FAIL_PROXY,             //!< Call to proxy modules failed.
        REQUEST_FAIL_PROXY_SEND,        //!< Proxy_send didn't like it.
-       REQUEST_FAIL_NO_RESPONSE,       //!< We weren't told to respond,
-                                       //!< so we reject.
+       REQUEST_FAIL_NO_RESPONSE,       //!< We weren't told to respond, so we reject.
        REQUEST_FAIL_HOME_SERVER,       //!< The home server didn't respond.
        REQUEST_FAIL_HOME_SERVER2,      //!< Another case of the above.
        REQUEST_FAIL_HOME_SERVER3,      //!< Another case of the above.
        REQUEST_FAIL_NORMAL_REJECT,     //!< Authentication failure.
-       REQUEST_FAIL_SERVER_TIMEOUT     //!< The server took too long to
-                                       //!< process the request.
+       REQUEST_FAIL_SERVER_TIMEOUT     //!< The server took too long to process the request.
 } request_fail_t;
 
 /*
@@ -501,7 +486,6 @@ extern log_debug_t  debug_flag;
 extern char const      *radacct_dir;
 extern char const      *radlog_dir;
 extern char const      *radlib_dir;
-extern char const      *radius_dir;
 extern char const      *radius_libdir;
 extern uint32_t                expiration_seconds;
 extern bool            log_stripped_names;
@@ -509,14 +493,15 @@ extern bool               log_auth_detail;
 extern char const      *radiusd_version;
 void                   radius_signal_self(int flag);
 
-#define RADIUS_SIGNAL_SELF_NONE                (0)
-#define RADIUS_SIGNAL_SELF_HUP         (1 << 0)
-#define RADIUS_SIGNAL_SELF_TERM                (1 << 1)
-#define RADIUS_SIGNAL_SELF_EXIT                (1 << 2)
-#define RADIUS_SIGNAL_SELF_DETAIL      (1 << 3)
-#define RADIUS_SIGNAL_SELF_NEW_FD      (1 << 4)
-#define RADIUS_SIGNAL_SELF_MAX         (1 << 5)
-
+typedef enum {
+       RADIUS_SIGNAL_SELF_NONE         = (0),
+       RADIUS_SIGNAL_SELF_HUP          = (1 << 0),
+       RADIUS_SIGNAL_SELF_TERM         = (1 << 1),
+       RADIUS_SIGNAL_SELF_EXIT         = (1 << 2),
+       RADIUS_SIGNAL_SELF_DETAIL       = (1 << 3),
+       RADIUS_SIGNAL_SELF_NEW_FD       = (1 << 4),
+       RADIUS_SIGNAL_SELF_MAX          = (1 << 5)
+} radius_signal_t;
 /*
  *     Function prototypes.
  */
@@ -525,12 +510,11 @@ void                      radius_signal_self(int flag);
 int            rad_accounting(REQUEST *);
 
 /* session.c */
-int            rad_check_ts(uint32_t nasaddr, unsigned int port, char const *user,
-                            char const *sessionid);
+int            rad_check_ts(uint32_t nasaddr, uint32_t nas_port, char const *user, char const *sessionid);
 int            session_zap(REQUEST *request, uint32_t nasaddr,
-                           unsigned int port, char const *user,
+                           uint32_t nas_port, char const *user,
                            char const *sessionid, uint32_t cliaddr,
-                           char proto,int session_time);
+                           char proto, int session_time);
 
 /* radiusd.c */
 #undef debug_pair
@@ -545,10 +529,7 @@ void (*reset_signal(int signo, void (*func)(int)))(int);
 void           request_free(REQUEST **request);
 int                    request_opaque_free(REQUEST *request);
 int            rad_mkdir(char *directory, mode_t mode);
-int            rad_checkfilename(char const *filename);
-int            rad_file_exists(char const *filename);
 void           *rad_malloc(size_t size); /* calls exit(1) on error! */
-void           *rad_calloc(size_t size); /* calls exit(1) on error! */
 void           rad_const_free(void const *ptr);
 REQUEST                *request_alloc(TALLOC_CTX *ctx);
 REQUEST                *request_alloc_fake(REQUEST *oldreq);
@@ -563,13 +544,13 @@ void              *request_data_reference(REQUEST *request,
 int            rad_copy_string(char *dst, char const *src);
 int            rad_copy_string_bare(char *dst, char const *src);
 int            rad_copy_variable(char *dst, char const *from);
-int            rad_pps(int *past, int *present, time_t *then,
-                       struct timeval *now);
+uint32_t       rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now);
 int            rad_expand_xlat(REQUEST *request, char const *cmd,
                                int max_argc, char *argv[], bool can_fail,
                                size_t argv_buflen, char *argv_buf);
 void           rad_regcapture(REQUEST *request, int compare, char const *value,
                               regmatch_t rxmatch[]);
+void           verify_request(REQUEST *request);                       /* only for special debug builds */
 
 /* client.c */
 RADCLIENT_LIST *clients_init(CONF_SECTION *cs);
@@ -579,10 +560,11 @@ void              client_free(RADCLIENT *client);
 int            client_add(RADCLIENT_LIST *clients, RADCLIENT *client);
 #ifdef WITH_DYNAMIC_CLIENTS
 void           client_delete(RADCLIENT_LIST *clients, RADCLIENT *client);
-RADCLIENT      *client_from_query(TALLOC_CTX *ctx, char const *identifier, char const *secret, char const *shortname,
-                                  char const *type, char const *server, bool require_ma);
 RADCLIENT      *client_from_request(RADCLIENT_LIST *clients, REQUEST *request);
 #endif
+RADCLIENT      *client_from_query(TALLOC_CTX *ctx, char const *identifier, char const *secret, char const *shortname,
+                                  char const *type, char const *server, bool require_ma) CC_HINT(nonnull(2, 3));
+
 RADCLIENT      *client_find(RADCLIENT_LIST const *clients,
                             fr_ipaddr_t const *ipaddr, int proto);
 
@@ -598,54 +580,56 @@ int               pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int comp
 void           pairlist_free(PAIR_LIST **);
 
 /* version.c */
-int            ssl_check_version(void);
+int            rad_check_lib_magic(uint64_t magic);
+int            ssl_check_consistency(void);
+char const     *ssl_version_by_num(uint64_t version);
+char const     *ssl_version_range(uint64_t low, uint64_t high);
 char const     *ssl_version(void);
 void           version(void);
 
 /* auth.c */
-char   *auth_name(char *buf, size_t buflen, REQUEST *request, int do_cli);
+char   *auth_name(char *buf, size_t buflen, REQUEST *request, bool do_cli);
 int            rad_authenticate (REQUEST *);
 int            rad_postauth(REQUEST *);
 int            rad_virtual_server(REQUEST *);
 
 /* exec.c */
-pid_t radius_start_program(char const *cmd, REQUEST *request,
-                       int exec_wait,
-                       int *input_fd,
-                       int *output_fd,
-                       VALUE_PAIR *input_pairs,
-                       int shell_escape);
+pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
+                          int *input_fd, int *output_fd,
+                          VALUE_PAIR *input_pairs, bool shell_escape);
 int radius_readfrom_program(REQUEST *request, int fd, pid_t pid, int timeout,
                            char *answer, int left);
 int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool shell_escape,
                        char *user_msg, size_t msg_len, int timeout,
                        VALUE_PAIR *input_pairs, VALUE_PAIR **output_pairs);
-void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quench);
+void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quench)
+     CC_HINT(nonnull (3));
 
 /* valuepair.c */
 int paircompare_register(DICT_ATTR const *attribute, DICT_ATTR const *from,
-          bool first_only, RAD_COMPARE_FUNC func, void *instance);
+         bool first_only, RAD_COMPARE_FUNC func, void *instance);
 void           paircompare_unregister(DICT_ATTR const *attr, RAD_COMPARE_FUNC func);
 void           paircompare_unregister_instance(void *instance);
 int            paircompare(REQUEST *request, VALUE_PAIR *req_list,
                            VALUE_PAIR *check, VALUE_PAIR **rep_list);
+value_pair_tmpl_t *radius_xlat2tmpl(TALLOC_CTX *ctx, xlat_exp_t *xlat);
 int            radius_xlat_do(REQUEST *request, VALUE_PAIR *vp);
-void           radius_xlat_move(REQUEST *, VALUE_PAIR **to, VALUE_PAIR **from);
 int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp);
 int radius_callback_compare(REQUEST *request, VALUE_PAIR *req,
                            VALUE_PAIR *check, VALUE_PAIR *check_pairs,
                            VALUE_PAIR **reply_pairs);
 int radius_find_compare(DICT_ATTR const *attribute);
-VALUE_PAIR     *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
-                                  unsigned int attribute, unsigned int vendor);
-void module_failure_msg(REQUEST *request, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
+VALUE_PAIR     *radius_paircreate(TALLOC_CTX *ctx, VALUE_PAIR **vps, unsigned int attribute, unsigned int vendor);
+
+void module_failure_msg(REQUEST *request, char const *fmt, ...) CC_HINT(format (printf, 2, 3));
+void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap) CC_HINT(format (printf, 2, 0));
 
 /*
- *     Less code == less bugs
+ *     Less code == fewer bugs
+ *
+ * @param _a attribute
+ * @param _b value
+ * @param _c op
  */
 #define pairmake_packet(_a, _b, _c) pairmake(request->packet, &request->packet->vps, _a, _b, _c)
 #define pairmake_reply(_a, _b, _c) pairmake(request->reply, &request->reply->vps, _a, _b, _c)
@@ -656,41 +640,48 @@ void module_failure_msg(REQUEST *request, char const *fmt, ...)
 typedef size_t (*RADIUS_ESCAPE_STRING)(REQUEST *, char *out, size_t outlen, char const *in, void *arg);
 
 ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape,
-                   void *escape_ctx);
+                   void *escape_ctx)
+       CC_HINT(nonnull (1 ,3 ,4));
+
+ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+       CC_HINT(nonnull (1, 2, 3));
 
-ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape,
-                         void *escape_ctx);
+ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape,
+                           void *ctx)
+       CC_HINT(nonnull (1, 2, 3));
 
 typedef ssize_t (*RAD_XLAT_FUNC)(void *instance, REQUEST *, char const *, char *, size_t);
 int            xlat_register(char const *module, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape,
                              void *instance);
-void           xlat_unregister(char const *module, RAD_XLAT_FUNC func,
-                               void *instance);
+void           xlat_unregister(char const *module, RAD_XLAT_FUNC func, void *instance);
+ssize_t                xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt);
 void           xlat_free(void);
 
 /* threads.c */
-extern         int thread_pool_init(CONF_SECTION *cs, int *spawn_flag);
-extern         void thread_pool_stop(void);
-extern         int thread_pool_addrequest(REQUEST *, RAD_REQUEST_FUNP);
-extern         pid_t rad_fork(void);
-extern         pid_t rad_waitpid(pid_t pid, int *status);
-extern   int total_active_threads(void);
-extern   void thread_pool_lock(void);
-extern   void thread_pool_unlock(void);
-extern         void thread_pool_queue_stats(int array[RAD_LISTEN_MAX], int pps[2]);
+extern int thread_pool_init(CONF_SECTION *cs, bool *spawn_flag);
+extern void thread_pool_stop(void);
+extern int thread_pool_addrequest(REQUEST *, RAD_REQUEST_FUNP);
+extern pid_t rad_fork(void);
+extern pid_t rad_waitpid(pid_t pid, int *status);
+extern int total_active_threads(void);
+extern void thread_pool_lock(void);
+extern void thread_pool_unlock(void);
+extern void thread_pool_queue_stats(int array[RAD_LISTEN_MAX], int pps[2]);
 
 #ifndef HAVE_PTHREAD_H
 #define rad_fork(n) fork()
 #define rad_waitpid(a,b) waitpid(a,b, 0)
 #endif
 
-/* mainconfig.c */
+/* main_config.c */
 /* Define a global config structure */
-extern struct main_config_t mainconfig;
+extern struct main_config_t main_config;
 
-int read_mainconfig(int reload);
-int free_mainconfig(void);
-void hup_mainconfig(void);
+void set_radius_dir(TALLOC_CTX *ctx, char const *path);
+char const *get_radius_dir(void);
+int main_config_init(void);
+int main_config_free(void);
+void main_config_hup(void);
 void hup_logfile(void);
 void fr_suid_down(void);
 void fr_suid_up(void);
@@ -698,26 +689,30 @@ void fr_suid_down_permanent(void);
 
 /* listen.c */
 void listen_free(rad_listen_t **head);
-int listen_init(CONF_SECTION *cs, rad_listen_t **head, int spawn_flag);
-rad_listen_t *proxy_new_listener(home_server *home, int src_port);
-RADCLIENT *client_listener_find(rad_listen_t *listener,
-                               fr_ipaddr_t const *ipaddr, int src_port);
+int listen_init(CONF_SECTION *cs, rad_listen_t **head, bool spawn_flag);
+rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port);
+RADCLIENT *client_listener_find(rad_listen_t *listener, fr_ipaddr_t const *ipaddr, uint16_t src_port);
 
 #ifdef WITH_STATS
-RADCLIENT_LIST *listener_find_client_list(fr_ipaddr_t const *ipaddr,
-                                         int port);
+RADCLIENT_LIST *listener_find_client_list(fr_ipaddr_t const *ipaddr, uint16_t port);
 #endif
-rad_listen_t *listener_find_byipaddr(fr_ipaddr_t const *ipaddr, int port,
-                                    int proto);
+rad_listen_t *listener_find_byipaddr(fr_ipaddr_t const *ipaddr, uint16_t port, int proto);
 int rad_status_server(REQUEST *request);
 
 /* event.c */
-int radius_event_init(CONF_SECTION *cs, int spawn_flag);
+typedef enum event_corral_t {
+       EVENT_CORRAL_MAIN = 0,  //!< Always main thread event list
+       EVENT_CORRAL_AUX        //!< Maybe main thread or one shared by modules
+} event_corral_t;
+
+fr_event_list_t *radius_event_list_corral(event_corral_t hint);
+int radius_event_init(TALLOC_CTX *ctx);
+int radius_event_start(CONF_SECTION *cs, bool spawn_flag);
 void radius_event_free(void);
 int radius_event_process(void);
-int event_new_fd(rad_listen_t *listener);
+void radius_update_listener(rad_listen_t *listener);
 void revive_home_server(void *ctx);
-void mark_home_server_dead(home_server *home, struct timeval *when);
+void mark_home_server_dead(home_server_t *home, struct timeval *when);
 
 /* evaluate.c */
 typedef struct fr_cond_t fr_cond_t;
@@ -729,23 +724,27 @@ int radius_evaluate_map(REQUEST *request, int modreturn, int depth,
                        fr_cond_t const *c);
 int radius_evaluate_cond(REQUEST *request, int modreturn, int depth,
                         fr_cond_t const *c);
-void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from);
+void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat) CC_HINT(nonnull);
 
 VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list);
+TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list_name);
 pair_lists_t radius_list_name(char const **name, pair_lists_t unknown);
 int radius_request(REQUEST **request, request_refs_t name);
 request_refs_t radius_request_name(char const **name, request_refs_t unknown);
 
 int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map);
-int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx);
-int radius_map2request(REQUEST *request, value_pair_map_t const *map,
-                      char const *src, radius_tmpl_getvalue_t func, void *ctx);
-
-int radius_str2vp(REQUEST *request, char const *raw,
-                 request_refs_t dst_request_def, pair_lists_t dst_list_def,
-                 request_refs_t src_request_def, pair_lists_t src_list_def);
-VALUE_PAIR *radius_vpt_get_vp(REQUEST *request, value_pair_tmpl_t const *vpt);
-int radius_get_vp(VALUE_PAIR **vp_p, REQUEST *request, char const *name);
+int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx) CC_HINT(nonnull (1,2,3));
+void radius_map_debug(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR const *vp) CC_HINT(nonnull(1, 2));
+int radius_map2request(REQUEST *request, value_pair_map_t const *map, radius_tmpl_getvalue_t func, void *ctx);
+
+int radius_strpair2map(value_pair_map_t **out, REQUEST *request, char const *raw,
+                      request_refs_t dst_request_def, pair_lists_t dst_list_def,
+                      request_refs_t src_request_def, pair_lists_t src_list_def);
+bool radius_map_dst_valid(REQUEST *request, value_pair_map_t const *map);
+int radius_tmpl_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt);
+int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name);
+int radius_tmpl_copy_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt);
+int radius_copy_vp(VALUE_PAIR **out, REQUEST *request, char const *name);
 
 #ifdef WITH_TLS
 /*
@@ -757,6 +756,11 @@ int proxy_tls_recv(rad_listen_t *listener);
 int proxy_tls_send(rad_listen_t *listener, REQUEST *request);
 #endif
 
+/*
+ *     For radmin over TCP.
+ */
+#define PW_RADMIN_PORT 18120
+
 #ifdef __cplusplus
 }
 #endif
index 0f3f999..608be28 100644 (file)
@@ -1,24 +1,26 @@
 /*
- *  radsniff.h Structures and defines for the RADIUS sniffer.
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
  *
- *  Version:    $Id$
+ *   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.
  *
- *  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
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file radsniff.h
+ * @brief Structures and prototypes for the RADIUS sniffer.
  *
- *  Copyright 2006  The FreeRADIUS server project
- *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
+ * @copyright 2013 Arran Cudbard-Bell <arran.cudbardb@freeradius.org>
+ * @copyright 2006 The FreeRADIUS server project
+ * @copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
  */
 
 RCSIDH(radsniff_h, "$Id$")
@@ -26,74 +28,286 @@ RCSIDH(radsniff_h, "$Id$")
 #include <sys/types.h>
 #include <netinet/in.h>
 
+#include <pcap/pcap.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/pcap.h>
+#include <freeradius-devel/event.h>
+
+#ifdef HAVE_COLLECTDC_H
+#  include <collectd/client.h>
+#endif
+
+#define RS_DEFAULT_PREFIX      "radsniff"      //!< Default instance
+#define RS_DEFAULT_SECRET      "testing123"    //!< Default secret
+#define RS_DEFAULT_TIMEOUT     5200            //!< Standard timeout of 5s + 300ms to cover network latency
+#define RS_FORCE_YIELD         1000            //!< Service another descriptor every X number of packets
+#define RS_RETRANSMIT_MAX      5               //!< Maximum number of times we expect to see a packet retransmitted
+#define RS_MAX_ATTRS           50              //!< Maximum number of attributes we can filter on.
+#define RS_SOCKET_REOPEN_DELAY  5000           //!< How long we delay re-opening a collectd socket.
+
 /*
- *     The number of bytes in an ethernet (MAC) address.
+ *     Logging macros
  */
-#define ETHER_ADDR_LEN         6
+#undef DEBUG2
+#define DEBUG2(fmt, ...)       if (fr_debug_flag > 2) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
+#undef DEBUG
+#define DEBUG(fmt, ...)                if (fr_debug_flag > 1) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
+#undef INFO
+#define INFO(fmt, ...)         if (fr_debug_flag > 0) fprintf(fr_log_fp , fmt "\n", ## __VA_ARGS__)
 
-/*
- *     Structure of a DEC/Intel/Xerox or 802.3 Ethernet header.
+#define ERROR(fmt, ...)                fr_perror("radsniff: " fmt, ## __VA_ARGS__)
+
+#define RIDEBUG_ENABLED()      (conf->print_packet && (fr_debug_flag > 0))
+#define RDEBUG_ENABLED()       (conf->print_packet && (fr_debug_flag > 1))
+#define RDEBUG_ENABLED2()      (conf->print_packet && (fr_debug_flag > 2))
+
+#define REDEBUG(fmt, ...)      if (conf->print_packet) fr_perror("%s (%" PRIu64 ") " fmt , timestr, count, ## __VA_ARGS__)
+#define RIDEBUG(fmt, ...)      if (conf->print_packet && (fr_debug_flag > 0)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+#define RDEBUG(fmt, ...)       if (conf->print_packet && (fr_debug_flag > 1)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+#define RDEBUG2(fmt, ...)      if (conf->print_packet && (fr_debug_flag > 2)) fprintf(fr_log_fp , "%s (%" PRIu64 ") " fmt "\n", timestr, count, ## __VA_ARGS__)
+
+typedef enum {
+       RS_NORMAL       = 0x01,
+       RS_UNLINKED     = 0x02,
+       RS_RTX          = 0x04,
+       RS_REUSED       = 0x08,
+       RS_ERROR        = 0x10,
+       RS_LOST         = 0x20
+} rs_status_t;
+
+typedef void (*rs_packet_logger_t)(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
+                                  struct timeval *elapsed, struct timeval *latency, bool response, bool body);
+typedef enum {
+#ifdef HAVE_COLLECTDC_H
+       RS_STATS_OUT_COLLECTD = 1,
+#endif
+       RS_STATS_OUT_STDIO
+} stats_out_t;
+
+typedef struct rs rs_t;
+
+#ifdef HAVE_COLLECTDC_H
+typedef struct rs_stats_tmpl rs_stats_tmpl_t;
+typedef struct rs_stats_value_tmpl rs_stats_value_tmpl_t;
+#endif
+
+typedef struct rs_counters {
+       uint64_t type[PW_CODE_MAX];
+} rs_counters_t;
+
+/** Stats for a single interval
+ *
+ * And interval is defined as the time between a call to the stats output function.
  */
-struct  ethernet_header {
-       uint8_t ethernet_dhost[ETHER_ADDR_LEN];
-       uint8_t ethernet_shost[ETHER_ADDR_LEN];
-       uint16_t        ethernet_type;
-};
+typedef struct rs_latency {
+       int                     intervals;                      //!< Number of stats intervals.
 
-/*
- *     Length of a DEC/Intel/Xerox or 802.3 Ethernet header.
- *     Note that some compilers may pad "struct ether_header" to
- *     a multiple of 4 *bytes, for example, so "sizeof (struct
- *     ether_header)" may not give the right answer.
+       double                  latency_smoothed;               //!< Smoothed moving average.
+       uint64_t                latency_smoothed_count;         //!< Number of CMA datapoints processed.
+
+       struct {
+               uint64_t                received_total;         //!< Total received over interval.
+               uint64_t                linked_total;           //!< Total request/response pairs over interval.
+               uint64_t                unlinked_total;         //!< Total unlinked over interval.
+               uint64_t                reused_total;           //!< Total reused over interval.
+               uint64_t                lost_total;             //!< Total packets definitely lost in this interval.
+               uint64_t                rt_total[RS_RETRANSMIT_MAX + 1];        //!< Number of RTX until complete
+                                                                               //!< over interval.
+
+
+               double                  received;               //!< Number of this type of packet we've received.
+               double                  linked;                 //!< Number of request/response pairs
+               double                  unlinked;               //!< Response with no request.
+               double                  reused;                 //!< ID re-used too quickly.
+               double                  lost;                   //!< Never got a response to a request.
+               double                  rt[RS_RETRANSMIT_MAX + 1];      //!< Number of times we saw the same
+                                                                       //!< request packet.
+
+               long double             latency_total;          //!< Total latency between requests/responses in the
+                                                               //!< interval.
+               double                  latency_average;        //!< Average latency (this iteration).
+
+               double                  latency_high;           //!< Latency high water mark.
+               double                  latency_low;            //!< Latency low water mark.
+       } interval;
+} rs_latency_t;
+
+typedef struct rs_malformed {
+       uint64_t                min_length_packet;
+       uint64_t                min_length_field;
+       uint64_t                min_length_mimatch;
+       uint64_t                header_overflow;
+       uint64_t                invalid_attribute;
+       uint64_t                attribute_too_short;
+       uint64_t                attribute_overflow;
+       uint64_t                ma_invalid_length;
+       uint64_t                attribute_underflow;
+       uint64_t                too_many_attributes;
+       uint64_t                ma_missing;
+} rs_malformed_t;
+
+/** One set of statistics
+ *
  */
-#define ETHER_HDRLEN           14
+typedef struct rs_stats {
+       int                     intervals;              //!< Number of stats intervals.
 
-/*
- *     Structure of an internet header, naked of options.
+       rs_latency_t            exchange[PW_CODE_MAX];  //!< We end up allocating ~16K, but memory is cheap so
+                                                       //!< what the hell.  This is required because instances of
+                                                       //!< FreeRADIUS delay Access-Rejects, which would artificially
+                                                       //!< increase latency stats for Access-Requests.
+
+       struct timeval          quiet;                  //!< We may need to 'mute' the stats if libpcap starts
+                                                       //!< dropping packets, or we run out of memory.
+} rs_stats_t;
+
+/** Wrapper for RADIUS_PACKET
+ *
+ * Allows an event to be associated with a request packet.  This is required because we need to disarm
+ * the event timer when a response is received, so we don't erroneously log the response as lost.
+ */
+typedef struct rs_request {
+       uint64_t                id;                     //!< Monotonically increasing packet counter.
+       fr_event_t              *event;                 //!< Event created when we received the original request.
+
+       struct timeval          when;                   //!< Time when the packet was received, or next time an event
+                                                       //!< is scheduled.
+       fr_pcap_t               *in;                    //!< PCAP handle the original request was received on.
+       RADIUS_PACKET           *packet;                //!< The original packet.
+       RADIUS_PACKET           *expect;                //!< Request/response.
+       RADIUS_PACKET           *linked;                //!< The subsequent response or forwarded request the packet
+                                                       //!< was linked against.
+
+       uint64_t                rt_req;                 //!< Number of times we saw the same request packet.
+       uint64_t                rt_rsp;                 //!< Number of times we saw a retransmitted response
+                                                       //!< packet.
+       rs_latency_t            *stats_req;             //!< Latency entry for the request type.
+       rs_latency_t            *stats_rsp;             //!< Latency entry for the request type.
+
+       bool                    silent_cleanup;         //!< Cleanup was forced before normal expiry period,
+                                                       //!< ignore stats about packet loss.
+
+       VALUE_PAIR              *link_vps;              //!< VALUE_PAIRs used to link retransmissions.
+
+       bool                    in_request_tree;        //!< Whether the request is currently in the request tree.
+       bool                    in_link_tree;           //!< Whether the request is currently in the linked tree.
+} rs_request_t;
+
+/** Statistic write/print event
+ *
+ */
+typedef struct rs_event {
+       fr_event_list_t         *list;                  //!< The event list.
+
+       fr_pcap_t               *in;                    //!< PCAP handle event occurred on.
+       fr_pcap_t               *out;                   //!< Where to write output.
+
+       rs_stats_t              *stats;                 //!< Where to write stats.
+} rs_event_t;
+
+/** FD data which gets passed to callbacks
+ *
+ */
+typedef struct rs_update {
+       fr_event_list_t         *list;                  //!< List to insert new event into.
+
+       fr_pcap_t               *in;                    //!< Linked list of PCAP handles to check for drops.
+       rs_stats_t              *stats;                 //!< Stats to process.
+} rs_update_t;
+
+
+struct rs {
+       bool                    from_file;              //!< Were reading pcap data from files.
+       bool                    from_dev;               //!< Were reading pcap data from devices.
+       bool                    from_stdin;             //!< Were reading pcap data from stdin.
+       bool                    to_file;                //!< Were writing pcap data to files.
+       bool                    to_stdout;              //!< Were writing pcap data to stdout.
+
+       bool                    daemonize;              //!< Daemonize and write PID out to file.
+       char const              *pidfile;               //!< File to write PID to.
+
+       bool                    from_auto;              //!< From list was auto-generated.
+       bool                    promiscuous;            //!< Capture in promiscuous mode.
+       bool                    print_packet;           //!< Print packet info, disabled with -W
+       bool                    decode_attrs;           //!< Whether we should decode attributes in the request
+                                                       //!< and response.
+       bool                    verify_udp_checksum;    //!< Check UDP checksum in packets.
+
+       char const              *radius_secret;         //!< Secret to decode encrypted attributes.
+
+       char                    *pcap_filter;           //!< PCAP filter string applied to live capture devices.
+
+       char                    *list_attributes;       //!< Raw attribute filter string.
+       DICT_ATTR const         *list_da[RS_MAX_ATTRS]; //!< Output CSV with these attribute values.
+       int                     list_da_num;
+
+       char                    *link_attributes;       //!< Names of DICT_ATTRs to use for rtx.
+       DICT_ATTR const         *link_da[RS_MAX_ATTRS]; //!< DICT_ATTRs to link on.
+       int                     link_da_num;            //!< Number of rtx DICT_ATTRs.
+
+       char const              *filter_request;        //!< Raw request filter string.
+       char const              *filter_response;       //!< Raw response filter string.
+
+       VALUE_PAIR              *filter_request_vps;    //!< Sorted filter vps.
+       VALUE_PAIR              *filter_response_vps;   //!< Sorted filter vps.
+       PW_CODE                 filter_request_code;    //!< Filter request packets by code.
+       PW_CODE                 filter_response_code;   //!< Filter response packets by code.
+
+       rs_status_t             event_flags;            //!< Events we log and capture on.
+       rs_packet_logger_t      logger;                 //!< Packet logger
+
+       int                     buffer_pkts;            //!< Size of the ring buffer to setup for live capture.
+       uint64_t                limit;                  //!< Maximum number of packets to capture
+
+       struct {
+               int                     interval;               //!< Time between stats updates in seconds.
+               stats_out_t             out;                    //!< Where to write stats.
+               int                     timeout;                //!< Maximum length of time we wait for a response.
+
+#ifdef HAVE_COLLECTDC_H
+               char const              *collectd;              //!< Collectd server/port/unixsocket
+               char const              *prefix;                //!< Prefix collectd stats with this value.
+               lcc_connection_t        *handle;                //!< Collectd client handle.
+               rs_stats_tmpl_t         *tmpl;                  //!< The stats templates we created on startup.
+#endif
+       } stats;
+};
+
+#ifdef HAVE_COLLECTDC_H
+
+/** Callback for processing stats values.
+ *
  */
-struct ip_header {
-       uint8_t ip_vhl;  /* header length, version */
-#define IP_V(ip)       (((ip)->ip_vhl & 0xf0) >> 4)
-#define IP_HL(ip)       ((ip)->ip_vhl & 0x0f)
-       uint8_t ip_tos;  /* type of service */
-       uint16_t       ip_len;   /* total length */
-       uint16_t       ip_id;     /* identification */
-       uint16_t       ip_off;   /* fragment offset field */
-#define I_DF 0x4000                /* dont fragment flag */
-#define IP_MF 0x2000               /* more fragments flag */
-#define IP_OFFMASK 0x1fff             /* mask for fragmenting bits */
-       uint8_t ip_ttl;  /* time to live */
-       uint8_t ip_p;      /* protocol */
-       uint16_t       ip_sum;   /* checksum */
-       struct in_addr  ip_src,ip_dst;  /* source and dest address */
+typedef void (*rs_stats_cb_t)(rs_t *conf, rs_stats_value_tmpl_t *tmpl);
+struct rs_stats_value_tmpl {
+       void                    *src;                   //!< Pointer to source field in struct. Must be set by
+                                                       //!< stats_collectdc_init caller.
+       int                     type;                   //!< Stats type.
+       rs_stats_cb_t           cb;                     //!< Callback used to process stats
+       void                    *dst;                   //!< Pointer to dst field in value struct. Must be set
+                                                       //!< by stats_collectdc_init caller.
 };
 
-/*
- *     UDP protocol header.
- *     Per RFC 768, September, 1981.
+/** Stats templates
+ *
+ * This gets processed to turn radsniff stats structures into collectd lcc_value_list_t structures.
  */
-struct udp_header {
-       uint16_t       udp_sport;              /* source port */
-       uint16_t       udp_dport;              /* destination port */
-       uint16_t       udp_ulen;                /* udp length */
-       uint16_t       udp_sum;          /* udp checksum */
+struct rs_stats_tmpl
+{
+       rs_stats_value_tmpl_t   *value_tmpl;            //!< Value template
+       void                    *stats;                 //!< Struct containing the raw stats to process
+       lcc_value_list_t        *value;                 //!< Collectd stats struct to populate
+
+       rs_stats_tmpl_t         *next;                  //!< Next...
 };
 
 /*
- *     RADIUS packet length.
- *     RFC 2865, Section 3., subsection 'length' says:
- *     " ... and maximum length is 4096."
+ *     collectd.c - Registration and processing functions
  */
-#define MAX_RADIUS_LEN 4096
-#define MIN_RADIUS_LEN 20
-#define SNAPLEN (sizeof(struct ethernet_header) + sizeof(struct ip_header) + sizeof(struct udp_header) + MAX_RADIUS_LEN)
-
-typedef struct radius_packet_t {
-       uint8_t       code;
-       uint8_t       id;
-       uint8_t       length[2];
-       uint8_t       vector[AUTH_VECTOR_LEN];
-       uint8_t       data[1];
-} radius_packet_t;
-
-#define AUTH_HDR_LEN 20
+rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
+                                               char const *type, rs_latency_t *stats, PW_CODE code);
+void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now);
+int rs_stats_collectd_open(rs_t *conf);
+int rs_stats_collectd_close(rs_t *conf);
+
+#endif
index 9d81556..43552f6 100644 (file)
@@ -15,29 +15,35 @@ RCSIDH(realms_h, "$Id$")
 extern "C" {
 #endif
 
-#define HOME_TYPE_INVALID (0)
-#define HOME_TYPE_AUTH    (1)
-#define HOME_TYPE_ACCT    (2)
+typedef enum {
+       HOME_TYPE_INVALID = 0,
+       HOME_TYPE_AUTH,
+       HOME_TYPE_ACCT
 #ifdef WITH_COA
-#define HOME_TYPE_COA     (3)
+       ,HOME_TYPE_COA
 #endif
+} home_type_t;
 
-#define HOME_PING_CHECK_NONE           (0)
-#define HOME_PING_CHECK_STATUS_SERVER  (1)
-#define HOME_PING_CHECK_REQUEST                (2)
+typedef enum {
+       HOME_PING_CHECK_NONE = 0,
+       HOME_PING_CHECK_STATUS_SERVER,
+       HOME_PING_CHECK_REQUEST
+} home_ping_check_t;
 
-#define HOME_STATE_ALIVE               (0)
-#define HOME_STATE_ZOMBIE              (1)
-#define HOME_STATE_IS_DEAD             (2)
-#define HOME_STATE_UNKNOWN             (3)
+typedef enum {
+       HOME_STATE_ALIVE = 0,
+       HOME_STATE_ZOMBIE,
+       HOME_STATE_IS_DEAD,
+       HOME_STATE_UNKNOWN
+} home_state_t;
 
 typedef struct fr_socket_limit_t {
-       int             max_connections;
-       int             num_connections;
-       int             max_requests;
-       int             num_requests;
-       int             lifetime;
-       int             idle_timeout;
+       uint32_t        max_connections;
+       uint32_t        num_connections;
+       uint32_t        max_requests;
+       uint32_t        num_requests;
+       uint32_t        lifetime;
+       uint32_t        idle_timeout;
 } fr_socket_limit_t;
 
 typedef struct home_server {
@@ -49,7 +55,7 @@ typedef struct home_server {
 
        fr_ipaddr_t     ipaddr;
 
-       int             port;
+       uint16_t        port;
        int             type;           /* auth/acct */
 
        int             proto;
@@ -62,15 +68,16 @@ typedef struct home_server {
        fr_event_t      *ev;
        struct timeval  when;
 
-       int             response_window;
-       int             max_outstanding; /* don't overload it */
-       int             currently_outstanding;
+       struct timeval  response_window;
+       uint32_t        max_outstanding; /* don't overload it */
+       uint32_t        currently_outstanding;
 
        time_t          last_packet_sent;
        time_t          last_packet_recv;
+       time_t          last_failed_open;
        struct timeval  revive_time;
        struct timeval  zombie_period_start;
-       int             zombie_period; /* unresponsive for T, mark it dead */
+       uint32_t        zombie_period; /* unresponsive for T, mark it dead */
 
        int             state;
 
@@ -78,19 +85,19 @@ typedef struct home_server {
        char const      *ping_user_name;
        char const      *ping_user_password;
 
-       int             ping_interval;
-       int             num_pings_to_alive;
-       int             num_sent_pings;
-       int             num_received_pings;
-       int             ping_timeout;
+       uint32_t        ping_interval;
+       uint32_t        num_pings_to_alive;
+       uint32_t        num_sent_pings;
+       uint32_t        num_received_pings;
+       uint32_t        ping_timeout;
 
-       int             revive_interval; /* if it doesn't support pings */
+       uint32_t        revive_interval; /* if it doesn't support pings */
        CONF_SECTION    *cs;
 #ifdef WITH_COA
-       int                     coa_irt;
-       int                     coa_mrc;
-       int                     coa_mrt;
-       int                     coa_mrd;
+       uint32_t        coa_irt;
+       uint32_t        coa_mrc;
+       uint32_t        coa_mrt;
+       uint32_t        coa_mrd;
 #endif
 #ifdef WITH_TLS
        fr_tls_server_conf_t    *tls;
@@ -103,7 +110,7 @@ typedef struct home_server {
 
        fr_stats_ema_t  ema;
 #endif
-} home_server;
+} home_server_t;
 
 
 typedef enum home_pool_type_t {
@@ -125,12 +132,12 @@ typedef struct home_pool_t {
 
        char const              *virtual_server; /* for pre/post-proxy */
 
-       home_server             *fallback;
+       home_server_t           *fallback;
        int                     in_fallback;
        time_t                  time_all_dead;
 
        int                     num_home_servers;
-       home_server             *servers[1];
+       home_server_t           *servers[1];
 } home_pool_t;
 
 
@@ -150,19 +157,19 @@ int realms_init(CONF_SECTION *config);
 void realms_free(void);
 REALM *realm_find(char const *name); /* name is from a packet */
 REALM *realm_find2(char const *name); /* ... with name taken from realm_find */
-  int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual);
+  int realms_home_server_add(home_server_t *home, CONF_SECTION *cs, int dual);
   int realms_pool_add(home_pool_t *pool, CONF_SECTION *cs);
   int realms_realm_add( REALM *r, CONF_SECTION *cs);
 
 
-void home_server_update_request(home_server *home, REQUEST *request);
-home_server *home_server_ldb(char const *realmname, home_pool_t *pool, REQUEST *request);
-home_server *home_server_find(fr_ipaddr_t *ipaddr, int port, int proto);
+void home_server_update_request(home_server_t *home, REQUEST *request);
+home_server_t *home_server_ldb(char const *realmname, home_pool_t *pool, REQUEST *request);
+home_server_t *home_server_find(fr_ipaddr_t *ipaddr, uint16_t port, int proto);
 #ifdef WITH_COA
-home_server *home_server_byname(char const *name, int type);
+home_server_t *home_server_byname(char const *name, int type);
 #endif
 #ifdef WITH_STATS
-home_server *home_server_bynumber(int number);
+home_server_t *home_server_bynumber(int number);
 #endif
 home_pool_t *home_pool_byname(char const *name, int type);
 
index a519074..ab32f8c 100644 (file)
@@ -30,7 +30,7 @@ RCSIDH(soh_h, "$Id$")
 extern "C" {
 #endif
 
-int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len);
+int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) CC_HINT(nonnull);
 uint16_t soh_pull_be_16(uint8_t const *p);
 uint32_t soh_pull_be_24(uint8_t const *p);
 uint32_t soh_pull_be_32(uint8_t const *p);
index c59f79a..e76e7e9 100644 (file)
@@ -37,29 +37,28 @@ typedef uint32_t fr_uint_t;
 
 #ifdef WITH_STATS
 typedef struct fr_stats_t {
-       fr_uint_t               total_requests;
-       fr_uint_t               total_invalid_requests;
-       fr_uint_t               total_dup_requests;
-       fr_uint_t               total_responses;
-       fr_uint_t               total_access_accepts;
-       fr_uint_t               total_access_rejects;
-       fr_uint_t               total_access_challenges;
-       fr_uint_t               total_malformed_requests;
-       fr_uint_t               total_bad_authenticators;
-       fr_uint_t               total_packets_dropped;
-       fr_uint_t               total_no_records;
-       fr_uint_t               total_unknown_types;
-       fr_uint_t               total_timeouts;
-       time_t                  last_packet;
-       fr_uint_t               elapsed[8];
+       fr_uint_t       total_requests;
+       fr_uint_t       total_invalid_requests;
+       fr_uint_t       total_dup_requests;
+       fr_uint_t       total_responses;
+       fr_uint_t       total_access_accepts;
+       fr_uint_t       total_access_rejects;
+       fr_uint_t       total_access_challenges;
+       fr_uint_t       total_malformed_requests;
+       fr_uint_t       total_bad_authenticators;
+       fr_uint_t       total_packets_dropped;
+       fr_uint_t       total_no_records;
+       fr_uint_t       total_unknown_types;
+       fr_uint_t       total_timeouts;
+       time_t          last_packet;
+       fr_uint_t       elapsed[8];
 } fr_stats_t;
 
 typedef struct fr_stats_ema_t {
-       int             window;
-
-       int             f1, f10;
-       int             ema1, ema10;
+       uint32_t        window;
 
+       uint32_t        f1, f10;
+       uint32_t        ema1, ema10;
 } fr_stats_ema_t;
 
 extern fr_stats_t      radius_auth_stats;
index c2a7a5d..13a47b5 100644 (file)
@@ -13,10 +13,10 @@ RCSIDH(sysutmp_h, "$Id$")
  *  If we have BOTH utmp.h and utmpx.h, then
  *  we prefer to use utmp.h, but only on systems other than Solaris.
  */
-#if !defined(sun) && !defined(sgi) && !defined(hpux)
-#ifdef HAVE_UTMP_H
-#undef HAVE_UTMPX_H
-#endif
+#if !defined(__sun) && !defined(sgi) && !defined(hpux)
+#  ifdef HAVE_UTMP_H
+#    undef HAVE_UTMPX_H
+#  endif
 #endif
 
 #if defined(HAVE_UTMP_H) || defined(HAVE_UTMPX_H)
index 5fb86c5..24c6a21 100644 (file)
 
 RCSIDH(tcp_h, "$Id$")
 
-int fr_tcp_socket(fr_ipaddr_t *ipaddr, int port);
-int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, int dst_port);
+int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr, fr_ipaddr_t *dst_ipaddr, uint16_t dst_port);
 int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags);
 RADIUS_PACKET *fr_tcp_recv(int sockfd, int flags);
-RADIUS_PACKET *fr_tcp_accept(int sockfd);
-ssize_t fr_tcp_write_packet(RADIUS_PACKET *packet);
 #endif /* FR_TCP_H */
index afe33d1..55af56c 100644 (file)
@@ -97,10 +97,12 @@ static inline _t __fr_thread_local_init_##_n(pthread_destructor_t func)\
        (void) pthread_once(&__fr_thread_local_once_##_n, __fr_thread_local_key_init_##_n);\
        return pthread_getspecific(__fr_thread_local_key_##_n);\
 }\
+DIAG_OFF(unused-function)\
 static inline _t __fr_thread_local_get_##_n(void)\
 {\
        return pthread_getspecific(__fr_thread_local_key_##_n);\
 }\
+DIAG_ON(unused-function)\
 static inline int __fr_thread_local_set_##_n(_t val)\
 {\
        return pthread_setspecific(__fr_thread_local_key_##_n, val);\
index 20ec1e6..4a1df0b 100644 (file)
@@ -138,6 +138,7 @@ typedef struct _tls_session_t {
        unsigned int    (*record_minus)(record_t *buf, void *ptr,
                                        unsigned int size);
 
+       bool            invalid_hb_used;
 
        /*
         * Framed-MTU attribute in RADIUS,
@@ -294,6 +295,8 @@ int         cbtls_verify(int ok, X509_STORE_CTX *ctx);
 
 /* TLS */
 void           tls_global_init(void);
+int            tls_global_version_check(char const *acknowledged);
+void           tls_global_cleanup(void);
 tls_session_t  *tls_new_session(fr_tls_server_conf_t *conf, REQUEST *request,
                               int client_cert);
 tls_session_t  *tls_new_client_session(fr_tls_server_conf_t *conf, int fd);
@@ -317,55 +320,57 @@ void              session_free(void *ssn);
 void           session_close(tls_session_t *ssn);
 void           session_init(tls_session_t *ssn);
 
-#define FR_TLS_EX_INDEX_HANDLER (0)
-#define FR_TLS_EX_INDEX_CONF   (1)
-#define FR_TLS_EX_INDEX_REQUEST        (2)
-#define FR_TLS_EX_INDEX_CERTS  (3)
-#define FR_TLS_EX_INDEX_IDENTITY (4)
-#define FR_TLS_EX_INDEX_STORE  (6)
-#define FR_TLS_EX_INDEX_SSN    (7)
+#define FR_TLS_EX_INDEX_HANDLER  (10)
+#define FR_TLS_EX_INDEX_CONF    (11)
+#define FR_TLS_EX_INDEX_REQUEST         (12)
+#define FR_TLS_EX_INDEX_IDENTITY (13)
+#define FR_TLS_EX_INDEX_STORE   (14)
+#define FR_TLS_EX_INDEX_SSN     (15)
+#define FR_TLS_EX_INDEX_TALLOC  (16)
+
+extern int FR_TLS_EX_INDEX_CERTS;
 
 /* configured values goes right here */
 struct fr_tls_server_conf_t {
        SSL_CTX         *ctx;
        CONF_SECTION    *cs;
 
-       char            *private_key_password;
-       char            *private_key_file;
-       char            *certificate_file;
-       char            *random_file;
-       char            *ca_path;
-       char            *ca_file;
-       char            *dh_file;
-       char            *rsa_file;
+       char const      *private_key_password;
+       char const      *private_key_file;
+       char const      *certificate_file;
+       char const      *random_file;
+       char const      *ca_path;
+       char const      *ca_file;
+       char const      *dh_file;
+       char const      *rsa_file;
        bool            rsa_key;
        bool            dh_key;
-       int             rsa_key_length;
-       int             dh_key_length;
-       int             verify_depth;
+       uint32_t        rsa_key_length;
+       uint32_t        dh_key_length;
+       uint32_t        verify_depth;
        bool            file_type;
        bool            include_length;
 
        /*
         *      Always < 4096 (due to radius limit), 0 by default = 2048
         */
-       int             fragment_size;
+       uint32_t        fragment_size;
        bool            check_crl;
        bool            allow_expired_crl;
-       char            *check_cert_cn;
-       char            *cipher_list;
-       char            *check_cert_issuer;
+       char const      *check_cert_cn;
+       char const      *cipher_list;
+       char const      *check_cert_issuer;
 
        bool            session_cache_enable;
-       int             session_timeout;
-       int             session_cache_size;
-       char            *session_id_name;
-       char            *session_cache_path;
+       uint32_t        session_timeout;
+       uint32_t        session_cache_size;
+       char const      *session_id_name;
+       char const      *session_cache_path;
        char            session_context_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
        time_t          session_last_flushed;
 
-       char            *verify_tmp_dir;
-       char            *verify_client_cert_cmd;
+       char const      *verify_tmp_dir;
+       char const      *verify_client_cert_cmd;
        bool            require_client_cert;
 
 #ifdef HAVE_OPENSSL_OCSP_H
@@ -374,22 +379,22 @@ struct fr_tls_server_conf_t {
         */
        bool            ocsp_enable;
        bool            ocsp_override_url;
-       char            *ocsp_url;
+       char const      *ocsp_url;
        bool            ocsp_use_nonce;
        X509_STORE      *ocsp_store;
-       int             ocsp_timeout;
+       uint32_t        ocsp_timeout;
        bool            ocsp_softfail;
 #endif
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
-       char            *ecdh_curve;
+       char const      *ecdh_curve;
 #endif
 #endif
 
 #ifdef PSK_MAX_IDENTITY_LEN
-       char            *psk_identity;
-       char            *psk_password;
+       char const      *psk_identity;
+       char const      *psk_password;
 #endif
 
 };
index ea967f6..243b8a4 100644 (file)
@@ -54,7 +54,7 @@ typedef enum fr_token_t {
   T_OP_REG_EQ,                 /* =~ */
   T_OP_REG_NE,                 /* !~ */
   T_OP_CMP_TRUE,               /* =*           20 */
-  T_OP_CMP_FALSE,             /* !* */
+  T_OP_CMP_FALSE,              /* !* */
   T_OP_CMP_EQ,                 /* == */
   T_HASH,                      /* # */
   T_BARE_WORD,                 /* bare word */
@@ -80,10 +80,9 @@ char const *fr_int2str(FR_NAME_NUMBER const *table, int number,
                         char const *def);
 
 
-int            getword (char const **ptr, char *buf, int buflen);
-int            getbareword (char const **ptr, char *buf, int buflen);
-FR_TOKEN       gettoken(char const **ptr, char *buf, int buflen);
-FR_TOKEN       getstring(char const **ptr, char *buf, int buflen);
+int            getword (char const **ptr, char *buf, int buflen, bool unescape);
+FR_TOKEN       gettoken(char const **ptr, char *buf, int buflen, bool unescape);
+FR_TOKEN       getstring(char const **ptr, char *buf, int buflen, bool unescape);
 char const     *fr_token_name(int);
 
 #ifdef __cplusplus
index 8add30a..fffbb23 100644 (file)
@@ -457,7 +457,7 @@ DAMAGES.
 
                     END OF TERMS AND CONDITIONS
 \f
-           How to Apply These Terms to Your New Libraries
+          How to Apply These Terms to Your New Libraries
 
   If you develop a new library, and you want it to be of the greatest
 possible use to the public, we recommend making it free software that
index d14d689..b215cb1 100644 (file)
@@ -5,14 +5,14 @@
 #
 TARGET         := libfreeradius-radius.a
 
-SOURCES                := 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 event.c getaddrinfo.c \
-                 heap.c tcp.c base64.c
+SOURCES                := cbuff.c cursor.c debug.c dict.c filters.c hash.c hmac.c hmacsha1.c \
+                  isaac.c log.c  misc.c missing.c md4.c md5.c pcap.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 event.c getaddrinfo.c heap.c tcp.c base64.c version.c
 
 SRC_CFLAGS     := -D_LIBRADIUS -I$(top_builddir)/src
 
 # System libraries discovered by our top level configure script, links things
 # like pthread and the regexp libraries.
-TGT_LDLIBS     := $(LIBS)
+TGT_LDLIBS     := $(LIBS) $(PCAP_LIBS)
+TGT_LDFLAGS    := $(LDFLAGS) $(PCAP_LDFLAGS)
index 1fe5b1d..1509400 100644 (file)
@@ -27,23 +27,20 @@ RCSID("$Id$")
 #include <freeradius-devel/libradius.h>
 #include <freeradius-devel/base64.h>
 
-/* Get UCHAR_MAX from stdint.h, in src/include/missing.h */
-#include <limits.h>
-
 #define us(x) (uint8_t) x
 
 /** Base 64 encode binary data
  *
  * Base64 encode IN array of size INLEN into OUT array of size OUTLEN.
  *
- * @param[in] in Data to encode.
- * @param[in] inlen Length of data to encode.
  * @param[out] out Where to write Base64 string.
  * @param[in] outlen size of buffer including NULL byte.
+ * @param[in] in Data to encode.
+ * @param[in] inlen Length of data to encode.
  * @return The amount of data we wrote to the buffer or -1 if output buffer
  *     was too small.
  */
-size_t fr_base64_encode(uint8_t const *in, size_t inlen, char *out, size_t outlen)
+size_t fr_base64_encode(char *out, size_t outlen, uint8_t const *in, size_t inlen)
 {
        static char const b64str[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm"
                                       "nopqrstuvwxyz0123456789+/";
@@ -242,14 +239,13 @@ int fr_isbase64(char c)
  * This means that, when applicable, you must remove any line terminators
  * that is part of the data stream before calling this function.
  *
- * @param[in] in Base64 string to decode.
- * @param[in] inlen length of Base64 string.
  * @param[out] out Where to write the decoded data.
  * @param[in] outlen The length of the output buffer.
+ * @param[in] in Base64 string to decode.
+ * @param[in] inlen length of Base64 string.
  * @return -1 on error, else the length of decoded data.
  */
-ssize_t fr_base64_decode(char const *in, size_t inlen, uint8_t *out,
-                        size_t outlen)
+ssize_t fr_base64_decode(uint8_t *out, size_t outlen, char const *in, size_t inlen)
 {
        uint8_t *p = out;
 
@@ -278,19 +274,19 @@ ssize_t fr_base64_decode(char const *in, size_t inlen, uint8_t *out,
                        if (in[3] == '=') {
                                if (inlen != 4) break;
                        } else {
-                               if (!fr_isbase64(in[3])) break;
+                               if (!fr_isbase64(in[3])) break;
 
                                *p++ = ((b64[us(in[2])] << 6) & 0xc0) | b64[us(in[3])];
-                       }
+                       }
                }
 
                in += 4;
                inlen -= 4;
-       }
+       }
 
        if (inlen != 0) {
                return -1;
        }
 
-       return p - out;
+       return p - out;
 }
index 2e3791c..1667bd0 100644 (file)
@@ -113,8 +113,7 @@ void fr_cbuff_rp_insert(fr_cbuff_t *cbuff, void *obj)
                TALLOC_FREE(cbuff->elem[cbuff->in]);
        }
 
-       cbuff->elem[cbuff->in] = obj;
-       talloc_steal(cbuff, obj);
+       cbuff->elem[cbuff->in] = talloc_steal(cbuff, obj);
 
        cbuff->in = (cbuff->in + 1) & cbuff->size;
 
diff --git a/src/lib/cursor.c b/src/lib/cursor.c
new file mode 100644 (file)
index 0000000..39b9f9e
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file cursor.c
+ * @brief Functions to iterate over collections of VALUE_PAIRs
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 The FreeRADIUS Server Project.
+ */
+
+#include <freeradius-devel/libradius.h>
+
+/** Setup a cursor to iterate over attribute pairs
+ *
+ * @param cursor Where to initialise the cursor (uses existing structure).
+ * @param node to start from.
+ */
+VALUE_PAIR *_fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR const * const *node)
+{
+       memset(cursor, 0, sizeof(*cursor));
+
+       if (!node || !cursor) {
+               return NULL;
+       }
+
+       /*
+        *  Useful check to see if uninitialised memory is pointed
+        *  to by node
+        */
+#ifndef NDEBUG
+       if (*node) VERIFY_VP(*node);
+#endif
+       memcpy(&cursor->first, &node, sizeof(cursor->first));
+       cursor->current = *cursor->first;
+
+       if (cursor->current) {
+               VERIFY_VP(cursor->current);
+               cursor->next = cursor->current->next;
+       }
+
+       return cursor->current;
+}
+
+void fr_cursor_copy(vp_cursor_t *out, vp_cursor_t *in)
+{
+       memcpy(out, in, sizeof(*out));
+}
+
+VALUE_PAIR *fr_cursor_first(vp_cursor_t *cursor)
+{
+       cursor->current = *cursor->first;
+
+       if (cursor->current) {
+               VERIFY_VP(cursor->current);
+               cursor->next = cursor->current->next;
+               if (cursor->next) VERIFY_VP(cursor->next);
+               cursor->found = NULL;
+       }
+
+       return cursor->current;
+}
+
+/** Return the last pair in the list
+ *
+ */
+VALUE_PAIR *fr_cursor_last(vp_cursor_t *cursor)
+{
+       if (!*cursor->first) return NULL;
+
+       /* Need to start at the start */
+       if (!cursor->current) {
+               fr_cursor_first(cursor);
+       }
+
+       /* Wind to the end */
+       while (cursor->next) {
+               fr_cursor_next(cursor);
+       }
+
+       return fr_cursor_current(cursor);
+}
+
+/** Iterate over attributes of a given type in the pairlist
+ *
+ *
+ */
+VALUE_PAIR *fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag)
+{
+       VALUE_PAIR *i;
+
+       i = pairfind(!cursor->found ? cursor->current : cursor->found->next, attr, vendor, tag);
+       if (!i) {
+               cursor->next = NULL;
+               cursor->current = NULL;
+
+               return NULL;
+       }
+
+       cursor->next = i->next;
+       cursor->current = i;
+       cursor->found = i;
+
+       return i;
+}
+
+/** Iterate over attributes of a given DA in the pairlist
+ *
+ *
+ */
+VALUE_PAIR *fr_cursor_next_by_da(vp_cursor_t *cursor, DICT_ATTR const *da, int8_t tag)
+{
+       VALUE_PAIR *i;
+
+       i = pairfind_da(!cursor->found ? cursor->current : cursor->found->next, da, tag);
+       if (!i) {
+               cursor->next = NULL;
+               cursor->current = NULL;
+
+               return NULL;
+       }
+
+       cursor->next = i->next;
+       cursor->current = i;
+       cursor->found = i;
+
+       return i;
+}
+
+/** Retrieve the next VALUE_PAIR
+ *
+ *
+ */
+VALUE_PAIR *fr_cursor_next(vp_cursor_t *cursor)
+{
+       cursor->current = cursor->next;
+       if (cursor->current) {
+               VERIFY_VP(cursor->current);
+
+               /*
+                *      Set this now in case 'current' gets freed before
+                *      fr_cursor_next is called again.
+                */
+               cursor->next = cursor->current->next;
+
+               /*
+                *      Next call to fr_cursor_next_by_num will start from the current
+                *      position in the list, not the last found instance.
+                */
+               cursor->found = NULL;
+       }
+
+       return cursor->current;
+}
+
+VALUE_PAIR *fr_cursor_current(vp_cursor_t *cursor)
+{
+       if (cursor->current) {
+               VERIFY_VP(cursor->current);
+       }
+
+       return cursor->current;
+}
+
+/** Insert a VP
+ *
+ * @todo don't use with pairdelete
+ */
+void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *add)
+{
+       VALUE_PAIR *i;
+
+       if (!add) {
+               return;
+       }
+
+       VERIFY_VP(add);
+
+       /*
+        *      Cursor was initialised with a pointer to a NULL value_pair
+        */
+       if (!*cursor->first) {
+               *cursor->first = add;
+               cursor->current = add;
+
+               return;
+       }
+
+       /*
+        *      We don't yet know where the last VALUE_PAIR is
+        *
+        *      Assume current is closer to the end of the list and use that if available.
+        */
+       if (!cursor->last) {
+               cursor->last = cursor->current ? cursor->current : *cursor->first;
+       }
+
+       VERIFY_VP(cursor->last);
+
+       /*
+        *      Something outside of the cursor added another VALUE_PAIR
+        */
+       if (cursor->last->next) {
+               for (i = cursor->last; i; i = i->next) {
+                       VERIFY_VP(i);
+                       cursor->last = i;
+               }
+       }
+
+       /*
+        *      Either current was never set, or something iterated to the end of the
+        *      attribute list.
+        */
+       if (!cursor->current) {
+               cursor->current = add;
+       }
+
+       /*
+        *      If there's no next cursor, and the pair we just inserted has additional
+        *      linked pairs, we need to set next to be the next VP in the list.
+        */
+       if (!cursor->next) {
+               cursor->next = add->next;
+       }
+
+       cursor->last->next = add;
+}
+
+/** Remove the current pair
+ *
+ * @todo this is really inefficient and should be fixed...
+ *
+ * @param cursor to remove the current pair from.
+ * @return NULL on error, else the VALUE_PAIR we just removed.
+ */
+VALUE_PAIR *fr_cursor_remove(vp_cursor_t *cursor)
+{
+       VALUE_PAIR *vp, **last;
+
+       vp = fr_cursor_current(cursor);
+       if (!vp) {
+               return NULL;
+       }
+
+       last = cursor->first;
+       while (*last != vp) {
+               last = &(*last)->next;
+       }
+
+       fr_cursor_next(cursor);   /* Advance the cursor past the one were about to delete */
+
+       *last = vp->next;
+       vp->next = NULL;
+
+       /* Fixup cursor->found if we removed the VP it was referring to */
+       if (vp == cursor->found) cursor->found = *last;
+
+       return vp;
+}
+
+/** Replace the current pair
+ *
+ * @todo this is really inefficient and should be fixed...
+ *
+ * @param cursor to replace the current pair in.
+ * @param new VALUE_PAIR to insert.
+ * @return NULL on error, else the VALUE_PAIR we just replaced.
+ */
+VALUE_PAIR *fr_cursor_replace(vp_cursor_t *cursor, VALUE_PAIR *new)
+{
+       VALUE_PAIR *vp, **last;
+
+       vp = fr_cursor_current(cursor);
+       if (!vp) {
+               *cursor->first = new;
+               return NULL;
+       }
+
+       last = cursor->first;
+       while (*last != vp) {
+           last = &(*last)->next;
+       }
+
+       fr_cursor_next(cursor);   /* Advance the cursor past the one were about to replace */
+
+       *last = new;
+       new->next = vp->next;
+       vp->next = NULL;
+
+       return vp;
+}
diff --git a/src/lib/debug.c b/src/lib/debug.c
new file mode 100644 (file)
index 0000000..3c4ff8b
--- /dev/null
@@ -0,0 +1,869 @@
+/*
+ *   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
+ */
+
+/**
+ * @file debug.c
+ * @brief Various functions to aid in debugging
+ *
+ * @copyright 2013  The FreeRADIUS server project
+ * @copyright 2013  Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#include <assert.h>
+#include <freeradius-devel/libradius.h>
+#include <sys/stat.h>
+
+#if defined(HAVE_MALLOPT) && defined(HAVE_MALLOC_H)
+#  include <malloc.h>
+#endif
+
+/*
+ *     runtime backtrace functions are not POSIX but are included in
+ *     glibc, OSX >= 10.5 and various BSDs
+ */
+#ifdef HAVE_EXECINFO
+#  include <execinfo.h>
+#endif
+
+#ifdef HAVE_SYS_PRCTL_H
+#  include <sys/prctl.h>
+#endif
+
+#ifdef HAVE_SYS_RESOURCE_H
+#  include <sys/resource.h>
+#endif
+
+#ifdef HAVE_PTHREAD_H
+#  define PTHREAD_MUTEX_LOCK pthread_mutex_lock
+#  define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
+#else
+#  define PTHREAD_MUTEX_LOCK(_x)
+#  define PTHREAD_MUTEX_UNLOCK(_x)
+#endif
+
+#ifdef HAVE_EXECINFO
+#  define MAX_BT_FRAMES 128
+#  define MAX_BT_CBUFF  65536                          //!< Should be a power of 2
+
+#  ifdef HAVE_PTHREAD_H
+static pthread_mutex_t fr_debug_init = PTHREAD_MUTEX_INITIALIZER;
+#  endif
+
+typedef struct fr_bt_info {
+       void            *obj;                           //!< Memory address of the block of allocated memory.
+       void            *frames[MAX_BT_FRAMES];         //!< Backtrace frame data
+       int             count;                          //!< Number of frames stored
+} fr_bt_info_t;
+
+struct fr_bt_marker {
+       void            *obj;                           //!< Pointer to the parent object, this is our needle
+                                                       //!< when we iterate over the contents of the circular buffer.
+       fr_cbuff_t      *cbuff;                         //!< Where we temporarily store the backtraces
+};
+#endif
+
+static char panic_action[512];                         //!< The command to execute when panicking.
+static fr_fault_cb_t panic_cb = NULL;                  //!< Callback to execute whilst panicking, before the
+                                                       //!< panic_action.
+static fr_fault_log_t fr_fault_log = NULL;             //!< Function to use to process logging output.
+static int fr_fault_log_fd = STDERR_FILENO;            //!< Where to write debug output.
+
+static int fr_debugger_present = -1;                   //!< Whether were attached to by a debugger.
+
+#ifdef HAVE_SYS_RESOURCE_H
+static struct rlimit core_limits;
+#endif
+
+#define FR_FAULT_LOG(fmt, ...) fr_fault_log(fmt "\n", ## __VA_ARGS__)
+
+/** Stub callback to see if the SIGTRAP handler is overriden
+ *
+ * @param signum signal raised.
+ */
+static void _sigtrap_handler(UNUSED int signum)
+{
+       fr_debugger_present = 0;
+       signal(SIGTRAP, SIG_DFL);
+}
+
+/** Break in debugger (if were running under a debugger)
+ *
+ * If the server is running under a debugger this will raise a
+ * SIGTRAP which will pause the running process.
+ *
+ * If the server is not running under debugger then this will do nothing.
+ */
+void fr_debug_break(void)
+{
+       if (fr_debugger_present == -1) {
+               fr_debugger_present = 0;
+               signal(SIGTRAP, _sigtrap_handler);
+               raise(SIGTRAP);
+       } else if (fr_debugger_present == 1) {
+               raise(SIGTRAP);
+       }
+}
+
+#ifdef HAVE_EXECINFO
+/** Generate a backtrace for an object during destruction
+ *
+ * If this is the first entry being inserted
+ */
+static int _fr_do_bt(fr_bt_marker_t *marker)
+{
+       fr_bt_info_t *bt;
+
+       if (!fr_assert(marker->obj) || !fr_assert(marker->cbuff)) {
+               return -1;
+       }
+
+       bt = talloc_zero(marker->cbuff, fr_bt_info_t);
+       if (!bt) {
+               return -1;
+       }
+       bt->count = backtrace(bt->frames, MAX_BT_FRAMES);
+       fr_cbuff_rp_insert(marker->cbuff, bt);
+
+       return 0;
+}
+
+/** Print backtrace entry for a given object
+ *
+ * @param cbuff to search in.
+ * @param obj pointer to original object
+ */
+void backtrace_print(fr_cbuff_t *cbuff, void *obj)
+{
+       fr_bt_info_t *p;
+       bool found = false;
+       int i = 0;
+       char **frames;
+
+       while ((p = fr_cbuff_rp_next(cbuff, NULL))) {
+               if ((p == obj) || !obj) {
+                       found = true;
+                       frames = backtrace_symbols(p->frames, p->count);
+
+                       fprintf(stderr, "Stacktrace for: %p\n", p);
+                       for (i = 0; i < p->count; i++) {
+                               fprintf(stderr, "%s\n", frames[i]);
+                       }
+
+                       /* We were only asked to look for one */
+                       if (obj) {
+                               return;
+                       }
+               }
+       };
+
+       if (!found) {
+               fprintf(stderr, "No backtrace available for %p", obj);
+       }
+}
+
+/** Inserts a backtrace marker into the provided context
+ *
+ * Allows for maximum laziness and will initialise a circular buffer if one has not already been created.
+ *
+ * Code augmentation should look something like:
+@verbatim
+       // Create a static cbuffer pointer, the first call to backtrace_attach will initialise it
+       static fr_cbuff *my_obj_bt;
+
+       my_obj_t *alloc_my_obj(TALLOC_CTX *ctx) {
+               my_obj_t *this;
+
+               this = talloc(ctx, my_obj_t);
+
+               // Attach backtrace marker to object
+               backtrace_attach(&my_obj_bt, this);
+
+               return this;
+       }
+@endverbatim
+ *
+ * Then, later when a double free occurs:
+@verbatim
+       (gdb) call backtrace_print(&my_obj_bt, <pointer to double freed memory>)
+@endverbatim
+ *
+ * which should print a limited backtrace to stderr. Note, this backtrace will not include any argument
+ * values, but should at least show the code path taken.
+ *
+ * @param cbuff this should be a pointer to a static *fr_cbuff.
+ * @param obj we want to generate a backtrace for.
+ */
+fr_bt_marker_t *fr_backtrace_attach(fr_cbuff_t **cbuff, TALLOC_CTX *obj)
+{
+       fr_bt_marker_t *marker;
+
+       if (*cbuff == NULL) {
+               PTHREAD_MUTEX_LOCK(&fr_debug_init);
+               /* Check again now we hold the mutex - eww*/
+               if (*cbuff == NULL) {
+                       TALLOC_CTX *ctx;
+
+                       ctx = fr_autofree_ctx();
+                       *cbuff = fr_cbuff_alloc(ctx, MAX_BT_CBUFF, true);
+               }
+               PTHREAD_MUTEX_UNLOCK(&fr_debug_init);
+       }
+
+       marker = talloc(obj, fr_bt_marker_t);
+       if (!marker) {
+               return NULL;
+       }
+
+       marker->obj = (void *) obj;
+       marker->cbuff = *cbuff;
+
+       talloc_set_destructor(marker, _fr_do_bt);
+
+       return marker;
+}
+#else
+void backtrace_print(UNUSED fr_cbuff_t *cbuff, UNUSED void *obj)
+{
+       fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
+}
+fr_bt_marker_t *fr_backtrace_attach(UNUSED fr_cbuff_t **cbuff, UNUSED TALLOC_CTX *obj)
+{
+       fprintf(stderr, "Server built without fr_backtrace_* support, requires execinfo.h and possibly -lexecinfo\n");
+       abort();
+}
+#endif /* ifdef HAVE_EXECINFO */
+
+static int _panic_on_free(UNUSED char *foo)
+{
+       fr_fault(SIGUSR1);
+       return -1;      /* this should make the free fail */
+}
+
+/** Insert memory into the context of another talloc memory chunk which
+ * causes a panic when freed.
+ *
+ * @param ctx TALLOC_CTX to monitor for frees.
+ */
+void fr_panic_on_free(TALLOC_CTX *ctx)
+{
+       char *ptr;
+
+       ptr = talloc(ctx, char);
+       talloc_set_destructor(ptr, _panic_on_free);
+}
+
+/** Set the dumpable flag, also controls whether processes can PATTACH
+ *
+ * @param dumpable whether we should allow core dumping
+ */
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_SET_DUMPABLE)
+static int fr_set_dumpable_flag(bool dumpable)
+{
+       if (prctl(PR_SET_DUMPABLE, dumpable ? 1 : 0) < 0) {
+               fr_strerror_printf("Cannot re-enable core dumps: prctl(PR_SET_DUMPABLE) failed: %s",
+                                  fr_syserror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+#else
+static int fr_set_dumpable_flag(UNUSED bool dumpable)
+{
+       fr_strerror_printf("Changing value of PR_DUMPABLE not supported on this system");
+       return -2;
+}
+#endif
+
+/** Get the processes dumpable flag
+ *
+ */
+#if defined(HAVE_SYS_PRCTL_H) && defined(PR_GET_DUMPABLE)
+static int fr_get_dumpable_flag(void)
+{
+       int ret;
+
+       ret = prctl(PR_GET_DUMPABLE);
+       if (ret < 0) {
+               fr_strerror_printf("Cannot get dumpable flag: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       /*
+        *  Linux is crazy and prctl sometimes returns 2 for disabled
+        */
+       if (ret != 1) return 0;
+       return 1;
+}
+#else
+static int fr_get_dumpable_flag(void)
+{
+       fr_strerror_printf("Getting value of PR_DUMPABLE not supported on this system");
+       return -2;
+}
+#endif
+
+
+/** Get the current maximum for core files
+ *
+ * Do this before anything else so as to ensure it's properly initialized.
+ */
+int fr_set_dumpable_init(void)
+{
+#ifdef HAVE_SYS_RESOURCE_H
+       if (getrlimit(RLIMIT_CORE, &core_limits) < 0) {
+               fr_strerror_printf("Failed to get current core limit:  %s", fr_syserror(errno));
+               return -1;
+       }
+#endif
+       return 0;
+}
+
+/** Enable or disable core dumps
+ *
+ * @param allow_core_dumps whether to enable or disable core dumps.
+ */
+int fr_set_dumpable(bool allow_core_dumps)
+{
+       /*
+        *      If configured, turn core dumps off.
+        */
+       if (!allow_core_dumps) {
+#ifdef HAVE_SYS_RESOURCE_H
+               struct rlimit no_core;
+
+               no_core.rlim_cur = 0;
+               no_core.rlim_max = 0;
+
+               if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
+                       fr_strerror_printf("Failed disabling core dumps: %s", fr_syserror(errno));
+
+                       return -1;
+               }
+#endif
+               return 0;
+       }
+
+       if (fr_set_dumpable_flag(true) < 0) return -1;
+
+       /*
+        *      Reset the core dump limits to their original value.
+        */
+#ifdef HAVE_SYS_RESOURCE_H
+       if (setrlimit(RLIMIT_CORE, &core_limits) < 0) {
+               fr_strerror_printf("Cannot update core dump limit: %s", fr_syserror(errno));
+
+               return -1;
+       }
+#endif
+       return 0;
+}
+
+/** Check to see if panic_action file is world writeable
+ *
+ * @return 0 if file is OK, else -1.
+ */
+static int fr_fault_check_permissions(void)
+{
+       char const *p, *q;
+       size_t len;
+       char filename[256];
+       struct stat statbuf;
+
+       /*
+        *      Try and guess which part of the command is the binary, and check to see if
+        *      it's world writeable, to try and save the admin from their own stupidity.
+        *
+        *      @fixme we should do this properly and take into account single and double
+        *      quotes.
+        */
+       if ((q = strchr(panic_action, ' '))) {
+               /*
+                *      need to use a static buffer, because mallocing memory in a signal handler
+                *      is a bad idea and can result in deadlock.
+                */
+               len = snprintf(filename, sizeof(filename), "%.*s", (int)(q - panic_action), panic_action);
+               if (is_truncated(len, sizeof(filename))) {
+                       fr_strerror_printf("Failed writing panic_action to temporary buffer (truncated)");
+                       return -1;
+               }
+               p = filename;
+       } else {
+               p = panic_action;
+       }
+
+       if (stat(p, &statbuf) == 0) {
+#ifdef S_IWOTH
+               if ((statbuf.st_mode & S_IWOTH) != 0) {
+                       fr_strerror_printf("panic_action file \"%s\" is globally writable", p);
+                       return -1;
+               }
+#endif
+       }
+
+       return 0;
+}
+
+/** Prints a simple backtrace (if execinfo is available) and calls panic_action if set.
+ *
+ * @param sig caught
+ */
+void fr_fault(int sig)
+{
+       char cmd[sizeof(panic_action) + 20];
+       char *out = cmd;
+       size_t left = sizeof(cmd), ret;
+
+       char const *p = panic_action;
+       char const *q;
+
+       int code;
+
+       /*
+        *      Makes the backtraces slightly cleaner
+        */
+       memset(cmd, 0, sizeof(cmd));
+
+       FR_FAULT_LOG("CAUGHT SIGNAL: %s", strsignal(sig));
+
+       /*
+        *      Check for administrator sanity.
+        */
+       if (fr_fault_check_permissions() < 0) {
+               FR_FAULT_LOG("Refusing to execute panic action: %s", fr_strerror());
+               goto finish;
+       }
+
+       /*
+        *      Run the callback if one was registered
+        */
+       if (panic_cb && (panic_cb(sig) < 0)) goto finish;
+
+       /*
+        *      Produce a simple backtrace - They've very basic but at least give us an
+        *      idea of the area of the code we hit the issue in.
+        */
+#ifdef HAVE_EXECINFO
+       {
+               size_t frame_count, i;
+               void *stack[MAX_BT_FRAMES];
+               char **strings;
+
+               frame_count = backtrace(stack, MAX_BT_FRAMES);
+
+               FR_FAULT_LOG("Backtrace of last %zu frames:", frame_count);
+
+               /*
+                *      Only use backtrace_symbols() if we don't have a logging fd.
+                *      If the server has experienced memory corruption, there's
+                *      a high probability that calling backtrace_symbols() which
+                *      mallocs more memory, will fail.
+                */
+               if (fr_fault_log_fd < 0) {
+                       strings = backtrace_symbols(stack, frame_count);
+                       for (i = 0; i < frame_count; i++) {
+                               FR_FAULT_LOG("%s", strings[i]);
+                       }
+                       free(strings);
+               } else {
+                       backtrace_symbols_fd(stack, frame_count, fr_fault_log_fd);
+               }
+       }
+#endif
+
+       /* No panic action set... */
+       if (panic_action[0] == '\0') {
+               FR_FAULT_LOG("No panic action set");
+               goto finish;
+       }
+
+       /* Substitute %p for the current PID (useful for attaching a debugger) */
+       while ((q = strstr(p, "%p"))) {
+               out += ret = snprintf(out, left, "%.*s%d", (int) (q - p), p, (int) getpid());
+               if (left <= ret) {
+               oob:
+                       FR_FAULT_LOG("Panic action too long");
+                       fr_exit_now(1);
+               }
+               left -= ret;
+               p = q + 2;
+       }
+       if (strlen(p) >= left) goto oob;
+       strlcpy(out, p, left);
+
+       FR_FAULT_LOG("Calling: %s", cmd);
+
+       {
+               bool disable = false;
+
+               /*
+                *      Here we temporarily enable the dumpable flag so if GBD or LLDB
+                *      is called in the panic_action, they can pattach tot he running
+                *      process.
+                */
+               if (fr_get_dumpable_flag() == 0) {
+                       if ((fr_set_dumpable_flag(true) < 0) || !fr_get_dumpable_flag()) {
+                               FR_FAULT_LOG("Failed setting dumpable flag, pattach may not work: %s", fr_strerror());
+                       } else {
+                               disable = true;
+                       }
+                       FR_FAULT_LOG("Temporarily setting PR_DUMPABLE to 1");
+               }
+
+               code = system(cmd);
+
+               /*
+                *      We only want to error out here, if dumpable was originally disabled
+                *      and we managed to change the value to enabled, but failed
+                *      setting it back to disabled.
+                */
+               if (disable) {
+                       FR_FAULT_LOG("Resetting PR_DUMPABLE to 0");
+                       if (fr_set_dumpable_flag(false) < 0) {
+                               FR_FAULT_LOG("Failed reseting dumpable flag to off: %s", fr_strerror());
+                               FR_FAULT_LOG("Exiting due to insecure process state");
+                               fr_exit_now(1);
+                       }
+               }
+       }
+
+       FR_FAULT_LOG("Panic action exited with %i", code);
+
+finish:
+#ifdef SIGUSR1
+       if (sig == SIGUSR1) {
+               return;
+       }
+#endif
+       fr_exit_now(1);
+}
+
+#ifdef SIGABRT
+/** Work around debuggers which can't backtrace past the signal handler
+ *
+ * At least this provides us some information when we get talloc errors.
+ */
+static void _fr_talloc_fault(char const *reason)
+{
+       fr_fault_log("talloc abort: %s\n", reason);
+       fr_fault(SIGABRT);
+}
+#endif
+
+/** Wrapper to pass talloc log output to our fr_fault_log function
+ *
+ */
+static void _fr_talloc_log(char const *msg)
+{
+       fr_fault_log("%s\n", msg);
+}
+
+/** Generate a talloc memory report for a context and print to stderr/stdout
+ *
+ * @param ctx to generate a report for, may be NULL in which case the root context is used.
+ */
+int fr_log_talloc_report(TALLOC_CTX *ctx)
+{
+       FILE *log;
+       char const *null_ctx = NULL;
+       int i = 0;
+       int fd;
+
+       fd = dup(fr_fault_log_fd);
+       if (fd < 0) {
+               fr_strerror_printf("Couldn't write memory report, failed to dup log fd: %s", fr_syserror(errno));
+               return -1;
+       }
+       log = fdopen(fd, "w");
+       if (!log) {
+               close(fd);
+               fr_strerror_printf("Couldn't write memory report, fdopen failed: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       fprintf(log, "Current state of talloced memory:\n");
+       if (ctx) {
+               null_ctx = talloc_get_name(NULL);
+       }
+
+       if (!ctx) {
+               talloc_report_full(NULL, log);
+       } else do {
+               fprintf(log, "Context level %i", i++);
+
+               talloc_report_full(ctx, log);
+       } while ((ctx = talloc_parent(ctx)) && (talloc_get_name(ctx) != null_ctx));  /* Stop before we hit NULL ctx */
+
+       fclose(log);
+
+       return 0;
+}
+
+/** Signal handler to print out a talloc memory report
+ *
+ * @param sig caught
+ */
+static void _fr_fault_mem_report(int sig)
+{
+       fr_fault_log("CAUGHT SIGNAL: %s\n", strsignal(sig));
+
+       if (fr_log_talloc_report(NULL) < 0) fr_perror("memreport");
+}
+
+static int _fr_disable_null_tracking(UNUSED bool *p)
+{
+       talloc_disable_null_tracking();
+       return 0;
+}
+
+/** Registers signal handlers to execute panic_action on fatal signal
+ *
+ * May be called multiple time to change the panic_action/program.
+ *
+ * @param cmd to execute on fault. If present %p will be substituted
+ *        for the parent PID before the command is executed, and %e
+ *        will be substituted for the currently running program.
+ * @param program Name of program currently executing (argv[0]).
+ * @return 0 on success -1 on failure.
+ */
+int fr_fault_setup(char const *cmd, char const *program)
+{
+       static bool setup = false;
+
+       char *out = panic_action;
+       size_t left = sizeof(panic_action), ret;
+
+       char const *p = cmd;
+       char const *q;
+
+       if (cmd) {
+               /* Substitute %e for the current program */
+               while ((q = strstr(p, "%e"))) {
+                       out += ret = snprintf(out, left, "%.*s%s", (int) (q - p), p, program ? program : "");
+                       if (left <= ret) {
+                       oob:
+                               fr_strerror_printf("Panic action too long");
+                               return -1;
+                       }
+                       left -= ret;
+                       p = q + 2;
+               }
+               if (strlen(p) >= left) goto oob;
+               strlcpy(out, p, left);
+       } else {
+               *panic_action = '\0';
+       }
+
+       /*
+        *      Check for administrator sanity.
+        */
+       if (fr_fault_check_permissions() < 0) return -1;
+
+       /* Unsure what the side effects of changing the signal handler mid execution might be */
+       if (!setup) {
+#ifdef SIGSEGV
+               if (fr_set_signal(SIGSEGV, fr_fault) < 0) return -1;
+#endif
+#ifdef SIGBUS
+               if (fr_set_signal(SIGBUS, fr_fault) < 0) return -1;
+#endif
+#ifdef SIGABRT
+               if (fr_set_signal(SIGABRT, fr_fault) < 0) return -1;
+               /*
+                *  Use this instead of abort so we get a
+                *  full backtrace with broken versions of LLDB
+                */
+               talloc_set_abort_fn(_fr_talloc_fault);
+#endif
+#ifdef SIGFPE
+               if (fr_set_signal(SIGFPE, fr_fault) < 0) return -1;
+#endif
+
+#ifdef SIGUSR1
+               if (fr_set_signal(SIGUSR1, fr_fault) < 0) return -1;
+#endif
+
+#ifdef SIGUSR2
+               if (fr_set_signal(SIGUSR2, _fr_fault_mem_report) < 0) return -1;
+#endif
+
+               /*
+                *  Setup the default logger
+                */
+               if (!fr_fault_log) fr_fault_set_log_fn(NULL);
+               talloc_set_log_fn(_fr_talloc_log);
+
+               /*
+                *  Needed for memory reports
+                *
+                *  Disable null tracking on exit, else valgrind complains
+                */
+               {
+                       TALLOC_CTX *autofree;
+                       bool *marker;
+
+                       talloc_enable_null_tracking();
+
+                       autofree = talloc_autofree_context();
+                       marker = talloc(autofree, bool);
+                       talloc_set_destructor(marker, _fr_disable_null_tracking);
+               }
+
+               /*
+                *  If were using glibc malloc > 2.4 this scribbles over
+                *  uninitialised and freed memory, to make memory issues easier
+                *  to track down.
+                */
+#if defined(HAVE_MALLOPT) && !defined(NDEBUG)
+               mallopt(M_PERTURB, 0x42);
+               mallopt(M_CHECK_ACTION, 3);
+#endif
+       }
+       setup = true;
+
+       return 0;
+}
+
+/** Set a callback to be called before fr_fault()
+ *
+ * @param func to execute. If callback returns < 0
+ *     fr_fault will exit before running panic_action code.
+ */
+void fr_fault_set_cb(fr_fault_cb_t func)
+{
+       panic_cb = func;
+};
+
+/** Default logger, logs output to stderr
+ *
+ */
+static void CC_HINT(format (printf, 1, 2)) _fr_fault_log(char const *msg, ...)
+{
+       va_list ap;
+
+       va_start(ap, msg);
+       vfprintf(stderr, msg, ap);
+       va_end(ap);
+}
+
+
+/** Set a file descriptor to log panic_action output to.
+ *
+ * @param func to call to output log messages.
+ */
+void fr_fault_set_log_fn(fr_fault_log_t func)
+{
+       fr_fault_log = func ? func : _fr_fault_log;
+}
+
+/** Set a file descriptor to log memory reports to.
+ *
+ * @param fd to write output to.
+ */
+void fr_fault_set_log_fd(int fd)
+{
+       fr_fault_log_fd = fd;
+}
+
+
+#ifdef WITH_VERIFY_PTR
+
+/*
+ *     Verify a VALUE_PAIR
+ */
+inline void fr_verify_vp(VALUE_PAIR const *vp)
+{
+       (void) talloc_get_type_abort(vp, VALUE_PAIR);
+
+       if (vp->data.ptr) switch (vp->da->type) {
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+       {
+               size_t len;
+
+               if (!talloc_get_type(vp->data.ptr, uint8_t)) {
+                       fr_perror("Type check failed for attribute \"%s\"", vp->da->name);
+                       (void) talloc_get_type_abort(vp->data.ptr, uint8_t);
+               }
+
+               len = talloc_array_length(vp->vp_octets);
+               if (vp->length > len) {
+                       fr_perror("VALUE_PAIR length %zu does not equal uint8_t buffer length %zu", vp->length, len);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+       }
+               break;
+
+       case PW_TYPE_STRING:
+       {
+               size_t len;
+
+               if (!talloc_get_type(vp->data.ptr, char)) {
+                       fr_perror("Type check failed for attribute \"%s\"", vp->da->name);
+                       (void) talloc_get_type_abort(vp->data.ptr, char);
+               }
+
+               len = (talloc_array_length(vp->vp_strvalue) - 1);
+               if (vp->length > len) {
+                       fr_perror("VALUE_PAIR %s length %zu is too small for char buffer length %zu",
+                                 vp->da->name, vp->length, len);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+               if (vp->vp_strvalue[vp->length] != '\0') {
+                       fr_perror("VALUE_PAIR %s buffer not \\0 terminated", vp->da->name);
+                       fr_assert(0);
+                       fr_exit_now(1);
+               }
+       }
+               break;
+
+       default:
+               break;
+       }
+}
+
+/*
+ *     Verify a pair list
+ */
+void fr_verify_list(TALLOC_CTX *expected, VALUE_PAIR *vps)
+{
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
+       TALLOC_CTX *parent;
+
+       for (vp = fr_cursor_init(&cursor, &vps);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               VERIFY_VP(vp);
+
+               parent = talloc_parent(vp);
+               if (expected && (parent != expected)) {
+                       fr_perror("Expected VALUE_PAIR (%s) to be parented by %p (%s), "
+                                 "but parented by %p (%s)",
+                                 vp->da->name,
+                                 expected, talloc_get_name(expected),
+                                 parent, parent ? talloc_get_name(parent) : "NULL");
+
+                       fr_log_talloc_report(expected);
+                       if (parent) fr_log_talloc_report(parent);
+
+                       assert(0);
+               }
+
+       }
+}
+#endif
index 49b751a..0d87b47 100644 (file)
@@ -63,13 +63,9 @@ static DICT_ATTR *dict_base_attrs[256];
  */
 typedef struct dict_stat_t {
        struct dict_stat_t *next;
-       char               *name;
-       time_t             mtime;
+       struct stat stat_buf;
 } dict_stat_t;
 
-static char *stat_root_dir = NULL;
-static char *stat_root_file = NULL;
-
 static dict_stat_t *stat_head = NULL;
 static dict_stat_t *stat_tail = NULL;
 
@@ -88,17 +84,17 @@ static value_fixup_t *value_fixup = NULL;
 const FR_NAME_NUMBER dict_attr_types[] = {
        { "integer",    PW_TYPE_INTEGER },
        { "string",     PW_TYPE_STRING },
-       { "ipaddr",     PW_TYPE_IPADDR },
+       { "ipaddr",     PW_TYPE_IPV4_ADDR },
        { "date",       PW_TYPE_DATE },
        { "abinary",    PW_TYPE_ABINARY },
        { "octets",     PW_TYPE_OCTETS },
        { "ifid",       PW_TYPE_IFID },
-       { "ipv6addr",   PW_TYPE_IPV6ADDR },
-       { "ipv6prefix", PW_TYPE_IPV6PREFIX },
+       { "ipv6addr",   PW_TYPE_IPV6_ADDR },
+       { "ipv6prefix", PW_TYPE_IPV6_PREFIX },
        { "byte",       PW_TYPE_BYTE },
        { "short",      PW_TYPE_SHORT },
        { "ether",      PW_TYPE_ETHERNET },
-       { "combo-ip",   PW_TYPE_COMBO_IP },
+       { "combo-ip",   PW_TYPE_IP_ADDR },
        { "tlv",        PW_TYPE_TLV },
        { "signed",     PW_TYPE_SIGNED },
        { "extended",   PW_TYPE_EXTENDED },
@@ -110,8 +106,8 @@ const FR_NAME_NUMBER dict_attr_types[] = {
        { "int32",      PW_TYPE_SIGNED },
        { "integer64",  PW_TYPE_INTEGER64 },
        { "uint64",     PW_TYPE_INTEGER64 },
-       { "ipv4prefix", PW_TYPE_IPV4PREFIX },
-       { "cidr",       PW_TYPE_IPV4PREFIX },
+       { "ipv4prefix", PW_TYPE_IPV4_PREFIX },
+       { "cidr",       PW_TYPE_IPV4_PREFIX },
        { "vsa",        PW_TYPE_VSA },
        { NULL, 0 }
 };
@@ -123,24 +119,24 @@ const size_t dict_attr_sizes[PW_TYPE_MAX][2] = {
        [PW_TYPE_INVALID]       = { ~0, 0 },
        [PW_TYPE_STRING]        = { 0, ~0 },
        [PW_TYPE_INTEGER]       = {4, 4 },
-       [PW_TYPE_IPADDR]        = {4, 4},
+       [PW_TYPE_IPV4_ADDR]     = {4, 4},
        [PW_TYPE_DATE]          = {4, 4},
        [PW_TYPE_ABINARY]       = {32, ~0},
        [PW_TYPE_OCTETS]        = {0, ~0},
        [PW_TYPE_IFID]          = {8, 8},
-       [PW_TYPE_IPV6ADDR]      = { 16, 16},
-       [PW_TYPE_IPV6PREFIX]    = {2, 18},
+       [PW_TYPE_IPV6_ADDR]     = { 16, 16},
+       [PW_TYPE_IPV6_PREFIX]   = {2, 18},
        [PW_TYPE_BYTE]          = {1, 1},
        [PW_TYPE_SHORT]         = {2, 2},
        [PW_TYPE_ETHERNET]      = {6, 6},
        [PW_TYPE_SIGNED]        = {4, 4},
-       [PW_TYPE_COMBO_IP]      = {4, 16},
+       [PW_TYPE_IP_ADDR]       = {4, 16},
        [PW_TYPE_TLV]           = {2, ~0},
        [PW_TYPE_EXTENDED]      = {2, ~0},
        [PW_TYPE_LONG_EXTENDED] = {3, ~0},
        [PW_TYPE_EVS]           = {6, ~0},
        [PW_TYPE_INTEGER64]     = {8, 8},
-       [PW_TYPE_IPV4PREFIX]    = {6, 6},
+       [PW_TYPE_IPV4_PREFIX]   = {6, 6},
        [PW_TYPE_VSA]           = {4, ~0}
 };
 
@@ -348,11 +344,6 @@ static void dict_stat_free(void)
 {
        dict_stat_t *this, *next;
 
-       free(stat_root_dir);
-       stat_root_dir = NULL;
-       free(stat_root_file);
-       stat_root_file = NULL;
-
        if (!stat_head) {
                stat_tail = NULL;
                return;
@@ -360,7 +351,6 @@ static void dict_stat_free(void)
 
        for (this = stat_head; this != NULL; this = next) {
                next = this->next;
-               free(this->name);
                free(this);
        }
 
@@ -371,7 +361,7 @@ static void dict_stat_free(void)
 /*
  *     Add an entry to the list of stat buffers.
  */
-static void dict_stat_add(char const *name, struct stat const *stat_buf)
+static void dict_stat_add(struct stat const *stat_buf)
 {
        dict_stat_t *this;
 
@@ -379,8 +369,7 @@ static void dict_stat_add(char const *name, struct stat const *stat_buf)
        if (!this) return;
        memset(this, 0, sizeof(*this));
 
-       this->name = strdup(name);
-       this->mtime = stat_buf->st_mtime;
+       memcpy(&(this->stat_buf), stat_buf, sizeof(this->stat_buf));
 
        if (!stat_head) {
                stat_head = stat_tail = this;
@@ -395,26 +384,49 @@ static void dict_stat_add(char const *name, struct stat const *stat_buf)
  *     See if any dictionaries have changed.  If not, don't
  *     do anything.
  */
-static int dict_stat_check(char const *root_dir, char const *root_file)
+static int dict_stat_check(char const *dir, char const *file)
 {
-       struct stat buf;
+       struct stat stat_buf;
        dict_stat_t *this;
+       char buffer[2048];
 
-       if (!stat_root_dir) return 0;
-       if (!stat_root_file) return 0;
-
-       if (strcmp(root_dir, stat_root_dir) != 0) return 0;
-       if (strcmp(root_file, stat_root_file) != 0) return 0;
+       /*
+        *      Nothing cached, all files are new.
+        */
+       if (!stat_head) return 0;
 
-       if (!stat_head) return 0; /* changed, reload */
+       /*
+        *      Stat the file.
+        */
+       snprintf(buffer, sizeof(buffer), "%s/%s", dir, file);
+       if (stat(buffer, &stat_buf) < 0) return 0;
 
+       /*
+        *      Find the cache entry.
+        *      FIXME: use a hash table.
+        *      FIXME: check dependencies, via children.
+        *             if A loads B and B changes, we probably want
+        *             to reload B at the minimum.
+        */
        for (this = stat_head; this != NULL; this = this->next) {
-               if (stat(this->name, &buf) < 0) return 0;
+               if (this->stat_buf.st_dev != stat_buf.st_dev) continue;
+               if (this->stat_buf.st_ino != stat_buf.st_ino) continue;
+
+               /*
+                *      The file has changed.  Re-read it.
+                */
+               if (this->stat_buf.st_mtime < stat_buf.st_mtime) return 0;
 
-               if (buf.st_mtime != this->mtime) return 0;
+               /*
+                *      The file is the same.  Ignore it.
+                */
+               return 1;
        }
 
-       return 1;
+       /*
+        *      Not in the cache.
+        */
+       return 0;
 }
 
 typedef struct fr_pool_t {
@@ -620,7 +632,7 @@ const int dict_attr_allowed_chars[256] = {
 /*
  *     Add an attribute to the dictionary.
  */
-int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
+int dict_addattr(char const *name, int attr, unsigned int vendor, PW_TYPE type,
                 ATTR_FLAGS flags)
 {
        size_t namelen;
@@ -676,7 +688,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
         */
        if (flags.extended || flags.long_extended || flags.evs) {
                if (vendor && (vendor < FR_MAX_VENDOR)) {
-                       fr_strerror_printf("dict_addattr: VSAs cannot use the \"extended\" or \"evs\" attribute formats.");
+                       fr_strerror_printf("dict_addattr: VSAs cannot use the \"extended\" or \"evs\" attribute formats");
                        return -1;
                }
                if (flags.has_tag
@@ -684,7 +696,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
                    || flags.array
 #endif
                    || (flags.encrypt != FLAG_ENCRYPT_NONE)) {
-                       fr_strerror_printf("dict_addattr: The \"extended\" attributes MUST NOT have any flags set.");
+                       fr_strerror_printf("dict_addattr: The \"extended\" attributes MUST NOT have any flags set");
                        return -1;
                }
        }
@@ -727,19 +739,19 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
        }
 
        if (vendor && flags.concat) {
-               fr_strerror_printf("VSAs cannot have the \"concat\" flag set.");
+               fr_strerror_printf("VSAs cannot have the \"concat\" flag set");
                return -1;
        }
 
        if (flags.concat && (type != PW_TYPE_OCTETS)) {
-               fr_strerror_printf("The \"concat\" flag can only be set for attributes of type \"octets\".");
+               fr_strerror_printf("The \"concat\" flag can only be set for attributes of type \"octets\"");
                return -1;
        }
 
        if (flags.concat && (flags.has_tag || flags.array || flags.is_tlv || flags.has_tlv ||
                             flags.length || flags.evs || flags.extended || flags.long_extended ||
                             (flags.encrypt != FLAG_ENCRYPT_NONE))) {
-               fr_strerror_printf("The \"concat\" flag cannot be used with any other flag.");
+               fr_strerror_printf("The \"concat\" flag cannot be used with any other flag");
                return -1;
        }
 
@@ -793,7 +805,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
                 *      properly.
                 */
                if ((dv->type == 1) && (attr >= 256) && !flags.is_tlv) {
-                       fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (larger than 255).");
+                       fr_strerror_printf("dict_addattr: ATTRIBUTE has invalid number (larger than 255)");
                        return -1;
                } /* else 256..65535 are allowed */
 
@@ -855,7 +867,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
         */
        if ((n = fr_pool_alloc(sizeof(*n) + namelen)) == NULL) {
        oom:
-               fr_strerror_printf("dict_adnttr: out of memory");
+               fr_strerror_printf("dict_addattr: out of memory");
                return -1;
        }
 
@@ -879,7 +891,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
                a = fr_hash_table_finddata(attributes_byname, n);
                if (a && (strcasecmp(a->name, n->name) == 0)) {
                        if (a->attr != n->attr) {
-                               fr_strerror_printf("dict_adnttr: Duplicate attribute name %s", name);
+                               fr_strerror_printf("dict_addattr: Duplicate attribute name %s", name);
                                fr_pool_free(n);
                                return -1;
                        }
@@ -896,7 +908,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
                fr_hash_table_delete(attributes_byvalue, a);
 
                if (!fr_hash_table_replace(attributes_byname, n)) {
-                       fr_strerror_printf("dict_adnttr: Internal error storing attribute %s", name);
+                       fr_strerror_printf("dict_addattr: Internal error storing attribute %s", name);
                        fr_pool_free(n);
                        return -1;
                }
@@ -912,41 +924,34 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
         *      by value) we want to use the NEW name.
         */
        if (!fr_hash_table_replace(attributes_byvalue, n)) {
-               fr_strerror_printf("dict_adnttr: Failed inserting attribute name %s", name);
+               fr_strerror_printf("dict_addattr: Failed inserting attribute name %s", name);
                return -1;
        }
 
        /*
         *      Hacks for combo-IP
         */
-       if (n->type == PW_TYPE_COMBO_IP) {
+       if (n->type == PW_TYPE_IP_ADDR) {
                DICT_ATTR *v4, *v6;
 
                v4 = fr_pool_alloc(sizeof(*v4));
                if (!v4) goto oom;
 
                v6 = fr_pool_alloc(sizeof(*v6));
-               if (!v6) {
-                       free(v4);
-                       goto oom;
-               }
+               if (!v6) goto oom;
 
                memcpy(v4, n, sizeof(*v4));
-               v4->type = PW_TYPE_IPADDR;
+               v4->type = PW_TYPE_IPV4_ADDR;
 
                memcpy(v6, n, sizeof(*v6));
-               v6->type = PW_TYPE_IPV6ADDR;
-
-               if (!fr_hash_table_insert(attributes_combo, v4)) {
+               v6->type = PW_TYPE_IPV6_ADDR;
+               if (!fr_hash_table_replace(attributes_combo, v4)) {
                        fr_strerror_printf("dict_addattr: Failed inserting attribute name %s - IPv4", name);
-                       free(v4);
-                       free(v6);
                        return -1;
                }
 
-               if (!fr_hash_table_insert(attributes_combo, v6)) {
+               if (!fr_hash_table_replace(attributes_combo, v6)) {
                        fr_strerror_printf("dict_addattr: Failed inserting attribute name %s - IPv6", name);
-                       free(v6);
                        return -1;
                }
        }
@@ -965,7 +970,7 @@ int dict_addattr(char const *name, int attr, unsigned int vendor, int type,
 int dict_addvalue(char const *namestr, char const *attrstr, int value)
 {
        size_t          length;
-       DICT_ATTR const *dattr;
+       DICT_ATTR const *da;
        DICT_VALUE      *dval;
 
        static DICT_ATTR const *last_attr = NULL;
@@ -995,31 +1000,31 @@ int dict_addvalue(char const *namestr, char const *attrstr, int value)
         *      caching the last attribute.
         */
        if (last_attr && (strcasecmp(attrstr, last_attr->name) == 0)) {
-               dattr = last_attr;
+               da = last_attr;
        } else {
-               dattr = dict_attrbyname(attrstr);
-               last_attr = dattr;
+               da = dict_attrbyname(attrstr);
+               last_attr = da;
        }
 
        /*
         *      Remember which attribute is associated with this
         *      value, if possible.
         */
-       if (dattr) {
-               if (dattr->flags.has_value_alias) {
+       if (da) {
+               if (da->flags.has_value_alias) {
                        fr_strerror_printf("dict_addvalue: Cannot add VALUE for ATTRIBUTE \"%s\": It already has a VALUE-ALIAS", attrstr);
                        return -1;
                }
 
-               dval->attr = dattr->attr;
-               dval->vendor = dattr->vendor;
+               dval->attr = da->attr;
+               dval->vendor = da->vendor;
 
                /*
                 *      Enforce valid values
                 *
                 *      Don't worry about fixups...
                 */
-               switch (dattr->type) {
+               switch (da->type) {
                        case PW_TYPE_BYTE:
                                if (value > 255) {
                                        fr_pool_free(dval);
@@ -1048,7 +1053,7 @@ int dict_addvalue(char const *namestr, char const *attrstr, int value)
                        default:
                                fr_pool_free(dval);
                                fr_strerror_printf("dict_addvalue: VALUEs cannot be defined for attributes of type '%s'",
-                                          fr_int2str(dict_attr_types, dattr->type, "?Unknown?"));
+                                          fr_int2str(dict_attr_types, da->type, "?Unknown?"));
                                return -1;
                }
        } else {
@@ -1082,7 +1087,7 @@ int dict_addvalue(char const *namestr, char const *attrstr, int value)
                memcpy(&tmp, &dval, sizeof(tmp));
 
                if (!fr_hash_table_insert(values_byname, tmp)) {
-                       if (dattr) {
+                       if (da) {
                                DICT_VALUE *old;
 
                                /*
@@ -1090,7 +1095,7 @@ int dict_addvalue(char const *namestr, char const *attrstr, int value)
                                 *      name and value.  There are lots in
                                 *      dictionary.ascend.
                                 */
-                               old = dict_valbyname(dattr->attr, dattr->vendor, namestr);
+                               old = dict_valbyname(da->attr, da->vendor, namestr);
                                if (old && (old->value == dval->value)) {
                                        fr_pool_free(dval);
                                        return 0;
@@ -1181,7 +1186,7 @@ int dict_str2oid(char const *ptr, unsigned int *pvalue, unsigned int *pvendor,
        if (*pvalue) {
                da = dict_attrbyvalue(*pvalue, *pvendor);
                if (!da) {
-                       fr_strerror_printf("Parent attribute is undefined.");
+                       fr_strerror_printf("Parent attribute is undefined");
                        return -1;
                }
 
@@ -1422,7 +1427,7 @@ static int process_attribute(char const* fn, int const line,
                        break;
 
                case PW_TYPE_DATE:
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                case PW_TYPE_INTEGER:
                case PW_TYPE_SIGNED:
                        length = 4;
@@ -1440,7 +1445,7 @@ static int process_attribute(char const* fn, int const line,
                        length = 8;
                        break;
 
-               case PW_TYPE_IPV6ADDR:
+               case PW_TYPE_IPV6_ADDR:
                        length = 16;
                        break;
 
@@ -1474,7 +1479,7 @@ static int process_attribute(char const* fn, int const line,
                        break;
                }
 
-               flags.length = length;
+               flags.length = length;
 
        } else {                /* argc == 4: we have options */
                char *key, *next, *last;
@@ -1526,7 +1531,7 @@ static int process_attribute(char const* fn, int const line,
                                flags.array = 1;
 
                                switch (type) {
-                                       case PW_TYPE_IPADDR:
+                                       case PW_TYPE_IPV4_ADDR:
                                        case PW_TYPE_BYTE:
                                        case PW_TYPE_SHORT:
                                        case PW_TYPE_INTEGER:
@@ -1790,7 +1795,7 @@ static int process_vendor(char const* fn, int const line, char **argv,
                          int argc)
 {
        int             value;
-       int             continuation = 0;
+       bool            continuation = false;
        char const      *format = NULL;
 
        if ((argc < 2) || (argc > 3)) {
@@ -1875,7 +1880,7 @@ static int process_vendor(char const* fn, int const line, char **argv,
                                           fn, line, p);
                                return -1;
                        }
-                       continuation = 1;
+                       continuation = true;
 
                        if ((value != VENDORPEC_WIMAX) ||
                            (type != 1) || (length != 1)) {
@@ -2038,13 +2043,26 @@ static int my_dict_init(char const *parent, char const *filename,
 
        }
 
+       /*
+        *      Check if we've loaded this file before.  If so, ignore it.
+        */
+       p = strrchr(fn, FR_DIR_SEP);
+       if (p) {
+               *p = '\0';
+               if (dict_stat_check(fn, p + 1)) {
+                       *p = FR_DIR_SEP;
+                       return 0;
+               }
+               *p = FR_DIR_SEP;
+       }
+
        if ((fp = fopen(fn, "r")) == NULL) {
                if (!src_file) {
                        fr_strerror_printf("dict_init: Couldn't open dictionary \"%s\": %s",
-                                  fn, strerror(errno));
+                                  fn, fr_syserror(errno));
                } else {
                        fr_strerror_printf("dict_init: %s[%d]: Couldn't open dictionary \"%s\": %s",
-                                  src_file, src_line, fn, strerror(errno));
+                                  src_file, src_line, fn, fr_syserror(errno));
                }
                return -2;
        }
@@ -2070,7 +2088,7 @@ static int my_dict_init(char const *parent, char const *filename,
        }
 #endif
 
-       dict_stat_add(fn, &statbuf);
+       dict_stat_add(&statbuf);
 
        /*
         *      Seed the random pool with data.
@@ -2373,8 +2391,6 @@ int dict_init(char const *dir, char const *fn)
         *      Free the dictionaries, and the stat cache.
         */
        dict_free();
-       stat_root_dir = strdup(dir);
-       stat_root_file = strdup(fn);
 
        /*
         *      Create the table of vendor by name.   There MAY NOT
@@ -2759,8 +2775,7 @@ DICT_ATTR const *dict_attrunknownbyname(char const *attribute, int vp_free)
 
                        vendor = dict_vendorbyname(buffer);
                        if (!vendor) {
-                               fr_strerror_printf("Unknown vendor name in "
-                                                  "attribute name \"%s\"",
+                               fr_strerror_printf("Unknown attribute \"%s\"",
                                                   attribute);
                                return NULL;
                        }
@@ -2781,7 +2796,7 @@ DICT_ATTR const *dict_attrunknownbyname(char const *attribute, int vp_free)
         *      Attr-%d
         */
        if (strncasecmp(p, "Attr-", 5) != 0) {
-               fr_strerror_printf("Invalid format in attribute name \"%s\"",
+               fr_strerror_printf("Unknown attribute \"%s\"",
                                   attribute);
                return NULL;
        }
@@ -2910,14 +2925,14 @@ DICT_ATTR const *dict_attrunknownbyname(char const *attribute, int vp_free)
  */
 DICT_ATTR const *dict_attrbyvalue(unsigned int attr, unsigned int vendor)
 {
-       DICT_ATTR dattr;
+       DICT_ATTR da;
 
        if ((attr > 0) && (attr < 256) && !vendor) return dict_base_attrs[attr];
 
-       dattr.attr = attr;
-       dattr.vendor = vendor;
+       da.attr = attr;
+       da.vendor = vendor;
 
-       return fr_hash_table_finddata(attributes_byvalue, &dattr);
+       return fr_hash_table_finddata(attributes_byvalue, &da);
 }
 
 
@@ -2931,13 +2946,13 @@ DICT_ATTR const *dict_attrbyvalue(unsigned int attr, unsigned int vendor)
 DICT_ATTR const *dict_attrbytype(unsigned int attr, unsigned int vendor,
                                 PW_TYPE type)
 {
-       DICT_ATTR dattr;
+       DICT_ATTR da;
 
-       dattr.attr = attr;
-       dattr.vendor = vendor;
-       dattr.type = type;
+       da.attr = attr;
+       da.vendor = vendor;
+       da.type = type;
 
-       return fr_hash_table_finddata(attributes_combo, &dattr);
+       return fr_hash_table_finddata(attributes_combo, &da);
 }
 
 /**
@@ -2947,7 +2962,7 @@ int dict_attr_child(DICT_ATTR const *parent,
                    unsigned int *pattr, unsigned int *pvendor)
 {
        unsigned int attr, vendor;
-       DICT_ATTR dattr;
+       DICT_ATTR da;
 
        if (!parent || !pattr || !pvendor) return false;
 
@@ -2973,8 +2988,8 @@ int dict_attr_child(DICT_ATTR const *parent,
        /*
         *      Bootstrap by starting off with the parents values.
         */
-       dattr.attr = parent->attr;
-       dattr.vendor = parent->vendor;
+       da.attr = parent->attr;
+       da.vendor = parent->vendor;
 
        /*
         *      Do various butchery to insert the "attr" value.
@@ -2986,10 +3001,10 @@ int dict_attr_child(DICT_ATTR const *parent,
         *      EEVID   000000AA        EVS with vendor VID, attr AAA
         *      EEVID   DDCCBBAA        EVS with TLVs
         */
-       if (!dattr.vendor) {
-               dattr.vendor = parent->attr * FR_MAX_VENDOR;
-               dattr.vendor |= vendor;
-               dattr.attr = attr;
+       if (!da.vendor) {
+               da.vendor = parent->attr * FR_MAX_VENDOR;
+               da.vendor |= vendor;
+               da.attr = attr;
 
        } else {
                int i;
@@ -3003,7 +3018,7 @@ int dict_attr_child(DICT_ATTR const *parent,
 
                for (i = MAX_TLV_NEST - 1; i >= 0; i--) {
                        if ((parent->attr & (fr_attr_mask[i] << fr_attr_shift[i]))) {
-                               dattr.attr |= (attr & fr_attr_mask[i + 1]) << fr_attr_shift[i + 1];
+                               da.attr |= (attr & fr_attr_mask[i + 1]) << fr_attr_shift[i + 1];
                                goto find;
                        }
                }
@@ -3015,11 +3030,11 @@ find:
 #if 0
        fprintf(stderr, "LOOKING FOR %08x %08x + %08x %08x --> %08x %08x\n",
                parent->vendor, parent->attr, attr, vendor,
-               dattr.vendor, dattr.attr);
+               da.vendor, da.attr);
 #endif
 
-       *pattr = dattr.attr;
-       *pvendor = dattr.vendor;
+       *pattr = da.attr;
+       *pvendor = da.vendor;
        return true;
 }
 
@@ -3029,17 +3044,17 @@ find:
 DICT_ATTR const *dict_attrbyparent(DICT_ATTR const *parent, unsigned int attr, unsigned int vendor)
 {
        unsigned int my_attr, my_vendor;
-       DICT_ATTR dattr;
+       DICT_ATTR da;
 
        my_attr = attr;
        my_vendor = vendor;
 
        if (!dict_attr_child(parent, &my_attr, &my_vendor)) return NULL;
 
-       dattr.attr = my_attr;
-       dattr.vendor = my_vendor;
+       da.attr = my_attr;
+       da.vendor = my_vendor;
 
-       return fr_hash_table_finddata(attributes_byvalue, &dattr);
+       return fr_hash_table_finddata(attributes_byvalue, &da);
 }
 
 
@@ -3060,6 +3075,41 @@ DICT_ATTR const *dict_attrbyname(char const *name)
 }
 
 /*
+ *     Get an attribute by its name, where the name might have a tag
+ *     or something else after it.
+ */
+DICT_ATTR const *dict_attrbytagged_name(char const *name)
+{
+       DICT_ATTR *da;
+       char *p;
+       uint32_t buffer[(sizeof(*da) + DICT_ATTR_MAX_NAME_LEN + 3)/4];
+
+       if (!name) return NULL;
+
+       da = (DICT_ATTR *) buffer;
+       strlcpy(da->name, name, DICT_ATTR_MAX_NAME_LEN + 1);
+
+       /*
+        *      The name might have a tag or array reference.  That
+        *      isn't properly part of the name, and can be ignored on
+        *      lookup.
+        */
+       for (p = &da->name[0]; *p; p++) {
+               if (*p == ':') {
+                       *p = '\0';
+                       break;
+               }
+
+               if (*p == '[') {
+                       *p = '\0';
+                       break;
+               }
+       }
+
+       return fr_hash_table_finddata(attributes_byname, da);
+}
+
+/*
  *     Associate a value with an attribute and return it.
  */
 DICT_VALUE *dict_valbyattr(unsigned int attr, unsigned int vendor, int value)
index 449d5d1..198a65e 100644 (file)
@@ -41,16 +41,17 @@ typedef struct fr_event_fd_t {
 struct fr_event_list_t {
        fr_heap_t       *times;
 
-       int             changed;
+       bool            changed;
 
        int             exit;
 
        fr_event_status_t status;
 
        struct timeval  now;
-       int             dispatch;
+       bool            dispatch;
 
        int             max_readers;
+       int             num_readers;
        fr_event_fd_t   readers[FR_EV_MAX_FDS];
 };
 
@@ -61,7 +62,7 @@ struct fr_event_t {
        fr_event_callback_t     callback;
        void                    *ctx;
        struct timeval          when;
-       fr_event_t              **ev_p;
+       fr_event_t              **parent;
        int                     heap;
 };
 
@@ -81,34 +82,35 @@ static int fr_event_list_time_cmp(void const *one, void const *two)
 }
 
 
-void fr_event_list_free(fr_event_list_t *el)
+static int _event_list_free(fr_event_list_t *list)
 {
+       fr_event_list_t *el = list;
        fr_event_t *ev;
 
-       if (!el) return;
-
        while ((ev = fr_heap_peek(el->times)) != NULL) {
                fr_event_delete(el, &ev);
        }
 
        fr_heap_delete(el->times);
-       free(el);
+
+       return 0;
 }
 
 
-fr_event_list_t *fr_event_list_create(fr_event_status_t status)
+fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status)
 {
        int i;
        fr_event_list_t *el;
 
-       el = malloc(sizeof(*el));
-       if (!el) return NULL;
-       memset(el, 0, sizeof(*el));
+       el = talloc_zero(ctx, fr_event_list_t);
+       if (!fr_assert(el)) {
+               return NULL;
+       }
+       talloc_set_destructor(el, _event_list_free);
 
-       el->times = fr_heap_create(fr_event_list_time_cmp,
-                                  offsetof(fr_event_t, heap));
+       el->times = fr_heap_create(fr_event_list_time_cmp, offsetof(fr_event_t, heap));
        if (!el->times) {
-               fr_event_list_free(el);
+               talloc_free(el);
                return NULL;
        }
 
@@ -117,11 +119,18 @@ fr_event_list_t *fr_event_list_create(fr_event_status_t status)
        }
 
        el->status = status;
-       el->changed = 1;        /* force re-set of fds's */
+       el->changed = true;     /* force re-set of fds's */
 
        return el;
 }
 
+int fr_event_list_num_fds(fr_event_list_t *el)
+{
+       if (!el) return 0;
+
+       return el->num_readers;
+}
+
 int fr_event_list_num_elements(fr_event_list_t *el)
 {
        if (!el) return 0;
@@ -130,49 +139,77 @@ int fr_event_list_num_elements(fr_event_list_t *el)
 }
 
 
-int fr_event_delete(fr_event_list_t *el, fr_event_t **ev_p)
+int fr_event_delete(fr_event_list_t *el, fr_event_t **parent)
 {
+       int ret;
+
        fr_event_t *ev;
 
-       if (!el || !ev_p || !*ev_p) return 0;
+       if (!el || !parent || !*parent) return 0;
 
-       ev = *ev_p;
-       if (ev->ev_p) *(ev->ev_p) = NULL;
-       *ev_p = NULL;
+#ifndef NDEBUG
+       /*
+        *  Validate the event_t struct to detect memory issues early.
+        */
+       ev = talloc_get_type_abort(*parent, fr_event_t);
 
-       fr_heap_extract(el->times, ev);
-       free(ev);
+#else
+       ev = *parent;
+#endif
 
-       return 1;
+       if (ev->parent) {
+               fr_assert(*(ev->parent) == ev);
+               *ev->parent = NULL;
+       }
+       *parent = NULL;
+
+       ret = fr_heap_extract(el->times, ev);
+       fr_assert(ret == 1);    /* events MUST be in the heap */
+       talloc_free(ev);
+
+       return ret;
 }
 
 
-int fr_event_insert(fr_event_list_t *el,
-                     fr_event_callback_t callback,
-                     void *ctx, struct timeval *when,
-                     fr_event_t **ev_p)
+int fr_event_insert(fr_event_list_t *el, fr_event_callback_t callback, void *ctx, struct timeval *when,
+                   fr_event_t **parent)
 {
        fr_event_t *ev;
 
-       if (!el || !callback | !when || (when->tv_usec >= USEC)) return 0;
+       if (!el) {
+               fr_strerror_printf("Invalid arguments (NULL event list)");
+               return 0;
+       }
+
+       if (!callback) {
+               fr_strerror_printf("Invalid arguments (NULL callback)");
+               return 0;
+       }
+
+       if (!when || (when->tv_usec >= USEC)) {
+               fr_strerror_printf("Invalid arguments (time)");
+               return 0;
+       }
 
-       if (ev_p && *ev_p) fr_event_delete(el, ev_p);
+       if (!parent) {
+               fr_strerror_printf("Invalid arguments (NULL parent)");
+               return 0;
+       }
 
-       ev = malloc(sizeof(*ev));
-       if (!ev) return 0;
-       memset(ev, 0, sizeof(*ev));
+       if (*parent) fr_event_delete(el, parent);
 
+       ev = talloc_zero(el, fr_event_t);
        ev->callback = callback;
        ev->ctx = ctx;
        ev->when = *when;
-       ev->ev_p = ev_p;
+       ev->parent = parent;
 
        if (!fr_heap_insert(el->times, ev)) {
-               free(ev);
+               talloc_free(ev);
                return 0;
        }
 
-       if (ev_p) *ev_p = ev;
+       *parent = ev;
        return 1;
 }
 
@@ -198,6 +235,10 @@ int fr_event_run(fr_event_list_t *el, struct timeval *when)
                return 0;
        }
 
+#ifndef NDEBUG
+       ev = talloc_get_type_abort(ev, fr_event_t);
+#endif
+
        /*
         *      See if it's time to do this one.
         */
@@ -241,11 +282,35 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
        int i;
        fr_event_fd_t *ef;
 
-       if (!el || (fd < 0) || !handler || !ctx) return 0;
+       if (!el) {
+               fr_strerror_printf("Invalid arguments (NULL event list)");
+               return 0;
+       }
 
-       if (type != 0) return 0;
+       if (!handler) {
+               fr_strerror_printf("Invalid arguments (NULL handler)");
+               return 0;
+       }
 
-       if (el->max_readers >= FR_EV_MAX_FDS) return 0;
+       if (!ctx) {
+               fr_strerror_printf("Invalid arguments (NULL ctx)");
+               return 0;
+       }
+
+       if (fd < 0) {
+               fr_strerror_printf("Invalid arguments (bad FD %i)", fd);
+               return 0;
+       }
+
+       if (type != 0) {
+               fr_strerror_printf("Invalid type %i", type);
+               return 0;
+       }
+
+       if (el->max_readers >= FR_EV_MAX_FDS) {
+               fr_strerror_printf("Too many readers");
+               return 0;
+       }
 
        ef = NULL;
        for (i = 0; i <= el->max_readers; i++) {
@@ -255,6 +320,7 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
                if (el->readers[i].fd == fd) {
                        if ((el->readers[i].handler != handler) ||
                            (el->readers[i].ctx != ctx)) {
+                               fr_strerror_printf("Multiple handlers for same FD");
                                return 0;
                        }
 
@@ -266,19 +332,23 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd,
 
                if (el->readers[i].fd < 0) {
                        ef = &el->readers[i];
+                       el->num_readers++;
 
                        if (i == el->max_readers) el->max_readers = i + 1;
                        break;
                }
        }
 
-       if (!ef) return 0;
+       if (!ef) {
+               fr_strerror_printf("Failed assigning FD");
+               return 0;
+       }
 
        ef->handler = handler;
        ef->ctx = ctx;
        ef->fd = fd;
 
-       el->changed = 1;
+       el->changed = true;
 
        return 1;
 }
@@ -294,8 +364,10 @@ int fr_event_fd_delete(fr_event_list_t *el, int type, int fd)
        for (i = 0; i < el->max_readers; i++) {
                if (el->readers[i].fd == fd) {
                        el->readers[i].fd = -1;
+                       el->num_readers--;
+
                        if ((i + 1) == el->max_readers) el->max_readers = i;
-                       el->changed = 1;
+                       el->changed = true;
                        return 1;
                }
        }
@@ -311,6 +383,10 @@ void fr_event_loop_exit(fr_event_list_t *el, int code)
        el->exit = code;
 }
 
+bool fr_event_loop_exiting(fr_event_list_t *el)
+{
+       return (el->exit != 0);
+}
 
 int fr_event_loop(fr_event_list_t *el)
 {
@@ -319,16 +395,19 @@ int fr_event_loop(fr_event_list_t *el)
        fd_set read_fds, master_fds;
 
        el->exit = 0;
-       el->dispatch = 1;
-       el->changed = 1;
+       el->dispatch = true;
+       el->changed = true;
 
        while (!el->exit) {
                /*
                 *      Cache the list of FD's to watch.
                 */
                if (el->changed) {
+#ifdef __clang_analyzer__
+                       memset(&master_fds, 0, sizeof(master_fds));
+#else
                        FD_ZERO(&master_fds);
-
+#endif
                        for (i = 0; i < el->max_readers; i++) {
                                if (el->readers[i].fd < 0) continue;
 
@@ -338,7 +417,7 @@ int fr_event_loop(fr_event_list_t *el)
                                FD_SET(el->readers[i].fd, &master_fds);
                        }
 
-                       el->changed = 0;
+                       el->changed = false;
                }
 
                /*
@@ -392,9 +471,8 @@ int fr_event_loop(fr_event_list_t *el)
                read_fds = master_fds;
                rcode = select(maxfd + 1, &read_fds, NULL, NULL, wake);
                if ((rcode < 0) && (errno != EINTR)) {
-                       fr_strerror_printf("Failed in select: %s",
-                                          strerror(errno));
-                       el->dispatch = 0;
+                       fr_strerror_printf("Failed in select: %s", fr_syserror(errno));
+                       el->dispatch = false;
                        return -1;
                }
 
@@ -420,7 +498,7 @@ int fr_event_loop(fr_event_list_t *el)
                }
        }
 
-       el->dispatch = 0;
+       el->dispatch = false;
        return el->exit;
 }
 
@@ -474,7 +552,7 @@ int main(int argc, char **argv)
        struct timeval now, when;
        fr_event_list_t *el;
 
-       el = fr_event_list_create();
+       el = fr_event_list_create(NULL, NULL);
        if (!el) exit(1);
 
        memset(&rand_pool, 0, sizeof(rand_pool));
@@ -509,7 +587,7 @@ int main(int argc, char **argv)
                }
        }
 
-       fr_event_list_free(el);
+       talloc_free(el);
 
        return 0;
 }
index 1848bc5..fa436f5 100644 (file)
@@ -895,11 +895,6 @@ static int ascend_parse_generic(int argc, char **argv,
        token = fr_hex2bin(filter->value, argv[2], sizeof(filter->value));
        if (token != sizeof(filter->value)) return -1;
 
-       /*
-        *      The mask and value MUST be the same length.
-        */
-       if (rcode != token) return -1;
-
        filter->len = rcode;
        filter->len = htons(filter->len);
 
@@ -958,8 +953,7 @@ static int ascend_parse_generic(int argc, char **argv,
  *
  *     return:                 -1 for error or 0.
  */
-int
-ascend_parse_filter(VALUE_PAIR *pair)
+int ascend_parse_filter(VALUE_PAIR *vp, char const *value, size_t len)
 {
        int             token, type;
        int             rcode;
@@ -983,7 +977,8 @@ ascend_parse_filter(VALUE_PAIR *pair)
         *      Once the filter is *completelty* parsed, then we will
         *      over-write it with the final binary filter.
         */
-       p = talloc_strdup(pair, pair->vp_strvalue);
+       p = talloc_memdup(vp, value, len);
+       p[len] = '\0';
        argc = str2argv(p, argv, 32);
        if (argc < 3) {
                talloc_free(p);
@@ -1030,7 +1025,6 @@ ascend_parse_filter(VALUE_PAIR *pair)
                fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
                talloc_free(p);
                return -1;
-               break;
        }
 
        /*
@@ -1056,8 +1050,7 @@ ascend_parse_filter(VALUE_PAIR *pair)
 
        switch (type) {
        case RAD_FILTER_GENERIC:
-               rcode = ascend_parse_generic(argc - 3, &argv[3],
-                                         &filter.u.generic);
+               rcode = ascend_parse_generic(argc - 3, &argv[3], &filter.u.generic);
                break;
 
        case RAD_FILTER_IP:
@@ -1073,8 +1066,8 @@ ascend_parse_filter(VALUE_PAIR *pair)
         *      Touch the VP only if everything was OK.
         */
        if (rcode == 0) {
-               pair->length = sizeof(filter);
-               memcpy(pair->vp_filter, &filter, sizeof(filter));
+               vp->length = sizeof(filter);
+               memcpy(vp->vp_filter, &filter, sizeof(filter));
        }
 
        talloc_free(p);
@@ -1087,10 +1080,10 @@ ascend_parse_filter(VALUE_PAIR *pair)
      * the previous 'more' to be valid. If any should fail then TURN OFF
      * previous 'more'
      */
-    if( prevRadPair ) {
-       filt = ( RadFilter * )prevRadPair->vp_strvalue;
+    if( prevRadvp ) {
+       filt = ( RadFilter * )prevRadvp->vp_strvalue;
        if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
-          ( prevRadPair->attribute != pair->attribute ) ||
+          ( prevRadvp->attribute != vp->attribute ) ||
           ( filt->indirection != radFil.indirection ) ||
           ( filt->forward != radFil.forward ) ) {
            gen = &filt->u.generic;
@@ -1099,15 +1092,15 @@ ascend_parse_filter(VALUE_PAIR *pair)
                     valstr);
        }
     }
-    prevRadPair = NULL;
+    prevRadvp = NULL;
     if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
        if( radFil.u.generic.more ) {
-           prevRadPair = pair;
+           prevRadvp = vp;
        }
     }
 
     if( rc != -1 ) {
-           pairmemcpy(pair, &radFil, pair->length );
+           vpmemcpy(vp, &radFil, vp->length );
     }
     return(rc);
 
@@ -1122,177 +1115,176 @@ ascend_parse_filter(VALUE_PAIR *pair)
  *     Note we don't bother checking 'len' after the snprintf's.
  *     This function should ONLY be called with a large (~1k) buffer.
  */
-void print_abinary(VALUE_PAIR const *vp, char *buffer, size_t len, int8_t quote)
+void print_abinary(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t quote)
 {
-  size_t               i;
-  char                 *p;
-  ascend_filter_t      *filter;
-
-  static char const *action[] = {"drop", "forward"};
-  static char const *direction[] = {"out", "in"};
-
-  p = buffer;
-
-  /*
-   *  Just for paranoia: wrong size filters get printed as octets
-   */
-  if (vp->length != sizeof(*filter)) {
-          uint8_t *f = (uint8_t *) &vp->vp_filter;
-         strcpy(p, "0x");
-         p += 2;
-         len -= 2;
-         for (i = 0; i < vp->length; i++) {
-                 snprintf(p, len, "%02x", f[i]);
-                 p += 2;
-                 len -= 2;
-         }
-         return;
-  }
-
-  if (quote > 0) {
-       *(p++) = (char) quote;
-       len -= 3;                       /* account for leading & trailing quotes */
-  }
-
-  filter = (ascend_filter_t *) &(vp->vp_filter);
-  i = snprintf(p, len, "%s %s %s",
-              fr_int2str(filterType, filter->type, "??"),
-              direction[filter->direction & 0x01],
-              action[filter->forward & 0x01]);
-
-  p += i;
-  len -= i;
-
-  /*
-   *   Handle IP filters
-   */
-  if (filter->type == RAD_FILTER_IP) {
-
-    if (filter->u.ip.srcip) {
-      i = snprintf(p, len, " srcip %d.%d.%d.%d/%d",
-                  ((uint8_t *) &filter->u.ip.srcip)[0],
-                  ((uint8_t *) &filter->u.ip.srcip)[1],
-                  ((uint8_t *) &filter->u.ip.srcip)[2],
-                  ((uint8_t *) &filter->u.ip.srcip)[3],
-                  filter->u.ip.srcmask);
-      p += i;
-      len -= i;
-    }
+       size_t  i;
+       char    *p;
+       ascend_filter_t *filter;
 
-    if (filter->u.ip.dstip) {
-      i = snprintf(p, len, " dstip %d.%d.%d.%d/%d",
-                  ((uint8_t *) &filter->u.ip.dstip)[0],
-                  ((uint8_t *) &filter->u.ip.dstip)[1],
-                  ((uint8_t *) &filter->u.ip.dstip)[2],
-                  ((uint8_t *) &filter->u.ip.dstip)[3],
-                  filter->u.ip.dstmask);
-      p += i;
-      len -= i;
-    }
+       static char const *action[] = {"drop", "forward"};
+       static char const *direction[] = {"out", "in"};
 
-    i =  snprintf(p, len, " %s",
-                 fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
-    p += i;
-    len -= i;
-
-    if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
-      i = snprintf(p, len, " srcport %s %d",
-                  fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
-                  ntohs(filter->u.ip.srcport));
-      p += i;
-      len -= i;
-    }
+       p = out;
 
-    if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
-      i = snprintf(p, len, " dstport %s %d",
-                  fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
-                  ntohs(filter->u.ip.dstport));
-      p += i;
-      len -= i;
-    }
+       /*
+        *  Just for paranoia: wrong size filters get printed as octets
+        */
+       if (vp->length != sizeof(*filter)) {
+               uint8_t *f = (uint8_t *) &vp->vp_filter;
+
+               strcpy(p, "0x");
+               p += 2;
+               outlen -= 2;
+               for (i = 0; i < vp->length; i++) {
+                       snprintf(p, outlen, "%02x", f[i]);
+                       p += 2;
+                       outlen -= 2;
+               }
+               return;
+       }
 
-    if (filter->u.ip.established) {
-      i = snprintf(p, len, " est");
-      p += i;
-    }
+       if (quote > 0) {
+               *(p++) = (char) quote;
+               outlen -= 3;                    /* account for leading & trailing quotes */
+       }
 
-    /*
-     * Handle IPX filters
-     */
-  } else if (filter->type == RAD_FILTER_IPX) {
-    /* print for source */
-    if (filter->u.ipx.src.net) {
-      i = snprintf(p, len, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
-                 (unsigned int)ntohl(filter->u.ipx.src.net),
-                 filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
-                 filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
-                 filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
-      p += i;
-      len -= i;
-
-      if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
-       i = snprintf(p, len, " srcipxsock %s 0x%04x",
-                    fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
-                    ntohs(filter->u.ipx.src.socket));
-       p += i;
-       len -= i;
-      }
-    }
+       filter = (ascend_filter_t *) &(vp->vp_filter);
+       i = snprintf(p, outlen, "%s %s %s", fr_int2str(filterType, filter->type, "??"),
+                    direction[filter->direction & 0x01], action[filter->forward & 0x01]);
 
-    /* same for destination */
-    if (filter->u.ipx.dst.net) {
-      i = snprintf(p, len, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
-                 (unsigned int)ntohl(filter->u.ipx.dst.net),
-                 filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
-                 filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
-                 filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
-      p += i;
-      len -= i;
-
-      if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
-       i = snprintf(p, len, " dstipxsock %s 0x%04x",
-                    fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
-                    ntohs(filter->u.ipx.dst.socket));
        p += i;
-      }
-    }
+       outlen -= i;
 
+       /*
+       *       Handle IP filters
+       */
+       if (filter->type == RAD_FILTER_IP) {
+
+               if (filter->u.ip.srcip) {
+                       i = snprintf(p, outlen, " srcip %d.%d.%d.%d/%d",
+                                    ((uint8_t *) &filter->u.ip.srcip)[0],
+                                    ((uint8_t *) &filter->u.ip.srcip)[1],
+                                    ((uint8_t *) &filter->u.ip.srcip)[2],
+                                    ((uint8_t *) &filter->u.ip.srcip)[3],
+                                    filter->u.ip.srcmask);
+                       p += i;
+                       outlen -= i;
+               }
 
-  } else if (filter->type == RAD_FILTER_GENERIC) {
-    int count;
+               if (filter->u.ip.dstip) {
+                       i = snprintf(p, outlen, " dstip %d.%d.%d.%d/%d",
+                                    ((uint8_t *) &filter->u.ip.dstip)[0],
+                                    ((uint8_t *) &filter->u.ip.dstip)[1],
+                                    ((uint8_t *) &filter->u.ip.dstip)[2],
+                                    ((uint8_t *) &filter->u.ip.dstip)[3],
+                                    filter->u.ip.dstmask);
+                       p += i;
+                       outlen -= i;
+               }
 
-    i = snprintf(p, len, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
-    p += i;
+               i = snprintf(p, outlen, " %s", fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
+               p += i;
+               outlen -= i;
 
-    /* show the mask */
-    for (count = 0; count < ntohs(filter->u.generic.len); count++) {
-      i = snprintf(p, len, "%02x", filter->u.generic.mask[count]);
-      p += i;
-      len -= i;
-    }
+               if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
+                       i = snprintf(p, outlen, " srcport %s %d",
+                                    fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
+                                    ntohs(filter->u.ip.srcport));
+                       p += i;
+                       outlen -= i;
+               }
 
-    strcpy(p, " ");
-    p++;
-    len--;
+               if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
+                       i = snprintf(p, outlen, " dstport %s %d",
+                                    fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
+                                    ntohs(filter->u.ip.dstport));
+                       p += i;
+                       outlen -= i;
+               }
 
-    /* show the value */
-    for (count = 0; count < ntohs(filter->u.generic.len); count++) {
-      i = snprintf(p, len, "%02x", filter->u.generic.value[count]);
-      p += i;
-      len -= i;
-    }
+               if (filter->u.ip.established) {
+                       i = snprintf(p, outlen, " est");
+                       p += i;
+               }
+
+               /*
+                *      Handle IPX filters
+                */
+       } else if (filter->type == RAD_FILTER_IPX) {
+               /* print for source */
+               if (filter->u.ipx.src.net) {
+                       i = snprintf(p, outlen, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
+                                 (unsigned int)ntohl(filter->u.ipx.src.net),
+                                 filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
+                                 filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
+                                 filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
+                       p += i;
+                       outlen -= i;
+
+                       if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
+                               i = snprintf(p, outlen, " srcipxsock %s 0x%04x",
+                                            fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
+                                            ntohs(filter->u.ipx.src.socket));
+                               p += i;
+                               outlen -= i;
+                       }
+               }
 
-    i = snprintf(p, len, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
-    p += i;
-    len -= i;
+               /* same for destination */
+               if (filter->u.ipx.dst.net) {
+                       i = snprintf(p, outlen, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
+                                 (unsigned int)ntohl(filter->u.ipx.dst.net),
+                                 filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
+                                 filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
+                                 filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
+                       p += i;
+                       outlen -= i;
+
+                       if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
+                               i = snprintf(p, outlen, " dstipxsock %s 0x%04x",
+                                            fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
+                                            ntohs(filter->u.ipx.dst.socket));
+                               p += i;
+                       }
+               }
+       } else if (filter->type == RAD_FILTER_GENERIC) {
+               int count;
 
-    if (filter->u.generic.more != 0) {
-      i = snprintf(p, len, " more");
-      p += i;
-    }
-  }
+               i = snprintf(p, outlen, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
+               p += i;
 
-  if (quote > 0) *(p++) = (char) quote;
-  *p = '\0';
+               /* show the mask */
+               for (count = 0; count < ntohs(filter->u.generic.len); count++) {
+                       i = snprintf(p, outlen, "%02x", filter->u.generic.mask[count]);
+                       p += i;
+                       outlen -= i;
+               }
+
+               strcpy(p, " ");
+               p++;
+               outlen--;
+
+               /* show the value */
+               for (count = 0; count < ntohs(filter->u.generic.len); count++) {
+                       i = snprintf(p, outlen, "%02x", filter->u.generic.value[count]);
+                       p += i;
+                       outlen -= i;
+               }
+
+               i = snprintf(p, outlen, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
+               p += i;
+               outlen -= i;
+
+               if (filter->u.generic.more != 0) {
+                       i = snprintf(p, outlen, " more");
+                       p += i;
+               }
+       }
+
+       if (quote > 0) {
+               *(p++) = (char) quote;
+       }
+       *p = '\0';
 }
+
 #endif
index 676f1f3..315ff46 100644 (file)
@@ -139,7 +139,7 @@ gethostbyname_r(char const *hostname, struct hostent *result,
 
 #ifdef HAVE_PTHREAD_H
     if (fr_hostbyname == 0) {
-       pthread_mutex_init(&fr_hostbyname_mutex, NULL);
+       pthread_mutex_init(&fr_hostbyname_mutex, NULL);
        fr_hostbyname = 1;
     }
     pthread_mutex_lock(&fr_hostbyname_mutex);
@@ -172,7 +172,7 @@ gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
 
 #ifdef HAVE_PTHREAD_H
     if (fr_hostbyaddr == 0) {
-       pthread_mutex_init(&fr_hostbyaddr_mutex, NULL);
+       pthread_mutex_init(&fr_hostbyaddr_mutex, NULL);
        fr_hostbyaddr = 1;
     }
     pthread_mutex_lock(&fr_hostbyaddr_mutex);
@@ -204,7 +204,7 @@ gethostbyaddr_r(char const *addr, int len, int type, struct hostent *result,
 
 #ifndef HAVE_GETADDRINFO
 static struct addrinfo *
-malloc_ai(int port, u_long addr, int socktype, int proto)
+malloc_ai(uint16_t port, u_long addr, int socktype, int proto)
 {
     struct addrinfo *ai;
 
@@ -266,7 +266,8 @@ getaddrinfo(char const *hostname, char const *servname,
     struct hostent *hp;
     struct hostent result;
     struct in_addr in;
-    int i, port = 0, socktype, proto;
+    int i, socktype, proto;
+    uint16_t port = 0;
     int error;
     char buffer[2048];
 
index a915b56..14eca18 100644 (file)
@@ -44,7 +44,7 @@ typedef struct fr_hash_entry_t {
        struct fr_hash_entry_t *next;
        uint32_t        reversed;
        uint32_t        key;
-       void const      *data;
+       void const      *data;
 } fr_hash_entry_t;
 
 
@@ -613,7 +613,7 @@ int fr_hash_table_walk(fr_hash_table_t *ht,
 
                        next = node->next;
 
-                       memcpy(&arg, node->data, sizeof(arg));
+                       memcpy(&arg, &node->data, sizeof(arg));
                        rcode = callback(context, arg);
 
                        if (rcode != 0) return rcode;
@@ -759,33 +759,6 @@ uint32_t fr_hash_update(void const *data, size_t size, uint32_t hash)
 }
 
 /*
- *     Return a "folded" hash, where the lower "bits" are the
- *     hash, and the upper bits are zero.
- *
- *     If you need a non-power-of-two hash, cope.
- */
-uint32_t fr_hash_fold(uint32_t hash, int bits)
-{
-       int count;
-       uint32_t result;
-
-       if ((bits <= 0) || (bits >= 32)) return hash;
-
-       result = hash;
-
-       /*
-        *      Never use the same bits twice in an xor.
-        */
-       for (count = 0; count < 32; count += bits) {
-               hash >>= bits;
-               result ^= hash;
-       }
-
-       return result & (((uint32_t) (1 << bits)) - 1);
-}
-
-
-/*
  *     Hash a C string, so we loop over it once.
  */
 uint32_t fr_hash_string(char const *p)
index 2d029b9..e3675cf 100644 (file)
@@ -223,46 +223,129 @@ int fr_heap_num_elements(fr_heap_t *hp)
 
 
 #ifdef TESTING
+static bool fr_heap_check(fr_heap_t *hp, void *data)
+{
+       int i;
+
+       if (!hp || (hp->num_elements == 0)) return false;
+
+       for (i = 0; i < hp->num_elements; i++) {
+               if (hp->p[i] == data) {
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+typedef struct heap_thing {
+       int data;
+       int heap;               /* for the heap */
+} heap_thing;
+
+
 /*
  *  cc -g -DTESTING -I .. heap.c -o heap
  *
  *  ./heap
  */
-static int heap_cmp(void const *a, void const *b)
+static int heap_cmp(void const *one, void const *two)
 {
-       return *(int *)a - *(int *) b;
+       heap_thing const *a;
+       heap_thing const *b;
+
+       a = (heap_thing const *) one;
+       b = (heap_thing const *) two;
+
+       return a->data - b->data;
 
 }
 
+#define ARRAY_SIZE (1024)
 
-int main(int argc, char **arg)
+void NEVER_RETURNS _fr_exit(char const *file, int line, int status)
+{
+#ifndef NDEBUG
+       fprintf(stderr, "EXIT CALLED %s[%u]: %i: ", file, line, status);
+#endif
+       fflush(stderr);
+       exit(status);
+}
+
+int main(int argc, char **argv)
 {
        fr_heap_t *hp;
-       int i, array[1024];
+       int i;
+       heap_thing array[ARRAY_SIZE];
+       int skip = 0;
+       int left;
+
+       if (argc > 1) {
+               skip = atoi(argv[1]);
+       }
 
-       hp = fr_heap_create(heap_cmp, 0);
+       hp = fr_heap_create(heap_cmp, offsetof(heap_thing, heap));
        if (!hp) {
                fprintf(stderr, "Failed creating heap!\n");
                fr_exit(1);
        }
 
-       for (i = 0; i < 1024; i++) {
-               array[i] = (i * 257) % 65537;
+       for (i = 0; i < ARRAY_SIZE; i++) {
+               array[i].data = rand() % 65537;
                if (!fr_heap_insert(hp, &array[i])) {
                        fprintf(stderr, "Failed inserting %d\n", i);
                        fr_exit(1);
                }
+       
+               if (!fr_heap_check(hp, &array[i])) {
+                       fprintf(stderr, "Inserted but not in heap %d\n", i);
+                       fr_exit(1);
+               }
        }
 
-       for (i = 0; i < 1024; i++) {
-               int *p = fr_heap_peek(hp);
+#if 0
+       for (i = 0; i < ARRAY_SIZE; i++) {
+               printf("Array %d has value %d at offset %d\n",
+                      i, array[i].data, array[i].heap);
+       }
+#endif
+
+       if (skip) {
+               int entry;
+
+               printf("%d elements to remove\n", ARRAY_SIZE / skip);
+
+               for (i = 0; i < ARRAY_SIZE / skip; i++) {
+                       entry = i * skip;
+
+                       if (!fr_heap_extract(hp, &array[entry])) {
+                               fprintf(stderr, "Failed removing %d\n", entry);
+                       }
+
+                       if (fr_heap_check(hp, &array[entry])) {
+                               fprintf(stderr, "Deleted but still in heap %d\n", entry);
+                               fr_exit(1);
+                       }
 
-               if (!p) {
+                       if (array[entry].heap != -1) {
+                               fprintf(stderr, "heap offset is wrong %d\n", entry);
+                               fr_exit(1);
+                       }
+               }
+       }
+
+       left = fr_heap_num_elements(hp);
+       printf("%d elements left in the heap\n", left);
+
+       for (i = 0; i < left; i++) {
+               heap_thing *t = fr_heap_peek(hp);
+
+               if (!t) {
                        fprintf(stderr, "Failed peeking %d\n", i);
                        fr_exit(1);
                }
 
-               printf("%d\t%d\n", i, *p);
+               printf("%d\t%d\n", i, t->data);
 
                if (!fr_heap_extract(hp, NULL)) {
                        fprintf(stderr, "Failed extracting %d\n", i);
@@ -270,6 +353,11 @@ int main(int argc, char **arg)
                }
        }
 
+       if (fr_heap_num_elements(hp) > 0) {
+               fprintf(stderr, "%d elements left at the end", fr_heap_num_elements(hp));
+               fr_exit(1);
+       }
+
        fr_heap_delete(hp);
 
        return 0;
index 25cc121..8b38888 100644 (file)
@@ -25,121 +25,177 @@ RCSID("$Id$")
 
 #include <freeradius-devel/libradius.h>
 
-
-#define FR_STRERROR_BUFSIZE (1024)
-
-#ifdef HAVE_THREAD_TLS
 /*
- *     GCC on most Linux systems
+ *     Are we using glibc or a close relative?
  */
-#define THREAD_TLS __thread
+#ifdef HAVE_FEATURES_H
+#  include <features.h>
+#endif
 
-#elif defined(HAVE_DECLSPEC_THREAD)
-/*
- *     Visual C++, Borland
- */
-#define THREAD_TLS __declspec(thread)
-#else
+#define FR_STRERROR_BUFSIZE (2048)
 
-/*
- *     We don't have thread-local storage.  Ensure we don't
- *     ask for it.
- */
-#define THREAD_TLS
+fr_thread_local_setup(char *, fr_strerror_buffer)      /* macro */
+fr_thread_local_setup(char *, fr_syserror_buffer)      /* macro */
 
-/*
- *     Use pthread keys if we have pthreads.  For MAC, which should
- *     be very fast.
- */
-#ifdef HAVE_PTHREAD_H
-#define USE_PTHREAD_FOR_TLS (1)
-#endif
-#endif
 
-#ifndef USE_PTHREAD_FOR_TLS
 /*
- *     Try to create a thread-local-storage version of this buffer.
+ *     Explicitly cleanup the memory allocated to the error buffer,
+ *     just in case valgrind complains about it.
  */
-static THREAD_TLS char fr_strerror_buffer[FR_STRERROR_BUFSIZE];
-
-#else
-#include <pthread.h>
-
-static pthread_key_t  fr_strerror_key;
-static pthread_once_t fr_strerror_once = PTHREAD_ONCE_INIT;
-
-/* Create Key */
-static void fr_strerror_make_key(void)
+static void _fr_logging_free(void *arg)
 {
-       pthread_key_create(&fr_strerror_key, NULL);
+       free(arg);
 }
-#endif
 
-/*
- *     Log to a buffer, trying to be thread-safe.
+/** Log to thread local error buffer
+ *
+ * @param fmt printf style format string. If NULL sets the 'new' byte to false,
+ *       effectively clearing the last message.
  */
 void fr_strerror_printf(char const *fmt, ...)
 {
        va_list ap;
 
-#ifdef USE_PTHREAD_FOR_TLS
        char *buffer;
 
-       pthread_once(&fr_strerror_once, fr_strerror_make_key);
-
-       buffer = pthread_getspecific(fr_strerror_key);
+       buffer = fr_thread_local_init(fr_strerror_buffer, _fr_logging_free);
        if (!buffer) {
                int ret;
 
-               buffer = malloc(FR_STRERROR_BUFSIZE);
-               if (!buffer) return; /* panic and die! */
+               /*
+                *      malloc is thread safe, talloc is not
+                */
+               buffer = malloc(sizeof(char) * (FR_STRERROR_BUFSIZE + 1));      /* One byte extra for status */
+               if (!buffer) {
+                       fr_perror("Failed allocating memory for libradius error buffer");
+                       return;
+               }
 
-               ret = pthread_setspecific(fr_strerror_key, buffer);
+               ret = fr_thread_local_set(fr_strerror_buffer, buffer);
                if (ret != 0) {
-                       fr_perror("Failed recording thread error: %s",
-                                 strerror(ret));
-
+                       fr_perror("Failed setting up TLS for libradius error buffer: %s", fr_syserror(ret));
+                       free(buffer);
                        return;
                }
        }
 
-       va_start(ap, fmt);
-       vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
+       /*
+        *      NULL has a special meaning, setting the new byte to false.
+        */
+       if (!fmt) {
+               buffer[FR_STRERROR_BUFSIZE] = '\0';
+               return;
+       }
 
-#else
        va_start(ap, fmt);
-       vsnprintf(fr_strerror_buffer, sizeof(fr_strerror_buffer), fmt, ap);
-#endif
-
+       vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
+       buffer[FR_STRERROR_BUFSIZE] = '\1';                     /* Flip the 'new' byte to true */
        va_end(ap);
 }
 
+/** Get the last library error
+ *
+ * Will only return the last library error once, after which it will return a zero length string.
+ *
+ * @return library error or zero length string
+ */
 char const *fr_strerror(void)
 {
-#ifndef USE_PTHREAD_FOR_TLS
-       return fr_strerror_buffer;
+       char *buffer;
 
-#else
-       char const *msg;
+       buffer = fr_thread_local_get(fr_strerror_buffer);
+       if (buffer && (buffer[FR_STRERROR_BUFSIZE] != '\0')) {
+               buffer[FR_STRERROR_BUFSIZE] = '\0';             /* Flip the 'new' byte to false */
+               return buffer;
+       }
+
+       return "";
+}
+
+/** Guaranteed to be thread-safe version of strerror
+ *
+ * @param num errno as returned by function or from global errno.
+ * @return local specific error string relating to errno.
+ */
+char const *fr_syserror(int num)
+{
+       char *buffer;
+       int ret;
 
-       pthread_once(&fr_strerror_once, fr_strerror_make_key);
+       buffer = fr_thread_local_init(fr_syserror_buffer, _fr_logging_free);
+       if (!buffer) {
+               /*
+                *      malloc is thread safe, talloc is not
+                */
+               buffer = malloc(sizeof(char) * FR_STRERROR_BUFSIZE);
+               if (!buffer) {
+                       fr_perror("Failed allocating memory for system error buffer");
+                       return NULL;
+               }
 
-       msg = pthread_getspecific(fr_strerror_key);
-       if (msg) return msg;
+               ret = fr_thread_local_set(fr_syserror_buffer, buffer);
+               if (ret != 0) {
+                       fr_perror("Failed setting up TLS for system error buffer: %s", fr_syserror(ret));
+                       free(buffer);
+                       return NULL;
+               }
+       }
 
-       return "(unknown error)"; /* DON'T return NULL! */
+       if (!num) {
+               return "No error";
+       }
+
+       /*
+        *      XSI-Compliant version
+        */
+#if !defined(HAVE_FEATURES_H) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 500) && ! _GNU_SOURCE)
+       if ((ret = strerror_r(num, buffer, (size_t) FR_STRERROR_BUFSIZE) != 0)) {
+#  ifndef NDEBUG
+               fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p (%zu bytes), "
+                       "returned %i: %s\n", num, buffer, (size_t) FR_STRERROR_BUFSIZE, ret, strerror(ret));
+#  endif
+               buffer[0] = '\0';
+       }
+       return buffer;
+       /*
+        *      GNU Specific version
+        *
+        *      The GNU Specific version returns a char pointer. That pointer may point
+        *      the buffer you just passed in, or to an immutable static string.
+        */
+#else
+       {
+               char const *p;
+               p = strerror_r(num, buffer, (size_t) FR_STRERROR_BUFSIZE);
+               if (!p) {
+#  ifndef NDEBUG
+                       fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p "
+                               "(%zu bytes): %s\n", num, buffer, (size_t) FR_STRERROR_BUFSIZE, strerror(errno));
+#  endif
+                       buffer[0] = '\0';
+                       return buffer;
+               }
+               return p;
+       }
 #endif
+
 }
 
 void fr_perror(char const *fmt, ...)
 {
+       char const *error;
        va_list ap;
 
        va_start(ap, fmt);
        vfprintf(stderr, fmt, ap);
-       if (strchr(fmt, ':') == NULL)
-               fprintf(stderr, ": ");
-       fprintf(stderr, "%s\n", fr_strerror());
+
+       error = fr_strerror();
+       if (error && (error[0] != '\0')) {
+               fprintf(stderr, ": %s\n", error);
+       } else {
+               fputs("\n", stderr);
+       }
+
        va_end(ap);
 }
 
@@ -147,6 +203,9 @@ bool fr_assert_cond(char const *file, int line, char const *expr, bool cond)
 {
        if (!cond) {
                fr_perror("SOFT ASSERT FAILED %s[%u]: %s", file, line, expr);
+#if !defined(NDEBUG) && defined(SIGUSR1)
+               fr_fault(SIGUSR1);
+#endif
                return false;
        }
 
@@ -156,8 +215,15 @@ bool fr_assert_cond(char const *file, int line, char const *expr, bool cond)
 void NEVER_RETURNS _fr_exit(char const *file, int line, int status)
 {
 #ifndef NDEBUG
-       fr_perror("EXIT CALLED %s[%u]: %i", file, line, status);
+       char const *error = fr_strerror();
+
+       if (error && (status != 0)) {
+               fr_perror("EXIT(%i) CALLED %s[%u].  Last error was: %s", status, file, line, error);
+       } else {
+               fr_perror("EXIT(%i) CALLED %s[%u]", status, file, line);
+       }
 #endif
+       fr_perror("If running under a debugger it should break <<here>>");
        fflush(stderr);
 
        fr_debug_break();       /* If running under GDB we'll break here */
@@ -168,8 +234,15 @@ void NEVER_RETURNS _fr_exit(char const *file, int line, int status)
 void NEVER_RETURNS _fr_exit_now(char const *file, int line, int status)
 {
 #ifndef NDEBUG
-       fr_perror("_EXIT CALLED %s[%u]: %i", file, line, status);
+       char const *error = fr_strerror();
+
+       if (error && (status != 0)) {
+               fr_perror("_EXIT(%i) CALLED %s[%u].  Last error was: %s", status, file, line, error);
+       } else {
+               fr_perror("_EXIT(%i) CALLED %s[%u]", status, file, line);
+       }
 #endif
+       fr_perror("If running under a debugger it should break <<here>>");
        fflush(stderr);
 
        fr_debug_break();       /* If running under GDB we'll break here */
index 45d169e..88cb31d 100644 (file)
@@ -13,7 +13,7 @@ RCSID("$Id$")
  *  FORCE MD4 TO USE OUR MD4 HEADER FILE!
  *  If we don't do this, it might pick up the systems broken MD4.
  */
-#include "../include/md4.h"
+#include <freeradius-devel/md4.h>
 
 void fr_md4_calc(output, input, inlen)
 unsigned char *output;
@@ -27,7 +27,7 @@ unsigned int inlen;                /* length of input block */
        fr_MD4Final(output, &context);
 }
 
-#ifndef WITH_OPENSSL_MD4
+#ifndef HAVE_OPENSSL_MD4_H
 /*     The below was retrieved from
  *     http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/lib/libc/hash/md4.c?rev=1.2
  *     with the following changes:
index 20e643f..5f57c32 100644 (file)
@@ -15,7 +15,7 @@ RCSID("$Id$")
  *  FORCE MD5 TO USE OUR MD5 HEADER FILE!
  *  If we don't do this, it might pick up the systems broken MD5.
  */
-#include "../include/md5.h"
+#include <freeradius-devel/md5.h>
 
 void fr_md5_calc(uint8_t *output, uint8_t const *input,
                     unsigned int inlen)
@@ -28,7 +28,7 @@ void fr_md5_calc(uint8_t *output, uint8_t const *input,
 }
 
 
-#ifndef WITH_OPENSSL_MD5
+#ifndef HAVE_OPENSSL_MD5_H
 /*     The below was retrieved from
  *     http://www.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/crypto/md5.c?rev=1.1
  *     with the following changes:
@@ -172,7 +172,6 @@ void fr_MD5Final(uint8_t digest[MD5_DIGEST_LENGTH], FR_MD5_CTX *ctx)
        memset(ctx, 0, sizeof(*ctx));   /* in case it's sensitive */
 }
 
-
 /* The four core functions - F1 is optimized somewhat */
 
 /* #define F1(x, y, z) (x & y | ~x & z) */
index a1c5dc5..61f9c59 100644 (file)
@@ -27,7 +27,6 @@ RCSID("$Id$")
 #include       <ctype.h>
 #include       <sys/file.h>
 #include       <fcntl.h>
-#include       <signal.h>
 
 #define FR_PUT_LE16(a, val)\
        do {\
@@ -35,7 +34,14 @@ RCSID("$Id$")
                a[0] = ((uint16_t) (val)) & 0xff;\
        } while (0)
 
-static int     fr_debugger_present = -1;
+#ifdef HAVE_PTHREAD_H
+static pthread_mutex_t autofree_context = PTHREAD_MUTEX_INITIALIZER;
+#  define PTHREAD_MUTEX_LOCK pthread_mutex_lock
+#  define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
+#else
+#  define PTHREAD_MUTEX_LOCK(_x)
+#  define PTHREAD_MUTEX_UNLOCK(_x)
+#endif
 
 bool   fr_dns_lookups = false;     /* IP -> hostname lookups? */
 bool    fr_hostname_lookups = true; /* hostname -> IP lookups? */
@@ -45,32 +51,110 @@ static char const *months[] = {
        "jan", "feb", "mar", "apr", "may", "jun",
        "jul", "aug", "sep", "oct", "nov", "dec" };
 
+fr_thread_local_setup(char *, fr_inet_ntop_buffer);    /* macro */
+
+/** Sets a signal handler using sigaction if available, else signal
+ *
+ * @param sig to set handler for.
+ * @param func handler to set.
+ */
+int fr_set_signal(int sig, sig_t func)
+{
+#ifdef HAVE_SIGACTION
+       struct sigaction act;
+
+       memset(&act, 0, sizeof(act));
+       act.sa_flags = 0;
+       sigemptyset(&act.sa_mask);
+       act.sa_handler = func;
+
+       if (sigaction(sig, &act, NULL) < 0) {
+               fr_strerror_printf("Failed setting signal %i handler via sigaction(): %s", sig, fr_syserror(errno));
+               return -1;
+       }
+#else
+       if (signal(sig, func) < 0) {
+               fr_strerror_printf("Failed setting signal %i handler via signal(): %s", sig, fr_syserror(errno));
+               return -1;
+       }
+#endif
+       return 0;
+}
+
 /** Allocates a new talloc context from the root autofree context
  *
- * @param signum signal raised.
+ * This function is threadsafe, whereas using the NULL context is not.
+ *
+ * @note The returned context must be freed by the caller.
+ * @returns a new talloc context parented by the root autofree context.
  */
-static void _sigtrap_handler(UNUSED int signum)
+TALLOC_CTX *fr_autofree_ctx(void)
 {
-    fr_debugger_present = 0;
-    signal(SIGTRAP, SIG_DFL);
+       static TALLOC_CTX *ctx = NULL, *child;
+       PTHREAD_MUTEX_LOCK(&autofree_context);
+       if (!ctx) {
+               ctx = talloc_autofree_context();
+       }
+
+       child = talloc_new(ctx);
+       PTHREAD_MUTEX_UNLOCK(&autofree_context);
+
+       return child;
 }
 
-/** Break in GDB (if were running under GDB)
+/*
+ *     Explicitly cleanup the memory allocated to the error inet_ntop
+ *     buffer.
+ */
+static void _fr_inet_ntop_free(void *arg)
+{
+       free(arg);
+}
+
+/** Wrapper around inet_ntop, prints IPv4/IPv6 addresses
+ *
+ * inet_ntop requires the caller pass in a buffer for the address.
+ * This would be annoying and cumbersome, seeing as quite often the ASCII
+ * address is only used for logging output.
  *
- * If the server is running under GDB this will raise a SIGTRAP which
- * will pause the running process.
+ * So as with lib/log.c use TLS to allocate thread specific buffers, and
+ * write the IP address there instead.
  *
- * If the server is not running under GDB then this will do nothing.
+ * @param af address family, either AF_INET or AF_INET6.
+ * @param src pointer to network address structure.
+ * @return NULL on error, else pointer to ASCII buffer containing text version of address.
  */
-void fr_debug_break(void)
+char const *fr_inet_ntop(int af, void const *src)
 {
-    if (fr_debugger_present == -1) {
-       fr_debugger_present = 0;
-        signal(SIGTRAP, _sigtrap_handler);
-        raise(SIGTRAP);
-    } else if (fr_debugger_present == 1) {
-       raise(SIGTRAP);
-    }
+       char *buffer;
+
+       if (!src) {
+               return NULL;
+       }
+
+       buffer = fr_thread_local_init(fr_inet_ntop_buffer, _fr_inet_ntop_free);
+       if (!buffer) {
+               int ret;
+
+               /*
+                *      malloc is thread safe, talloc is not
+                */
+               buffer = malloc(sizeof(char) * INET6_ADDRSTRLEN);
+               if (!buffer) {
+                       fr_perror("Failed allocating memory for inet_ntop buffer");
+                       return NULL;
+               }
+
+               ret = fr_thread_local_set(fr_inet_ntop_buffer, buffer);
+               if (ret != 0) {
+                       fr_perror("Failed setting up TLS for inet_ntop buffer: %s", fr_syserror(ret));
+                       free(buffer);
+                       return NULL;
+               }
+       }
+       buffer[0] = '\0';
+
+       return inet_ntop(af, src, buffer, INET6_ADDRSTRLEN);
 }
 
 /*
@@ -90,6 +174,281 @@ char const *ip_ntoa(char *buffer, uint32_t ipaddr)
        return buffer;
 }
 
+/** Parse an IPv4 address or IPv4 prefix in presentation format (and others)
+ *
+ * @param out Where to write the ip address value.
+ * @param value to parse, may be dotted quad [+ prefix], or integer, or octal number, or '*' (INADDR_ANY).
+ * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
+ * @param fallback to IPv4 resolution if no A records can be found.
+ * @return 0 if ip address was parsed successfully, else -1 on error.
+ */
+int fr_pton4(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve, bool fallback)
+{
+       char *p;
+       unsigned int prefix;
+       char *eptr;
+
+       /* Dotted quad + / + [0-9]{1,2} */
+       char buffer[INET_ADDRSTRLEN + 3];
+
+       /*
+        *      Copy to intermediary buffer if we were given a length
+        */
+       if (inlen > 0) {
+               if (inlen >= sizeof(buffer)) {
+                       fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
+                       return -1;
+               }
+               memcpy(buffer, value, inlen);
+               buffer[inlen] = '\0';
+       }
+
+       p = strchr(value, '/');
+       /*
+        *      192.0.2.2 is parsed as if it was /32
+        */
+       if (!p) {
+               /*
+                *      Allow '*' as the wildcard address usually 0.0.0.0
+                */
+               if ((value[0] == '*') && (value[1] == '\0')) {
+                       out->ipaddr.ip4addr.s_addr = htonl(INADDR_ANY);
+               /*
+                *      Convert things which are obviously integers to IP addresses
+                *
+                *      We assume the number is the bigendian representation of the
+                *      IP address.
+                */
+               } else if (is_integer(value)) {
+                       out->ipaddr.ip4addr.s_addr = htonl(strtoul(value, NULL, 0));
+               } else if (!resolve) {
+                       if (inet_pton(AF_INET, value, &(out->ipaddr.ip4addr.s_addr)) <= 0) {
+                               fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
+                               return -1;
+                       }
+               } else if (ip_hton(out, AF_INET, value, fallback) < 0) return -1;
+
+               out->prefix = 32;
+               out->af = AF_INET;
+
+               return 0;
+       }
+
+       /*
+        *      Otherwise parse the prefix
+        */
+       if ((size_t)(p - value) >= INET_ADDRSTRLEN) {
+               fr_strerror_printf("Invalid IPv4 address string \"%s\"", value);
+               return -1;
+       }
+
+       /*
+        *      Copy the IP portion into a temporary buffer if we haven't already.
+        */
+       if (inlen == 0) memcpy(buffer, value, p - value);
+       buffer[p - value] = '\0';
+
+       if (!resolve) {
+               if (inet_pton(AF_INET, buffer, &(out->ipaddr.ip4addr.s_addr)) <= 0) {
+                       fr_strerror_printf("Failed to parse IPv4 address string \"%s\"", value);
+                       return -1;
+               }
+       } else if (ip_hton(out, AF_INET, buffer, fallback) < 0) return -1;
+
+       prefix = strtoul(p + 1, &eptr, 10);
+       if (prefix > 32) {
+               fr_strerror_printf("Invalid IPv4 mask length \"%s\".  Should be between 0-32", p);
+               return -1;
+       }
+       if (eptr[0] != '\0') {
+               fr_strerror_printf("Failed to parse IPv4 address string \"%s\", "
+                                  "got garbage after mask length \"%s\"", value, eptr);
+               return -1;
+       }
+
+       if (prefix < 32) {
+               out->ipaddr.ip4addr = fr_inaddr_mask(&(out->ipaddr.ip4addr), prefix);
+       }
+
+       out->prefix = (uint8_t) prefix;
+       out->af = AF_INET;
+
+       return 0;
+}
+
+/** Parse an IPv6 address or IPv6 prefix in presentation format (and others)
+ *
+ * @param out Where to write the ip address value.
+ * @param value to parse.
+ * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
+ * @param fallback to IPv4 resolution if no AAAA records can be found.
+ * @return 0 if ip address was parsed successfully, else -1 on error.
+ */
+int fr_pton6(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve, bool fallback)
+{
+       char const *p;
+       unsigned int prefix;
+       char *eptr;
+
+       /* IPv6  + / + [0-9]{1,3} */
+       char buffer[INET6_ADDRSTRLEN + 4];
+
+       /*
+        *      Copy to intermediary buffer if we were given a length
+        */
+       if (inlen > 0) {
+               if (inlen >= sizeof(buffer)) {
+                       fr_strerror_printf("Invalid IPv6 address string \"%s\"", value);
+                       return -1;
+               }
+               memcpy(buffer, value, inlen);
+               buffer[inlen] = '\0';
+       }
+
+       p = strchr(value, '/');
+       if (!p) {
+               /*
+                *      Allow '*' as the wildcard address
+                */
+               if ((value[0] == '*') && (value[1] == '\0')) {
+                       memset(&out->ipaddr.ip6addr.s6_addr, 0, sizeof(out->ipaddr.ip6addr.s6_addr));
+               } else if (!resolve) {
+                       if (inet_pton(AF_INET6, value, &(out->ipaddr.ip6addr.s6_addr)) <= 0) {
+                               fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
+                               return -1;
+                       }
+               } else if (ip_hton(out, AF_INET6, value, fallback) < 0) return -1;
+
+               out->prefix = 128;
+               out->af = AF_INET6;
+
+               return 0;
+       }
+
+       if ((p - value) >= INET6_ADDRSTRLEN) {
+               fr_strerror_printf("Invalid IPv6 address string \"%s\"", value);
+               return -1;
+       }
+
+       /*
+        *      Copy string to temporary buffer if we didn't do it earlier
+        */
+       if (inlen == 0) memcpy(buffer, value, p - value);
+       buffer[p - value] = '\0';
+
+       if (!resolve) {
+               if (inet_pton(AF_INET6, buffer, &(out->ipaddr.ip6addr.s6_addr)) <= 0) {
+                       fr_strerror_printf("Failed to parse IPv6 address string \"%s\"", value);
+                       return -1;
+               }
+       } else if (ip_hton(out, AF_INET6, buffer, fallback) < 0) return -1;
+
+       prefix = strtoul(p + 1, &eptr, 10);
+       if (prefix > 128) {
+               fr_strerror_printf("Invalid IPv6 mask length \"%s\".  Should be between 0-128", p);
+               return -1;
+       }
+       if (eptr[0] != '\0') {
+               fr_strerror_printf("Failed to parse IPv6 address string \"%s\", "
+                                  "got garbage after mask length \"%s\"", value, eptr);
+               return -1;
+       }
+
+       if (prefix < 128) {
+               struct in6_addr addr;
+
+               addr = fr_in6addr_mask(&(out->ipaddr.ip6addr), prefix);
+               memcpy(&(out->ipaddr.ip6addr.s6_addr), &addr, sizeof(addr));
+       }
+
+       out->prefix = (uint8_t) prefix;
+       out->af = AF_INET6;
+
+       return 0;
+}
+
+/** Simple wrapper to decide whether an IP value is v4 or v6 and call the appropriate parser.
+ *
+ * @param out Where to write the ip address value.
+ * @param value to parse.
+ * @param inlen Length of value, if value is \0 terminated inlen may be 0.
+ * @param resolve If true and value doesn't look like an IP address, try and resolve value as a hostname.
+ * @return 0 if ip address was parsed successfully, else -1 on error.
+ */
+int fr_pton(fr_ipaddr_t *out, char const *value, size_t inlen, bool resolve)
+{
+       size_t len, i;
+
+       len = (inlen == 0) ? strlen(value) : inlen;
+       for (i = 0; i < len; i++) switch (value[i]) {
+       /*
+        *      Chars illegal in domain names and IPv4 addresses.
+        *      Must be v6 and cannot be a domain.
+        */
+       case ':':
+       case '[':
+       case ']':
+               return fr_pton6(out, value, inlen, false, false);
+
+       /*
+        *      Chars which don't really tell us anything
+        */
+       case '.':
+       case '/':
+               continue;
+
+       default:
+               /*
+                *      Outside the range of IPv4 chars, must be a domain
+                *      Use A record in preference to AAAA record.
+                */
+               if ((value[i] < '0') || (value[i] > '9')) {
+                       if (!resolve) return -1;
+                       return fr_pton4(out, value, inlen, true, true);
+               }
+               break;
+       }
+
+       /*
+        *      All chars were in the IPv4 set [0-9/.], must be an IPv4
+        *      address.
+        */
+       return fr_pton4(out, value, inlen, false, false);
+}
+
+/** Check if the IP address is equivalent to INADDR_ANY
+ *
+ * @param addr to chec.
+ * @return true if IP address matches INADDR_ANY or INADDR6_ANY (assumed to be 0), else false.
+ */
+bool is_wildcard(fr_ipaddr_t *addr)
+{
+       static struct in6_addr in6_addr;
+
+       switch (addr->af) {
+       case AF_INET:
+               return (addr->ipaddr.ip4addr.s_addr == htons(INADDR_ANY));
+
+       case AF_INET6:
+               return (memcmp(addr->ipaddr.ip6addr.s6_addr, in6_addr.s6_addr, sizeof(in6_addr.s6_addr)) == 0) ? true :false;
+
+       default:
+               fr_assert(0);
+               return false;
+       }
+}
+
+int fr_ntop(char *out, size_t outlen, fr_ipaddr_t *addr)
+{
+       char buffer[INET6_ADDRSTRLEN];
+
+       if (inet_ntop(addr->af, &(addr->ipaddr), buffer, sizeof(buffer)) == NULL) return -1;
+
+       return snprintf(out, outlen, "%s/%i", buffer, addr->prefix);
+}
+
 /*
  *     Internal wrapper for locking, to minimize the number of ifdef's
  *
@@ -415,7 +774,7 @@ char const *inet_ntop(int af, void const *src, char *dst, size_t cnt)
         *      in missing.h
         */
        if (af == AF_INET6) {
-               struct const in6_addr *ipaddr = src;
+               struct in6_addr const *ipaddr = src;
 
                if (cnt <= INET6_ADDRSTRLEN) return NULL;
 
@@ -435,44 +794,34 @@ char const *inet_ntop(int af, void const *src, char *dst, size_t cnt)
 }
 #endif
 
-
-/*
- *     Try to convert the address to v4 then v6
- */
-int ip_ptonx(char const *src, fr_ipaddr_t *dst)
-{
-       if (inet_pton(AF_INET, src, &dst->ipaddr.ip4addr) == 1) {
-               dst->af = AF_INET;
-               return 1;
-       }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       if (inet_pton(AF_INET6, src, &dst->ipaddr.ip6addr) == 1) {
-               dst->af = AF_INET6;
-               return 1;
-       }
-#endif
-
-       return 0;
-}
-
-/*
- *     Wrappers for IPv4/IPv6 host to IP address lookup.
- *     This API returns only one IP address, of the specified
- *     address family, or the first address (of whatever family),
- *     if AF_UNSPEC is used.
+/** Wrappers for IPv4/IPv6 host to IP address lookup
+ *
+ * This function returns only one IP address, of the specified address family,
+ * or the first address (of whatever family), if AF_UNSPEC is used.
+ *
+ * If fallback is specified and af is AF_INET, but no AF_INET records were
+ * found and a record for AF_INET6 exists that record will be returned.
+ *
+ * If fallback is specified and af is AF_INET6, and a record with AF_INET4 exists
+ * that record will be returned instead.
+ *
+ * @param out Where to write result.
+ * @param af To search for in preference.
+ * @param hostname to search for.
+ * @param fallback to the other adress family, if no records matching af, found.
+ * @return 0 on success, else -1 on failure.
  */
-int ip_hton(char const *src, int af, fr_ipaddr_t *dst)
+int ip_hton(fr_ipaddr_t *out, int af, char const *hostname, bool fallback)
 {
        int rcode;
-       struct addrinfo hints, *ai = NULL, *res = NULL;
+       struct addrinfo hints, *ai = NULL, *alt = NULL, *res = NULL;
 
        if (!fr_hostname_lookups) {
 #ifdef HAVE_STRUCT_SOCKADDR_IN6
                if (af == AF_UNSPEC) {
                        char const *p;
 
-                       for (p = src; *p != '\0'; p++) {
+                       for (p = hostname; *p != '\0'; p++) {
                                if ((*p == ':') ||
                                    (*p == '[') ||
                                    (*p == ']')) {
@@ -485,11 +834,11 @@ int ip_hton(char const *src, int af, fr_ipaddr_t *dst)
 
                if (af == AF_UNSPEC) af = AF_INET;
 
-               if (!inet_pton(af, src, &(dst->ipaddr))) {
+               if (!inet_pton(af, hostname, &(out->ipaddr))) {
                        return -1;
                }
-               
-               dst->af = af;
+
+               out->af = af;
                return 0;
        }
 
@@ -505,30 +854,31 @@ int ip_hton(char const *src, int af, fr_ipaddr_t *dst)
                /*
                 *      If it's all numeric, avoid getaddrinfo()
                 */
-               if (inet_pton(af, src, &dst->ipaddr.ip4addr) == 1) {
+               if (inet_pton(af, hostname, &out->ipaddr.ip4addr) == 1) {
                        return 0;
                }
        }
 #endif
 
-       if ((rcode = getaddrinfo(src, NULL, &hints, &res)) != 0) {
+       if ((rcode = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
                fr_strerror_printf("ip_hton: %s", gai_strerror(rcode));
                return -1;
        }
 
        for (ai = res; ai; ai = ai->ai_next) {
-               if ((af == ai->ai_family) || (af == AF_UNSPEC))
-                       break;
+               if ((af == ai->ai_family) || (af == AF_UNSPEC)) break;
+               if (!alt && fallback && ((ai->ai_family == AF_INET) || (ai->ai_family == AF_INET6))) alt = ai;
        }
 
+       if (!ai) ai = alt;
        if (!ai) {
-               fr_strerror_printf("ip_hton failed to find requested information for host %.100s", src);
+               fr_strerror_printf("ip_hton failed to find requested information for host %.100s", hostname);
                freeaddrinfo(ai);
                return -1;
        }
 
        rcode = fr_sockaddr2ipaddr((struct sockaddr_storage *)ai->ai_addr,
-                                  ai->ai_addrlen, dst, NULL);
+                                  ai->ai_addrlen, out, NULL);
        freeaddrinfo(ai);
        if (!rcode) return -1;
 
@@ -563,6 +913,83 @@ char const *ip_ntoh(fr_ipaddr_t const *src, char *dst, size_t cnt)
        return dst;
 }
 
+/** Mask off a portion of an IPv4 address
+ *
+ * @param ipaddr to mask.
+ * @param prefix Number of contiguous bits to mask.
+ * @return an ipv6 address with the host portion zeroed out.
+ */
+struct in_addr fr_inaddr_mask(struct in_addr const *ipaddr, uint8_t prefix)
+{
+       uint32_t ret;
+
+       if (prefix > 32) {
+               prefix = 32;
+       }
+
+       /* Short circuit */
+       if (prefix == 32) {
+               return *ipaddr;
+       }
+
+       ret = htonl(~((0x00000001UL << (32 - prefix)) - 1)) & ipaddr->s_addr;
+       return (*(struct in_addr *)&ret);
+}
+
+/** Mask off a portion of an IPv6 address
+ *
+ * @param ipaddr to mask.
+ * @param prefix Number of contiguous bits to mask.
+ * @return an ipv6 address with the host portion zeroed out.
+ */
+struct in6_addr fr_in6addr_mask(struct in6_addr const *ipaddr, uint8_t prefix)
+{
+       uint64_t const *p = (uint64_t const *) ipaddr;
+       uint64_t ret[2], *o = ret;
+
+       if (prefix > 128) {
+               prefix = 128;
+       }
+
+       /* Short circuit */
+       if (prefix == 128) {
+               return *ipaddr;
+       }
+
+       if (prefix >= 64) {
+               prefix -= 64;
+               *o++ = 0xffffffffffffffffULL & *p++;
+       } else {
+               ret[1] = 0;
+       }
+
+       *o = htonll(~((0x0000000000000001ULL << (64 - prefix)) - 1)) & *p;
+
+       return *(struct in6_addr *) &ret;
+}
+
+/** Zeroes out the host portion of an fr_ipaddr_t
+ *
+ * @param[in,out] addr to mask
+ * @param[in] prefix Length of the network portion.
+ */
+void fr_ipaddr_mask(fr_ipaddr_t *addr, uint8_t prefix)
+{
+
+       switch (addr->af) {
+       case AF_INET:
+               addr->ipaddr.ip4addr = fr_inaddr_mask(&addr->ipaddr.ip4addr, prefix);
+               break;
+
+       case AF_INET6:
+               addr->ipaddr.ip6addr = fr_in6addr_mask(&addr->ipaddr.ip6addr, prefix);
+               break;
+
+       default:
+               return;
+       }
+       addr->prefix = prefix;
+}
 
 static char const *hextab = "0123456789abcdef";
 
@@ -632,17 +1059,41 @@ uint32_t fr_strtoul(char const *value, char **end)
        return strtoul(value, end, 10);
 }
 
-/** Check whether the rest of the string is whitespace
+/** Check whether the string is all whitespace
  *
  * @return true if the entirety of the string is whitespace, else false.
  */
-bool fr_whitespace_check(char const *value)
+bool is_whitespace(char const *value)
 {
-       while (*value) {
-               if (!isspace((int) *value)) return false;
+       do {
+               if (!isspace(*value)) return false;
+       } while (*++value);
 
-               value++;
-       }
+       return true;
+}
+
+/** Check whether the string is all numbers
+ *
+ * @return true if the entirety of the string is are numebrs, else false.
+ */
+bool is_integer(char const *value)
+{
+       do {
+               if (!isdigit(*value)) return false;
+       } while (*++value);
+
+       return true;
+}
+
+/** Check whether the string is allzeros
+ *
+ * @return true if the entirety of the string is are numebrs, else false.
+ */
+bool is_zero(char const *value)
+{
+       do {
+               if (*value != '0') return false;
+       } while (*++value);
 
        return true;
 }
@@ -708,7 +1159,7 @@ int fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
        return -1;
 }
 
-int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, int port,
+int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, uint16_t port,
                       struct sockaddr_storage *sa, socklen_t *salen)
 {
        if (ipaddr->af == AF_INET) {
@@ -746,7 +1197,7 @@ int fr_ipaddr2sockaddr(fr_ipaddr_t const *ipaddr, int port,
 
 
 int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
-                      fr_ipaddr_t *ipaddr, int *port)
+                      fr_ipaddr_t *ipaddr, uint16_t *port)
 {
        if (sa->ss_family == AF_INET) {
                struct sockaddr_in      s4;
@@ -758,6 +1209,7 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
 
                memcpy(&s4, sa, sizeof(s4));
                ipaddr->af = AF_INET;
+               ipaddr->prefix = 32;
                ipaddr->ipaddr.ip4addr = s4.sin_addr;
                if (port) *port = ntohs(s4.sin_port);
 
@@ -772,6 +1224,7 @@ int fr_sockaddr2ipaddr(struct sockaddr_storage const *sa, socklen_t salen,
 
                memcpy(&s6, sa, sizeof(s6));
                ipaddr->af = AF_INET6;
+               ipaddr->prefix = 128;
                ipaddr->ipaddr.ip6addr = s6.sin6_addr;
                if (port) *port = ntohs(s6.sin6_port);
                ipaddr->scope = s6.sin6_scope_id;
@@ -841,6 +1294,53 @@ ssize_t fr_utf8_to_ucs2(uint8_t *out, size_t outlen, char const *in, size_t inle
        return out - start;
 }
 
+/** Write 128bit unsigned integer to buffer
+ *
+ * @author Alexey Frunze
+ *
+ * @param out where to write result to.
+ * @param outlen size of out.
+ * @param num 128 bit integer.
+ */
+size_t fr_prints_uint128(char *out, size_t outlen, uint128_t const num)
+{
+       char buff[128 / 3 + 1 + 1];
+       uint64_t n[2];
+       char *p = buff;
+       int i;
+
+       memset(buff, '0', sizeof(buff) - 1);
+       buff[sizeof(buff) - 1] = '\0';
+
+       memcpy(n, &num, sizeof(n));
+
+       for (i = 0; i < 128; i++) {
+               ssize_t j;
+               int carry;
+
+               carry = (n[1] >= 0x8000000000000000);
+
+               // Shift n[] left, doubling it
+               n[1] = ((n[1] << 1) & 0xffffffffffffffff) + (n[0] >= 0x8000000000000000);
+               n[0] = ((n[0] << 1) & 0xffffffffffffffff);
+
+               // Add s[] to itself in decimal, doubling it
+               for (j = sizeof(buff) - 2; j >= 0; j--) {
+                       buff[j] += buff[j] - '0' + carry;
+                       carry = (buff[j] > '9');
+                       if (carry) {
+                               buff[j] -= 10;
+                       }
+               }
+       }
+
+       while ((*p == '0') && (p < &buff[sizeof(buff) - 2])) {
+               p++;
+       }
+
+       return strlcpy(out, p, outlen);
+}
+
 /** Calculate powers
  *
  * @author Orson Peters
@@ -968,7 +1468,7 @@ int fr_get_time(char const *date_str, time_t *date)
        char            buf[64];
        char            *p;
        char            *f[4];
-       char            *tail = '\0';
+       char            *tail = NULL;
 
        /*
         * Test for unix timestamp date
@@ -1093,6 +1593,62 @@ int fr_get_time(char const *date_str, time_t *date)
        return 0;
 }
 
+/** Compares two pointers
+ *
+ * @param a first pointer to compare.
+ * @param b second pointer to compare.
+ * @return -1 if a < b, +1 if b > a, or 0 if both equal.
+ */
+int8_t fr_pointer_cmp(void const *a, void const *b)
+{
+       if (a < b) return -1;
+       if (a == b) return 0;
+
+       return 1;
+}
+
+static int _quick_partition(void const *to_sort[], int min, int max, fr_cmp_t cmp) {
+       void const *pivot = to_sort[min];
+       int i = min;
+       int j = max + 1;
+       void const *tmp;
+
+       for (;;) {
+               do ++i; while((cmp(to_sort[i], pivot) <= 0) && i <= max);
+               do --j; while(cmp(to_sort[j], pivot) > 0);
+
+               if (i >= j) break;
+
+               tmp = to_sort[i];
+               to_sort[i] = to_sort[j];
+               to_sort[j] = tmp;
+       }
+
+       tmp = to_sort[min];
+       to_sort[min] = to_sort[j];
+       to_sort[j] = tmp;
+
+       return j;
+}
+
+/** Quick sort an array of pointers using a comparator
+ *
+ * @param to_sort array of pointers to sort.
+ * @param min_idx the lowest index (usually 0).
+ * @param max_idx the highest index (usually length of array - 1).
+ * @param cmp the comparison function to use to sort the array elements.
+ */
+void fr_quick_sort(void const *to_sort[], int min_idx, int max_idx, fr_cmp_t cmp)
+{
+       int part;
+
+       if (min_idx >= max_idx) return;
+
+       part = _quick_partition(to_sort, min_idx, max_idx, cmp);
+       fr_quick_sort(to_sort, min_idx, part - 1, cmp);
+       fr_quick_sort(to_sort, part + 1, max_idx, cmp);
+}
+
 #ifdef TALLOC_DEBUG
 void fr_talloc_verify_cb(UNUSED const void *ptr, UNUSED int depth,
                         UNUSED int max_depth, UNUSED int is_ref,
index 0fae3ba..4598c8f 100644 (file)
@@ -272,3 +272,67 @@ ntp2timeval(struct timeval *tv, char const *ntp)
        tv->tv_sec = sec - NTP_EPOCH_OFFSET;
        tv->tv_usec = usec / 4295; /* close enough */
 }
+
+#if !defined(HAVE_128BIT_INTEGERS) && defined(LITTLE_ENDIAN)
+/** Swap byte order of 128 bit integer
+ *
+ * @param num 128bit integer to swap.
+ * @return 128bit integer reversed.
+ */
+uint128_t ntohlll(uint128_t const num)
+{
+       uint64_t const *p = (uint64_t const *) &num;
+       uint64_t ret[2];
+
+       /* swapsies */
+       ret[1] = ntohll(p[0]);
+       ret[0] = ntohll(p[1]);
+
+       return *(uint128_t *)ret;
+}
+#endif
+
+/** Call talloc strdup, setting the type on the new chunk correctly
+ *
+ * For some bizarre reason the talloc string functions don't set the
+ * memory chunk type to char, which causes all kinds of issues with
+ * verifying VALUE_PAIRs.
+ *
+ * @param[in] t The talloc context to hang the result off.
+ * @param[in] p The string you want to duplicate.
+ * @return The duplicated string, NULL on error.
+ */
+char *talloc_typed_strdup(void const *t, char const *p)
+{
+       char *n;
+
+       n = talloc_strdup(t, p);
+       if (!n) return NULL;
+       talloc_set_type(n, char);
+
+       return n;
+}
+
+/** Call talloc vasprintf, setting the type on the new chunk correctly
+ *
+ * For some bizarre reason the talloc string functions don't set the
+ * memory chunk type to char, which causes all kinds of issues with
+ * verifying VALUE_PAIRs.
+ *
+ * @param[in] t The talloc context to hang the result off.
+ * @param[in] fmt The format string.
+ * @return The formatted string, NULL on error.
+ */
+char *talloc_typed_asprintf(void const *t, char const *fmt, ...)
+{
+       char *n;
+       va_list ap;
+
+       va_start(ap, fmt);
+       n = talloc_vasprintf(t, fmt, ap);
+       va_end(ap);
+       if (!n) return NULL;
+       talloc_set_type(n, char);
+
+       return n;
+}
index 01e1df2..13af027 100644 (file)
@@ -97,39 +97,44 @@ void fr_request_from_reply(RADIUS_PACKET *request,
        request->dst_ipaddr = reply->src_ipaddr;
 }
 
-
-int fr_nonblock(UNUSED int fd)
-{
-       int flags = 0;
-
 #ifdef O_NONBLOCK
+int fr_nonblock(int fd)
+{
+       int flags;
 
        flags = fcntl(fd, F_GETFL, NULL);
-       if (flags >= 0) {
-               flags |= O_NONBLOCK;
-               return fcntl(fd, F_SETFL, flags);
+       if (flags < 0)  {
+               fr_strerror_printf("Failure getting socket flags: %s", fr_syserror(errno));
+               return -1;
        }
-#endif
+
+       flags |= O_NONBLOCK;
+       if (fcntl(fd, F_SETFL, flags) < 0) {
+               fr_strerror_printf("Failure setting socket flags: %s", fr_syserror(errno));
+               return -1;
+       }
+
        return flags;
 }
+#else
+int fr_nonblock(UNUSED int fd)
+{
+       return 0;
+}
+#endif
 
 /*
  *     Open a socket on the given IP and port.
  */
-int fr_socket(fr_ipaddr_t *ipaddr, int port)
+int fr_socket(fr_ipaddr_t *ipaddr, uint16_t port)
 {
        int sockfd;
        struct sockaddr_storage salocal;
        socklen_t       salen;
 
-       if ((port < 0) || (port > 65535)) {
-               fr_strerror_printf("Port %d is out of allowed bounds", port);
-               return -1;
-       }
-
        sockfd = socket(ipaddr->af, SOCK_DGRAM, 0);
        if (sockfd < 0) {
-               fr_strerror_printf("cannot open socket: %s", strerror(errno));
+               fr_strerror_printf("cannot open socket: %s", fr_syserror(errno));
                return sockfd;
        }
 
@@ -139,7 +144,7 @@ int fr_socket(fr_ipaddr_t *ipaddr, int port)
         */
        if (udpfromto_init(sockfd) != 0) {
                close(sockfd);
-               fr_strerror_printf("cannot initialize udpfromto: %s", strerror(errno));
+               fr_strerror_printf("cannot initialize udpfromto: %s", fr_syserror(errno));
                return -1;
        }
 #endif
@@ -166,7 +171,7 @@ int fr_socket(fr_ipaddr_t *ipaddr, int port)
                                close(sockfd);
                                fr_strerror_printf("Failed setting sockopt "
                                                   "IPPROTO_IPV6 - IPV6_V6ONLY"
-                                                  ": %s", strerror(errno));
+                                                  ": %s", fr_syserror(errno));
                                return -1;
                        }
                }
@@ -189,7 +194,7 @@ int fr_socket(fr_ipaddr_t *ipaddr, int port)
                        close(sockfd);
                        fr_strerror_printf("Failed setting sockopt "
                                           "IPPROTO_IP - IP_MTU_DISCOVER: %s",
-                                          strerror(errno));
+                                          fr_syserror(errno));
                        return -1;
                }
 #endif
@@ -204,7 +209,7 @@ int fr_socket(fr_ipaddr_t *ipaddr, int port)
                        close(sockfd);
                        fr_strerror_printf("Failed setting sockopt "
                                           "IPPROTO_IP - IP_DONTFRAG: %s",
-                                          strerror(errno));
+                                          fr_syserror(errno));
                        return -1;
                }
 #endif
@@ -212,7 +217,7 @@ int fr_socket(fr_ipaddr_t *ipaddr, int port)
 
        if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
                close(sockfd);
-               fr_strerror_printf("cannot bind socket: %s", strerror(errno));
+               fr_strerror_printf("cannot bind socket: %s", fr_syserror(errno));
                return -1;
        }
 
@@ -227,17 +232,17 @@ typedef struct fr_packet_socket_t {
        int             sockfd;
        void            *ctx;
 
-       int             num_outgoing;
+       uint32_t        num_outgoing;
 
        int             src_any;
        fr_ipaddr_t     src_ipaddr;
-       int             src_port;
+       uint16_t        src_port;
 
        int             dst_any;
        fr_ipaddr_t     dst_ipaddr;
-       int             dst_port;
+       uint16_t        dst_port;
 
-       int             dont_use;
+       bool            dont_use;
 
 #ifdef WITH_TCP
        int             proto;
@@ -262,7 +267,7 @@ struct fr_packet_list_t {
        rbtree_t        *tree;
 
        int             alloc_id;
-       int             num_outgoing;
+       uint32_t        num_outgoing;
        int             last_recv;
        int             num_sockets;
 
@@ -304,7 +309,7 @@ bool fr_packet_list_socket_freeze(fr_packet_list_t *pl, int sockfd)
                return false;
        }
 
-       ps->dont_use = 1;
+       ps->dont_use = true;
        return true;
 }
 
@@ -317,7 +322,7 @@ bool fr_packet_list_socket_thaw(fr_packet_list_t *pl, int sockfd)
        ps = fr_socket_find(pl, sockfd);
        if (!ps) return false;
 
-       ps->dont_use = 0;
+       ps->dont_use = false;
        return true;
 }
 
@@ -331,7 +336,10 @@ bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd)
        ps = fr_socket_find(pl, sockfd);
        if (!ps) return false;
 
-       if (ps->num_outgoing != 0) return false;
+       if (ps->num_outgoing != 0) {
+               fr_strerror_printf("socket is still in use");
+               return false;
+       }
 
        ps->sockfd = -1;
        pl->num_sockets--;
@@ -341,7 +349,7 @@ bool fr_packet_list_socket_del(fr_packet_list_t *pl, int sockfd)
 
 
 bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
-                             fr_ipaddr_t *dst_ipaddr, int dst_port,
+                             fr_ipaddr_t *dst_ipaddr, uint16_t dst_port,
                              void *ctx)
 {
        int i, start;
@@ -400,7 +408,7 @@ bool fr_packet_list_socket_add(fr_packet_list_t *pl, int sockfd, int proto,
        memset(&src, 0, sizeof_src);
        if (getsockname(sockfd, (struct sockaddr *) &src,
                        &sizeof_src) < 0) {
-               fr_strerror_printf("%s", strerror(errno));
+               fr_strerror_printf("%s", fr_syserror(errno));
                return false;
        }
 
@@ -555,7 +563,7 @@ bool fr_packet_list_yank(fr_packet_list_t *pl, RADIUS_PACKET *request)
        return true;
 }
 
-int fr_packet_list_num_elements(fr_packet_list_t *pl)
+uint32_t fr_packet_list_num_elements(fr_packet_list_t *pl)
 {
        if (!pl) return 0;
 
@@ -773,7 +781,7 @@ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto,
        if (fr_packet_list_insert(pl, request_p)) {
                if (pctx) *pctx = ps->ctx;
                ps->num_outgoing++;
-               pl->num_outgoing++;             
+               pl->num_outgoing++;
                return true;
        }
 
@@ -821,23 +829,24 @@ bool fr_packet_list_id_free(fr_packet_list_t *pl,
        pl->num_outgoing--;
 
        request->id = -1;
+       request->src_ipaddr.af = AF_UNSPEC; /* id_alloc checks this */
+       request->src_port = 0;
 
        return true;
 }
 
 /*
- *     We always walk DeleteOrder, which is like InOrder, except that
+ *     We always walk RBTREE_DELETE_ORDER, which is like RBTREE_IN_ORDER, except that
  *     <0 means error, stop
  *     0  means OK, continue
  *     1  means delete current node and stop
  *     2  means delete current node and continue
  */
-int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx,
-                       fr_hash_table_walk_t callback)
+int fr_packet_list_walk(fr_packet_list_t *pl, void *ctx, rb_walker_t callback)
 {
        if (!pl || !callback) return 0;
 
-       return rbtree_walk(pl->tree, DeleteOrder, callback, ctx);
+       return rbtree_walk(pl->tree, RBTREE_DELETE_ORDER, callback, ctx);
 }
 
 int fr_packet_list_fd_set(fr_packet_list_t *pl, fd_set *set)
@@ -903,9 +912,9 @@ RADIUS_PACKET *fr_packet_list_recv(fr_packet_list_t *pl, fd_set *set)
        return NULL;
 }
 
-int fr_packet_list_num_incoming(fr_packet_list_t *pl)
+uint32_t fr_packet_list_num_incoming(fr_packet_list_t *pl)
 {
-       int num_elements;
+       uint32_t num_elements;
 
        if (!pl) return 0;
 
@@ -915,7 +924,7 @@ int fr_packet_list_num_incoming(fr_packet_list_t *pl)
        return num_elements - pl->num_outgoing;
 }
 
-int fr_packet_list_num_outgoing(fr_packet_list_t *pl)
+uint32_t fr_packet_list_num_outgoing(fr_packet_list_t *pl)
 {
        if (!pl) return 0;
 
diff --git a/src/lib/pcap.c b/src/lib/pcap.c
new file mode 100644 (file)
index 0000000..c64ae1a
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file pcap.c
+ * @brief Wrappers around libpcap functions
+ *
+ * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#ifdef HAVE_LIBPCAP
+
+#include <sys/ioctl.h>
+#include <pcap/pcap.h>
+#include <freeradius-devel/pcap.h>
+
+const FR_NAME_NUMBER pcap_types[] = {
+       { "interface",  PCAP_INTERFACE_IN },
+       { "file",       PCAP_FILE_IN },
+       { "stdio",      PCAP_STDIO_IN },
+       { "interface",  PCAP_INTERFACE_OUT },
+       { "file",       PCAP_FILE_OUT },
+       { "stdio",      PCAP_STDIO_OUT },
+
+       { NULL, 0}
+};
+
+/** Talloc destructor to free pcap resources associated with a handle.
+ *
+ * @param pcap to free.
+ * @return 0
+ */
+static int _free_pcap(fr_pcap_t *pcap) {
+       switch (pcap->type) {
+               case PCAP_INTERFACE_IN:
+               case PCAP_INTERFACE_OUT:
+               case PCAP_FILE_IN:
+               case PCAP_STDIO_IN:
+                       if (pcap->handle) {
+                               pcap_close(pcap->handle);
+
+                               if (pcap->fd > 0) {
+                                       close(pcap->fd);
+                               }
+                       }
+
+                       break;
+
+               case PCAP_FILE_OUT:
+               case PCAP_STDIO_OUT:
+                       if (pcap->dumper) {
+                               pcap_dump_flush(pcap->dumper);
+                               pcap_dump_close(pcap->dumper);
+                       }
+
+                       break;
+               case PCAP_INVALID:
+                       break;
+       }
+
+       return 0;
+}
+
+/** Initialise a pcap handle abstraction
+ *
+ * @param ctx talloc TALLOC_CTX to allocate handle in.
+ * @param name of interface or file to open.
+ * @param type of handle to initialise.
+ * @return new handle or NULL on error.
+ */
+fr_pcap_t *fr_pcap_init(TALLOC_CTX *ctx, char const *name, fr_pcap_type_t type)
+{
+       fr_pcap_t *this = talloc_zero(ctx, fr_pcap_t);
+       if (!this) {
+               return NULL;
+       }
+
+       talloc_set_destructor(this, _free_pcap);
+       this->name = talloc_typed_strdup(this, name);
+       this->type = type;
+       this->link_type = -1;
+
+       return this;
+}
+
+/** Open a PCAP handle abstraction
+ *
+ * This opens interfaces for capture or injection, or files/streams for reading/writing.
+ * @param pcap created with fr_pcap_init.
+ * @return 0 on success, -1 on error.
+ */
+int fr_pcap_open(fr_pcap_t *pcap)
+{
+       switch (pcap->type) {
+       case PCAP_INTERFACE_OUT:
+       case PCAP_INTERFACE_IN:
+       {
+#if defined(HAVE_PCAP_CREATE) && defined(HAVE_PCAP_ACTIVATE)
+               pcap->handle = pcap_create(pcap->name, pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+                       return -1;
+               }
+               if (pcap_set_snaplen(pcap->handle, SNAPLEN) != 0) {
+               create_error:
+                       fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+                       pcap_close(pcap->handle);
+                       pcap->handle = NULL;
+                       return -1;
+               }
+               if (pcap_set_timeout(pcap->handle, PCAP_NONBLOCK_TIMEOUT) != 0) {
+                       goto create_error;
+               }
+               if (pcap_set_promisc(pcap->handle, pcap->promiscuous) != 0) {
+                       goto create_error;
+               }
+
+               if (pcap_set_buffer_size(pcap->handle, SNAPLEN *
+                                        (pcap->buffer_pkts ? pcap->buffer_pkts : PCAP_BUFFER_DEFAULT)) != 0) {
+                       goto create_error;
+               }
+               if (pcap_activate(pcap->handle) != 0) {
+                       goto create_error;
+               }
+#else
+               /*
+                *      Alternative functions for libpcap < 1.0
+                */
+               pcap->handle = pcap_open_live(pcap->name, SNAPLEN, pcap->promiscuous, PCAP_NONBLOCK_TIMEOUT,
+                                             pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+                       return -1;
+               }
+#endif
+               /*
+                *      Despite accepting an errbuff, pcap_setnonblock doesn't seem to write
+                *      error message there in newer versions.
+                */
+               if (pcap_setnonblock(pcap->handle, true, pcap->errbuf) != 0) {
+                       fr_strerror_printf("%s", *pcap->errbuf != '\0' ?
+                                          pcap->errbuf : pcap_geterr(pcap->handle));
+                       pcap_close(pcap->handle);
+                       pcap->handle = NULL;
+                       return -1;
+               }
+
+               pcap->fd = pcap_get_selectable_fd(pcap->handle);
+               pcap->link_type = pcap_datalink(pcap->handle);
+#ifndef __linux__
+               {
+                       int value = 1;
+                       if (ioctl(pcap->fd, BIOCIMMEDIATE, &value) < 0) {
+                               fr_strerror_printf("Failed setting BIOCIMMEDIATE: %s", fr_syserror(errno));
+                       }
+               }
+#endif
+       }
+               break;
+
+       case PCAP_FILE_IN:
+               pcap->handle = pcap_open_offline(pcap->name, pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+
+                       return -1;
+               }
+               pcap->fd = pcap_get_selectable_fd(pcap->handle);
+               pcap->link_type = pcap_datalink(pcap->handle);
+               break;
+
+       case PCAP_FILE_OUT:
+               if (pcap->link_type < 0) {
+                       pcap->link_type = DLT_EN10MB;
+               }
+               pcap->handle = pcap_open_dead(pcap->link_type, SNAPLEN);
+               if (!pcap->handle) {
+                       fr_strerror_printf("Unknown error occurred opening dead PCAP handle");
+
+                       return -1;
+               }
+               pcap->dumper = pcap_dump_open(pcap->handle, pcap->name);
+               if (!pcap->dumper) {
+                       fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+                       return -1;
+               }
+               break;
+
+#ifdef HAVE_PCAP_FOPEN_OFFLINE
+       case PCAP_STDIO_IN:
+               pcap->handle = pcap_fopen_offline(stdin, pcap->errbuf);
+               if (!pcap->handle) {
+                       fr_strerror_printf("%s", pcap->errbuf);
+
+                       return -1;
+               }
+               pcap->fd = pcap_get_selectable_fd(pcap->handle);
+               pcap->link_type = pcap_datalink(pcap->handle);
+               break;
+#else
+       case PCAP_STDIO_IN:
+               fr_strerror_printf("This version of libpcap does not support reading pcap data from streams");
+
+               return -1;
+#endif
+#ifdef HAVE_PCAP_DUMP_FOPEN
+       case PCAP_STDIO_OUT:
+               pcap->handle = pcap_open_dead(DLT_EN10MB, SNAPLEN);
+               pcap->dumper = pcap_dump_fopen(pcap->handle, stdout);
+               if (!pcap->dumper) {
+                       fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+                       return -1;
+               }
+               break;
+#else
+       case PCAP_STDIO_OUT:
+               fr_strerror_printf("This version of libpcap does not support writing pcap data to streams");
+
+               return -1;
+#endif
+               case PCAP_INVALID:
+       default:
+               fr_assert(0);
+               fr_strerror_printf("Bad handle type (%i)", pcap->type);
+               return -1;
+       }
+
+       return 0;
+}
+
+/** Apply capture filter to an interface
+ *
+ * @param pcap handle to apply filter to.
+ * @param expression PCAP expression to use as a filter.
+ * @return 0 on success, 1 can't apply to interface, -1 on error.
+ */
+int fr_pcap_apply_filter(fr_pcap_t *pcap, char const *expression)
+{
+       bpf_u_int32 mask = 0;                           /* Our netmask */
+       bpf_u_int32 net = 0;                            /* Our IP */
+       struct bpf_program fp;
+
+       /*
+        *      nflog devices are in the set of devices selected by default.
+        *      Unfortunately there's a bug in all released version of libpcap (as of 2/1/2014)
+        *      which triggers an abort if pcap_setfilter is called on an nflog interface.
+        *
+        *      See here:
+        *      https://github.com/the-tcpdump-group/libpcap/commit/676cf8a61ed240d0a86d471ef419f45ba35dba80
+        */
+#ifdef DLT_NFLOG
+       if (pcap->link_type == DLT_NFLOG) {
+               fr_strerror_printf("NFLOG link-layer type filtering not implemented");
+
+               return 1;
+       }
+#endif
+
+       if (pcap->type == PCAP_INTERFACE_IN) {
+               if (pcap_lookupnet(pcap->name, &net, &mask, pcap->errbuf) < 0) {
+                       fr_strerror_printf("Failed getting IP for interface \"%s\", using defaults: %s",
+                                          pcap->name, pcap->errbuf);
+               }
+       }
+
+       if (pcap_compile(pcap->handle, &fp, expression, 0, net) < 0) {
+               fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+               return -1;
+       }
+
+       if (pcap_setfilter(pcap->handle, &fp) < 0) {
+               fr_strerror_printf("%s", pcap_geterr(pcap->handle));
+
+               return -1;
+       }
+
+       return 0;
+}
+
+char *fr_pcap_device_names(TALLOC_CTX *ctx, fr_pcap_t *pcap, char c)
+{
+       fr_pcap_t *pcap_p;
+       char *buff, *p;
+       size_t len = 0, left = 0, wrote;
+
+       if (!pcap) {
+               goto null;
+       }
+
+       for (pcap_p = pcap;
+            pcap_p;
+            pcap_p = pcap_p->next) {
+               len += talloc_array_length(pcap_p->name);       // Talloc array length includes the \0
+       }
+
+       if (!len) {
+               null:
+               return talloc_zero_array(ctx, char, 1);
+       }
+
+       left = len + 1;
+       buff = p = talloc_zero_array(ctx, char, left);
+       for (pcap_p = pcap;
+            pcap_p;
+            pcap_p = pcap_p->next) {
+               wrote = snprintf(p, left, "%s%c", pcap_p->name, c);
+               left -= wrote;
+               p += wrote;
+       }
+       buff[len - 1] = '\0';
+
+       return buff;
+}
+
+/** Returns the length of the link layer header
+ *
+ * Libpcap does not include a decoding function to skip the L2 header, but it does
+ * at least inform us of the type.
+ *
+ * Unfortunately some headers are of variable length (like ethernet), so additional
+ * decoding logic is required.
+ *
+ * @note No header data is returned, this is only meant to be used to determine how
+ * data to consume before attempting to parse the IP header.
+ *
+ * @param data start of PCAP data.
+ * @param len caplen.
+ * @param link_type value returned from pcap_linktype.
+ * @return the length of the header, or -1 on error.
+ */
+ssize_t fr_pcap_link_layer_offset(uint8_t const *data, size_t len, int link_type)
+{
+       uint8_t const *p = data;
+
+       switch (link_type) {
+       case DLT_RAW:
+               break;
+
+       case DLT_NULL:
+       case DLT_LOOP:
+               p += 4;
+               if (((size_t)(p - data)) > len) {
+                       goto ood;
+               }
+               break;
+
+       case DLT_EN10MB:
+               {
+                       uint16_t ether_type;    /* Ethernet type */
+                       int i;
+
+                       p += 12;                /* SRC/DST Mac-Addresses */
+                       if (((size_t)(p - data)) > len) {
+                               goto ood;
+                       }
+
+                       for (i = 0; i < 3; i++) {
+                               ether_type = ntohs(*((uint16_t const *) p));
+                               switch (ether_type) {
+                               /*
+                                *      There are a number of devices out there which
+                                *      double tag with 0x8100 *sigh*
+                                */
+                               case 0x8100:    /* CVLAN */
+                               case 0x9100:    /* SVLAN */
+                               case 0x9200:    /* SVLAN */
+                               case 0x9300:    /* SVLAN */
+                                       p += 4;
+                                       if (((size_t)(p - data)) > len) {
+                                               goto ood;
+                                       }
+                                       break;
+
+                               default:
+                                       p += 2;
+                                       if (((size_t)(p - data)) > len) {
+                                               goto ood;
+                                       }
+                                       goto done;
+                               }
+                       }
+                       fr_strerror_printf("Exceeded maximum level of VLAN tag nesting (2)");
+                       return -1;
+               }
+
+       case DLT_LINUX_SLL:
+               p += 16;
+               if (((size_t)(p - data)) > len) {
+                       goto ood;
+               }
+               break;
+
+       case DLT_PFLOG:
+               p += 28;
+               if (((size_t)(p - data)) > len) {
+                       goto ood;
+               }
+               break;
+
+       default:
+               fr_strerror_printf("Unsupported link layer type %i", link_type);
+       }
+
+       done:
+       return p - data;
+
+       ood:
+       fr_strerror_printf("Out of data, needed %zu bytes, have %zu bytes", (size_t)(p - data), len);
+
+       return -1;
+}
+
+/** Calculate UDP checksum
+ *
+ * Zero out UDP checksum in UDP header before calling fr_udp_checksum to get 'expected' checksum.
+ *
+ * @param data Pointer to the start of the UDP header
+ * @param len value of udp length field in host byte order. Must be validated to make
+ *       sure it won't overrun data buffer.
+ * @param checksum current checksum, leave as 0 to just enable validation.
+ * @param src_addr in network byte order.
+ * @param dst_addr in network byte order.
+ * @return 0 if the checksum is correct, else another number.
+ */
+uint16_t fr_udp_checksum(uint8_t const *data, uint16_t len, uint16_t checksum,
+                        struct in_addr const src_addr, struct in_addr const dst_addr)
+{
+       uint64_t sum = 0;       /* using 64bits avoids overflow check */
+       uint16_t const *p = (uint16_t const *)data;
+
+       uint16_t const *ip_src = (void const *) &src_addr.s_addr;
+       uint16_t const *ip_dst = (void const *) &dst_addr.s_addr;
+       uint16_t i;
+
+       sum += *(ip_src++);
+       sum += *ip_src;
+       sum += *(ip_dst++);
+       sum += *ip_dst;
+
+       sum += htons(IPPROTO_UDP);
+       sum += htons(len);
+
+       for (i = len; i > 1; i -= 2) {
+               sum += *p++;
+       }
+
+       if (i) {
+               sum += (0xff & *(uint8_t const *)p) << 8;
+       }
+
+       sum -= checksum;
+
+       while (sum >> 16) {
+               sum = (sum & 0xffff) + (sum >> 16);
+       }
+
+       return ((uint16_t) ~sum);
+}
+#endif
index a6e5391..e4e85af 100644 (file)
@@ -121,90 +121,181 @@ int fr_utf8_char(uint8_t const *str)
        return 0;
 }
 
-/*
- *     Convert a string to something printable.  The output string
- *     has to be larger than the input string by at least 5 bytes.
- *     If not, the output is silently truncated...
+/** Escape any non printable or non-UTF8 characters in the input string
+ *
+ * @param[in] in string to escape.
+ * @param[in] inlen length of string to escape (lets us deal with embedded NULLs)
+ * @param[out] out where to write the escaped string.
+ * @param[out] outlen the length of the buffer pointed to by out.
+ * @return the number of bytes written to the out buffer, or a number > outlen if truncation has occurred.
  */
 size_t fr_print_string(char const *in, size_t inlen, char *out, size_t outlen)
 {
-       char const      *start = out;
-       uint8_t const   *str = (uint8_t const *) in;
+       uint8_t const   *p = (uint8_t const *) in;
        int             sp = 0;
        int             utf8 = 0;
+       size_t          freespace = outlen;
 
-       if (!in) {
-               if (outlen) {
-                       *out = '\0';
-               }
+       /* Can't '\0' terminate */
+       if (freespace == 0) {
+               return inlen;
+       }
 
+       /* No input, so no output... */
+       if (!in) {
+       no_input:
+               *out = '\0';
                return 0;
        }
 
-       if (inlen == 0) {
-               inlen = strlen(in);
+       /* Figure out the length of the input string */
+       if (inlen == 0) inlen = strlen(in);
+
+       /* Not enough space to hold one char */
+       if (freespace < 2) {
+               /* And there's input data... */
+               if (inlen > 0) {
+                       *out = '\0';
+                       return inlen;
+               }
+
+               goto no_input;
        }
 
-       /*
-        *
-        */
-       while ((inlen > 0) && (outlen > 4)) {
+       while (inlen > 0) {
                /*
                 *      Hack: never print trailing zero.
-                *      Some clients send strings with an off-by-one
+                *      Some clients send pings with an off-by-one
                 *      length (confused with strings in C).
                 */
-               if ((inlen == 1) && (*str == 0)) break;
-
-               switch (*str) {
-                       case '\\':
-                               sp = '\\';
-                               break;
-                       case '\r':
-                               sp = 'r';
-                               break;
-                       case '\n':
-                               sp = 'n';
-                               break;
-                       case '\t':
-                               sp = 't';
-                               break;
-                       case '"':
-                               sp = '"';
-                               break;
-                       default:
-                               sp = 0;
-                               break;
+               if ((inlen == 1) && (*p == '\0')) {
+                       inlen--;
+                       break;
+               }
+               switch (*p) {
+               case '\\':
+                       sp = '\\';
+                       break;
+               case '\r':
+                       sp = 'r';
+                       break;
+               case '\n':
+                       sp = 'n';
+                       break;
+               case '\t':
+                       sp = 't';
+                       break;
+               case '"':
+                       sp = '"';
+                       break;
+               default:
+                       sp = '\0';
+                       break;
                }
 
                if (sp) {
+                       if (freespace < 3) break; /* \ + <c> + \0 */
                        *out++ = '\\';
                        *out++ = sp;
-                       outlen -= 2;
-                       str++;
+                       freespace -= 2;
+                       p++;
                        inlen--;
                        continue;
                }
 
-               utf8 = fr_utf8_char(str);
-               if (!utf8) {
-                       snprintf(out, outlen, "\\%03o", *str);
-                       out  += 4;
-                       outlen -= 4;
-                       str++;
+               utf8 = fr_utf8_char(p);
+               if (utf8 == 0) {
+                       if (freespace < 5) break; /* \ + <o><o><o> + \0 */
+                       snprintf(out, freespace, "\\%03o", *p);
+                       out += 4;
+                       freespace -= 4;
+                       p++;
                        inlen--;
                        continue;
                }
 
                do {
-                       *out++ = *str++;
-                       outlen--;
+                       if (freespace < 2) goto finish; /* <c> + \0 */
+                       *out++ = *p++;
+                       freespace--;
                        inlen--;
                } while (--utf8 > 0);
        }
+
+finish:
        *out = '\0';
 
-       return out - start;
+       /* Indicate truncation occurred */
+       if (inlen > 0) return outlen + inlen;
+
+       return outlen - freespace;
+}
+
+/** Find the length of the buffer required to fully escape a string with fr_print_string
+ *
+ * Were assuming here that's it's cheaper to figure out the length and do one
+ * alloc than repeatedly expand the buffer when we find extra chars which need
+ * to be added.
+ *
+ * @param in string to calculate the escaped length for.
+ * @param inlen length of the input string, if 0 strlen will be used to check the length.
+ * @return the size of buffer required to hold the escaped string excluding the NULL byte.
+ */
+size_t fr_print_string_len(char const *in, size_t inlen)
+{
+       uint8_t const   *p = (uint8_t const *) in;
+       size_t          outlen = 0;
+       int             utf8 = 0;
+
+       if (!in) {
+               return 0;
+       }
+
+       if (inlen == 0) {
+               inlen = strlen(in);
+       }
+
+       while (inlen > 0) {
+               /*
+                *      Hack: never print trailing zero. Some clients send pings
+                *      with an off-by-one length (confused with strings in C).
+                */
+               if ((inlen == 1) && (*p == '\0')) {
+                       inlen--;
+                       break;
+               }
+
+               switch (*p) {
+               case '\\':
+               case '\r':
+               case '\n':
+               case '\t':
+               case '"':
+                       outlen += 2;
+                       p++;
+                       inlen--;
+                       continue;
+
+               default:
+                       break;
+               }
+
+               utf8 = fr_utf8_char(p);
+               if (utf8 == 0) {
+                       outlen += 4;
+                       p++;
+                       inlen--;
+                       continue;
+               }
+
+               do {
+                       outlen++;
+                       p++;
+                       inlen--;
+               } while (--utf8 > 0);
+       }
+
+       return outlen;
 }
 
 
@@ -215,29 +306,32 @@ size_t fr_print_string(char const *in, size_t inlen, char *out, size_t outlen)
  * @param[in] vp to print.
  * @param[in] quote Char to add before and after printed value, if 0 no char will be added, if < 0 raw string will be
  *     added.
- * @return length of data written to out or 0 on error.
+ * @return the length of data written to out, or a value >= outlen on truncation.
  */
 size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t quote)
 {
        DICT_VALUE      *v;
-       char            buf[1024];
+       char            buf[1024];      /* Interim buffer to use with poorly behaved printing functions */
        char const      *a = NULL;
        time_t          t;
        struct tm       s_tm;
 
-       char            *start = out;
-       size_t          len, freespace = outlen;
-
-       *out = '\0';
+       size_t          len = 0, freespace = outlen;
 
        if (!vp) return 0;
 
+       VERIFY_VP(vp);
+
+       if (outlen == 0) return vp->length;
+
+       *out = '\0';
+
        switch (vp->da->type) {
        case PW_TYPE_STRING:
                /* need to copy the escaped value, but quoted */
                if (quote > 0) {
                        if (freespace < 3) {
-                               return 0;
+                               return vp->length + 2;
                        }
 
                        *out++ = (char) quote;
@@ -248,7 +342,7 @@ size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t qu
                        if (len >= (freespace - 1)) {
                                out[outlen - 2] = (char) quote;
                                out[outlen - 1] = '\0';
-                               return outlen - 1;
+                               return len + 2;
                        }
                        out += len;
                        freespace -= len;
@@ -257,95 +351,130 @@ size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t qu
                        freespace--;
                        *out = '\0';
 
-                       return out - start;
+                       return len + 2;
                }
 
                /* xlat.c - need to copy raw value verbatim */
-               if (quote < 0) {
-                       strlcpy(out, vp->vp_strvalue, outlen);
-                       return strlen(out);
+               else if (quote < 0) {
+                       if (outlen > vp->length) {
+                               memcpy(out, vp->vp_strvalue, vp->length + 1);
+                               return vp->length;
+                       }
+
+                       memcpy(out, vp->vp_strvalue, outlen);
+                       out[outlen - 1] = '\0';
+                       return vp->length;      /* not a typo */
                }
 
-               return fr_print_string(vp->vp_strvalue, vp->length, out, sizeof(out));
+               return fr_print_string(vp->vp_strvalue, vp->length, out, outlen);
 
        case PW_TYPE_INTEGER:
                if (vp->da->flags.has_tag) {
                        /* Attribute value has a tag, need to ignore it */
-                       if ((v = dict_valbyattr(vp->da->attr, vp->da->vendor, (vp->vp_integer & 0xffffff)))
-                           != NULL)
+                       if ((v = dict_valbyattr(vp->da->attr, vp->da->vendor, (vp->vp_integer & 0xffffff))) != NULL) {
                                a = v->name;
-                       else {
-                               snprintf(buf, sizeof(buf), "%u", (vp->vp_integer & 0xffffff));
+                               len = strlen(a);
+                       } else {
+                               /* should never be truncated */
+                               len = snprintf(buf, sizeof(buf), "%u", (vp->vp_integer & 0xffffff));
                                a = buf;
                        }
                } else {
        case PW_TYPE_BYTE:
        case PW_TYPE_SHORT:
                        /* Normal, non-tagged attribute */
-                       if ((v = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer))
-                           != NULL)
+                       if ((v = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer)) != NULL) {
                                a = v->name;
-                       else {
-                               snprintf(buf, sizeof(buf), "%u", vp->vp_integer);
+                               len = strlen(a);
+                       } else {
+                               /* should never be truncated */
+                               len = snprintf(buf, sizeof(buf), "%u", vp->vp_integer);
                                a = buf;
                        }
                }
                break;
 
        case PW_TYPE_INTEGER64:
-               snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
-               return strlen(out);
+               return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
 
        case PW_TYPE_DATE:
                t = vp->vp_date;
                if (quote > 0) {
-                       len = strftime(buf, sizeof(buf) - 1, "%%%b %e %Y %H:%M:%S %Z%%",
-                                      localtime_r(&t, &s_tm));
+                       len = strftime(buf, sizeof(buf) - 1, "%%%b %e %Y %H:%M:%S %Z%%", localtime_r(&t, &s_tm));
                        buf[0] = (char) quote;
                        buf[len - 1] = (char) quote;
                        buf[len] = '\0';
                } else {
-                       len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z",
-                                      localtime_r(&t, &s_tm));
+                       len = strftime(buf, sizeof(buf), "%b %e %Y %H:%M:%S %Z", localtime_r(&t, &s_tm));
                }
-               if (len > 0) a = buf;
+               a = buf;
                break;
 
        case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
-               snprintf(buf, sizeof(buf), "%d", vp->vp_signed);
+               len = snprintf(buf, sizeof(buf), "%d", vp->vp_signed);
                a = buf;
                break;
 
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
                a = inet_ntop(AF_INET, &(vp->vp_ipaddr), buf, sizeof(buf));
+               len = strlen(buf);
                break;
 
        case PW_TYPE_ABINARY:
 #ifdef WITH_ASCEND_BINARY
+
+               print_abinary(buf, sizeof(buf), vp, quote);
                a = buf;
-               print_abinary(vp, buf, sizeof(buf), quote);
+               len = strlen(buf);
                break;
 #else
        /* FALL THROUGH */
 #endif
        case PW_TYPE_OCTETS:
-               if (outlen <= (2 * (vp->length + 1))) return 0;
+       case PW_TYPE_TLV:
+       {
+               size_t max;
+
+               /* Return the number of bytes we would have written */
+               len = (vp->length * 2) + 2;
+               if (freespace <= 1) {
+                       return len;
+               }
 
-               strcpy(buf, "0x");
+               *out++ = '0';
+               freespace--;
 
-               fr_bin2hex(buf + 2, vp->vp_octets, vp->length);
-               a = buf;
+               if (freespace <= 1) {
+                       *out = '\0';
+                       return len;
+               }
+               *out++ = 'x';
+               freespace--;
+
+               if (freespace <= 2) {
+                       *out = '\0';
+                       return len;
+               }
+
+               /* Get maximum number of bytes we can encode given freespace */
+               max = ((freespace % 2) ? freespace - 1 : freespace - 2) / 2;
+               fr_bin2hex(out, vp->vp_octets, (vp->length > max) ? max : vp->length);
+
+               return len;
+       }
                break;
 
        case PW_TYPE_IFID:
                a = ifid_ntoa(buf, sizeof(buf), vp->vp_ifid);
+               len = strlen(buf);
                break;
 
-       case PW_TYPE_IPV6ADDR:
+       case PW_TYPE_IPV6_ADDR:
                a = inet_ntop(AF_INET6, &vp->vp_ipv6addr, buf, sizeof(buf));
+               len = strlen(buf);
                break;
 
-       case PW_TYPE_IPV6PREFIX:
+       case PW_TYPE_IPV6_PREFIX:
        {
                struct in6_addr addr;
 
@@ -356,14 +485,16 @@ size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t qu
 
                a = inet_ntop(AF_INET6, &addr, buf, sizeof(buf));
                if (a) {
-                       char *p = buf + strlen(buf);
-                       snprintf(p, buf + sizeof(buf) - p - 1, "/%u",
-                                (unsigned int) vp->vp_ipv6prefix[1]);
+                       char *p = buf;
+
+                       len = strlen(buf);
+                       p += len;
+                       len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) vp->vp_ipv6prefix[1]);
                }
        }
                break;
 
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
        {
                struct in_addr addr;
 
@@ -374,46 +505,37 @@ size_t vp_prints_value(char *out, size_t outlen, VALUE_PAIR const *vp, int8_t qu
 
                a = inet_ntop(AF_INET, &addr, buf, sizeof(buf));
                if (a) {
-                       char *p = buf + strlen(buf);
-                       snprintf(p, buf + sizeof(buf) - p - 1, "/%u",
-                                (unsigned int) (vp->vp_ipv4prefix[1] & 0x3f));
+                       char *p = buf;
+
+                       len = strlen(buf);
+                       p += len;
+                       len += snprintf(p, sizeof(buf) - len, "/%u", (unsigned int) (vp->vp_ipv4prefix[1] & 0x3f));
                }
        }
                break;
 
        case PW_TYPE_ETHERNET:
-               snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
-                        vp->vp_ether[0], vp->vp_ether[1],
-                        vp->vp_ether[2], vp->vp_ether[3],
-                        vp->vp_ether[4], vp->vp_ether[5]);
-               a = buf;
-               break;
-
-       case PW_TYPE_TLV:
-               if (outlen <= (2 * (vp->length + 1))) return 0;
-
-               strcpy(buf, "0x");
-
-               fr_bin2hex(buf + 2, vp->vp_tlv, vp->length);
-               a = buf;
-               break;
+               return snprintf(out, outlen, "%02x:%02x:%02x:%02x:%02x:%02x",
+                               vp->vp_ether[0], vp->vp_ether[1],
+                               vp->vp_ether[2], vp->vp_ether[3],
+                               vp->vp_ether[4], vp->vp_ether[5]);
 
        default:
                a = "UNKNOWN-TYPE";
+               len = strlen(a);
                break;
        }
 
-       if (a != NULL) strlcpy(out, a, outlen);
+       if (a) strlcpy(out, a, outlen);
 
-       return strlen(out);
+       return len;     /* Return the number of bytes we would of written (for truncation detection) */
 }
 
-
-char *vp_aprinttype(TALLOC_CTX *ctx, PW_TYPE type)
+char *vp_aprint_type(TALLOC_CTX *ctx, PW_TYPE type)
 {
        switch (type) {
        case PW_TYPE_STRING :
-               return talloc_strdup(ctx, "_");
+               return talloc_typed_strdup(ctx, "_");
 
        case PW_TYPE_INTEGER64:
        case PW_TYPE_SIGNED:
@@ -421,173 +543,38 @@ char *vp_aprinttype(TALLOC_CTX *ctx, PW_TYPE type)
        case PW_TYPE_SHORT:
        case PW_TYPE_INTEGER:
        case PW_TYPE_DATE :
-               return talloc_strdup(ctx, "0");
+               return talloc_typed_strdup(ctx, "0");
 
-       case PW_TYPE_IPADDR :
-               return talloc_strdup(ctx, "?.?.?.?");
+       case PW_TYPE_IPV4_ADDR :
+               return talloc_typed_strdup(ctx, "?.?.?.?");
 
-       case PW_TYPE_IPV4PREFIX:
-               return talloc_strdup(ctx, "?.?.?.?/?");
+       case PW_TYPE_IPV4_PREFIX:
+               return talloc_typed_strdup(ctx, "?.?.?.?/?");
 
-       case PW_TYPE_IPV6ADDR:
-               return talloc_strdup(ctx, "[:?:]");
+       case PW_TYPE_IPV6_ADDR:
+               return talloc_typed_strdup(ctx, "[:?:]");
 
-       case PW_TYPE_IPV6PREFIX:
-               return talloc_strdup(ctx, "[:?:]/?");
+       case PW_TYPE_IPV6_PREFIX:
+               return talloc_typed_strdup(ctx, "[:?:]/?");
 
        case PW_TYPE_OCTETS:
-               return talloc_strdup(ctx, "0x??");
+               return talloc_typed_strdup(ctx, "??");
 
        case PW_TYPE_ETHERNET:
-               return talloc_strdup(ctx, "??:??:??:??:??:??:??:??");
+               return talloc_typed_strdup(ctx, "??:??:??:??:??:??:??:??");
 
 #ifdef WITH_ASCEND_BINARY
        case PW_TYPE_ABINARY:
-               return talloc_strdup(ctx, "??");
+               return talloc_typed_strdup(ctx, "??");
 #endif
 
        default :
                break;
        }
 
-       return talloc_strdup(ctx, "<UNKNOWN-TYPE>");
-}
-
-/*
- *     vp_prints_value for talloc
- */
-char *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
-{
-       char *p;
-
-       switch (vp->da->type) {
-       case PW_TYPE_STRING:
-               /*
-                *      FIXME: deal with \r\n" ??
-                */
-               p = talloc_strdup(ctx, vp->vp_strvalue);
-               break;
-
-       case PW_TYPE_BYTE:
-       case PW_TYPE_SHORT:
-       case PW_TYPE_INTEGER:
-               {
-                       DICT_VALUE *dv;
-
-                       dv = dict_valbyattr(vp->da->attr, vp->da->vendor,
-                                           vp->vp_integer);
-                       if (dv) {
-                               p = talloc_strdup(ctx, dv->name);
-                       } else {
-                               p = talloc_asprintf(ctx, "%u", vp->vp_integer);
-                       }
-               }
-               break;
-
-       case PW_TYPE_SIGNED:
-               p = talloc_asprintf(ctx, "%d", vp->vp_signed);
-               break;
-
-       case PW_TYPE_INTEGER64:
-               p = talloc_asprintf(ctx, "%" PRIu64 , vp->vp_integer64);
-               break;
-
-       case PW_TYPE_ETHERNET:
-               p = talloc_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
-                                   vp->vp_ether[0], vp->vp_ether[1],
-                                   vp->vp_ether[2], vp->vp_ether[3],
-                                   vp->vp_ether[4], vp->vp_ether[5]);
-               break;
-
-       case PW_TYPE_ABINARY:
-#ifdef WITH_ASCEND_BINARY
-               p = talloc_array(ctx, char, 128);
-               if (!p) return NULL;
-               print_abinary(vp, p, 128, 0);
-               break;
-#else
-                 /* FALL THROUGH */
-#endif
-
-       case PW_TYPE_OCTETS:
-               p = talloc_array(ctx, char, 3 + vp->length * 2);
-               if (!p) return NULL;
-               memcpy(p, "0x", 2);
-               fr_bin2hex(p + 2, vp->vp_octets, vp->length);
-               break;
-
-       case PW_TYPE_DATE:
-       {
-               time_t      t;
-               struct tm   s_tm;
-
-               t = vp->vp_date;
-
-               p = talloc_array(ctx, char, 64);
-               strftime(p, 64, "%b %e %Y %H:%M:%S %Z",
-                        localtime_r(&t, &s_tm));
-               break;
-       }
-
-       case PW_TYPE_IPADDR:
-               p = talloc_asprintf(ctx, "%u.%u.%u.%u",
-                                   vp->vp_ipv4prefix[0], /* network byte order */
-                                   vp->vp_ipv4prefix[1],
-                                   vp->vp_ipv4prefix[2],
-                                   vp->vp_ipv4prefix[3]);
-               break;
-
-       case PW_TYPE_IPV4PREFIX:
-               p = talloc_asprintf(ctx, "%u.%u.%u.%u/%u",
-                                   vp->vp_ipv4prefix[2],
-                                   vp->vp_ipv4prefix[3],
-                                   vp->vp_ipv4prefix[4],
-                                   vp->vp_ipv4prefix[5],
-                                   vp->vp_ipv4prefix[1] & 0x3f);
-               break;
-
-       case PW_TYPE_IPV6ADDR:
-               p = talloc_asprintf(ctx, "%x:%x:%x:%x:%x:%x:%x:%x",
-                                   (vp->vp_ipv6addr.s6_addr[0] << 8) | vp->vp_ipv6addr.s6_addr[1],
-                                   (vp->vp_ipv6addr.s6_addr[2] << 8) | vp->vp_ipv6addr.s6_addr[3],
-                                   (vp->vp_ipv6addr.s6_addr[4] << 8) | vp->vp_ipv6addr.s6_addr[5],
-                                   (vp->vp_ipv6addr.s6_addr[6] << 8) | vp->vp_ipv6addr.s6_addr[7],
-                                   (vp->vp_ipv6addr.s6_addr[8] << 8) | vp->vp_ipv6addr.s6_addr[9],
-                                   (vp->vp_ipv6addr.s6_addr[10] << 8) | vp->vp_ipv6addr.s6_addr[11],
-                                   (vp->vp_ipv6addr.s6_addr[12] << 8) | vp->vp_ipv6addr.s6_addr[13],
-                                   (vp->vp_ipv6addr.s6_addr[14] << 8) | vp->vp_ipv6addr.s6_addr[15]);
-               break;
-
-       case PW_TYPE_IPV6PREFIX:
-               p = talloc_asprintf(ctx, "%x:%x:%x:%x:%x:%x:%x:%x/%u",
-                                   (vp->vp_ipv6prefix[2] << 8) | vp->vp_ipv6prefix[3],
-                                   (vp->vp_ipv6prefix[4] << 8) | vp->vp_ipv6prefix[5],
-                                   (vp->vp_ipv6prefix[6] << 8) | vp->vp_ipv6prefix[7],
-                                   (vp->vp_ipv6prefix[8] << 8) | vp->vp_ipv6prefix[9],
-                                   (vp->vp_ipv6prefix[10] << 8) | vp->vp_ipv6prefix[11],
-                                   (vp->vp_ipv6prefix[12] << 8) | vp->vp_ipv6prefix[13],
-                                   (vp->vp_ipv6prefix[14] << 8) | vp->vp_ipv6prefix[15],
-                                   (vp->vp_ipv6prefix[16] << 8) | vp->vp_ipv6prefix[17],
-                                   vp->vp_ipv6prefix[2]);
-               break;
-
-       case PW_TYPE_IFID:
-               p = talloc_asprintf(ctx, "%x:%x:%x:%x",
-                                   (vp->vp_ifid[0] << 8) | vp->vp_ifid[1],
-                                   (vp->vp_ifid[2] << 8) | vp->vp_ifid[3],
-                                   (vp->vp_ifid[4] << 8) | vp->vp_ifid[5],
-                                   (vp->vp_ifid[6] << 8) | vp->vp_ifid[7]);
-               break;
-
-       default:
-               p = NULL;
-               break;
-       }
-
-       return p;
+       return talloc_typed_strdup(ctx, "<UNKNOWN-TYPE>");
 }
 
-
 /**  Prints attribute values escaped suitably for use as JSON values
  *
  *  Returns < 0 if the buffer may be (or have been) too small to write the encoded
@@ -600,7 +587,6 @@ char *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
  */
 size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
 {
-       char            *start = out;
        char const      *q;
        size_t          len, freespace = outlen;
 
@@ -611,26 +597,26 @@ size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
                        case PW_TYPE_SHORT:
                                if (vp->da->flags.has_value) break;
 
-                               len = snprintf(out, freespace, "%u", vp->vp_integer);
-                               return len;
+                               return snprintf(out, freespace, "%u", vp->vp_integer);
 
                        case PW_TYPE_SIGNED:
-                               len = snprintf(out, freespace, "%d", vp->vp_signed);
-                               return len;
+                               return snprintf(out, freespace, "%d", vp->vp_signed);
 
                        default:
                                break;
                }
        }
 
-       if (freespace < 2) return -1;
+       /* Indicate truncation */
+       if (freespace < 2) return outlen + 1;
        *out++ = '"';
        freespace--;
 
        switch (vp->da->type) {
                case PW_TYPE_STRING:
                        for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->length; q++) {
-                               if (freespace < 3) return -1;
+                               /* Indicate truncation */
+                               if (freespace < 3) return outlen + 1;
 
                                if (*q == '"') {
                                        *out++ = '\\';
@@ -678,7 +664,7 @@ size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
                                                break;
                                        default:
                                                len = snprintf(out, freespace, "u%04X", *q);
-                                               if (len >= freespace) return outlen;
+                                               if (is_truncated(len, freespace)) return (outlen - freespace) + len;
                                                out += len;
                                                freespace -= len;
                                        }
@@ -688,17 +674,19 @@ size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp)
 
                default:
                        len = vp_prints_value(out, freespace, vp, 0);
-                       if (len >= freespace) return outlen;
+                       if (is_truncated(len, freespace)) return (outlen - freespace) + len;
                        out += len;
                        freespace -= len;
                        break;
        }
 
-       if (freespace < 2) return outlen;
+       /* Indicate truncation */
+       if (freespace < 2) return outlen + 1;
        *out++ = '"';
+       freespace--;
        *out = '\0'; // We don't increment out, because the nul byte should not be included in the length
 
-       return out - start;
+       return outlen - freespace;
 }
 
 /*
@@ -739,107 +727,6 @@ extern int fr_attr_max_tlv;
 extern int fr_attr_shift[];
 extern int fr_attr_mask[];
 
-/** Print an attribute OID (does not include vendor)
- *
- * @param out Where to write the string.
- * @param outlen Lenth of output buffer.
- * @param attr id.
- * @param dv_type Type of dictionary value.
- * @return the length of data written to out, or a value >= outlen on truncation.
- */
-static size_t vp_print_attr_oid(char *out, size_t outlen, unsigned int attr, int dv_type)
-{
-       int             nest;
-       char            *start = out;
-       size_t          len, freespace = outlen;
-
-       switch (dv_type) {
-       case 4:
-               return snprintf(out, freespace, "%u", attr);
-
-       case 2:
-               return snprintf(out, freespace, "%u", attr & 0xffff);
-
-       default:
-       case 1:
-               len = snprintf(out, freespace, "%u", attr & 0xff);
-               if (len >= freespace) return outlen;
-               out += len;
-               freespace -= len;
-               break;
-       }
-
-       if ((attr >> 8) == 0) return out - start;
-
-       for (nest = 1; nest <= fr_attr_max_tlv; nest++) {
-               if (((attr >> fr_attr_shift[nest]) & fr_attr_mask[nest]) == 0) break;
-
-               len = snprintf(out, freespace, ".%u", (attr >> fr_attr_shift[nest]) & fr_attr_mask[nest]);
-               if (len >= freespace) return outlen;
-               out += freespace;
-               freespace -= len;
-       }
-
-       return out - start;
-}
-
-/** Print the names of attributes which are not in the dictionaries
- *
- * Print name for an unknown attribute in the format:
-@verbatim
-       Attr-<vendor id>-<attribute oid>
-@endverbatim
- * to a string.
- *
- * @param out Where to write the string.
- * @param outlen Lenth of output buffer.
- * @param attr id.
- * @param vendor id.
- * @return the length of data written to out, or a value >= outlen on truncation.
- */
-size_t vp_print_name(char *out, size_t outlen, unsigned int attr, unsigned int vendor)
-{
-       int             dv_type = 1;
-       char            *start = out;
-       size_t          len, freespace = outlen;
-
-       if (!out) return 0;
-
-       len = snprintf(out, freespace, "Attr-");
-       if (len >= freespace) return outlen;
-       out += len;
-       freespace -= len;
-
-       if (vendor > FR_MAX_VENDOR) {
-               len = snprintf(out, freespace, "%u.", vendor / FR_MAX_VENDOR);
-               if (len >= freespace) return outlen;
-               out += len;
-               freespace -= len;
-
-               vendor &= (FR_MAX_VENDOR) - 1;
-       }
-
-       if (vendor) {
-               DICT_VENDOR *dv;
-
-               dv = dict_vendorbyvalue(vendor);
-               if (dv) {
-                       dv_type = dv->type;
-               }
-
-               len = snprintf(out, freespace, "26.%u.", vendor);
-               if (len >= freespace) return outlen;
-               out += len;
-               freespace -= len;
-       }
-
-       len = vp_print_attr_oid(out, freespace, attr, dv_type);
-       if (len >= freespace) return outlen;
-       out += len;
-
-       return out - start;
-}
-
 
 /** Print one attribute and value to a string
  *
@@ -857,7 +744,6 @@ size_t vp_print_name(char *out, size_t outlen, unsigned int attr, unsigned int v
 size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
 {
        char const      *token = NULL;
-       char            *start = out;
        size_t          len, freespace = outlen;
 
        if (!out) return 0;
@@ -873,23 +759,23 @@ size_t vp_prints(char *out, size_t outlen, VALUE_PAIR const *vp)
                token = "<INVALID-TOKEN>";
        }
 
-       if(vp->da->flags.has_tag) {
+       if (vp->da->flags.has_tag) {
                len = snprintf(out, freespace, "%s:%d %s ", vp->da->name, vp->tag, token);
        } else {
                len = snprintf(out, freespace, "%s %s ", vp->da->name, token);
        }
-       if (len >= freespace) return outlen;
+
+       if (is_truncated(len, freespace)) return len;
        out += len;
        freespace -= len;
 
        len = vp_prints_value(out, freespace, vp, '\'');
-       if (len >= freespace) return outlen;
-       out += len;
+       if (is_truncated(len, freespace)) return (outlen - freespace) + len;
+       freespace -= len;
 
-       return out - start;
+       return (outlen - freespace);
 }
 
-
 /** Print one attribute and value to FP
  *
  * Complete string with '\\t' and '\\n' is written to buffer before printing to
@@ -904,6 +790,8 @@ void vp_print(FILE *fp, VALUE_PAIR const *vp)
        char    *p = buf;
        size_t  len;
 
+       VERIFY_VP(vp);
+
        *p++ = '\t';
        len = vp_prints(p, sizeof(buf) - 1, vp);
        if (!len) {
@@ -933,8 +821,178 @@ void vp_print(FILE *fp, VALUE_PAIR const *vp)
 void vp_printlist(FILE *fp, VALUE_PAIR const *vp)
 {
        vp_cursor_t cursor;
-       for (vp = paircursorc(&cursor, &vp); vp; vp = pairnext(&cursor)) {
+       for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) {
                vp_print(fp, vp);
        }
 }
 
+
+/*
+ *     vp_prints_value for talloc
+ */
+char *vp_aprint_value(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
+{
+       char *p;
+
+       switch (vp->da->type) {
+       case PW_TYPE_STRING:
+       {
+               size_t len, ret;
+
+               /* Gets us the size of the buffer we need to alloc */
+               len = fr_print_string_len(vp->vp_strvalue, vp->length);
+               p = talloc_array(ctx, char, len + 1);   /* +1 for '\0' */
+               if (!p) return NULL;
+
+               ret = fr_print_string(vp->vp_strvalue, vp->length, p, len + 1);
+               if (!fr_assert(ret == len)) {
+                       talloc_free(p);
+                       return NULL;
+               }
+               break;
+       }
+
+       case PW_TYPE_BYTE:
+       case PW_TYPE_SHORT:
+       case PW_TYPE_INTEGER:
+               {
+                       DICT_VALUE *dv;
+
+                       dv = dict_valbyattr(vp->da->attr, vp->da->vendor,
+                                           vp->vp_integer);
+                       if (dv) {
+                               p = talloc_typed_strdup(ctx, dv->name);
+                       } else {
+                               p = talloc_typed_asprintf(ctx, "%u", vp->vp_integer);
+                       }
+               }
+               break;
+
+       case PW_TYPE_SIGNED:
+               p = talloc_typed_asprintf(ctx, "%d", vp->vp_signed);
+               break;
+
+       case PW_TYPE_INTEGER64:
+               p = talloc_typed_asprintf(ctx, "%" PRIu64 , vp->vp_integer64);
+               break;
+
+       case PW_TYPE_ETHERNET:
+               p = talloc_typed_asprintf(ctx, "%02x:%02x:%02x:%02x:%02x:%02x",
+                                   vp->vp_ether[0], vp->vp_ether[1],
+                                   vp->vp_ether[2], vp->vp_ether[3],
+                                   vp->vp_ether[4], vp->vp_ether[5]);
+               break;
+
+       case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+               p = talloc_array(ctx, char, 128);
+               if (!p) return NULL;
+               print_abinary(p, 128, vp, 0);
+               break;
+#else
+                 /* FALL THROUGH */
+#endif
+
+       case PW_TYPE_OCTETS:
+               p = talloc_array(ctx, char, 1 + vp->length * 2);
+               if (!p) return NULL;
+               fr_bin2hex(p, vp->vp_octets, vp->length);
+               break;
+
+       case PW_TYPE_DATE:
+       {
+               time_t t;
+               struct tm s_tm;
+
+               t = vp->vp_date;
+
+               p = talloc_array(ctx, char, 64);
+               strftime(p, 64, "%b %e %Y %H:%M:%S %Z",
+                        localtime_r(&t, &s_tm));
+               break;
+       }
+
+       /*
+        *      We need to use the proper inet_ntop functions for IP
+        *      addresses, else the output might not match output of
+        *      other functions, which makes testing difficult.
+        *
+        *      An example is tunnelled ipv4 in ipv6 addresses.
+        */
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV4_PREFIX:
+               {
+                       char buff[INET_ADDRSTRLEN  + 4]; // + /prefix
+
+                       buff[0] = '\0';
+                       vp_prints_value(buff, sizeof(buff), vp, 0);
+
+                       p = talloc_typed_strdup(ctx, buff);
+               }
+               break;
+
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IPV6_PREFIX:
+               {
+                       char buff[INET6_ADDRSTRLEN + 4]; // + /prefix
+
+                       buff[0] = '\0';
+                       vp_prints_value(buff, sizeof(buff), vp, 0);
+
+                       p = talloc_typed_strdup(ctx, buff);
+               }
+               break;
+
+       case PW_TYPE_IFID:
+               p = talloc_typed_asprintf(ctx, "%x:%x:%x:%x",
+                                   (vp->vp_ifid[0] << 8) | vp->vp_ifid[1],
+                                   (vp->vp_ifid[2] << 8) | vp->vp_ifid[3],
+                                   (vp->vp_ifid[4] << 8) | vp->vp_ifid[5],
+                                   (vp->vp_ifid[6] << 8) | vp->vp_ifid[7]);
+               break;
+
+       default:
+               p = NULL;
+               break;
+       }
+
+       return p;
+}
+
+/** Print one attribute and value to a string
+ *
+ * Print a VALUE_PAIR in the format:
+@verbatim
+       <attribute_name>[:tag] <op> <value>
+@endverbatim
+ * to a string.
+ *
+ * @param ctx to allocate string in.
+ * @param vp to print.
+ * @return a talloced buffer with the attribute operator and value.
+ */
+char *vp_aprint(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
+{
+       char const      *token = NULL;
+       char            *pair, *value;
+
+       if (!vp || !vp->da) return 0;
+
+       VERIFY_VP(vp);
+
+       if ((vp->op > T_OP_INVALID) && (vp->op < T_TOKEN_LAST)) {
+               token = vp_tokens[vp->op];
+       } else {
+               token = "<INVALID-TOKEN>";
+       }
+
+       value = vp_aprint_value(ctx, vp);
+       pair = vp->da->flags.has_tag ?
+              talloc_asprintf(ctx, "%s:%d %s %s", vp->da->name, vp->tag, token, value) :
+              talloc_asprintf(ctx, "%s %s %s", vp->da->name, token, value);
+       talloc_free(value);
+
+       return pair;
+}
+
+
index d8c4ebc..9cdeb19 100644 (file)
@@ -74,7 +74,7 @@ static void VP_HEXDUMP(char const *msg, uint8_t const *data, size_t len)
  *     is unsigned, and the attacker can use resources on the server,
  *     even if the end request is rejected.
  */
-int fr_max_attributes = 0;
+uint32_t fr_max_attributes = 0;
 FILE *fr_log_fp = NULL;
 
 typedef struct radius_packet_t {
@@ -91,7 +91,7 @@ static unsigned int salt_offset = 0;
 static uint8_t nullvector[AUTH_VECTOR_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* for CoA decode */
 
 char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
-  "",
+  "",                                  //!< 0
   "Access-Request",
   "Access-Accept",
   "Access-Reject",
@@ -101,7 +101,7 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "Password-Request",
   "Password-Accept",
   "Password-Reject",
-  "Accounting-Message",
+  "Accounting-Message",                        //!< 10
   "Access-Challenge",
   "Status-Server",
   "Status-Client",
@@ -111,7 +111,7 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "17",
   "18",
   "19",
-  "20",
+  "20",                                        //!< 20
   "Resource-Free-Request",
   "Resource-Free-Response",
   "Resource-Query-Request",
@@ -121,7 +121,7 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "NAS-Reboot-Response",
   "28",
   "Next-Passcode",
-  "New-Pin",
+  "New-Pin",                           //!< 30
   "Terminate-Session",
   "Password-Expired",
   "Event-Request",
@@ -131,7 +131,7 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "37",
   "38",
   "39",
-  "Disconnect-Request",
+  "Disconnect-Request",                        //!< 40
   "Disconnect-ACK",
   "Disconnect-NAK",
   "CoA-Request",
@@ -142,7 +142,7 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = {
   "48",
   "49",
   "IP-Address-Allocate",
-  "IP-Address-Release"
+  "IP-Address-Release",                        //!< 50
 };
 
 
@@ -251,11 +251,11 @@ void rad_print_hex(RADIUS_PACKET *packet)
  */
 static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
 #ifdef WITH_UDPFROMTO
-                     fr_ipaddr_t *src_ipaddr, int src_port,
+                     fr_ipaddr_t *src_ipaddr, uint16_t src_port,
 #else
-                     UNUSED fr_ipaddr_t *src_ipaddr, UNUSED int src_port,
+                     UNUSED fr_ipaddr_t *src_ipaddr, UNUSED uint16_t src_port,
 #endif
-                     fr_ipaddr_t *dst_ipaddr, int dst_port)
+                     fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
 {
        int rcode;
        struct sockaddr_storage dst;
@@ -296,7 +296,7 @@ static int rad_sendto(int sockfd, void *data, size_t data_len, int flags,
 done:
 #endif
        if (rcode < 0) {
-               DEBUG("rad_send() failed: %s\n", strerror(errno));
+               fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
        }
 
        return rcode;
@@ -314,8 +314,7 @@ void rad_recv_discard(int sockfd)
 }
 
 
-ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, int *src_port,
-                       int *code)
+ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code)
 {
        ssize_t                 data_len, packet_len;
        uint8_t                 header[4];
@@ -397,7 +396,7 @@ static ssize_t rad_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags,
        ssize_t                 data_len;
        uint8_t                 header[4];
        size_t                  len;
-       int                     port;
+       uint16_t                port;
 
        memset(&src, 0, sizeof_src);
        memset(&dst, 0, sizeof_dst);
@@ -847,24 +846,25 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
                break;
 
        case PW_TYPE_IFID:
-       case PW_TYPE_IPADDR:
-       case PW_TYPE_IPV6ADDR:
-       case PW_TYPE_IPV6PREFIX:
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IPV6_PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
        case PW_TYPE_ABINARY:
+       case PW_TYPE_ETHERNET:  /* just in case */
                data = (uint8_t const *) &vp->data;
                break;
 
        case PW_TYPE_BYTE:
                len = 1;        /* just in case */
-               array[0] = vp->vp_integer & 0xff;
+               array[0] = vp->vp_byte;
                data = array;
                break;
 
        case PW_TYPE_SHORT:
                len = 2;        /* just in case */
-               array[0] = (vp->vp_integer >> 8) & 0xff;
-               array[1] = vp->vp_integer & 0xff;
+               array[0] = (vp->vp_short >> 8) & 0xff;
+               array[1] = vp->vp_short & 0xff;
                data = array;
                break;
 
@@ -902,8 +902,7 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
        }
 
        default:                /* unknown type: ignore it */
-               fr_strerror_printf("ERROR: Unknown attribute type %d",
-                                  vp->da->type);
+               fr_strerror_printf("ERROR: Unknown attribute type %d", vp->da->type);
                return -1;
        }
 
@@ -946,24 +945,24 @@ static ssize_t vp2data_any(RADIUS_PACKET const *packet,
                if (room < (18 + lvalue)) return 0;
 
                switch (packet->code) {
-               case PW_AUTHENTICATION_ACK:
-               case PW_AUTHENTICATION_REJECT:
-               case PW_ACCESS_CHALLENGE:
+               case PW_CODE_AUTHENTICATION_ACK:
+               case PW_CODE_AUTHENTICATION_REJECT:
+               case PW_CODE_ACCESS_CHALLENGE:
                default:
                        if (!original) {
                                fr_strerror_printf("ERROR: No request packet, cannot encrypt %s attribute in the vp.", vp->da->name);
                                return -1;
                        }
 
-                       if (lvalue) ptr[0] = vp->tag;
+                       if (lvalue) ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
                        make_tunnel_passwd(ptr + lvalue, &len, data, len,
                                           room - lvalue,
                                           secret, original->vector);
                        break;
-               case PW_ACCOUNTING_REQUEST:
-               case PW_DISCONNECT_REQUEST:
-               case PW_COA_REQUEST:
-                       ptr[0] = vp->tag;
+               case PW_CODE_ACCOUNTING_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
+               case PW_CODE_COA_REQUEST:
+                       ptr[0] = TAG_VALID(vp->tag) ? vp->tag : TAG_NONE;
                        make_tunnel_passwd(ptr + 1, &len, data, len - 1, room,
                                           secret, packet->vector);
                        break;
@@ -1716,13 +1715,13 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
         */
        uint64_t        data[MAX_PACKET_LEN / sizeof(uint64_t)];
 
-       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+       if (is_radius_code(packet->code)) {
                what = fr_packet_codes[packet->code];
        } else {
                what = "Reply";
        }
 
-       DEBUG("Sending %s of id %d from %s port %u to %s port %u\n",
+       DEBUG("Sending %s Id %d from %s:%u to %s:%u\n",
              what, packet->id,
              inet_ntop(packet->src_ipaddr.af,
                        &packet->src_ipaddr.ipaddr,
@@ -1737,11 +1736,11 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
         *      Double-check some things based on packet code.
         */
        switch (packet->code) {
-       case PW_AUTHENTICATION_ACK:
-       case PW_AUTHENTICATION_REJECT:
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_AUTHENTICATION_REJECT:
+       case PW_CODE_ACCESS_CHALLENGE:
                if (!original) {
-                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+                       fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
                        return -1;
                }
                break;
@@ -1749,9 +1748,9 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                /*
                 *      These packet vectors start off as all zero.
                 */
-       case PW_ACCOUNTING_REQUEST:
-       case PW_DISCONNECT_REQUEST:
-       case PW_COA_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
+       case PW_CODE_DISCONNECT_REQUEST:
+       case PW_CODE_COA_REQUEST:
                memset(packet->vector, 0, sizeof(packet->vector));
                break;
 
@@ -1900,7 +1899,7 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
         *      It wasn't assigned an Id, this is bad!
         */
        if (packet->id < 0) {
-               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id.");
+               fr_strerror_printf("ERROR: RADIUS packets must be assigned an Id");
                return -1;
        }
 
@@ -1918,27 +1917,26 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                uint8_t calc_auth_vector[AUTH_VECTOR_LEN];
 
                switch (packet->code) {
-               case PW_ACCOUNTING_RESPONSE:
-                       if (original && original->code == PW_STATUS_SERVER) {
+               case PW_CODE_ACCOUNTING_RESPONSE:
+                       if (original && original->code == PW_CODE_STATUS_SERVER) {
                                goto do_ack;
                        }
 
-               case PW_ACCOUNTING_REQUEST:
-               case PW_DISCONNECT_REQUEST:
-               case PW_DISCONNECT_ACK:
-               case PW_DISCONNECT_NAK:
-               case PW_COA_REQUEST:
-               case PW_COA_ACK:
-               case PW_COA_NAK:
+               case PW_CODE_ACCOUNTING_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
+               case PW_CODE_DISCONNECT_ACK:
+               case PW_CODE_DISCONNECT_NAK:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_COA_ACK:
                        memset(hdr->vector, 0, AUTH_VECTOR_LEN);
                        break;
 
                do_ack:
-               case PW_AUTHENTICATION_ACK:
-               case PW_AUTHENTICATION_REJECT:
-               case PW_ACCESS_CHALLENGE:
+               case PW_CODE_AUTHENTICATION_ACK:
+               case PW_CODE_AUTHENTICATION_REJECT:
+               case PW_CODE_ACCESS_CHALLENGE:
                        if (!original) {
-                               fr_strerror_printf("ERROR: Cannot sign response packet without a request packet.");
+                               fr_strerror_printf("ERROR: Cannot sign response packet without a request packet");
                                return -1;
                        }
                        memcpy(hdr->vector, original->vector,
@@ -1978,8 +1976,8 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                 *      Request packets are not signed, bur
                 *      have a random authentication vector.
                 */
-       case PW_AUTHENTICATION_REQUEST:
-       case PW_STATUS_SERVER:
+       case PW_CODE_AUTHENTICATION_REQUEST:
+       case PW_CODE_STATUS_SERVER:
                break;
 
                /*
@@ -2025,7 +2023,7 @@ int rad_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                return 0;
        }
 
-       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+       if (is_radius_code(packet->code)) {
                what = fr_packet_codes[packet->code];
        } else {
                what = "Reply";
@@ -2055,10 +2053,10 @@ int rad_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
                 *      the VP list again only for debugging.
                 */
        } else if (fr_debug_flag) {
-               DEBUG("Sending %s of id %d from %s port %u to %s port %u\n", what,
-                     packet->id,
-                     inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
-                               ip_src_buffer, sizeof(ip_src_buffer)),
+               DEBUG("Sending %s Id %d from %s:%u to %s:%u\n", what,
+                     packet->id,
+                     inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr,
+                               ip_src_buffer, sizeof(ip_src_buffer)),
                      packet->src_port,
                      inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr,
                                ip_dst_buffer, sizeof(ip_dst_buffer)),
@@ -2075,6 +2073,24 @@ int rad_send(RADIUS_PACKET *packet, RADIUS_PACKET const *original,
        if ((fr_debug_flag > 3) && fr_log_fp) rad_print_hex(packet);
 #endif
 
+#ifdef WITH_TCP
+       /*
+        *      If the socket is TCP, call write().  Calling sendto()
+        *      is allowed on some platforms, but it's not nice.  Even
+        *      worse, if UDPFROMTO is defined, we *can't* use it on
+        *      TCP sockets.  So... just call write().
+        */
+       if (packet->proto == IPPROTO_TCP) {
+               ssize_t rcode;
+
+               rcode = write(packet->sockfd, packet->data, packet->data_len);
+               if (rcode >= 0) return rcode;
+
+               fr_strerror_printf("sendto failed: %s", fr_syserror(errno));
+               return -1;
+       }
+#endif
+
        /*
         *      And send it on it's way.
         */
@@ -2269,22 +2285,27 @@ int rad_tlv_ok(uint8_t const *data, size_t length,
 }
 
 
-/**
- * @brief See if the data pointed to by PTR is a valid RADIUS packet.
+/** See if the data pointed to by PTR is a valid RADIUS packet.
+ *
+ * Packet is not 'const * const' because we may update data_len, if there's more data
+ * in the UDP packet than in the RADIUS packet.
  *
- *     packet is not 'const * const' because we may update data_len,
- *     if there's more data in the UDP packet than in the RADIUS packet.
+ * @param packet to check
+ * @param flags to control decoding
+ * @param reason if not NULL, will have the failure reason written to where it points.
+ * @return bool, true on success, false on failure.
  */
-int rad_packet_ok(RADIUS_PACKET *packet, int flags)
+bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason)
 {
        uint8_t                 *attr;
        size_t                  totallen;
        int                     count;
        radius_packet_t         *hdr;
        char                    host_ipaddr[128];
-       int                     require_ma = 0;
-       int                     seen_ma = 0;
-       int                     num_attributes;
+       bool                    require_ma = false;
+       bool                    seen_ma = false;
+       uint32_t                num_attributes;
+       decode_fail_t           failure = DECODE_FAIL_NONE;
 
        /*
         *      Check for packets smaller than the packet header.
@@ -2299,7 +2320,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     packet->data_len, AUTH_HDR_LEN);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_PACKET;
+               goto finish;
        }
 
 
@@ -2322,19 +2344,20 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                           hdr->code);
-               return 0;
+               failure = DECODE_FAIL_UNKNOWN_PACKET_CODE;
+               goto finish;
        }
 
        /*
         *      Message-Authenticator is required in Status-Server
         *      packets, otherwise they can be trivially forged.
         */
-       if (hdr->code == PW_STATUS_SERVER) require_ma = 1;
+       if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true;
 
        /*
         *      It's also required if the caller asks for it.
         */
-       if (flags) require_ma = 1;
+       if (flags) require_ma = true;
 
        /*
         *      Repeat the length checks.  This time, instead of
@@ -2353,7 +2376,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     totallen, AUTH_HDR_LEN);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_FIELD;
+               goto finish;
        }
 
        /*
@@ -2385,7 +2409,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                                     packet->data_len, totallen);
-               return 0;
+               failure = DECODE_FAIL_MIN_LENGTH_MISMATCH;
+               goto finish;
        }
 
        /*
@@ -2429,7 +2454,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
-                       return 0;
+                       failure = DECODE_FAIL_HEADER_OVERFLOW;
+                       goto finish;
                }
 
                /*
@@ -2440,20 +2466,22 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)));
-                       return 0;
+                       failure = DECODE_FAIL_INVALID_ATTRIBUTE;
+                       goto finish;
                }
 
                /*
                 *      Attributes are at LEAST as long as the ID & length
                 *      fields.  Anything shorter is an invalid attribute.
                 */
-                       if (attr[1] < 2) {
+               if (attr[1] < 2) {
                        fr_strerror_printf("WARNING: Malformed RADIUS packet from host %s: attribute %u too short",
                                   inet_ntop(packet->src_ipaddr.af,
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
                                   attr[0]);
-                       return 0;
+                       failure = DECODE_FAIL_ATTRIBUTE_TOO_SHORT;
+                       goto finish;
                }
 
                /*
@@ -2466,7 +2494,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                             &packet->src_ipaddr.ipaddr,
                                             host_ipaddr, sizeof(host_ipaddr)),
                                           attr[0]);
-                       return 0;
+                       failure = DECODE_FAIL_ATTRIBUTE_OVERFLOW;
+                       goto finish;
                }
 
                /*
@@ -2481,7 +2510,7 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                         *      a Message-Authenticator.
                         */
                case PW_EAP_MESSAGE:
-                       require_ma = 1;
+                       require_ma = true;
                        break;
 
                case PW_MESSAGE_AUTHENTICATOR:
@@ -2491,9 +2520,10 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                                     &packet->src_ipaddr.ipaddr,
                                                     host_ipaddr, sizeof(host_ipaddr)),
                                           attr[1] - 2);
-                               return 0;
+                               failure = DECODE_FAIL_MA_INVALID_LENGTH;
+                               goto finish;
                        }
-                       seen_ma = 1;
+                       seen_ma = true;
                        break;
                }
 
@@ -2518,7 +2548,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
-               return 0;
+               failure = DECODE_FAIL_ATTRIBUTE_UNDERFLOW;
+               goto finish;
        }
 
        /*
@@ -2533,7 +2564,8 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)),
                           num_attributes, fr_max_attributes);
-               return 0;
+               failure = DECODE_FAIL_TOO_MANY_ATTRIBUTES;
+               goto finish;
        }
 
        /*
@@ -2547,12 +2579,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
         *      Similarly, Status-Server packets MUST contain
         *      Message-Authenticator attributes.
         */
-       if (require_ma && ! seen_ma) {
+       if (require_ma && !seen_ma) {
                fr_strerror_printf("WARNING: Insecure packet from host %s:  Packet does not contain required Message-Authenticator attribute",
                           inet_ntop(packet->src_ipaddr.af,
                                     &packet->src_ipaddr.ipaddr,
                                     host_ipaddr, sizeof(host_ipaddr)));
-               return 0;
+               failure = DECODE_FAIL_MA_MISSING;
+               goto finish;
        }
 
        /*
@@ -2562,7 +2595,13 @@ int rad_packet_ok(RADIUS_PACKET *packet, int flags)
        packet->id = hdr->id;
        memcpy(packet->vector, hdr->vector, AUTH_VECTOR_LEN);
 
-       return 1;
+
+       finish:
+
+       if (reason) {
+               *reason = failure;
+       }
+       return (failure == DECODE_FAIL_NONE);
 }
 
 
@@ -2598,7 +2637,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      Check for socket errors.
         */
        if (data_len < 0) {
-               fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+               fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2611,7 +2650,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet.
         */
        if (packet->data_len > MAX_PACKET_LEN) {
-               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+               fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -2624,7 +2663,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
         *      packet->data == NULL
         */
        if ((packet->data_len == 0) || !packet->data) {
-               fr_strerror_printf("Empty packet: Socket is not ready.");
+               fr_strerror_printf("Empty packet: Socket is not ready");
                rad_free(&packet);
                return NULL;
        }
@@ -2632,7 +2671,7 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
        /*
         *      See if it's a well-formed RADIUS packet.
         */
-       if (!rad_packet_ok(packet, flags)) {
+       if (!rad_packet_ok(packet, flags, NULL)) {
                rad_free(&packet);
                return NULL;
        }
@@ -2654,25 +2693,36 @@ RADIUS_PACKET *rad_recv(int fd, int flags)
        packet->vps = NULL;
 
        if (fr_debug_flag) {
-               char host_ipaddr[128];
+               char src_ipaddr[128];
+               char dst_ipaddr[128];
 
-               if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
-                       DEBUG("rad_recv: %s packet from host %s port %d",
+               if (is_radius_code(packet->code)) {
+                       DEBUG("Received %s Id %d from %s:%d to %s:%d length %d\n",
                              fr_packet_codes[packet->code],
+                             packet->id,
                              inet_ntop(packet->src_ipaddr.af,
                                        &packet->src_ipaddr.ipaddr,
-                                       host_ipaddr, sizeof(host_ipaddr)),
-                             packet->src_port);
+                                       src_ipaddr, sizeof(src_ipaddr)),
+                             packet->src_port,
+                             inet_ntop(packet->dst_ipaddr.af,
+                                       &packet->dst_ipaddr.ipaddr,
+                                       dst_ipaddr, sizeof(dst_ipaddr)),
+                             packet->dst_port,
+                             (int) packet->data_len);
                } else {
-                       DEBUG("rad_recv: Packet from host %s port %d code=%d",
+                       DEBUG("Received code %d Id %d from %s:%d to %s:%d length %d\n",
+                             packet->code,
+                             packet->id,
                              inet_ntop(packet->src_ipaddr.af,
                                        &packet->src_ipaddr.ipaddr,
-                                       host_ipaddr, sizeof(host_ipaddr)),
+                                       src_ipaddr, sizeof(src_ipaddr)),
                              packet->src_port,
-                             packet->code);
+                             inet_ntop(packet->dst_ipaddr.af,
+                                       &packet->dst_ipaddr.ipaddr,
+                                       dst_ipaddr, sizeof(dst_ipaddr)),
+                             packet->dst_port,
+                             (int) packet->data_len);
                }
-               DEBUG(", id=%d, length=%d\n",
-                     packet->id, (int) packet->data_len);
        }
 
 #ifndef NDEBUG
@@ -2724,28 +2774,28 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        default:
                                break;
 
-                       case PW_ACCOUNTING_RESPONSE:
+                       case PW_CODE_ACCOUNTING_RESPONSE:
                                if (original &&
-                                   (original->code == PW_STATUS_SERVER)) {
+                                   (original->code == PW_CODE_STATUS_SERVER)) {
                                        goto do_ack;
                                }
 
-                       case PW_ACCOUNTING_REQUEST:
-                       case PW_DISCONNECT_REQUEST:
-                       case PW_DISCONNECT_ACK:
-                       case PW_DISCONNECT_NAK:
-                       case PW_COA_REQUEST:
-                       case PW_COA_ACK:
-                       case PW_COA_NAK:
-                               memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
+                       case PW_CODE_ACCOUNTING_REQUEST:
+                       case PW_CODE_DISCONNECT_REQUEST:
+                       case PW_CODE_COA_REQUEST:
+                               memset(packet->data + 4, 0, AUTH_VECTOR_LEN);
                                break;
 
                        do_ack:
-                       case PW_AUTHENTICATION_ACK:
-                       case PW_AUTHENTICATION_REJECT:
-                       case PW_ACCESS_CHALLENGE:
+                       case PW_CODE_AUTHENTICATION_ACK:
+                       case PW_CODE_AUTHENTICATION_REJECT:
+                       case PW_CODE_ACCESS_CHALLENGE:
+                       case PW_CODE_DISCONNECT_ACK:
+                       case PW_CODE_DISCONNECT_NAK:
+                       case PW_CODE_COA_ACK:
+                       case PW_CODE_COA_NAK:
                                if (!original) {
-                                       fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet.");
+                                       fr_strerror_printf("ERROR: Cannot validate Message-Authenticator in response packet without a request packet");
                                        return -1;
                                }
                                memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN);
@@ -2797,21 +2847,21 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
        /*
         *      Calculate and/or verify Request or Response Authenticator.
         */
-       switch(packet->code) {
+       switch (packet->code) {
                int rcode;
                char buffer[32];
 
-               case PW_AUTHENTICATION_REQUEST:
-               case PW_STATUS_SERVER:
+               case PW_CODE_AUTHENTICATION_REQUEST:
+               case PW_CODE_STATUS_SERVER:
                        /*
                         *      The authentication vector is random
                         *      nonsense, invented by the client.
                         */
                        break;
 
-               case PW_COA_REQUEST:
-               case PW_DISCONNECT_REQUEST:
-               case PW_ACCOUNTING_REQUEST:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
+               case PW_CODE_ACCOUNTING_REQUEST:
                        if (calc_acctdigest(packet, secret) > 1) {
                                fr_strerror_printf("Received %s packet "
                                           "from client %s with invalid Request Authenticator!  (Shared secret is incorrect.)",
@@ -2824,14 +2874,14 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
                        break;
 
                        /* Verify the reply digest */
-               case PW_AUTHENTICATION_ACK:
-               case PW_AUTHENTICATION_REJECT:
-               case PW_ACCESS_CHALLENGE:
-               case PW_ACCOUNTING_RESPONSE:
-               case PW_DISCONNECT_ACK:
-               case PW_DISCONNECT_NAK:
-               case PW_COA_ACK:
-               case PW_COA_NAK:
+               case PW_CODE_AUTHENTICATION_ACK:
+               case PW_CODE_AUTHENTICATION_REJECT:
+               case PW_CODE_ACCESS_CHALLENGE:
+               case PW_CODE_ACCOUNTING_RESPONSE:
+               case PW_CODE_DISCONNECT_ACK:
+               case PW_CODE_DISCONNECT_NAK:
+               case PW_CODE_COA_ACK:
+               case PW_CODE_COA_NAK:
                        rcode = calc_replydigest(packet, original, secret);
                        if (rcode > 1) {
                                fr_strerror_printf("Received %s packet "
@@ -2860,13 +2910,6 @@ int rad_verify(RADIUS_PACKET *packet, RADIUS_PACKET *original,
 }
 
 
-static ssize_t data2vp(RADIUS_PACKET *packet,
-                      RADIUS_PACKET const *original,
-                      char const *secret,
-                      DICT_ATTR const *da, uint8_t const *start,
-                      size_t const attrlen, size_t const packetlen,
-                      VALUE_PAIR **pvp);
-
 /**
  * @brief convert a "concatenated" attribute to one long VP.
  */
@@ -3348,14 +3391,14 @@ static ssize_t data2vp_vsas(RADIUS_PACKET *packet,
  *
  * @return -1 on error, or "length".
  */
-static ssize_t data2vp(RADIUS_PACKET *packet,
-                      RADIUS_PACKET const *original,
-                      char const *secret,
-                      DICT_ATTR const *da, uint8_t const *start,
-                      size_t const attrlen, size_t const packetlen,
-                      VALUE_PAIR **pvp)
+ssize_t data2vp(RADIUS_PACKET *packet,
+               RADIUS_PACKET const *original,
+               char const *secret,
+               DICT_ATTR const *da, uint8_t const *start,
+               size_t const attrlen, size_t const packetlen,
+               VALUE_PAIR **pvp)
 {
-       int tag = 0;
+       int8_t tag = TAG_NONE;
        size_t datalen;
        ssize_t rcode;
        uint32_t vendor;
@@ -3400,11 +3443,11 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                 *      will break assumptions about CUI.  We know
                 *      this, but Coverity doesn't.
                 */
-               if (da->type != PW_TYPE_STRING) return -1;
+               if (da->type != PW_TYPE_OCTETS) return -1;
 #endif
 
-               data = (uint8_t const *) "";
-               datalen = 1;
+               data = NULL;
+               datalen = 0;
                goto alloc_cui; /* skip everything */
        }
 
@@ -3443,12 +3486,17 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
         *      Decrypt the attribute.
         */
        if (secret && packet && (da->flags.encrypt != FLAG_ENCRYPT_NONE)) {
+               /*
+                *      Encrypted attributes can only exist for the
+                *      old-style format.  Extended attributes CANNOT
+                *      be encrypted.
+                */
+               if (attrlen > 253) {
+                       return -1;
+               }
+
                if (data == start) {
-                       if (attrlen < sizeof(buffer)) {
-                               memcpy(buffer, data, attrlen);
-                       } else {
-                               memcpy(buffer, data, sizeof(buffer));
-                       }
+                       memcpy(buffer, data, attrlen);
                }
                data = buffer;
 
@@ -3520,7 +3568,7 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                break;
 
        case PW_TYPE_INTEGER:
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
        case PW_TYPE_DATE:
        case PW_TYPE_SIGNED:
                if (datalen != 4) goto raw;
@@ -3531,11 +3579,11 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                if (datalen != 8) goto raw;
                break;
 
-       case PW_TYPE_IPV6ADDR:
+       case PW_TYPE_IPV6_ADDR:
                if (datalen != 16) goto raw;
                break;
 
-       case PW_TYPE_IPV6PREFIX:
+       case PW_TYPE_IPV6_PREFIX:
                if ((datalen < 2) || (datalen > 18)) goto raw;
                if (data[1] > 128) goto raw;
                break;
@@ -3552,13 +3600,13 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                if (datalen != 6) goto raw;
                break;
 
-       case PW_TYPE_COMBO_IP:
+       case PW_TYPE_IP_ADDR:
                if (datalen == 4) {
                        child = dict_attrbytype(da->attr, da->vendor,
-                                               PW_TYPE_IPADDR);
+                                               PW_TYPE_IPV4_ADDR);
                } else if (datalen == 16) {
                        child = dict_attrbytype(da->attr, da->vendor,
-                                            PW_TYPE_IPV6ADDR);
+                                            PW_TYPE_IPV6_ADDR);
                } else {
                        goto raw;
                }
@@ -3566,7 +3614,7 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                da = child;     /* re-write it */
                break;
 
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
                if (datalen != 6) goto raw;
                if ((data[1] & 0x3f) > 32) goto raw;
                break;
@@ -3695,7 +3743,7 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                        fr_strerror_printf("Internal sanity check %d", __LINE__);
                        return -1;
                }
-               tag = 0;
+               tag = TAG_NONE;
 #ifndef NDEBUG
                /*
                 *      Fix for Coverity.
@@ -3728,7 +3776,7 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                break;
 
        case PW_TYPE_OCTETS:
-               vp->vp_octets = talloc_memdup(vp, data, vp->length);
+               pairmemcpy(vp, data, vp->length);
                break;
 
        case PW_TYPE_ABINARY:
@@ -3739,11 +3787,11 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                break;
 
        case PW_TYPE_BYTE:
-               vp->vp_integer = data[0];
+               vp->vp_byte = data[0];
                break;
 
        case PW_TYPE_SHORT:
-               vp->vp_integer = (data[0] << 8) | data[1];
+               vp->vp_short = (data[0] << 8) | data[1];
                break;
 
        case PW_TYPE_INTEGER:
@@ -3765,7 +3813,7 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                memcpy(&vp->vp_ether, data, 6);
                break;
 
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
                memcpy(&vp->vp_ipaddr, data, 4);
                break;
 
@@ -3773,11 +3821,11 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                memcpy(&vp->vp_ifid, data, 8);
                break;
 
-       case PW_TYPE_IPV6ADDR:
+       case PW_TYPE_IPV6_ADDR:
                memcpy(&vp->vp_ipv6addr, data, 16);
                break;
 
-       case PW_TYPE_IPV6PREFIX:
+       case PW_TYPE_IPV6_PREFIX:
                /*
                 *      FIXME: double-check that
                 *      (vp->vp_octets[1] >> 3) matches vp->length + 2
@@ -3789,9 +3837,9 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
                }
                break;
 
-       case PW_TYPE_IPV4PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
                /* FIXME: do the same double-check as for IPv6Prefix */
-               memcpy(&vp->vp_ipv4prefix, buffer, sizeof(vp->vp_ipv4prefix));
+               memcpy(&vp->vp_ipv4prefix, data, vp->length);
 
                /*
                 *      /32 means "keep all bits".  Otherwise, mask
@@ -3802,7 +3850,7 @@ static ssize_t data2vp(RADIUS_PACKET *packet,
 
                        memcpy(&addr, vp->vp_octets + 2, sizeof(addr));
                        mask = 1;
-                       mask <<= (32 - (buffer[1] & 0x3f));
+                       mask <<= (32 - (data[1] & 0x3f));
                        mask--;
                        mask = ~mask;
                        mask = htonl(mask);
@@ -3891,84 +3939,129 @@ ssize_t  rad_data2vp(unsigned int attribute, unsigned int vendor,
                       data, length, length, pvp);
 }
 
-/**
- * @brief Converts vp_data to network byte order
+fr_thread_local_setup(uint8_t *, rad_vp2data_buff);
+
+/** Converts vp_data to network byte order
+ *
+ * Provide a pointer to a buffer which contains the value of the VALUE_PAIR
+ * in an architecture independent format.
+ *
+ * The pointer is only guaranteed to be valid between calls to rad_vp2data, and so long
+ * as the source VALUE_PAIR is not freed.
+ *
+ * @param out where to write the pointer to the value.
+ * @param vp to get the value from.
  * @return -1 on error, or the length of the value
  */
-ssize_t rad_vp2data(VALUE_PAIR const *vp, uint8_t *out, size_t outlen)
+ssize_t rad_vp2data(uint8_t const **out, VALUE_PAIR const *vp)
 {
-       size_t          len = 0;
+       uint8_t         *buffer;
        uint32_t        lvalue;
        uint64_t        lvalue64;
 
-       VERIFY_VP(vp);
+       *out = NULL;
 
-       len = vp->length;
-       if (outlen < len) {
-               fr_strerror_printf("ERROR: rad_vp2data buffer passed too small");
-               return -1;
+       buffer = fr_thread_local_init(rad_vp2data_buff, free);
+       if (!buffer) {
+               int ret;
+
+               buffer = malloc(sizeof(uint8_t) * sizeof(value_data_t));
+               if (!buffer) {
+                       fr_strerror_printf("Failed allocating memory for rad_vp2data buffer");
+                       return -1;
+               }
+
+               ret = fr_thread_local_set(rad_vp2data_buff, buffer);
+               if (ret != 0) {
+                       fr_strerror_printf("Failed setting up TLS for rad_vp2data buffer: %s", strerror(errno));
+                       free(buffer);
+                       return -1;
+               }
        }
 
+       VERIFY_VP(vp);
+
        switch(vp->da->type) {
-               case PW_TYPE_STRING:
-               case PW_TYPE_OCTETS:
-               case PW_TYPE_TLV:
-                       memcpy(out, vp->data.ptr, len);
-                       break;
+       case PW_TYPE_STRING:
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+               memcpy(out, &vp->data.ptr, sizeof(*out));
+               break;
 
-                       /*
-                        *      All of this data is at the same
-                        *      location.
-                        */
-               case PW_TYPE_IFID:
-               case PW_TYPE_IPADDR:
-               case PW_TYPE_IPV6ADDR:
-               case PW_TYPE_IPV6PREFIX:
-               case PW_TYPE_IPV4PREFIX:
-               case PW_TYPE_ABINARY:
-                       memcpy(out, &vp->data, len);
-                       break;
+       /*
+        *      All of these values are at the same location.
+        */
+       case PW_TYPE_IFID:
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IPV6_PREFIX:
+       case PW_TYPE_IPV4_PREFIX:
+       case PW_TYPE_ABINARY:
+       case PW_TYPE_ETHERNET:
+       case PW_TYPE_IP_ADDR:
+       case PW_TYPE_IP_PREFIX:
+       {
+               void const *p = &vp->data;
+               memcpy(out, &p, sizeof(*out));
+               break;
+       }
 
-               case PW_TYPE_BYTE:
-                       out[0] = vp->vp_integer & 0xff;
-                       break;
+       case PW_TYPE_BOOLEAN:
+               buffer[0] = vp->vp_integer & 0x01;
+               *out = buffer;
+               break;
 
-               case PW_TYPE_SHORT:
-                       out[0] = (vp->vp_integer >> 8) & 0xff;
-                       out[1] = vp->vp_integer & 0xff;
-                       break;
+       case PW_TYPE_BYTE:
+               buffer[0] = vp->vp_integer & 0xff;
+               *out = buffer;
+               break;
 
-               case PW_TYPE_INTEGER:
-                       lvalue = htonl(vp->vp_integer);
-                       memcpy(out, &lvalue, sizeof(lvalue));
-                       break;
+       case PW_TYPE_SHORT:
+               buffer[0] = (vp->vp_integer >> 8) & 0xff;
+               buffer[1] = vp->vp_integer & 0xff;
+               *out = buffer;
+               break;
 
-               case PW_TYPE_INTEGER64:
-                       lvalue64 = htonll(vp->vp_integer64);
-                       memcpy(out, &lvalue64, sizeof(lvalue64));
-                       break;
+       case PW_TYPE_INTEGER:
+               lvalue = htonl(vp->vp_integer);
+               memcpy(buffer, &lvalue, sizeof(lvalue));
+               *out = buffer;
+               break;
 
-               case PW_TYPE_DATE:
-                       lvalue = htonl(vp->vp_date);
-                       memcpy(out, &lvalue, sizeof(lvalue));
-                       break;
+       case PW_TYPE_INTEGER64:
+               lvalue64 = htonll(vp->vp_integer64);
+               memcpy(buffer, &lvalue64, sizeof(lvalue64));
+               *out = buffer;
+               break;
 
-               case PW_TYPE_SIGNED:
-               {
-                       int32_t slvalue;
+       case PW_TYPE_DATE:
+               lvalue = htonl(vp->vp_date);
+               memcpy(buffer, &lvalue, sizeof(lvalue));
+               *out = buffer;
+               break;
 
-                       slvalue = htonl(vp->vp_signed);
-                       memcpy(out, &slvalue, sizeof(slvalue));
-                       break;
-               }
-               /* unknown type: ignore it */
-               default:
-                       fr_strerror_printf("ERROR: Unknown attribute type %d",
-                                          vp->da->type);
-                       return -1;
+       case PW_TYPE_SIGNED:
+       {
+               int32_t slvalue = htonl(vp->vp_signed);
+               memcpy(buffer, &slvalue, sizeof(slvalue));
+               *out = buffer;
+               break;
+       }
+
+       case PW_TYPE_INVALID:
+       case PW_TYPE_EXTENDED:
+       case PW_TYPE_LONG_EXTENDED:
+       case PW_TYPE_EVS:
+       case PW_TYPE_VSA:
+       case PW_TYPE_TIMEVAL:
+       case PW_TYPE_MAX:
+               fr_strerror_printf("Cannot get data for VALUE_PAIR type %i", vp->da->type);
+               return -1;
+
+       /* Don't add default */
        }
 
-       return len;
+       return vp->length;
 }
 
 /**
@@ -3979,7 +4072,7 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original,
               char const *secret)
 {
        int                     packet_length;
-       int                     num_attributes;
+       uint32_t                num_attributes;
        uint8_t                 *ptr;
        radius_packet_t         *hdr;
        VALUE_PAIR *head, **tail, *vp;
@@ -4478,7 +4571,7 @@ void fr_rand_seed(void const *data, size_t size)
                                            sizeof(fr_rand_pool.randrsl) - total);
                                if ((this < 0) && (errno != EINTR)) break;
                                if (this > 0) total += this;
-                       }
+                       }
                        close(fd);
                } else {
                        fr_rand_pool.randrsl[0] = fd;
@@ -4598,6 +4691,9 @@ RADIUS_PACKET *rad_alloc_reply(TALLOC_CTX *ctx, RADIUS_PACKET *packet)
        reply->data = NULL;
        reply->data_len = 0;
 
+#ifdef WITH_TCP
+       reply->proto = packet->proto;
+#endif
        return reply;
 }
 
@@ -4612,8 +4708,43 @@ void rad_free(RADIUS_PACKET **radius_packet_ptr)
        if (!radius_packet_ptr || !*radius_packet_ptr) return;
        radius_packet = *radius_packet_ptr;
 
+       VERIFY_PACKET(radius_packet);
+
        pairfree(&radius_packet->vps);
 
        talloc_free(radius_packet);
        *radius_packet_ptr = NULL;
 }
+
+/** Duplicate a RADIUS_PACKET
+ *
+ * @param ctx the context in which the packet is allocated. May be NULL if
+ *     the packet is not associated with a REQUEST.
+ * @param in The packet to copy
+ * @return a new RADIUS_PACKET or NULL on error.
+ */
+RADIUS_PACKET *rad_copy_packet(TALLOC_CTX *ctx, RADIUS_PACKET const *in)
+{
+       RADIUS_PACKET *out;
+
+       out = rad_alloc(ctx, 0);
+       if (!out) return NULL;
+
+       /*
+        *      Bootstrap by copying everything.
+        */
+       memcpy(out, in, sizeof(*out));
+
+       /*
+        *      Then reset necessary fields
+        */
+       out->sockfd = -1;
+
+       out->data = NULL;
+       out->data_len = 0;
+
+       out->vps = paircopy(out, in->vps);
+       out->offset = 0;
+
+       return out;
+}
index 777a646..cac330f 100644 (file)
@@ -1,20 +1,20 @@
 /*
- * rbtree.c    Red-black balanced binary trees.
+ * rbtree.c    RED-BLACK balanced binary trees.
  *
  * 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 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.1 of the License, or
+ *   (at your option) any later version.
  *
- *   This library is distributed in the hope that it will be useful,
+ *   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
- *   Lesser General Public License for more details.
+ *   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 Lesser General Public
- *   License along with this library; if not, write to the Free Software
+ *   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 2004,2006  The FreeRADIUS server project
@@ -34,47 +34,51 @@ RCSID("$Id$")
 #define PTHREAD_MUTEX_UNLOCK(_x)
 #endif
 
-/* red-black tree description */
-typedef enum { Black, Red } NodeColor;
+/* Red-Black tree description */
+typedef enum {
+       BLACK,
+       RED
+} node_colour_t;
 
 struct rbnode_t {
-    rbnode_t   *Left;          /* left child */
-    rbnode_t   *Right;         /* right child */
-    rbnode_t   *Parent;        /* parent */
-    NodeColor  Color;          /* node color (black, red) */
-    void       *Data;          /* data stored in node */
+    rbnode_t           *left;          //!< Left child
+    rbnode_t           *right;         //!< Right child
+    rbnode_t           *parent;        //!< Parent
+    node_colour_t      colour;         //!< Node colour (BLACK, RED)
+    void               *data;          //!< data stored in node
 };
 
-#define NIL &Sentinel     /* all leafs are sentinels */
-static rbnode_t Sentinel = { NIL, NIL, NULL, Black, NULL};
+#define NIL &sentinel     /* all leafs are sentinels */
+static rbnode_t sentinel = { NIL, NIL, NULL, BLACK, NULL};
 
 struct rbtree_t {
 #ifndef NDEBUG
-       uint32_t magic;
+       uint32_t                magic;
 #endif
-       rbnode_t *Root;
-       int     num_elements;
-       int (*Compare)(void const *, void const *);
-       int replace_flag;
-       void (*freeNode)(void *);
+       rbnode_t                *root;
+       int                     num_elements;
+       rb_comparator_t         compare;
+       rb_free_t               free;
+       bool                    replace;
 #ifdef HAVE_PTHREAD_H
-       int lock;
-       pthread_mutex_t mutex;
+       bool                    lock;
+       pthread_mutex_t         mutex;
 #endif
 };
 #define RBTREE_MAGIC (0x5ad09c42)
 
-/*
- *     Walks the tree to delete all nodes.
- *     Does NOT re-balance it!
+/** Walks the tree to delete all nodes Does NOT re-balance it!
+ *
  */
-static void FreeWalker(rbtree_t *tree, rbnode_t *X)
+static void free_walker(rbtree_t *tree, rbnode_t *x)
 {
-       if (X->Left != NIL) FreeWalker(tree, X->Left);
-       if (X->Right != NIL) FreeWalker(tree, X->Right);
+       (void) talloc_get_type_abort(x, rbnode_t);
 
-       if (tree->freeNode) tree->freeNode(X->Data);
-       free(X);
+       if (x->left != NIL) free_walker(tree, x->left);
+       if (x->right != NIL) free_walker(tree, x->right);
+
+       if (tree->free) tree->free(x->data);
+       talloc_free(x);
 }
 
 void rbtree_free(rbtree_t *tree)
@@ -84,201 +88,192 @@ void rbtree_free(rbtree_t *tree)
        PTHREAD_MUTEX_LOCK(tree);
 
        /*
-        *      Walk the tree, deleting the nodes...
+        *      walk the tree, deleting the nodes...
         */
-       if (tree->Root != NIL) FreeWalker(tree, tree->Root);
+       if (tree->root != NIL) free_walker(tree, tree->root);
 
 #ifndef NDEBUG
        tree->magic = 0;
 #endif
-       tree->Root = NULL;
+       tree->root = NULL;
 
 #ifdef HAVE_PTHREAD_H
        if (tree->lock) pthread_mutex_destroy(&tree->mutex);
 #endif
 
-       free(tree);
+       talloc_free(tree);
 }
 
-/*
- *     Create a new red-black tree.
+/** Create a new RED-BLACK tree
+ *
  */
-rbtree_t *rbtree_create(int (*Compare)(void const *, void const *),
-                       void (*freeNode)(void *),
-                       int flags)
+rbtree_t *rbtree_create(rb_comparator_t compare, rb_free_t node_free, int flags)
 {
-       rbtree_t        *tree;
+       rbtree_t *tree;
 
-       if (!Compare) return NULL;
+       if (!compare) return NULL;
 
-       tree = malloc(sizeof(*tree));
+       tree = talloc_zero(NULL, rbtree_t);
        if (!tree) return NULL;
 
-       memset(tree, 0, sizeof(*tree));
 #ifndef NDEBUG
        tree->magic = RBTREE_MAGIC;
 #endif
-       tree->Root = NIL;
-       tree->Compare = Compare;
-       tree->replace_flag = flags & RBTREE_FLAG_REPLACE;
+       tree->root = NIL;
+       tree->compare = compare;
+       tree->replace = (flags & RBTREE_FLAG_REPLACE) != 0 ? true : false;
 #ifdef HAVE_PTHREAD_H
-       tree->lock = flags & RBTREE_FLAG_LOCK;
+       tree->lock = (flags & RBTREE_FLAG_LOCK) != 0 ? true : false;
        if (tree->lock) {
                pthread_mutex_init(&tree->mutex, NULL);
        }
 #endif
-       tree->freeNode = freeNode;
+       tree->free = node_free;
 
        return tree;
 }
 
-
-static void RotateLeft(rbtree_t *tree, rbnode_t *X)
+/** Rotate Node x to left
+ *
+ */
+static void rotate_left(rbtree_t *tree, rbnode_t *x)
 {
-       /**************************
-        *  rotate Node X to left *
-        **************************/
-
-       rbnode_t *Y = X->Right;
-
-       /* establish X->Right link */
-       X->Right = Y->Left;
-       if (Y->Left != NIL) Y->Left->Parent = X;
-
-       /* establish Y->Parent link */
-       if (Y != NIL) Y->Parent = X->Parent;
-       if (X->Parent) {
-               if (X == X->Parent->Left)
-                       X->Parent->Left = Y;
-               else
-                       X->Parent->Right = Y;
+
+       rbnode_t *y = x->right;
+
+       /* establish x->right link */
+       x->right = y->left;
+       if (y->left != NIL) y->left->parent = x;
+
+       /* establish y->parent link */
+       if (y != NIL) y->parent = x->parent;
+       if (x->parent) {
+               if (x == x->parent->left) {
+                       x->parent->left = y;
+               } else {
+                       x->parent->right = y;
+               }
        } else {
-               tree->Root = Y;
+               tree->root = y;
        }
 
-       /* link X and Y */
-       Y->Left = X;
-       if (X != NIL) X->Parent = Y;
+       /* link x and y */
+       y->left = x;
+       if (x != NIL) x->parent = y;
 }
 
-static void RotateRight(rbtree_t *tree, rbnode_t *X)
+/** Rotate Node x to right
+ *
+ */
+static void rotate_right(rbtree_t *tree, rbnode_t *x)
 {
-       /****************************
-        *  rotate Node X to right  *
-        ****************************/
-
-       rbnode_t *Y = X->Left;
-
-       /* establish X->Left link */
-       X->Left = Y->Right;
-       if (Y->Right != NIL) Y->Right->Parent = X;
-
-       /* establish Y->Parent link */
-       if (Y != NIL) Y->Parent = X->Parent;
-       if (X->Parent) {
-               if (X == X->Parent->Right)
-                       X->Parent->Right = Y;
-               else
-                       X->Parent->Left = Y;
+       rbnode_t *y = x->left;
+
+       /* establish x->left link */
+       x->left = y->right;
+       if (y->right != NIL) y->right->parent = x;
+
+       /* establish y->parent link */
+       if (y != NIL) y->parent = x->parent;
+       if (x->parent) {
+               if (x == x->parent->right) {
+                       x->parent->right = y;
+               } else {
+                       x->parent->left = y;
+               }
        } else {
-               tree->Root = Y;
+               tree->root = y;
        }
 
-       /* link X and Y */
-       Y->Right = X;
-       if (X != NIL) X->Parent = Y;
+       /* link x and y */
+       y->right = x;
+       if (x != NIL) x->parent = y;
 }
 
-static void InsertFixup(rbtree_t *tree, rbnode_t *X)
+/** Maintain red-black tree balance after inserting node x
+ *
+ */
+static void insert_fixup(rbtree_t *tree, rbnode_t *x)
 {
-       /*************************************
-        *  maintain red-black tree balance  *
-        *  after inserting node X         *
-        *************************************/
-
-       /* check red-black properties */
-       while (X != tree->Root && X->Parent->Color == Red) {
+       /* check RED-BLACK properties */
+       while ((x != tree->root) && (x->parent->colour == RED)) {
                /* we have a violation */
-               if (X->Parent == X->Parent->Parent->Left) {
-                       rbnode_t *Y = X->Parent->Parent->Right;
-                       if (Y->Color == Red) {
-
-                               /* uncle is red */
-                               X->Parent->Color = Black;
-                               Y->Color = Black;
-                               X->Parent->Parent->Color = Red;
-                               X = X->Parent->Parent;
+               if (x->parent == x->parent->parent->left) {
+                       rbnode_t *y = x->parent->parent->right;
+                       if (y->colour == RED) {
+
+                               /* uncle is RED */
+                               x->parent->colour = BLACK;
+                               y->colour = BLACK;
+                               x->parent->parent->colour = RED;
+                               x = x->parent->parent;
                        } else {
 
-                               /* uncle is black */
-                               if (X == X->Parent->Right) {
-                                       /* make X a left child */
-                                       X = X->Parent;
-                                       RotateLeft(tree, X);
+                               /* uncle is BLACK */
+                               if (x == x->parent->right) {
+                                       /* make x a left child */
+                                       x = x->parent;
+                                       rotate_left(tree, x);
                                }
 
-                               /* recolor and rotate */
-                               X->Parent->Color = Black;
-                               X->Parent->Parent->Color = Red;
-                               RotateRight(tree, X->Parent->Parent);
+                               /* recolour and rotate */
+                               x->parent->colour = BLACK;
+                               x->parent->parent->colour = RED;
+                               rotate_right(tree, x->parent->parent);
                        }
                } else {
 
                        /* mirror image of above code */
-                       rbnode_t *Y = X->Parent->Parent->Left;
-                       if (Y->Color == Red) {
-
-                               /* uncle is red */
-                               X->Parent->Color = Black;
-                               Y->Color = Black;
-                               X->Parent->Parent->Color = Red;
-                               X = X->Parent->Parent;
+                       rbnode_t *y = x->parent->parent->left;
+                       if (y->colour == RED) {
+
+                               /* uncle is RED */
+                               x->parent->colour = BLACK;
+                               y->colour = BLACK;
+                               x->parent->parent->colour = RED;
+                               x = x->parent->parent;
                        } else {
 
-                               /* uncle is black */
-                               if (X == X->Parent->Left) {
-                                       X = X->Parent;
-                                       RotateRight(tree, X);
+                               /* uncle is BLACK */
+                               if (x == x->parent->left) {
+                                       x = x->parent;
+                                       rotate_right(tree, x);
                                }
-                               X->Parent->Color = Black;
-                               X->Parent->Parent->Color = Red;
-                               RotateLeft(tree, X->Parent->Parent);
+                               x->parent->colour = BLACK;
+                               x->parent->parent->colour = RED;
+                               rotate_left(tree, x->parent->parent);
                        }
                }
        }
 
-       tree->Root->Color = Black;
+       tree->root->colour = BLACK;
 }
 
 
-/*
- *     Insert an element into the tree.
+/** Insert an element into the tree
+ *
  */
-rbnode_t *rbtree_insertnode(rbtree_t *tree, void *Data)
+rbnode_t *rbtree_insert_node(rbtree_t *tree, void *data)
 {
-       rbnode_t *Current, *Parent, *X;
-
-       /***********************************************
-        *  allocate node for Data and insert in tree  *
-        ***********************************************/
+       rbnode_t *current, *parent, *x;
 
        PTHREAD_MUTEX_LOCK(tree);
 
        /* find where node belongs */
-       Current = tree->Root;
-       Parent = NULL;
-       while (Current != NIL) {
+       current = tree->root;
+       parent = NULL;
+       while (current != NIL) {
                int result;
 
                /*
                 *      See if two entries are identical.
                 */
-               result = tree->Compare(Data, Current->Data);
+               result = tree->compare(data, current->data);
                if (result == 0) {
                        /*
                         *      Don't replace the entry.
                         */
-                       if (tree->replace_flag == 0) {
+                       if (!tree->replace) {
                                PTHREAD_MUTEX_UNLOCK(tree);
                                return NULL;
                        }
@@ -286,199 +281,201 @@ rbnode_t *rbtree_insertnode(rbtree_t *tree, void *Data)
                        /*
                         *      Do replace the entry.
                         */
-                       if (tree->freeNode) tree->freeNode(Current->Data);
-                       Current->Data = Data;
+                       if (tree->free) tree->free(current->data);
+                       current->data = data;
                        PTHREAD_MUTEX_UNLOCK(tree);
-                       return Current;
+                       return current;
                }
 
-               Parent = Current;
-               Current = (result < 0) ? Current->Left : Current->Right;
+               parent = current;
+               current = (result < 0) ? current->left : current->right;
        }
 
        /* setup new node */
-       if ((X = malloc (sizeof(*X))) == NULL) {
+       x = talloc_zero(tree, rbnode_t);
+       if (!x) {
+               fr_strerror_printf("No memory for new rbtree node");
                PTHREAD_MUTEX_UNLOCK(tree);
                return NULL;
        }
 
-       X->Data = Data;
-       X->Parent = Parent;
-       X->Left = NIL;
-       X->Right = NIL;
-       X->Color = Red;
+       x->data = data;
+       x->parent = parent;
+       x->left = NIL;
+       x->right = NIL;
+       x->colour = RED;
 
        /* insert node in tree */
-       if (Parent) {
-               if (tree->Compare(Data, Parent->Data) <= 0)
-                       Parent->Left = X;
-               else
-                       Parent->Right = X;
+       if (parent) {
+               if (tree->compare(data, parent->data) <= 0) {
+                       parent->left = x;
+               } else {
+                       parent->right = x;
+               }
        } else {
-               tree->Root = X;
+               tree->root = x;
        }
 
-       InsertFixup(tree, X);
+       insert_fixup(tree, x);
 
        tree->num_elements++;
 
        PTHREAD_MUTEX_UNLOCK(tree);
-       return X;
+       return x;
 }
 
-bool rbtree_insert(rbtree_t *tree, void *Data)
+bool rbtree_insert(rbtree_t *tree, void *data)
 {
-       if (rbtree_insertnode(tree, Data)) return true;
+       if (rbtree_insert_node(tree, data)) return true;
        return false;
 }
 
-static void DeleteFixup(rbtree_t *tree, rbnode_t *X, rbnode_t *Parent)
+/** Maintain RED-BLACK tree balance after deleting node x
+ *
+ */
+static void delete_fixup(rbtree_t *tree, rbnode_t *x, rbnode_t *parent)
 {
-       /*************************************
-        *  maintain red-black tree balance  *
-        *  after deleting node X           *
-        *************************************/
-
-       while (X != tree->Root && X->Color == Black) {
-               if (X == Parent->Left) {
-                       rbnode_t *W = Parent->Right;
-                       if (W->Color == Red) {
-                               W->Color = Black;
-                               Parent->Color = Red; /* Parent != NIL? */
-                               RotateLeft(tree, Parent);
-                               W = Parent->Right;
+
+       while (x != tree->root && x->colour == BLACK) {
+               if (x == parent->left) {
+                       rbnode_t *w = parent->right;
+                       if (w->colour == RED) {
+                               w->colour = BLACK;
+                               parent->colour = RED; /* parent != NIL? */
+                               rotate_left(tree, parent);
+                               w = parent->right;
                        }
-                       if (W->Left->Color == Black && W->Right->Color == Black) {
-                               if (W != NIL) W->Color = Red;
-                               X = Parent;
-                               Parent = X->Parent;
+                       if ((w->left->colour == BLACK) && (w->right->colour == BLACK)) {
+                               if (w != NIL) w->colour = RED;
+                               x = parent;
+                               parent = x->parent;
                        } else {
-                               if (W->Right->Color == Black) {
-                                       if (W->Left != NIL) W->Left->Color = Black;
-                                       W->Color = Red;
-                                       RotateRight(tree, W);
-                                       W = Parent->Right;
+                               if (w->right->colour == BLACK) {
+                                       if (w->left != NIL) w->left->colour = BLACK;
+                                       w->colour = RED;
+                                       rotate_right(tree, w);
+                                       w = parent->right;
                                }
-                               W->Color = Parent->Color;
-                               if (Parent != NIL) Parent->Color = Black;
-                               if (W->Right->Color != Black) {
-                                       W->Right->Color = Black;
+                               w->colour = parent->colour;
+                               if (parent != NIL) parent->colour = BLACK;
+                               if (w->right->colour != BLACK) {
+                                       w->right->colour = BLACK;
                                }
-                               RotateLeft(tree, Parent);
-                               X = tree->Root;
+                               rotate_left(tree, parent);
+                               x = tree->root;
                        }
                } else {
-                       rbnode_t *W = Parent->Left;
-                       if (W->Color == Red) {
-                               W->Color = Black;
-                               Parent->Color = Red; /* Parent != NIL? */
-                               RotateRight(tree, Parent);
-                               W = Parent->Left;
+                       rbnode_t *w = parent->left;
+                       if (w->colour == RED) {
+                               w->colour = BLACK;
+                               parent->colour = RED; /* parent != NIL? */
+                               rotate_right(tree, parent);
+                               w = parent->left;
                        }
-                       if (W->Right->Color == Black && W->Left->Color == Black) {
-                               if (W != NIL) W->Color = Red;
-                               X = Parent;
-                               Parent = X->Parent;
+                       if ((w->right->colour == BLACK) && (w->left->colour == BLACK)) {
+                               if (w != NIL) w->colour = RED;
+                               x = parent;
+                               parent = x->parent;
                        } else {
-                               if (W->Left->Color == Black) {
-                                       if (W->Right != NIL) W->Right->Color = Black;
-                                       W->Color = Red;
-                                       RotateLeft(tree, W);
-                                       W = Parent->Left;
+                               if (w->left->colour == BLACK) {
+                                       if (w->right != NIL) w->right->colour = BLACK;
+                                       w->colour = RED;
+                                       rotate_left(tree, w);
+                                       w = parent->left;
                                }
-                               W->Color = Parent->Color;
-                               if (Parent != NIL) Parent->Color = Black;
-                               if (W->Left->Color != Black) {
-                                       W->Left->Color = Black;
+                               w->colour = parent->colour;
+                               if (parent != NIL) parent->colour = BLACK;
+                               if (w->left->colour != BLACK) {
+                                       w->left->colour = BLACK;
                                }
-                               RotateRight(tree, Parent);
-                               X = tree->Root;
+                               rotate_right(tree, parent);
+                               x = tree->root;
                        }
                }
        }
-       if (X != NIL) X->Color = Black; /* Avoid cache-dirty on NIL */
+       if (x != NIL) x->colour = BLACK; /* Avoid cache-dirty on NIL */
 }
 
-/*
- *     Delete an element from the tree.
+/** Delete an element (z) from the tree
+ *
  */
-static void rbtree_delete_internal(rbtree_t *tree, rbnode_t *Z, int skiplock)
+static void rbtree_delete_internal(rbtree_t *tree, rbnode_t *z, bool skiplock)
 {
-       rbnode_t *X, *Y;
-       rbnode_t *Parent;
-
-       /*****************************
-        *  delete node Z from tree  *
-        *****************************/
+       rbnode_t *x, *y;
+       rbnode_t *parent;
 
-       if (!Z || Z == NIL) return;
+       if (!z || z == NIL) return;
 
        if (!skiplock) {
                PTHREAD_MUTEX_LOCK(tree);
        }
 
-       if (Z->Left == NIL || Z->Right == NIL) {
-               /* Y has a NIL node as a child */
-               Y = Z;
+       if (z->left == NIL || z->right == NIL) {
+               /* y has a NIL node as a child */
+               y = z;
        } else {
                /* find tree successor with a NIL node as a child */
-               Y = Z->Right;
-               while (Y->Left != NIL) Y = Y->Left;
+               y = z->right;
+               while (y->left != NIL) y = y->left;
        }
 
-       /* X is Y's only child */
-       if (Y->Left != NIL)
-               X = Y->Left;
-       else
-               X = Y->Right;   /* may be NIL! */
+       /* x is y's only child */
+       if (y->left != NIL) {
+               x = y->left;
+       } else {
+               x = y->right;   /* may be NIL! */
+       }
 
-       /* remove Y from the parent chain */
-       Parent = Y->Parent;
-       if (X != NIL) X->Parent = Parent;
+       /* remove y from the parent chain */
+       parent = y->parent;
+       if (x != NIL) x->parent = parent;
 
-       if (Parent)
-               if (Y == Parent->Left)
-                       Parent->Left = X;
-               else
-                       Parent->Right = X;
-       else
-               tree->Root = X;
+       if (parent) {
+               if (y == parent->left) {
+                       parent->left = x;
+               } else {
+                       parent->right = x;
+               }
+       } else {
+               tree->root = x;
+       }
 
-       if (Y != Z) {
-               if (tree->freeNode) tree->freeNode(Z->Data);
-               Z->Data = Y->Data;
-               Y->Data = NULL;
+       if (y != z) {
+               if (tree->free) tree->free(z->data);
+               z->data = y->data;
+               y->data = NULL;
 
-               if ((Y->Color == Black) && Parent)
-                       DeleteFixup(tree, X, Parent);
+               if ((y->colour == BLACK) && parent) {
+                       delete_fixup(tree, x, parent);
+               }
 
                /*
-                *      The user structure in Y->Data MAY include a
-                *      pointer to Y.  In that case, we CANNOT delete
-                *      Y.  Instead, we copy Z (which is now in the
-                *      tree) to Y, and fix up the parent/child
+                *      The user structure in y->data MAy include a
+                *      pointer to y.  In that case, we CANNOT delete
+                *      y.  Instead, we copy z (which is now in the
+                *      tree) to y, and fix up the parent/child
                 *      pointers.
                 */
-               memcpy(Y, Z, sizeof(*Y));
+               memcpy(y, z, sizeof(*y));
 
-               if (!Y->Parent) {
-                       tree->Root = Y;
+               if (!y->parent) {
+                       tree->root = y;
                } else {
-                       if (Y->Parent->Left == Z) Y->Parent->Left = Y;
-                       if (Y->Parent->Right == Z) Y->Parent->Right = Y;
+                       if (y->parent->left == z) y->parent->left = y;
+                       if (y->parent->right == z) y->parent->right = y;
                }
-               if (Y->Left->Parent == Z) Y->Left->Parent = Y;
-               if (Y->Right->Parent == Z) Y->Right->Parent = Y;
+               if (y->left->parent == z) y->left->parent = y;
+               if (y->right->parent == z) y->right->parent = y;
 
-               free(Z);
+               talloc_free(z);
 
        } else {
-               if (tree->freeNode) tree->freeNode(Y->Data);
+               if (tree->free) tree->free(y->data);
 
-               if (Y->Color == Black)
-                       DeleteFixup(tree, X, Parent);
+               if (y->colour == BLACK)
+                       delete_fixup(tree, x, parent);
 
-               free(Y);
+               talloc_free(y);
        }
 
        tree->num_elements--;
@@ -486,13 +483,13 @@ static void rbtree_delete_internal(rbtree_t *tree, rbnode_t *Z, int skiplock)
                PTHREAD_MUTEX_UNLOCK(tree);
        }
 }
-void rbtree_delete(rbtree_t *tree, rbnode_t *Z) {
-       rbtree_delete_internal(tree, Z, 0);
+void rbtree_delete(rbtree_t *tree, rbnode_t *z) {
+       rbtree_delete_internal(tree, z, false);
 }
 
-/*
- *     Delete a node from the tree, based on given data, which MUST
- *     have come from rbtree_finddata().
+/** Delete a node from the tree, based on given data, which MUST have come from rbtree_finddata().
+ *
+ *
  */
 bool rbtree_deletebydata(rbtree_t *tree, void const *data)
 {
@@ -506,30 +503,25 @@ bool rbtree_deletebydata(rbtree_t *tree, void const *data)
 }
 
 
-/*
- *     Find an element in the tree, returning the data, not the node.
+/** Find an element in the tree, returning the data, not the node
+ *
  */
-rbnode_t *rbtree_find(rbtree_t *tree, void const *Data)
+rbnode_t *rbtree_find(rbtree_t *tree, void const *data)
 {
-       /*******************************
-        *  find node containing Data  *
-        *******************************/
-
-       rbnode_t *Current;
-
+       rbnode_t *current;
 
        PTHREAD_MUTEX_LOCK(tree);
-       Current = tree->Root;
+       current = tree->root;
 
-       while (Current != NIL) {
-               int result = tree->Compare(Data, Current->Data);
+       while (current != NIL) {
+               int result = tree->compare(data, current->data);
 
                if (result == 0) {
                        PTHREAD_MUTEX_UNLOCK(tree);
-                       return Current;
+                       return current;
                } else {
-                       Current = (result < 0) ?
-                               Current->Left : Current->Right;
+                       current = (result < 0) ?
+                               current->left : current->right;
                }
        }
 
@@ -537,111 +529,68 @@ rbnode_t *rbtree_find(rbtree_t *tree, void const *Data)
        return NULL;
 }
 
-/*
- *     Find the user data.
+/** Find the user data.
+ *
  */
-void *rbtree_finddata(rbtree_t *tree, void const *Data)
+void *rbtree_finddata(rbtree_t *tree, void const *data)
 {
-       rbnode_t *X;
+       rbnode_t *x;
 
-       X = rbtree_find(tree, Data);
-       if (!X) return NULL;
+       x = rbtree_find(tree, data);
+       if (!x) return NULL;
 
-       return X->Data;
+       return x->data;
 }
 
-/*
- * Find a node by data, perform a callback, and perhaps delete the node.
- */
-void *rbtree_callbydata(rbtree_t *tree, void const *Data,
-                       int (*callback)(void *, void *), void *context) {
-
-       /*******************************
-        *  find node containing Data  *
-        *******************************/
-
-       rbnode_t *Current;
-
-
-       PTHREAD_MUTEX_LOCK(tree);
-       Current = tree->Root;
-
-       while (Current != NIL) {
-               int result = tree->Compare(Data, Current->Data);
-
-               if (result == 0) {
-                       void *data = Current->Data;
-
-                       if (callback(context, data) > 0) {
-                               rbtree_delete_internal(tree, Current, 1);
-                               if (tree->freeNode) {
-                                       data = NULL;
-                               }
-                       }
-                       PTHREAD_MUTEX_UNLOCK(tree);
-                       return data;
-               } else {
-                       Current = (result < 0) ?
-                               Current->Left : Current->Right;
-               }
-       }
-
-       PTHREAD_MUTEX_UNLOCK(tree);
-       return NULL;
-}
-
-/*
- *     Walk the tree, Pre-order
+/** Walk the tree, Pre-order
  *
- *     We call ourselves recursively for each function, but that's OK,
- *     as the stack is only log(N) deep, which is ~12 entries deep.
+ * We call ourselves recursively for each function, but that's OK,
+ * as the stack is only log(N) deep, which is ~12 entries deep.
  */
-static int WalkNodePreOrder(rbnode_t *X,
-                           int (*callback)(void *, void *), void *context)
+static int walk_node_pre_order(rbnode_t *x, rb_walker_t compare, void *context)
 {
        int rcode;
-       rbnode_t *Left, *Right;
+       rbnode_t *left, *right;
 
-       Left = X->Left;
-       Right = X->Right;
+       left = x->left;
+       right = x->right;
 
-       rcode = callback(context, X->Data);
+       rcode = compare(context, x->data);
        if (rcode != 0) return rcode;
 
-       if (Left != NIL) {
-               rcode = WalkNodePreOrder(Left, callback, context);
+       if (left != NIL) {
+               rcode = walk_node_pre_order(left, compare, context);
                if (rcode != 0) return rcode;
        }
 
-       if (Right != NIL) {
-               rcode = WalkNodePreOrder(Right, callback, context);
+       if (right != NIL) {
+               rcode = walk_node_pre_order(right, compare, context);
                if (rcode != 0) return rcode;
        }
 
        return 0;               /* we know everything returned zero */
 }
 
-/*
- *     Inorder
+/** rbtree_in_order
+ *
  */
-static int WalkNodeInOrder(rbnode_t *X,
-                          int (*callback)(void *, void *), void *context)
+static int walk_node_in_order(rbnode_t *x, rb_walker_t compare, void *context)
 {
        int rcode;
-       rbnode_t *Right;
+       rbnode_t *right;
 
-       if (X->Left != NIL) {
-               rcode = WalkNodeInOrder(X->Left, callback, context);
+       if (x->left != NIL) {
+               rcode = walk_node_in_order(x->left, compare, context);
                if (rcode != 0) return rcode;
        }
 
-       Right = X->Right;
+       right = x->right;
 
-       rcode = callback(context, X->Data);
+       rcode = compare(context, x->data);
        if (rcode != 0) return rcode;
 
-       if (Right != NIL) {
-               rcode = WalkNodeInOrder(Right, callback, context);
+       if (right != NIL) {
+               rcode = walk_node_in_order(right, compare, context);
                if (rcode != 0) return rcode;
        }
 
@@ -649,80 +598,83 @@ static int WalkNodeInOrder(rbnode_t *X,
 }
 
 
-/*
- *     PostOrder
+/** rbtree_post_order
+ *
  */
-static int WalkNodePostOrder(rbnode_t *X,
-                            int (*callback)(void *, void*), void *context)
+static int walk_node_post_order(rbnode_t *x, rb_walker_t compare, void *context)
 {
        int rcode;
 
-       if (X->Left != NIL) {
-               rcode = WalkNodePostOrder(X->Left, callback, context);
+       if (x->left != NIL) {
+               rcode = walk_node_post_order(x->left, compare, context);
                if (rcode != 0) return rcode;
        }
 
-       if (X->Right != NIL) {
-               rcode = WalkNodePostOrder(X->Right, callback, context);
+       if (x->right != NIL) {
+               rcode = walk_node_post_order(x->right, compare, context);
                if (rcode != 0) return rcode;
        }
 
-       rcode = callback(context, X->Data);
+       rcode = compare(context, x->data);
        if (rcode != 0) return rcode;
 
        return 0;               /* we know everything returned zero */
 }
 
 
-/*
- *     DeleteOrder
+/** rbtree_delete_order
  *
- *     This executes an InOrder-like walk that adapts to changes in the
- *     tree above it, which may occur because we allow the callback to
+ *     This executes an rbtree_in_order-like walk that adapts to changes in the
+ *     tree above it, which may occur because we allow the compare to
  *     tell us to delete the current node.
+ *
+ *     The compare should return:
+ *
+ *             < 0  - on error
+ *             0    - continue walking, don't delete the node
+ *             1    - delete the node and stop walking
+ *             2    - delete the node and continue walking
  */
-static int WalkDeleteOrder(rbtree_t *tree, int (*callback)(void *, void *),
-                          void *context)
+static int walk_delete_order(rbtree_t *tree, rb_walker_t compare, void *context)
 {
-       rbnode_t *Solid, *X;
+       rbnode_t *solid, *x;
        int rcode = 0;
 
        /* Keep track of last node that refused deletion. */
-       Solid = NIL;
-       while (Solid == NIL) {
-               X = tree->Root;
-               if (X == NIL) break;
+       solid = NIL;
+       while (solid == NIL) {
+               x = tree->root;
+               if (x == NIL) break;
        descend:
-               while (X->Left != NIL) {
-                       X = X->Left;
+               while (x->left != NIL) {
+                       x = x->left;
                }
        visit:
-               rcode = callback(context, X->Data);
+               rcode = compare(context, x->data);
                if (rcode < 0) {
                        return rcode;
                }
                if (rcode) {
-                       rbtree_delete_internal(tree, X, 1);
+                       rbtree_delete_internal(tree, x, true);
                        if (rcode != 2) {
                                return rcode;
                        }
-               }
-               else {
-                       Solid = X;
+               } else {
+                       solid = x;
                }
        }
-       if (Solid != NIL) {
-               X = Solid;
-               if (X->Right != NIL) {
-                       X = X->Right;
+       if (solid != NIL) {
+               x = solid;
+               if (x->right != NIL) {
+                       x = x->right;
                        goto descend;
                }
-               while (X->Parent) {
-                       if (X->Parent->Left == X) {
-                               X = X->Parent;
+               while (x->parent) {
+                       if (x->parent->left == x) {
+                               x = x->parent;
                                goto visit;
                        }
-                       X = X->Parent;
+                       x = x->parent;
                }
        }
        return rcode;
@@ -730,33 +682,37 @@ static int WalkDeleteOrder(rbtree_t *tree, int (*callback)(void *, void *),
 
 
 /*
- *     Walk the entire tree.  The callback function CANNOT modify
+ *     walk the entire tree.  The compare function CANNOT modify
  *     the tree.
  *
- *     The callback function should return 0 to continue walking.
+ *     The compare function should return 0 to continue walking.
  *     Any other value stops the walk, and is returned.
  */
-int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order,
-               int (*callback)(void *, void *), void *context)
+int rbtree_walk(rbtree_t *tree, rb_order_t order, rb_walker_t compare, void *context)
 {
        int rcode;
 
-       if (tree->Root == NIL) return 0;
+       if (tree->root == NIL) return 0;
 
        PTHREAD_MUTEX_LOCK(tree);
+
        switch (order) {
-       case PreOrder:
-               rcode = WalkNodePreOrder(tree->Root, callback, context);
+       case RBTREE_PRE_ORDER:
+               rcode = walk_node_pre_order(tree->root, compare, context);
                break;
-       case InOrder:
-               rcode = WalkNodeInOrder(tree->Root, callback, context);
+
+       case RBTREE_IN_ORDER:
+               rcode = walk_node_in_order(tree->root, compare, context);
                break;
-       case PostOrder:
-               rcode = WalkNodePostOrder(tree->Root, callback, context);
+
+       case RBTREE_POST_ORDER:
+               rcode = walk_node_post_order(tree->root, compare, context);
                break;
-       case DeleteOrder:
-               rcode = WalkDeleteOrder(tree, callback, context);
+
+       case RBTREE_DELETE_ORDER:
+               rcode = walk_delete_order(tree, compare, context);
                break;
+
        default:
                rcode = -1;
                break;
@@ -766,14 +722,13 @@ int rbtree_walk(rbtree_t *tree, RBTREE_ORDER order,
        return rcode;
 }
 
-int rbtree_num_elements(rbtree_t *tree)
+uint32_t rbtree_num_elements(rbtree_t *tree)
 {
        if (!tree) return 0;
 
        return tree->num_elements;
 }
 
-
 /*
  *     Given a Node, return the data.
  */
@@ -781,24 +736,5 @@ void *rbtree_node2data(UNUSED rbtree_t *tree, rbnode_t *node)
 {
        if (!node) return NULL;
 
-       return node->Data;
-}
-
-/*
- *     Return left-most child.
- */
-void *rbtree_min(rbtree_t *tree)
-{
-       void *data;
-       rbnode_t *Current;
-
-       if (!tree || !tree->Root) return NULL;
-
-       PTHREAD_MUTEX_LOCK(tree);
-       Current = tree->Root;
-       while (Current->Left != NIL) Current = Current->Left;
-
-       data = Current->Data;
-       PTHREAD_MUTEX_UNLOCK(tree);
-       return data;
+       return node->data;
 }
index f50ce55..54d172f 100644 (file)
@@ -13,78 +13,81 @@ RCSID("$Id$")
 #include "../include/sha1.h"
 
 #ifndef WITH_OPENSSL_SHA1
-#define blk0(i) (block->l[i] = htonl(block->l[i]))
+#  define blk0(i) (block->l[i] = htonl(block->l[i]))
 
-#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+#  define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
 
 /* blk0() and blk() perform the initial expand. */
 /* I got the idea of expanding during the round function from SSLeay */
 
-#define blk0(i) (block->l[i] = htonl(block->l[i]))
+#  define blk0(i) (block->l[i] = htonl(block->l[i]))
 
-#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+#  define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
     ^block->l[(i+2)&15]^block->l[i&15],1))
 
 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
-#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
-#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
-#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
-#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+#  define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#  define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#  define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#  define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#  define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
 
 
 /* Hash a single 512-bit block. This is the core of the algorithm. */
 
 void fr_SHA1Transform(uint32_t state[5], uint8_t const buffer[64])
 {
-  uint32_t a, b, c, d, e;
-  typedef union {
-    uint8_t c[64];
-    uint32_t l[16];
-  } CHAR64LONG16;
-  CHAR64LONG16 *block;
-  uint8_t workspace[64];
-
-    block = (CHAR64LONG16*)workspace;
-    memcpy(block, buffer, 64);
-    /* Copy context->state[] to working vars */
-    a = state[0];
-    b = state[1];
-    c = state[2];
-    d = state[3];
-    e = state[4];
-    /* 4 rounds of 20 operations each. Loop unrolled. */
-    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
-    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
-    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
-    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
-    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
-    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
-    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
-    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
-    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
-    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
-    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
-    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
-    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
-    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
-    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
-    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
-    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
-    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
-    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
-    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
-    /* Add the working vars back into context.state[] */
-    state[0] += a;
-    state[1] += b;
-    state[2] += c;
-    state[3] += d;
-    state[4] += e;
-
-#ifndef __clang_analyzer__
-    /* Wipe variables */
-    a = b = c = d = e = 0;
-#endif
+       uint32_t a, b, c, d, e;
+       typedef union {
+               uint8_t c[64];
+               uint32_t l[16];
+       } CHAR64LONG16;
+       CHAR64LONG16 *block;
+       uint8_t workspace[64];
+
+       block = (CHAR64LONG16*)workspace;
+       memcpy(block, buffer, 64);
+
+       /* Copy context->state[] to working vars */
+       a = state[0];
+       b = state[1];
+       c = state[2];
+       d = state[3];
+       e = state[4];
+
+       /* 4 rounds of 20 operations each. Loop unrolled. */
+       R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+       R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+       R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+       R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+       R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+       R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+       R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+       R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+       R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+       R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+       R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+       R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+       R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+       R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+       R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+       R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+       R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+       R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+       R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+       R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+       /* Add the working vars back into context.state[] */
+       state[0] += a;
+       state[1] += b;
+       state[2] += c;
+       state[3] += d;
+       state[4] += e;
+
+#  ifndef __clang_analyzer__
+       /* Wipe variables */
+       a = b = c = d = e = 0;
+#  endif
 }
 
 
@@ -92,35 +95,37 @@ void fr_SHA1Transform(uint32_t state[5], uint8_t const buffer[64])
 
 void fr_SHA1Init(fr_SHA1_CTX* context)
 {
-    /* SHA1 initialization constants */
-    context->state[0] = 0x67452301;
-    context->state[1] = 0xEFCDAB89;
-    context->state[2] = 0x98BADCFE;
-    context->state[3] = 0x10325476;
-    context->state[4] = 0xC3D2E1F0;
-    context->count[0] = context->count[1] = 0;
+       /* SHA1 initialization constants */
+       context->state[0] = 0x67452301;
+       context->state[1] = 0xEFCDAB89;
+       context->state[2] = 0x98BADCFE;
+       context->state[3] = 0x10325476;
+       context->state[4] = 0xC3D2E1F0;
+       context->count[0] = context->count[1] = 0;
 }
 
-
 /* Run your data through this. */
-
 void fr_SHA1Update(fr_SHA1_CTX *context,uint8_t const *data, size_t len)
 {
-unsigned int i, j;
+       unsigned int i, j;
 
-    j = (context->count[0] >> 3) & 63;
-    if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
-    context->count[1] += (len >> 29);
-    if ((j + len) > 63) {
-       memcpy(&context->buffer[j], data, (i = 64-j));
-       fr_SHA1Transform(context->state, context->buffer);
-       for ( ; i + 63 < len; i += 64) {
-           fr_SHA1Transform(context->state, &data[i]);
+       j = (context->count[0] >> 3) & 63;
+       if ((context->count[0] += len << 3) < (len << 3)) {
+               context->count[1]++;
+       }
+
+       context->count[1] += (len >> 29);
+       if ((j + len) > 63) {
+               memcpy(&context->buffer[j], data, (i = 64-j));
+               fr_SHA1Transform(context->state, context->buffer);
+               for ( ; i + 63 < len; i += 64) {
+                       fr_SHA1Transform(context->state, &data[i]);
+               }
+               j = 0;
+       } else {
+               i = 0;
        }
-       j = 0;
-    }
-    else i = 0;
-    memcpy(&context->buffer[j], &data[i], len - i);
+       memcpy(&context->buffer[j], &data[i], len - i);
 }
 
 
@@ -128,56 +133,55 @@ unsigned int i, j;
 
 void fr_SHA1Final(uint8_t digest[20], fr_SHA1_CTX* context)
 {
-uint32_t i, j;
-uint8_t finalcount[8];
-
-    for (i = 0; i < 8; i++) {
-       finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)]
-        >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
-    }
-    fr_SHA1Update(context, (unsigned char const *) "\200", 1);
-    while ((context->count[0] & 504) != 448) {
-       fr_SHA1Update(context, (unsigned char const *) "\0", 1);
-    }
-    fr_SHA1Update(context, finalcount, 8);  /* Should cause a fr_SHA1Transform() */
-    for (i = 0; i < 20; i++) {
-       digest[i] = (uint8_t)
-        ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
-    }
-
-#ifndef __clang_analyzer__
-    /* Wipe variables */
-    i = j = 0;
-    memset(context->buffer, 0, 64);
-    memset(context->state, 0, 20);
-    memset(context->count, 0, 8);
-    memset(&finalcount, 0, 8);
-#endif
+       uint32_t i, j;
+       uint8_t finalcount[8];
 
-#ifdef SHA1HANDSOFF  /* make fr_SHA1Transform overwrite it's own static vars */
-    fr_SHA1Transform(context->state, context->buffer);
-#endif
+       for (i = 0; i < 8; i++) {
+               finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+       }
+
+       fr_SHA1Update(context, (unsigned char const *) "\200", 1);
+
+       while ((context->count[0] & 504) != 448) {
+               fr_SHA1Update(context, (unsigned char const *) "\0", 1);
+       }
+       fr_SHA1Update(context, finalcount, 8);  /* Should cause a fr_SHA1Transform() */
+       for (i = 0; i < 20; i++) {
+               digest[i] = (uint8_t)((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+       }
+
+#  ifndef __clang_analyzer__
+       /* Wipe variables */
+       i = j = 0;
+       memset(context->buffer, 0, 64);
+       memset(context->state, 0, 20);
+       memset(context->count, 0, 8);
+       memset(&finalcount, 0, 8);
+#  endif
+
+#  ifdef SHA1HANDSOFF  /* make fr_SHA1Transform overwrite it's own static vars */
+       fr_SHA1Transform(context->state, context->buffer);
+#  endif
 }
 
 void fr_SHA1FinalNoLen(uint8_t digest[20], fr_SHA1_CTX* context)
 {
-  uint32_t i, j;
-
-    for (i = 0; i < 20; i++) {
-       digest[i] = (uint8_t)
-        ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
-    }
-
-#ifndef __clang_analyzer__
-    /* Wipe variables */
-    i = j = 0;
-    memset(context->buffer, 0, 64);
-    memset(context->state, 0, 20);
-    memset(context->count, 0, 8);
-#endif
+       uint32_t i, j;
 
-#ifdef SHA1HANDSOFF  /* make fr_SHA1Transform overwrite it's own static vars */
-    fr_SHA1Transform(context->state, context->buffer);
-#endif
+       for (i = 0; i < 20; i++) {
+               digest[i] = (uint8_t)((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+       }
+
+#  ifndef __clang_analyzer__
+       /* Wipe variables */
+       i = j = 0;
+       memset(context->buffer, 0, 64);
+       memset(context->state, 0, 20);
+       memset(context->count, 0, 8);
+#  endif
+
+#  ifdef SHA1HANDSOFF  /* make fr_SHA1Transform overwrite it's own static vars */
+       fr_SHA1Transform(context->state, context->buffer);
+#  endif
 }
 #endif
index 7931b45..211b182 100644 (file)
@@ -30,96 +30,15 @@ RCSID("$Id$")
 #define MAX_PACKET_LEN 4096
 
 /*
- *     Open a socket on the given IP and port.
- */
-int fr_tcp_socket(fr_ipaddr_t *ipaddr, int port)
-{
-       int sockfd;
-       int on = 1;
-       struct sockaddr_storage salocal;
-       socklen_t       salen;
-
-       if ((port < 0) || (port > 65535)) {
-               fr_strerror_printf("Port %d is out of allowed bounds", port);
-               return -1;
-       }
-
-       sockfd = socket(ipaddr->af, SOCK_STREAM, 0);
-       if (sockfd < 0) {
-               return sockfd;
-       }
-
-       if (fr_nonblock(sockfd) < 0) {
-               close(sockfd);
-               return -1;
-       }
-
-       if (!fr_ipaddr2sockaddr(ipaddr, port, &salocal, &salen)) {
-               close(sockfd);
-               return -1;
-       }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       if (ipaddr->af == AF_INET6) {
-               /*
-                *      Listening on '::' does NOT get you IPv4 to
-                *      IPv6 mapping.  You've got to listen on an IPv4
-                *      address, too.  This makes the rest of the server
-                *      design a little simpler.
-                */
-#ifdef IPV6_V6ONLY
-
-               if (IN6_IS_ADDR_UNSPECIFIED(&ipaddr->ipaddr.ip6addr)) {
-                       if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,
-                                      (char *)&on, sizeof(on)) < 0) {
-                               fr_strerror_printf("Failed in setsockopt(): %s",
-                                                  strerror(errno));
-                               close(sockfd);
-                               return -1;
-                       }
-               }
-#endif /* IPV6_V6ONLY */
-       }
-#endif /* HAVE_STRUCT_SOCKADDR_IN6 */
-
-       if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
-               fr_strerror_printf("Failed in setsockopt(): %s", strerror(errno));
-               close(sockfd);
-               return -1;
-       }
-
-       if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
-               fr_strerror_printf("Failed in bind(): %s", strerror(errno));
-               close(sockfd);
-               return -1;
-       }
-
-       if (listen(sockfd, 8) < 0) {
-               fr_strerror_printf("Failed in listen(): %s", strerror(errno));
-               close(sockfd);
-               return -1;
-       }
-
-       return sockfd;
-}
-
-
-/*
  *     Open a socket TO the given IP and port.
  */
 int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
-                        fr_ipaddr_t *dst_ipaddr, int dst_port)
+                        fr_ipaddr_t *dst_ipaddr, uint16_t dst_port)
 {
        int sockfd;
        struct sockaddr_storage salocal;
        socklen_t       salen;
 
-       if ((dst_port < 0) || (dst_port > 65535)) {
-               fr_strerror_printf("Port %d is out of allowed bounds",
-                                  dst_port);
-               return -1;
-       }
-
        if (!dst_ipaddr) return -1;
 
        sockfd = socket(dst_ipaddr->af, SOCK_STREAM, 0);
@@ -127,28 +46,6 @@ int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
                return sockfd;
        }
 
-#if 0
-#ifdef O_NONBLOCK
-       {
-               int flags;
-
-               if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
-                       fr_strerror_printf("Failure getting socket flags: %s",
-                                  strerror(errno));
-                       close(sockfd);
-                       return -1;
-               }
-
-               flags |= O_NONBLOCK;
-               if( fcntl(sockfd, F_SETFL, flags) < 0) {
-                       fr_strerror_printf("Failure setting socket flags: %s",
-                                  strerror(errno));
-                       close(sockfd);
-                       return -1;
-               }
-       }
-#endif
-#endif
        /*
         *      Allow the caller to bind us to a specific source IP.
         */
@@ -160,14 +57,14 @@ int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
 
                if (bind(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
                        fr_strerror_printf("Failure binding to IP: %s",
-                                          strerror(errno));
+                                          fr_syserror(errno));
                        close(sockfd);
                        return -1;
                }
        }
 
        if (!fr_ipaddr2sockaddr(dst_ipaddr, dst_port, &salocal, &salen)) {
-                       close(sockfd);
+               close(sockfd);
                return -1;
        }
 
@@ -177,7 +74,7 @@ int fr_tcp_client_socket(fr_ipaddr_t *src_ipaddr,
         *      socket is ready...
         */
        if (connect(sockfd, (struct sockaddr *) &salocal, salen) < 0) {
-               fr_strerror_printf("Failed in connect(): %s", strerror(errno));
+               fr_strerror_printf("Failed in connect(): %s", fr_syserror(errno));
                close(sockfd);
                return -1;
        }
@@ -239,7 +136,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
 
                if (len < 0) {
                        fr_strerror_printf("Error receiving packet: %s",
-                                  strerror(errno));
+                                  fr_syserror(errno));
                        return -1;
                }
 
@@ -251,7 +148,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
                packet_len = (packet->vector[2] << 8) | packet->vector[3];
 
                if (packet_len < AUTH_HDR_LEN) {
-                       fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes.");
+                       fr_strerror_printf("Discarding packet: Smaller than RFC minimum of 20 bytes");
                        return -1;
                }
 
@@ -259,7 +156,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
                 *      If the packet is too big, then the socket is bad.
                 */
                if (packet_len > MAX_PACKET_LEN) {
-                       fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes.");
+                       fr_strerror_printf("Discarding packet: Larger than RFC limitation of 4096 bytes");
                        return -1;
                }
 
@@ -288,7 +185,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
 #endif
 
        if (len < 0) {
-               fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+               fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
                return -1;
        }
 
@@ -301,7 +198,7 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
        /*
         *      See if it's a well-formed RADIUS packet.
         */
-       if (!rad_packet_ok(packet, flags)) {
+       if (!rad_packet_ok(packet, flags, NULL)) {
                return -1;
        }
 
@@ -325,11 +222,11 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
                }
 
 
-               if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
-                       DEBUG("rad_recv: %s packet from %s",
+               if (is_radius_code(packet->code)) {
+                       DEBUG("Received %s packet from %s",
                              fr_packet_codes[packet->code], buffer);
                } else {
-                       DEBUG("rad_recv: Packet from %s code=%d",
+                       DEBUG("Received packet from %s code %d",
                              buffer, packet->code);
                }
                DEBUG(", id=%d, length=%zu\n", packet->id, packet->data_len);
@@ -338,94 +235,4 @@ int fr_tcp_read_packet(RADIUS_PACKET *packet, int flags)
        return 1;               /* done reading the packet */
 }
 
-RADIUS_PACKET *fr_tcp_accept(int sockfd)
-{
-       int newfd;
-       socklen_t salen;
-       RADIUS_PACKET *packet;
-       struct sockaddr_storage src;
-
-       salen = sizeof(src);
-
-       newfd = accept(sockfd, (struct sockaddr *) &src, &salen);
-       if (newfd < 0) {
-               /*
-                *      Non-blocking sockets must handle this.
-                */
-#ifdef EWOULDBLOCK
-               if (errno == EWOULDBLOCK) {
-                       packet = rad_alloc(NULL, 0);
-                       if (!packet) return NULL;
-
-                       packet->sockfd = sockfd;
-                       packet->src_ipaddr.af = AF_UNSPEC;
-                       return packet;
-               }
-#endif
-
-               return NULL;
-       }
-
-       packet = rad_alloc(NULL, 0);
-       if (!packet) {
-               close(newfd);
-               return NULL;
-       }
-
-       if (src.ss_family == AF_INET) {
-               struct sockaddr_in      s4;
-
-               memcpy(&s4, &src, sizeof(s4));
-               packet->src_ipaddr.af = AF_INET;
-               packet->src_ipaddr.ipaddr.ip4addr = s4.sin_addr;
-               packet->src_port = ntohs(s4.sin_port);
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       } else if (src.ss_family == AF_INET6) {
-               struct sockaddr_in6     s6;
-
-               memcpy(&s6, &src, sizeof(s6));
-               packet->src_ipaddr.af = AF_INET6;
-               packet->src_ipaddr.ipaddr.ip6addr = s6.sin6_addr;
-               packet->src_port = ntohs(s6.sin6_port);
-
-#endif
-       } else {
-               rad_free(&packet);
-               close(newfd);
-               return NULL;
-       }
-
-       packet->sockfd = newfd;
-
-       /*
-        *      Note: Caller has to set dst_ipaddr && dst_port.
-        */
-       return packet;
-}
-
-
-/*
- *     Writes a packet, assuming it's already been encoded.
- *
- *     It returns the number of bytes written, which MAY be less than
- *     the packet size (data_len).  It is the caller's responsibility
- *     to check the return code, and to schedule writes again.
- */
-ssize_t fr_tcp_write_packet(RADIUS_PACKET *packet)
-{
-       ssize_t rcode;
-
-       if (!packet || !packet->data) return 0;
-
-       if (packet->partial >= packet->data_len) return packet->data_len;
-
-       rcode = write(packet->sockfd, packet->data + packet->partial,
-                     packet->data_len - packet->partial);
-       if (rcode < 0) return packet->partial; /* ignore most errors */
-
-       packet->partial += rcode;
-
-       return packet->partial;
-}
 #endif /* WITH_TCP */
index 795aa7e..c077603 100644 (file)
@@ -69,16 +69,17 @@ const FR_NAME_NUMBER fr_tokens[] = {
  *     Returns 0 or special token value.
  */
 static FR_TOKEN getthing(char const **ptr, char *buf, int buflen, int tok,
-                        FR_NAME_NUMBER const *tokenlist)
+                        FR_NAME_NUMBER const *tokenlist, bool unescape)
 {
        char                    *s;
        char const              *p;
-       int                     quote, end = 0;
+       char                    quote;
+       bool                    end = false;
        unsigned int            x;
        FR_NAME_NUMBER const    *t;
        FR_TOKEN rcode;
 
-       buf[0] = 0;
+       buf[0] = '\0';
 
        /* Skip whitespace */
        p = *ptr;
@@ -105,18 +106,18 @@ static FR_TOKEN getthing(char const **ptr, char *buf, int buflen, int tok,
        }
 
        /* Read word. */
-       quote = 0;
+       quote = '\0';
        if ((*p == '"') ||
            (*p == '\'') ||
            (*p == '`')) {
                quote = *p;
-               end = 0;
+               end = false;
                p++;
        }
        s = buf;
 
        while (*p && buflen-- > 1) {
-               if (quote && (*p == '\\')) {
+               if (unescape && quote && (*p == '\\')) {
                        p++;
 
                        switch(*p) {
@@ -146,7 +147,7 @@ static FR_TOKEN getthing(char const **ptr, char *buf, int buflen, int tok,
                        continue;
                }
                if (quote && (*p == quote)) {
-                       end = 1;
+                       end = true;
                        p++;
                        break;
                }
@@ -201,39 +202,24 @@ static FR_TOKEN getthing(char const **ptr, char *buf, int buflen, int tok,
  *     Read a "word" - this means we don't honor
  *     tokens as delimiters.
  */
-int getword(char const **ptr, char *buf, int buflen)
+int getword(char const **ptr, char *buf, int buflen, bool unescape)
 {
-       return getthing(ptr, buf, buflen, 0, fr_tokens) == T_EOL ? 0 : 1;
+       return getthing(ptr, buf, buflen, 0, fr_tokens, unescape) == T_EOL ? 0 : 1;
 }
 
-/*
- *     Read a bare "word" - this means we don't honor
- *     tokens as delimiters.
- */
-int getbareword(char const **ptr, char *buf, int buflen)
-{
-       FR_TOKEN token;
-
-       token = getthing(ptr, buf, buflen, 0, NULL);
-       if (token != T_BARE_WORD) {
-               return 0;
-       }
-
-       return 1;
-}
 
 /*
  *     Read the next word, use tokens as delimiters.
  */
-FR_TOKEN gettoken(char const **ptr, char *buf, int buflen)
+FR_TOKEN gettoken(char const **ptr, char *buf, int buflen, bool unescape)
 {
-       return getthing(ptr, buf, buflen, 1, fr_tokens);
+       return getthing(ptr, buf, buflen, 1, fr_tokens, unescape);
 }
 
 /*
  *     Expect a string.
  */
-FR_TOKEN getstring(char const **ptr, char *buf, int buflen)
+FR_TOKEN getstring(char const **ptr, char *buf, int buflen, bool unescape)
 {
        char const *p;
 
@@ -246,10 +232,10 @@ FR_TOKEN getstring(char const **ptr, char *buf, int buflen)
        *ptr = p;
 
        if ((*p == '"') || (*p == '\'') || (*p == '`')) {
-               return gettoken(ptr, buf, buflen);
+               return gettoken(ptr, buf, buflen, unescape);
        }
 
-       return getthing(ptr, buf, buflen, 0, fr_tokens);
+       return getthing(ptr, buf, buflen, 0, fr_tokens, unescape);
 }
 
 /*
index 680e354..8973143 100644 (file)
@@ -83,6 +83,7 @@ RCSID("$Id$")
 /* Fallback to to using recvfrom */
 #    else
 #      undef IPV6_RECVPKTINFO
+#      undef IPV6_PKTINFO
 #    endif
 #  else
 #    define FR_IPV6_RECVPKTINFO IPV6_PKTINFO
@@ -97,7 +98,13 @@ int udpfromto_init(int s)
 
        errno = ENOSYS;
 
-       proto = -1;
+       /*
+        *      Clang analyzer doesn't see that getsockname initialises
+        *      the memory passed to it.
+        */
+#ifdef __clang_analyzer__
+       memset(&si, 0, sizeof(si));
+#endif
 
        if (getsockname(s, (struct sockaddr *) &si, &si_len) < 0) {
                return -1;
@@ -110,15 +117,18 @@ int udpfromto_init(int s)
                 */
                proto = SOL_IP;
                flag = IP_PKTINFO;
-#endif
-
+#else
 #ifdef IP_RECVDSTADDR
+
                /*
                 *      Set the IP_RECVDSTADDR option (BSD).  Note:
                 *      IP_RECVDSTADDR == IP_SENDSRCADDR
                 */
                proto = IPPROTO_IP;
                flag = IP_RECVDSTADDR;
+#else
+               return -1;
+#endif
 #endif
 
 #ifdef AF_INET6
@@ -133,6 +143,8 @@ int udpfromto_init(int s)
                 *      Work around Linux-specific hackery.
                 */
                flag = FR_IPV6_RECVPKTINFO;
+#else
+               return -1;
 #  endif
 #endif
        } else {
@@ -171,6 +183,14 @@ int recvfromto(int s, void *buf, size_t len, int flags,
        if (!to || !tolen) return recvfrom(s, buf, len, flags, from, fromlen);
 
        /*
+        *      Clang analyzer doesn't see that getsockname initialises
+        *      the memory passed to it.
+        */
+#ifdef __clang_analyzer__
+       memset(&si, 0, sizeof(si));
+#endif
+
+       /*
         *      recvmsg doesn't provide sin_port so we have to
         *      retrieve it using getsockname().
         */
@@ -224,6 +244,7 @@ int recvfromto(int s, void *buf, size_t len, int flags,
        }
 
        /* Set up iov and msgh structures. */
+       memset(&cbuf, 0, sizeof(cbuf));
        memset(&msgh, 0, sizeof(struct msghdr));
        iov.iov_base = buf;
        iov.iov_len  = len;
@@ -292,12 +313,41 @@ int sendfromto(int s, void *buf, size_t len, int flags,
        struct iovec iov;
        char cbuf[256];
 
-#if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
+#ifdef __FreeBSD__
+       /*
+        *      FreeBSD is extra pedantic about the use of IP_SENDSRCADDR,
+        *      and sendmsg will fail with EINVAL if IP_SENDSRCADDR is used
+        *      with a socket which is bound to something other than
+        *      INADDR_ANY
+        */
+       struct sockaddr bound;
+       socklen_t bound_len = sizeof(bound);
+
+       if (getsockname(s, &bound, &bound_len) < 0) {
+               return -1;
+       }
+
+       switch (bound.sa_family) {
+       case AF_INET:
+               if (((struct sockaddr_in *) &bound)->sin_addr.s_addr != INADDR_ANY) {
+                       from = NULL;
+               }
+               break;
+
+       case AF_INET6:
+               if (!IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *) &bound)->sin6_addr))) {
+                       from = NULL;
+               }
+               break;
+       }
+#else
+#  if !defined(IP_PKTINFO) && !defined(IP_SENDSRCADDR) && !defined(IPV6_PKTINFO)
        /*
         *      If the sendmsg() flags aren't defined, fall back to
         *      using sendto().
         */
        from = NULL;
+#  endif
 #endif
 
        /*
@@ -307,7 +357,8 @@ int sendfromto(int s, void *buf, size_t len, int flags,
                return sendto(s, buf, len, flags, to, tolen);
        }
 
-       /* Set up iov and msgh structures. */
+       /* Set up control buffer iov and msgh structures. */
+       memset(&cbuf, 0, sizeof(cbuf));
        memset(&msgh, 0, sizeof(msgh));
        memset(&iov, 0, sizeof(iov));
        iov.iov_base = buf;
@@ -413,7 +464,7 @@ int main(int argc, char **argv)
        struct sockaddr_in from, to, in;
        char buf[TESTLEN];
        char *destip = DESTIP;
-       int port = DEF_PORT;
+       uint16_t port = DEF_PORT;
        int n, server_socket, client_socket, fl, tl, pid;
 
        if (argc > 1) destip = argv[1];
index d388ecf..607bd07 100644 (file)
@@ -73,7 +73,7 @@ static int _pairfree(VALUE_PAIR *vp) {
        }
 
 #ifndef NDEBUG
-       vp->vp_integer = FREE_MAGIC;
+       vp->vp_integer = 0xf4eef4ee;
 #endif
 
 #ifdef TALLOC_DEBUG
@@ -97,7 +97,10 @@ VALUE_PAIR *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da)
        /*
         *      Caller must specify a da else we don't know what the attribute type is.
         */
-       if (!da) return NULL;
+       if (!da) {
+               fr_strerror_printf("Invalid arguments");
+               return NULL;
+       }
 
        vp = talloc_zero(ctx, VALUE_PAIR);
        if (!vp) {
@@ -107,8 +110,11 @@ VALUE_PAIR *pairalloc(TALLOC_CTX *ctx, DICT_ATTR const *da)
 
        vp->da = da;
        vp->op = T_OP_EQ;
+       vp->tag = TAG_ANY;
        vp->type = VT_NONE;
 
+       vp->length = da->flags.length;
+
        talloc_set_destructor(vp, _pairfree);
 
        return vp;
@@ -154,13 +160,13 @@ void pairfree(VALUE_PAIR **vps)
        VALUE_PAIR      *vp;
        vp_cursor_t     cursor;
 
-       if (!vps) {
+       if (!vps || !*vps) {
                return;
        }
 
-       for (vp = paircursor(&cursor, vps);
+       for (vp = fr_cursor_init(&cursor, vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                VERIFY_VP(vp);
                talloc_free(vp);
        }
@@ -192,23 +198,23 @@ int pair2unknown(VALUE_PAIR *vp)
        return 0;
 }
 
-/** Find the pair with the matching attribute
+/** Find the pair with the matching DAs
  *
- * @todo should take DAs and do a pointer comparison.
  */
-VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor,
-                    int8_t tag)
+VALUE_PAIR *pairfind_da(VALUE_PAIR *vp, DICT_ATTR const *da, int8_t tag)
 {
        vp_cursor_t     cursor;
        VALUE_PAIR      *i;
 
-       for (i = paircursor(&cursor, &vp);
+       if(!fr_assert(da)) {
+                return NULL;
+       }
+
+       for (i = fr_cursor_init(&cursor, &vp);
             i;
-            i = pairnext(&cursor)) {
+            i = fr_cursor_next(&cursor)) {
                VERIFY_VP(i);
-               if ((i->da->attr == attr) && (i->da->vendor == vendor)
-                   && ((tag == TAG_ANY) || (i->da->flags.has_tag &&
-                       (i->tag == tag)))) {
+               if ((i->da == da) && (!i->da->flags.has_tag || TAG_EQ(tag, i->tag))) {
                        return i;
                }
        }
@@ -216,186 +222,28 @@ VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor,
        return NULL;
 }
 
-/** Setup a cursor to iterate over attribute pairs
- *
- * @param cursor Where to initialise the cursor (uses existing structure).
- * @param node to start from.
- */
-VALUE_PAIR *paircursorc(vp_cursor_t *cursor, VALUE_PAIR const * const *node)
-{
-       memset(cursor, 0, sizeof(*cursor));
-
-       if (!node || !cursor) {
-               return NULL;
-       }
-
-       memcpy(&cursor->first, &node, sizeof(cursor->first));
-       cursor->current = *cursor->first;
-
-       if (cursor->current) {
-               VERIFY_VP(cursor->current);
-               cursor->next = cursor->current->next;
-       }
-
-       return cursor->current;
-}
-
-VALUE_PAIR *pairfirst(vp_cursor_t *cursor)
-{
-       cursor->current = *cursor->first;
-
-       if (cursor->current) {
-               VERIFY_VP(cursor->current);
-               cursor->next = cursor->current->next;
-               if (cursor->next) VERIFY_VP(cursor->next);
-               cursor->found = NULL;
-       }
-
-       return cursor->current;
-}
-
-/** Iterate over attributes of a given type in the pairlist
- *
- *
- */
-VALUE_PAIR *pairfindnext(vp_cursor_t *cursor, unsigned int attr, unsigned int vendor, int8_t tag)
-{
-       VALUE_PAIR *i;
-
-       i = pairfind(!cursor->found ? cursor->current : cursor->found->next, attr, vendor, tag);
-       if (!i) {
-               cursor->next = NULL;
-               cursor->current = NULL;
-
-               return NULL;
-       }
-
-       cursor->next = i->next;
-       cursor->current = i;
-       cursor->found = i;
-
-       return i;
-}
-
-/** Retrieve the next VALUE_PAIR
- *
- *
- */
-VALUE_PAIR *pairnext(vp_cursor_t *cursor)
-{
-       cursor->current = cursor->next;
-       if (cursor->current) {
-               VERIFY_VP(cursor->current);
-
-               /*
-                *      Set this now in case 'current' gets freed before
-                *      pairnext is called again.
-                */
-               cursor->next = cursor->current->next;
-
-               /*
-                *      Next call to pairfindnext will start from the current
-                *      position in the list, not the last found instance.
-                */
-               cursor->found = NULL;
-       }
-
-       return cursor->current;
-}
-
-VALUE_PAIR *paircurrent(vp_cursor_t *cursor)
-{
-       if (cursor->current) {
-               VERIFY_VP(cursor->current);
-       }
-
-       return cursor->current;
-}
 
-/** Insert a VP
+/** Find the pair with the matching attribute
  *
- * @todo don't use with pairdelete
+ * @todo should take DAs and do a pointer comparison.
  */
-void pairinsert(vp_cursor_t *cursor, VALUE_PAIR *add)
+VALUE_PAIR *pairfind(VALUE_PAIR *vp, unsigned int attr, unsigned int vendor, int8_t tag)
 {
-       VALUE_PAIR *i;
-
-       if (!add) {
-               return;
-       }
-
-       VERIFY_VP(add);
-
-       /*
-        *      Cursor was initialised with a pointer to a NULL value_pair
-        */
-       if (!*cursor->first) {
-               *cursor->first = add;
-               cursor->current = add;
-               cursor->next = cursor->current->next;
-
-               return;
-       }
-
-       /*
-        *      We don't yet know where the last VALUE_PAIR is
-        *
-        *      Assume current is closer to the end of the list and use that if available.
-        */
-       if (!cursor->last) {
-               cursor->last = cursor->current ? cursor->current : *cursor->first;
-       }
+       vp_cursor_t     cursor;
+       VALUE_PAIR      *i;
 
-       VERIFY_VP(cursor->last);
+       VERIFY_LIST(vp);
 
-       /*
-        *      Something outside of the cursor added another VALUE_PAIR
-        */
-       if (cursor->last->next) {
-               for (i = cursor->last; i; i = i->next) {
-                       VERIFY_VP(i);
-                       cursor->last = i;
+       for (i = fr_cursor_init(&cursor, &vp);
+            i;
+            i = fr_cursor_next(&cursor)) {
+               if ((i->da->attr == attr) && (i->da->vendor == vendor) && \
+                   (!i->da->flags.has_tag || TAG_EQ(tag, i->tag))) {
+                       return i;
                }
        }
 
-       /*
-        *      Either current was never set, or something iterated to the end of the
-        *      attribute list.
-        */
-       if (!cursor->current) {
-               cursor->current = add;
-       }
-
-       cursor->last->next = add;
-}
-
-/** Remove the current pair
- *
- * @todo this is really inefficient and should be fixed...
- *
- * @param cursor to remove the current pair from.
- * @return NULL on error, else the VALUE_PAIR we just removed.
- */
-VALUE_PAIR *pairremove(vp_cursor_t *cursor)
-{
-       VALUE_PAIR *vp, **last;
-
-       vp = paircurrent(cursor);
-       if (!vp) {
-           return NULL;
-       }
-
-       last = cursor->first;
-       while (*last != vp) {
-           last = &(*last)->next;
-       }
-
-       pairnext(cursor);   /* Advance the cursor past the one were about to delete */
-
-       *last = vp->next;
-       vp->next = NULL;
-
-       return vp;
+       return NULL;
 }
 
 /** Delete matching pairs
@@ -405,7 +253,7 @@ VALUE_PAIR *pairremove(vp_cursor_t *cursor)
  * @param[in,out] first VP in list.
  * @param[in] attr to match.
  * @param[in] vendor to match.
- * @param[in] tag to match. TAG_ANY matches any tag, TAG_UNUSED matches tagless VPs.
+ * @param[in] tag to match. TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
  *
  * @todo should take DAs and do a point comparison.
  */
@@ -419,8 +267,7 @@ void pairdelete(VALUE_PAIR **first, unsigned int attr, unsigned int vendor,
                VERIFY_VP(i);
                next = i->next;
                if ((i->da->attr == attr) && (i->da->vendor == vendor) &&
-                   ((tag == TAG_ANY) ||
-                    (i->da->flags.has_tag && (i->tag == tag)))) {
+                   (!i->da->flags.has_tag || TAG_EQ(tag, i->tag))) {
                        *last = next;
                        talloc_free(i);
                } else {
@@ -488,9 +335,7 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
                 *      Found the first attribute, replace it,
                 *      and return.
                 */
-               if ((i->da == replace->da) &&
-                   (!i->da->flags.has_tag || (i->tag == replace->tag))
-               ) {
+               if ((i->da == replace->da) && (!i->da->flags.has_tag || TAG_EQ(replace->tag, i->tag))) {
                        *prev = replace;
 
                        /*
@@ -514,6 +359,31 @@ void pairreplace(VALUE_PAIR **first, VALUE_PAIR *replace)
        *prev = replace;
 }
 
+int8_t attrtagcmp(void const *a, void const *b)
+{
+       VALUE_PAIR const *my_a = a;
+       VALUE_PAIR const *my_b = b;
+
+       VERIFY_VP(my_a);
+       VERIFY_VP(my_b);
+
+       uint8_t cmp;
+
+       cmp = fr_pointer_cmp(my_a->da, my_b->da);
+
+       if (cmp != 0) return cmp;
+
+       if (my_a->tag < my_b->tag) {
+               return -1;
+       }
+
+       if (my_a->tag > my_b->tag) {
+               return 1;
+       }
+
+       return 0;
+}
+
 static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **back)
 {
        VALUE_PAIR *fast;
@@ -523,11 +393,11 @@ static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **
         *      Stopping condition - no more elements left to split
         */
        if (!source || !source->next) {
-               *front = source;
-               *back = NULL;
+               *front = source;
+               *back = NULL;
 
-               return;
-       }
+               return;
+       }
 
        /*
         *      Fast advances twice as fast as slow, so when it gets to the end,
@@ -549,22 +419,22 @@ static void pairsort_split(VALUE_PAIR *source, VALUE_PAIR **front, VALUE_PAIR **
        slow->next = NULL;
 }
 
-static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, bool with_tag)
+static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, fr_cmp_t cmp)
 {
        VALUE_PAIR *result = NULL;
 
        if (!a) return b;
        if (!b) return a;
 
-       /*
-        *      Compare the DICT_ATTRs and tags
-        */
-       if ((with_tag && (a->tag < b->tag)) || (a->da <= b->da)) {
+       /*
+        *      Compare the DICT_ATTRs and tags
+        */
+       if (cmp(a, b) <= 0) {
                result = a;
-               result->next = pairsort_merge(a->next, b, with_tag);
-       } else {
+               result->next = pairsort_merge(a->next, b, cmp);
+       } else {
                result = b;
-               result->next = pairsort_merge(a, b->next, with_tag);
+               result->next = pairsort_merge(a, b->next, cmp);
        }
 
        return result;
@@ -573,9 +443,9 @@ static VALUE_PAIR *pairsort_merge(VALUE_PAIR *a, VALUE_PAIR *b, bool with_tag)
 /** Sort a linked list of VALUE_PAIRs using merge sort
  *
  * @param[in,out] vps List of VALUE_PAIRs to sort.
- * @param[in] with_tag sort by tag then by DICT_ATTR
+ * @param[in] cmp to sort with
  */
-void pairsort(VALUE_PAIR **vps, bool with_tag)
+void pairsort(VALUE_PAIR **vps, fr_cmp_t cmp)
 {
        VALUE_PAIR *head = *vps;
        VALUE_PAIR *a;
@@ -589,21 +459,73 @@ void pairsort(VALUE_PAIR **vps, bool with_tag)
        }
 
        pairsort_split(head, &a, &b);   /* Split into sublists */
-       pairsort(&a, with_tag);         /* Traverse left */
-       pairsort(&b, with_tag);         /* Traverse right */
+       pairsort(&a, cmp);              /* Traverse left */
+       pairsort(&b, cmp);              /* Traverse right */
+
+       /*
+        *      merge the two sorted lists together
+        */
+       *vps = pairsort_merge(a, b, cmp);
+}
 
-       /*
-        *      merge the two sorted lists together
-        */
-       *vps = pairsort_merge(a, b, with_tag);
+/** Write an error to the library errorbuff detailing the mismatch
+ *
+ * Retrieve output with fr_strerror();
+ *
+ * @todo add thread specific talloc contexts.
+ *
+ * @param ctx a hack until we have thread specific talloc contexts.
+ * @param failed pair of attributes which didn't match.
+ */
+void pairvalidate_debug(TALLOC_CTX *ctx, VALUE_PAIR const *failed[2])
+{
+       VALUE_PAIR const *filter = failed[0];
+       VALUE_PAIR const *list = failed[1];
+
+       char *value, *pair;
+
+       (void) fr_strerror();   /* Clear any existing messages */
+
+       if (!fr_assert(!(!filter && !list))) return;
+
+       if (!list) {
+               if (!filter) return;
+               fr_strerror_printf("Attribute \"%s\" not found in list", filter->da->name);
+               return;
+       }
+
+       if (!filter || (filter->da != list->da)) {
+               fr_strerror_printf("Attribute \"%s\" not found in filter", list->da->name);
+               return;
+       }
+
+       if (TAG_EQ(filter->tag, list->tag)) {
+               fr_strerror_printf("Attribute \"%s\" tag \"%i\" didn't match filter tag \"%i\"",
+                                  list->da->name, list->tag, filter->tag);
+               return;
+       }
+
+       pair = vp_aprint(ctx, filter);
+       value = vp_aprint_value(ctx, list);
+
+       fr_strerror_printf("Attribute value \"%s\" didn't match filter \"%s\"", value, pair);
+
+       talloc_free(pair);
+       talloc_free(value);
+
+       return;
 }
 
 /** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
  *
+ * @note will sort both filter and list in place.
+ *
+ * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match.
+ *       May be NULL.
  * @param filter attributes to check list against.
  * @param list attributes, probably a request or reply
  */
-bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
+bool pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
 {
        vp_cursor_t filter_cursor;
        vp_cursor_t list_cursor;
@@ -613,9 +535,6 @@ bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
        if (!filter && !list) {
                return true;
        }
-       if (!filter || !list) {
-               return false;
-       }
 
        /*
         *      This allows us to verify the sets of validate and reply are equal
@@ -623,21 +542,21 @@ bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
         *
         *      @todo this should be removed one we have sets and lists
         */
-       pairsort(&filter, true);
-       pairsort(&list, true);
+       pairsort(&filter, attrtagcmp);
+       pairsort(&list, attrtagcmp);
 
-       match = paircursor(&list_cursor, &list);
-       check = paircursor(&filter_cursor, &filter);
+       check = fr_cursor_init(&filter_cursor, &filter);
+       match = fr_cursor_init(&list_cursor, &list);
 
        while (true) {
+               if (!match && !check) goto mismatch;
+
                /*
                 *      The lists are sorted, so if the first
                 *      attributes aren't of the same type, then we're
                 *      done.
                 */
-               if (!attribute_eq(check, match)) {
-                       return false;
-               }
+               if (!attribute_eq(check, match)) goto mismatch;
 
                /*
                 *      They're of the same type, but don't have the
@@ -646,25 +565,100 @@ bool pairvalidate(VALUE_PAIR *filter, VALUE_PAIR *list)
                 *      Note that the RFCs say that for attributes of
                 *      the same type, order is important.
                 */
-               if (!paircmp(check, match)) {
-                       return false;
-               }
-
-               match = pairnext(&list_cursor);
-               check = pairnext(&filter_cursor);
+               if (!paircmp(check, match)) goto mismatch;
 
-               if (!match && !check) break;
+               check = fr_cursor_next(&filter_cursor);
+               match = fr_cursor_next(&list_cursor);
 
                /*
                 *      One list ended earlier than the others, they
                 *      didn't match.
                 */
-               if (!match || !check) {
-                       return false;
+               if (!match || !check) break;
+       }
+
+       return true;
+
+mismatch:
+       if (failed) {
+               failed[0] = check;
+               failed[1] = match;
+       }
+       return false;
+}
+
+/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check
+ *
+ * @note will sort both filter and list in place.
+ *
+ * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match.
+ *       May be NULL.
+ * @param filter attributes to check list against.
+ * @param list attributes, probably a request or reply
+ */
+bool pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list)
+{
+       vp_cursor_t filter_cursor;
+       vp_cursor_t list_cursor;
+
+       VALUE_PAIR *check, *last_check = NULL, *match = NULL;
+
+       if (!filter && !list) {
+               return true;
+       }
+
+       /*
+        *      This allows us to verify the sets of validate and reply are equal
+        *      i.e. we have a validate rule which matches every reply attribute.
+        *
+        *      @todo this should be removed one we have sets and lists
+        */
+       pairsort(&filter, attrtagcmp);
+       pairsort(&list, attrtagcmp);
+
+       fr_cursor_init(&list_cursor, &list);
+       for (check = fr_cursor_init(&filter_cursor, &filter);
+            check;
+            check = fr_cursor_next(&filter_cursor)) {
+               /*
+                *      Were processing check attributes of a new type.
+                */
+               if (!attribute_eq(last_check, check)) {
+                       /*
+                        *      Record the start of the matching attributes in the pair list
+                        *      For every other operator we require the match to be present
+                        */
+                       match = fr_cursor_next_by_da(&list_cursor, check->da, check->tag);
+                       if (!match) {
+                               if (check->op == T_OP_CMP_FALSE) continue;
+                               goto mismatch;
+                       }
+
+                       fr_cursor_init(&list_cursor, &match);
+                       last_check = check;
+               }
+
+               /*
+                *      Now iterate over all attributes of the same type.
+                */
+               for (match = fr_cursor_first(&list_cursor);
+                    attribute_eq(match, check);
+                    match = fr_cursor_next(&list_cursor)) {
+                       /*
+                        *      This attribute passed the filter
+                        */
+                       if (!paircmp(check, match)) goto mismatch;
                }
        }
 
        return true;
+
+mismatch:
+       if (failed) {
+               failed[0] = check;
+               failed[1] = match;
+       }
+       return false;
 }
 
 /** Copy a single valuepair
@@ -684,10 +678,7 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
        VERIFY_VP(vp);
 
        n = pairalloc(ctx, vp->da);
-       if (!n) {
-               fr_strerror_printf("out of memory");
-               return NULL;
-       }
+       if (!n) return NULL;
 
        memcpy(n, vp, sizeof(*n));
 
@@ -695,7 +686,7 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
         *      Now copy the value
         */
        if (vp->type == VT_XLAT) {
-               n->value.xlat = talloc_strdup(n, n->value.xlat);
+               n->value.xlat = talloc_typed_strdup(n, n->value.xlat);
        }
 
        n->da = dict_attr_copy(vp->da, true);
@@ -706,19 +697,20 @@ VALUE_PAIR *paircopyvp(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
 
        n->next = NULL;
 
-       if ((n->da->type == PW_TYPE_TLV) ||
-           (n->da->type == PW_TYPE_OCTETS)) {
-               if (n->vp_octets != NULL) {
-                       n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length);
-               }
+       switch (vp->da->type) {
+       case PW_TYPE_TLV:
+       case PW_TYPE_OCTETS:
+               n->vp_octets = NULL;    /* else pairmemcpy will free vp's value */
+               pairmemcpy(n, vp->vp_octets, n->length);
+               break;
 
-       } else if (n->da->type == PW_TYPE_STRING) {
-               if (n->vp_strvalue != NULL) {
-                       /*
-                        *      Equivalent to, and faster than strdup.
-                        */
-                       n->vp_strvalue = talloc_memdup(n, vp->vp_strvalue, n->length + 1);
-               }
+       case PW_TYPE_STRING:
+               n->vp_strvalue = NULL;  /* else pairstrnpy will free vp's value */
+               pairstrncpy(n, vp->vp_strvalue, n->length);
+               break;
+
+       default:
+               break;
        }
 
        return n;
@@ -773,17 +765,17 @@ VALUE_PAIR *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR cons
                        return NULL; /* can't do it */
 
                case PW_TYPE_INTEGER:
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                case PW_TYPE_DATE:
                case PW_TYPE_IFID:
-               case PW_TYPE_IPV6ADDR:
-               case PW_TYPE_IPV6PREFIX:
+               case PW_TYPE_IPV6_ADDR:
+               case PW_TYPE_IPV6_PREFIX:
                case PW_TYPE_BYTE:
                case PW_TYPE_SHORT:
                case PW_TYPE_ETHERNET:
                case PW_TYPE_SIGNED:
                case PW_TYPE_INTEGER64:
-               case PW_TYPE_IPV4PREFIX:
+               case PW_TYPE_IPV4_PREFIX:
                        break;
                }
 
@@ -805,32 +797,29 @@ VALUE_PAIR *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR cons
        }
 
        n = pairalloc(ctx, da);
-       if (!n) {
-               return NULL;
-       }
+       if (!n) return NULL;
 
        memcpy(n, vp, sizeof(*n));
        n->da = da;
 
        if (n->type == VT_XLAT) {
-               n->value.xlat = talloc_strdup(n, n->value.xlat);
+               n->value.xlat = talloc_typed_strdup(n, n->value.xlat);
        }
 
-       switch (n->da->type) {
-               case PW_TYPE_TLV:
-               case PW_TYPE_OCTETS:
-                       if (n->vp_octets != NULL) {
-                               n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length);
-                       }
-                       break;
+       if (n->data.ptr) switch (n->da->type) {
+       case PW_TYPE_TLV:
+       case PW_TYPE_OCTETS:
+               n->vp_octets = talloc_memdup(n, vp->vp_octets, n->length);
+               talloc_set_type(n->vp_octets, uint8_t);
+               break;
 
-               case PW_TYPE_STRING:
-                       if (n->vp_strvalue != NULL) {
-                               n->vp_strvalue = talloc_memdup(n, vp->vp_strvalue, n->length + 1);      /* NULL byte */
-                       }
-                       break;
-               default:
-                       return NULL;
+       case PW_TYPE_STRING:
+               n->vp_strvalue = talloc_memdup(n, vp->vp_strvalue, n->length + 1);      /* NULL byte */
+               talloc_set_type(n->vp_strvalue, char);
+               break;
+
+       default:
+               break;
        }
 
        n->next = NULL;
@@ -839,81 +828,99 @@ VALUE_PAIR *paircopyvpdata(TALLOC_CTX *ctx, DICT_ATTR const *da, VALUE_PAIR cons
 }
 
 
-/** Copy matching pairs
+/** Copy a pairlist.
  *
- * Copy pairs of a matching attribute number, vendor number and tag from the
- * the input list to a new list, and returns the head of this list.
+ * Copy all pairs from 'from' regardless of tag, attribute or vendor.
  *
- * @param[in] ctx for talloc
+ * @param[in] ctx for new VALUE_PAIRs to be allocated in.
  * @param[in] from whence to copy VALUE_PAIRs.
- * @param[in] attr to match, if 0 input list will not be filtered by attr.
- * @param[in] vendor to match.
- * @param[in] tag to match, TAG_ANY matches any tag, TAG_UNUSED matches tagless VPs.
  * @return the head of the new VALUE_PAIR list or NULL on error.
  */
-VALUE_PAIR *paircopy2(TALLOC_CTX *ctx, VALUE_PAIR *from,
-                     unsigned int attr, unsigned int vendor, int8_t tag)
+VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
 {
        vp_cursor_t src, dst;
 
        VALUE_PAIR *out = NULL, *vp;
 
-       paircursor(&dst, &out);
-       for (vp = paircursor(&src, &from);
+       fr_cursor_init(&dst, &out);
+       for (vp = fr_cursor_init(&src, &from);
             vp;
-            vp = pairnext(&src)) {
-               VERIFY_VP(vp);
-
-               if ((attr > 0) && ((vp->da->attr != attr) || (vp->da->vendor != vendor))) {
-                       continue;
-               }
-
-               if ((tag != TAG_ANY) && vp->da->flags.has_tag && (vp->tag != tag)) {
-                       continue;
-               }
-
+            vp = fr_cursor_next(&src)) {
+               VERIFY_VP(vp);
                vp = paircopyvp(ctx, vp);
                if (!vp) {
                        pairfree(&out);
                        return NULL;
                }
-               pairinsert(&dst, vp);
+               fr_cursor_insert(&dst, vp); /* paircopy sets next pointer to NULL */
        }
 
        return out;
 }
 
-
-/** Copy a pairlist.
+/** Copy matching pairs
  *
- * Copy all pairs from 'from' regardless of tag, attribute or vendor.
+ * Copy pairs of a matching attribute number, vendor number and tag from the
+ * the input list to a new list, and returns the head of this list.
  *
- * @param[in] ctx for new VALUE_PAIRs to be allocated in.
+ * @param[in] ctx for talloc
  * @param[in] from whence to copy VALUE_PAIRs.
+ * @param[in] attr to match, if 0 input list will not be filtered by attr.
+ * @param[in] vendor to match.
+ * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
  * @return the head of the new VALUE_PAIR list or NULL on error.
  */
-VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
+VALUE_PAIR *paircopy2(TALLOC_CTX *ctx, VALUE_PAIR *from,
+                     unsigned int attr, unsigned int vendor, int8_t tag)
 {
        vp_cursor_t src, dst;
 
        VALUE_PAIR *out = NULL, *vp;
 
-       paircursor(&dst, &out);
-       for (vp = paircursor(&src, &from);
+       fr_cursor_init(&dst, &out);
+       for (vp = fr_cursor_init(&src, &from);
             vp;
-            vp = pairnext(&src)) {
-               VERIFY_VP(vp);
-               vp = paircopyvp(ctx, vp);
-               if (!vp) {
-                       pairfree(&out);
-                       return NULL;
-               }
-               pairinsert(&dst, vp); /* paircopy sets next pointer to NULL */
+            vp = fr_cursor_next(&src)) {
+               VERIFY_VP(vp);
+
+               if ((vp->da->attr != attr) || (vp->da->vendor != vendor)) {
+                       continue;
+               }
+
+               if (vp->da->flags.has_tag && TAG_EQ(tag, vp->tag)) {
+                       continue;
+               }
+
+               vp = paircopyvp(ctx, vp);
+               if (!vp) {
+                       pairfree(&out);
+                       return NULL;
+               }
+               fr_cursor_insert(&dst, vp);
        }
 
        return out;
 }
 
+/** Steal all members of a VALUE_PAIR list
+ *
+ * @param[in] ctx to move VALUE_PAIRs into
+ * @param[in] from VALUE_PAIRs to move into the new context.
+ */
+VALUE_PAIR *pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *from)
+{
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
+
+       for (vp = fr_cursor_init(&cursor, &from);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               (void) talloc_steal(ctx, vp);
+       }
+
+       return from;
+}
+
 /** Move pairs from source list to destination list respecting operator
  *
  * @note This function does some additional magic that's probably not needed
@@ -932,190 +939,144 @@ VALUE_PAIR *paircopy(TALLOC_CTX *ctx, VALUE_PAIR *from)
  */
 void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
 {
-       VALUE_PAIR **tailto, *i, *j, *next;
-       VALUE_PAIR *tailfrom = NULL;
-       VALUE_PAIR *found;
-       int has_password = 0;
+       VALUE_PAIR *i, *found;
+       VALUE_PAIR *head_new, **tail_new;
+       VALUE_PAIR **tail_from;
 
        if (!to || !from || !*from) return;
 
        /*
-        *      First, see if there are any passwords here, and
-        *      point "tailto" to the end of the "to" list.
+        *      We're editing the "to" list while we're adding new
+        *      attributes to it.  We don't want the new attributes to
+        *      be edited, so we create an intermediate list to hold
+        *      them during the editing process.
         */
-       tailto = to;
-       if (*to) for (i = *to; i; i = i->next) {
-               VERIFY_VP(i);
-               if (!i->da->vendor &&
-                   (i->da->attr == PW_USER_PASSWORD ||
-                    i->da->attr == PW_CRYPT_PASSWORD))
-                       has_password = 1;
-               tailto = &i->next;
-       }
+       head_new = NULL;
+       tail_new = &head_new;
 
        /*
-        *      Loop over the "from" list.
+        *      We're looping over the "from" list, moving some
+        *      attributes out, but leaving others in place.
         */
-       for (i = *from; i; i = next) {
+       tail_from = from;
+       while ((i = *tail_from) != NULL) {
                VERIFY_VP(i);
-               next = i->next;
 
                /*
-                *      If there was a password in the "to" list,
-                *      do not move any other password from the
-                *      "from" to the "to" list.
+                *      We never move Fall-Through.
                 */
-               if (has_password && !i->da->vendor &&
-                   (i->da->attr == PW_USER_PASSWORD ||
-                    i->da->attr == PW_CRYPT_PASSWORD)) {
-                       tailfrom = i;
+               if (!i->da->vendor && i->da->attr == PW_FALL_THROUGH) {
+                       tail_from = &(i->next);
                        continue;
                }
 
+               /*
+                *      Unlike previous versions, we treat all other
+                *      attributes as normal.  i.e. there's no special
+                *      treatment for passwords or Hint.
+                */
+
                switch (i->op) {
                        /*
-                        *      These are COMPARISON attributes
-                        *      from a check list, and are not
-                        *      supposed to be copied!
+                        *      Anything else are operators which
+                        *      shouldn't occur.  We ignore them, and
+                        *      leave them in place.
                         */
-                       case T_OP_NE:
-                       case T_OP_GE:
-                       case T_OP_GT:
-                       case T_OP_LE:
-                       case T_OP_LT:
-                       case T_OP_CMP_TRUE:
-                       case T_OP_CMP_FALSE:
-                       case T_OP_CMP_EQ:
-                       case T_OP_REG_EQ:
-                       case T_OP_REG_NE:
-                               tailfrom = i;
-                               continue;
-
                        default:
-                               break;
-               }
-
-               /*
-                *      If the attribute is already present in "to",
-                *      do not move it from "from" to "to". We make
-                *      an exception for "Hint" which can appear multiple
-                *      times, and we never move "Fall-Through".
-                */
-               if (i->da->attr == PW_FALL_THROUGH ||
-                   (i->da->attr != PW_HINT && i->da->attr != PW_FRAMED_ROUTE)) {
-
+                               tail_from = &(i->next);
+                               continue;
 
-                       found = pairfind(*to, i->da->attr, i->da->vendor,
-                                        TAG_ANY);
+                       /*
+                        *      Add it to the "to" list, but only if
+                        *      it doesn't already exist.
+                        */
+                       case T_OP_EQ:
+                               found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
+                               if (!found) goto do_add;
 
-                       switch (i->op) {
+                               tail_from = &(i->next);
+                               continue;
 
                        /*
-                        *      If matching attributes are found,
-                        *      delete them.
+                        *      Add it to the "to" list, and delete any attribute
+                        *      of the same vendor/attr which already exists.
                         */
-                       case T_OP_SUB:          /* -= */
-                               if (found) {
-                                       if (!i->vp_strvalue[0] ||
-                                           (strcmp(found->vp_strvalue,
-                                                   i->vp_strvalue) == 0)){
-                                               pairdelete(to,
-                                                          found->da->attr,
-                                                          found->da->vendor,
-                                                          TAG_ANY);
-
-                                               /*
-                                                *      'tailto' may have been
-                                                *      deleted...
-                                                */
-                                               tailto = to;
-                                               for(j = *to; j; j = j->next) {
-                                                       tailto = &j->next;
-                                               }
-                                       }
-                               }
-                               tailfrom = i;
-                               continue;
+                       case T_OP_SET:
+                               found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
+                               if (!found) goto do_add;
 
-                       case T_OP_EQ:           /* = */
                                /*
-                                *  FIXME: Tunnel attributes with
-                                *  different tags are different
-                                *  attributes.
+                                *      Do NOT call pairdelete() here,
+                                *      due to issues with re-writing
+                                *      "request->username".
+                                *
+                                *      Everybody calls pairmove, and
+                                *      expects it to work.  We can't
+                                *      update request->username here,
+                                *      so instead we over-write the
+                                *      vp that it's pointing to.
                                 */
-                               if (found) {
-                                       tailfrom = i;
-                                       continue; /* with the loop */
+                               switch (found->da->type) {
+                                       VALUE_PAIR *j;
+
+                                       default:
+                                               j = found->next;
+                                               memcpy(found, i, sizeof(*found));
+                                               found->next = j;
+                                               break;
+
+                                       case PW_TYPE_TLV:
+                                               pairmemsteal(found, i->vp_tlv);
+                                               i->vp_tlv = NULL;
+                                               break;
+
+                                       case PW_TYPE_OCTETS:
+                                               pairmemsteal(found, i->vp_octets);
+                                               i->vp_octets = NULL;
+                                               break;
+
+                                       case PW_TYPE_STRING:
+                                               pairstrsteal(found, i->vp_strvalue);
+                                               i->vp_strvalue = NULL;
+                                               found->tag = i->tag;
+                                               break;
                                }
-                               break;
-
-                         /*
-                          *  If a similar attribute is found,
-                          *  replace it with the new one.  Otherwise,
-                          *  add the new one to the list.
-                          */
-                       case T_OP_SET:          /* := */
-                               if (found) {
-                                       VALUE_PAIR *mynext = found->next;
-
-                                       /*
-                                        *      Do NOT call pairdelete()
-                                        *      here, due to issues with
-                                        *      re-writing "request->username".
-                                        *
-                                        *      Everybody calls pairmove,
-                                        *      and expects it to work.
-                                        *      We can't update request->username
-                                        *      here, so instead we over-write
-                                        *      the vp that it's pointing to.
-                                        */
-                                       memcpy(found, i, sizeof(*found));
-                                       found->next = mynext;
 
-                                       pairdelete(&found->next,
-                                                  found->da->attr,
-                                                  found->da->vendor, TAG_ANY);
+                               /*
+                                *      Delete *all* of the attributes
+                                *      of the same number.
+                                */
+                               pairdelete(&found->next,
+                                          found->da->attr,
+                                          found->da->vendor, TAG_ANY);
 
-                                       /*
-                                        *      'tailto' may have been
-                                        *      deleted...
-                                        */
-                                       for(j = found; j; j = j->next) {
-                                               tailto = &j->next;
-                                       }
-                                       continue;
-                               }
-                               break;
+                               /*
+                                *      Remove this attribute from the
+                                *      "from" list.
+                                */
+                               *tail_from = i->next;
+                               i->next = NULL;
+                               pairfree(&i);
+                               continue;
 
-                         /*
-                          *  Add the new element to the list, even
-                          *  if similar ones already exist.
-                          */
-                       default:
-                       case T_OP_ADD: /* += */
-                               break;
-                       }
+                       /*
+                        *      Move it from the old list and add it
+                        *      to the new list.
+                        */
+                       case T_OP_ADD:
+               do_add:
+                               *tail_from = i->next;
+                               i->next = NULL;
+                               *tail_new = talloc_steal(ctx, i);
+                               tail_new = &(i->next);
+                               continue;
                }
-               if (tailfrom)
-                       tailfrom->next = next;
-               else
-                       *from = next;
+       } /* loop over the "from" list. */
 
-               /*
-                *      If ALL of the 'to' attributes have been deleted,
-                *      then ensure that the 'tail' is updated to point
-                *      to the head.
-                */
-               if (!*to) {
-                       tailto = to;
-               }
-               *tailto = i;
-               if (i) {
-                       tailto = &i->next;
-                       i->next = NULL;
-                       (void) talloc_steal(ctx, i);
-               }
-       }
+       /*
+        *      Take the "new" list, and append it to the "to" list.
+        */
+       pairadd(to, head_new);
 }
 
 /** Move matching pairs between VALUE_PAIR lists
@@ -1124,7 +1085,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
  * the input list to the output list.
  *
  * @note pairfree should be called on the head of the old list to free unmoved
-        attributes (if they're no longer needed).
+        attributes (if they're no longer needed).
  *
  * @param[in] ctx for talloc
  * @param[in,out] to destination list.
@@ -1132,7 +1093,7 @@ void pairmove(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from)
  * @param[in] attr to match, if PW_VENDOR_SPECIFIC and vendor 0, only VSAs will
  *           be copied.  If 0 and 0, all attributes will match
  * @param[in] vendor to match.
- * @param[in] tag to match, TAG_ANY matches any tag, TAG_UNUSED matches tagless VPs.
+ * @param[in] tag to match, TAG_ANY matches any tag, TAG_NONE matches tagless VPs.
  */
 void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned int attr, unsigned int vendor, int8_t tag)
 {
@@ -1176,8 +1137,7 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
                VERIFY_VP(i);
                next = i->next;
 
-               if ((tag != TAG_ANY) && i->da->flags.has_tag &&
-                   (i->tag != tag)) {
+               if (i->da->flags.has_tag && TAG_EQ(tag, i->tag)) {
                        continue;
                }
 
@@ -1235,16 +1195,21 @@ void pairfilter(TALLOC_CTX *ctx, VALUE_PAIR **to, VALUE_PAIR **from, unsigned in
 
 static char const *hextab = "0123456789abcdef";
 
-bool pairparsevalue(VALUE_PAIR *vp, char const *value)
+/** Convert string value to native attribute value
+ *
+ * @param vp to assign value to.
+ * @param value string to convert. Binary safe for variable length values if len is provided.
+ * @param inlen may be 0 in which case strlen(len) is used to determine length, else inline
+ *       should be the length of the string or sub string to parse.
+ * @return true on success, else false.
+ */
+int pairparsevalue(VALUE_PAIR *vp, char const *value, size_t inlen)
 {
-       char            *p;
-       char const      *cp, *cs;
-       int             x;
-       uint64_t        y;
-       size_t          length;
        DICT_VALUE      *dval;
+       size_t          len;
+       char            buffer[256];
 
-       if (!value) return false;
+       if (!value) return -1;
        VERIFY_VP(vp);
 
        /*
@@ -1255,94 +1220,268 @@ bool pairparsevalue(VALUE_PAIR *vp, char const *value)
                goto finish;
        }
 
+       len = (inlen == 0) ? strlen(value) : inlen;
+
+       /*
+        *      It's a variable length type so we just alloc a new buffer
+        *      of size len and copy.
+        */
        switch(vp->da->type) {
        case PW_TYPE_STRING:
+       {
+               size_t          vp_len;
+               char const      *cp;
+               char            *p;
+               int             x;
+
                /*
                 *      Do escaping here
                 */
-               p = talloc_strdup(vp, value);
-               vp->vp_strvalue = p;
-               cp = value;
-               length = 0;
+               vp->vp_strvalue = p = talloc_memdup(vp, value, len + 1);
+               p[len] = '\0';
+               talloc_set_type(p, char);
 
+               cp = value;
+               vp_len = 0;
                while (*cp) {
                        char c = *cp++;
 
-                       if (c == '\\') {
-                               switch (*cp) {
-                               case 'r':
-                                       c = '\r';
-                                       cp++;
-                                       break;
-                               case 'n':
-                                       c = '\n';
-                                       cp++;
-                                       break;
-                               case 't':
-                                       c = '\t';
-                                       cp++;
-                                       break;
-                               case '"':
-                                       c = '"';
-                                       cp++;
-                                       break;
-                               case '\'':
-                                       c = '\'';
-                                       cp++;
-                                       break;
-                               case '\\':
-                                       c = '\\';
-                                       cp++;
-                                       break;
-                               case '`':
-                                       c = '`';
-                                       cp++;
-                                       break;
-                               case '\0':
-                                       c = '\\'; /* no cp++ */
-                                       break;
-                               default:
-                                       if ((cp[0] >= '0') &&
-                                           (cp[0] <= '9') &&
-                                           (cp[1] >= '0') &&
-                                           (cp[1] <= '9') &&
-                                           (cp[2] >= '0') &&
-                                           (cp[2] <= '9') &&
-                                           (sscanf(cp, "%3o", &x) == 1)) {
-                                               c = x;
-                                               cp += 3;
-                                       } /* else just do '\\' */
-                               }
+                       if (c == '\\') switch (*cp) {
+                       case 'r':
+                               c = '\r';
+                               cp++;
+                               break;
+                       case 'n':
+                               c = '\n';
+                               cp++;
+                               break;
+                       case 't':
+                               c = '\t';
+                               cp++;
+                               break;
+                       case '"':
+                               c = '"';
+                               cp++;
+                               break;
+                       case '\'':
+                               c = '\'';
+                               cp++;
+                               break;
+                       case '\\':
+                               c = '\\';
+                               cp++;
+                               break;
+                       case '`':
+                               c = '`';
+                               cp++;
+                               break;
+                       case '\0':
+                               c = '\\'; /* no cp++ */
+                               break;
+                       default:
+                               if ((cp[0] >= '0') &&
+                                   (cp[0] <= '9') &&
+                                   (cp[1] >= '0') &&
+                                   (cp[1] <= '9') &&
+                                   (cp[2] >= '0') &&
+                                   (cp[2] <= '9') &&
+                                   (sscanf(cp, "%3o", &x) == 1)) {
+                                       c = x;
+                                       cp += 3;
+
+                               } else if (cp[0]) {
+                                       /*
+                                        *      \p --> p
+                                        */
+                                       c = *cp++;
+                               } /* else at EOL \ --> \ */
                        }
                        *p++ = c;
-                       length++;
+                       vp_len++;
                }
                *p = '\0';
-               vp->length = length;
-               break;
+               vp->length = vp_len;
+       }
+               goto finish;
+
+       /* raw octets: 0x01020304... */
+       case PW_TYPE_VSA:
+               if (strcmp(value, "ANY") == 0) {
+                       vp->length = 0;
+                       goto finish;
+               } /* else it's hex */
+
+       case PW_TYPE_OCTETS:
+       {
+               uint8_t *p;
 
-       case PW_TYPE_IPADDR:
                /*
-                *      FIXME: complain if hostname
-                *      cannot be resolved, or resolve later!
+                *      No 0x prefix, just copy verbatim.
                 */
-               p = NULL;
-               cs = value;
+               if ((len < 2) || (strncasecmp(value, "0x", 2) != 0)) {
+                       pairmemcpy(vp, (uint8_t const *) value, len);
+                       goto finish;
+               }
 
-               {
-                       fr_ipaddr_t ipaddr;
 
-                       if (ip_hton(cs, AF_INET, &ipaddr) < 0) {
-                               fr_strerror_printf("Failed to find IP address for %s", cs);
-                               return false;
-                       }
+#ifdef WITH_ASCEND_BINARY
+       do_octets:
+#endif
+               len -= 2;
 
-                       vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
+               /*
+                *      Invalid.
+                */
+               if ((len & 0x01) != 0) {
+                       fr_strerror_printf("Length of Hex String is not even, got %zu bytes", vp->length);
+                       return -1;
                }
-               vp->length = 4;
+
+               vp->length = len >> 1;
+               p = talloc_array(vp, uint8_t, vp->length);
+               if (fr_hex2bin(p, value + 2, len) != vp->length) {
+                       talloc_free(p);
+                       fr_strerror_printf("Invalid hex data");
+                       return -1;
+               }
+
+               vp->vp_octets = p;
+       }
+               goto finish;
+
+       case PW_TYPE_ABINARY:
+#ifdef WITH_ASCEND_BINARY
+               if ((len > 1) && (strncasecmp(value, "0x", 2) == 0)) goto do_octets;
+
+               if (ascend_parse_filter(vp, value, len) < 0 ) {
+                       /* Allow ascend_parse_filter's strerror to bubble up */
+                       return -1;
+               }
+               goto finish;
+#else
+               /*
+                *      If Ascend binary is NOT defined,
+                *      then fall through to raw octets, so that
+                *      the user can at least make them by hand...
+                */
+               goto do_octets;
+#endif
+
+       /* don't use this! */
+       case PW_TYPE_TLV:
+       {
+               uint8_t *p;
+
+               if ((len < 2) || (len & 0x01) || (strncasecmp(value, "0x", 2) != 0)) {
+                       fr_strerror_printf("Invalid TLV specification");
+                       return -1;
+               }
+               len -= 2;
+
+               vp->length = len >> 1;
+               p = talloc_array(vp, uint8_t, vp->length);
+               if (!p) {
+                       fr_strerror_printf("No memory");
+                       return -1;
+               }
+               if (fr_hex2bin(p, value + 2, len) != vp->length) {
+                       fr_strerror_printf("Invalid hex data in TLV");
+                       return -1;
+               }
+
+               vp->vp_tlv = p;
+       }
+               goto finish;
+
+       case PW_TYPE_IPV4_ADDR:
+       {
+               fr_ipaddr_t addr;
+
+               if (fr_pton4(&addr, value, inlen, false, false) < 0) return -1;
+
+               /*
+                *      We allow v4 addresses to have a /32 suffix as some databases (PostgreSQL)
+                *      print them this way.
+                */
+               if (addr.prefix != 32) {
+                       fr_strerror_printf("Invalid IPv4 mask length \"/%i\".  Only \"/32\" permitted "
+                                          "for non-prefix types", addr.prefix);
+                       return -1;
+               }
+
+               vp->vp_ipaddr = addr.ipaddr.ip4addr.s_addr;
+               vp->length = sizeof(vp->vp_ipaddr);
+       }
+               goto finish;
+
+       case PW_TYPE_IPV4_PREFIX:
+       {
+               fr_ipaddr_t addr;
+
+               if (fr_pton4(&addr, value, inlen, false, false) < 0) return -1;
+
+               vp->vp_ipv4prefix[1] = addr.prefix;
+               memcpy(vp->vp_ipv4prefix + 2, &addr.ipaddr.ip4addr.s_addr, sizeof(vp->vp_ipv4prefix) - 2);
+               vp->length = sizeof(vp->vp_ipv4prefix);
+       }
+               goto finish;
+
+       case PW_TYPE_IPV6_ADDR:
+       {
+               fr_ipaddr_t addr;
+
+               if (fr_pton6(&addr, value, inlen, false, false) < 0) return -1;
+
+               /*
+                *      We allow v6 addresses to have a /128 suffix as some databases (PostgreSQL)
+                *      print them this way.
+                */
+               if (addr.prefix != 128) {
+                       fr_strerror_printf("Invalid IPv6 mask length \"/%i\".  Only \"/128\" permitted "
+                                          "for non-prefix types", addr.prefix);
+                       return -1;
+               }
+
+               memcpy(&vp->vp_ipv6addr, &addr.ipaddr.ip6addr.s6_addr, sizeof(vp->vp_ipv6addr));
+               vp->length = sizeof(vp->vp_ipv6addr);
+       }
+               goto finish;
+
+       case PW_TYPE_IPV6_PREFIX:
+       {
+               fr_ipaddr_t addr;
+
+               if (fr_pton6(&addr, value, inlen, false, false) < 0) return -1;
+
+               vp->vp_ipv6prefix[1] = addr.prefix;
+               memcpy(vp->vp_ipv6prefix + 2, &addr.ipaddr.ip6addr.s6_addr, sizeof(vp->vp_ipv6prefix) - 2);
+               vp->length = sizeof(vp->vp_ipv6prefix);
+       }
+               goto finish;
+
+       default:
                break;
+       }
+
+       /*
+        *      It's a fixed size type, copy to a temporary buffer and
+        *      \0 terminate if insize >= 0.
+        */
+       if (inlen > 0) {
+               if (len >= sizeof(buffer)) {
+                       fr_strerror_printf("Temporary buffer too small");
+                       return -1;
+               }
+
+               memcpy(buffer, value, inlen);
+               buffer[inlen] = '\0';
+               value = buffer;
+       }
 
+       switch(vp->da->type) {
        case PW_TYPE_BYTE:
+       {
+               char *p;
                vp->length = 1;
 
                /*
@@ -1352,14 +1491,18 @@ bool pairparsevalue(VALUE_PAIR *vp, char const *value)
                if (!*p) {
                        if (vp->vp_integer > 255) {
                                fr_strerror_printf("Byte value \"%s\" is larger than 255", value);
-                               return false;
+                               return -1;
                        }
                        break;
                }
-               if (fr_whitespace_check(p)) break;
+               if (is_whitespace(p)) break;
+       }
                goto check_for_value;
 
        case PW_TYPE_SHORT:
+       {
+               char *p;
+
                /*
                 *      Note that ALL integers are unsigned!
                 */
@@ -1368,21 +1511,25 @@ bool pairparsevalue(VALUE_PAIR *vp, char const *value)
                if (!*p) {
                        if (vp->vp_integer > 65535) {
                                fr_strerror_printf("Byte value \"%s\" is larger than 65535", value);
-                               return false;
+                               return -1;
                        }
                        break;
                }
-               if (fr_whitespace_check(p)) break;
+               if (is_whitespace(p)) break;
+       }
                goto check_for_value;
 
        case PW_TYPE_INTEGER:
+       {
+               char *p;
+
                /*
                 *      Note that ALL integers are unsigned!
                 */
                vp->vp_integer = fr_strtoul(value, &p);
                vp->length = 4;
                if (!*p) break;
-               if (fr_whitespace_check(p)) break;
+               if (is_whitespace(p)) break;
 
        check_for_value:
                /*
@@ -1391,255 +1538,101 @@ bool pairparsevalue(VALUE_PAIR *vp, char const *value)
                 */
                if ((dval = dict_valbyname(vp->da->attr, vp->da->vendor, value)) == NULL) {
                        fr_strerror_printf("Unknown value '%s' for attribute '%s'", value, vp->da->name);
-                       return false;
+                       return -1;
                }
                vp->vp_integer = dval->value;
+       }
                break;
 
        case PW_TYPE_INTEGER64:
+       {
+               uint64_t y;
+
                /*
                 *      Note that ALL integers are unsigned!
                 */
                if (sscanf(value, "%" PRIu64, &y) != 1) {
                        fr_strerror_printf("Invalid value '%s' for attribute '%s'",
                                           value, vp->da->name);
-                       return false;
+                       return -1;
                }
                vp->vp_integer64 = y;
                vp->length = 8;
-               length = strspn(value, "0123456789");
-               if (fr_whitespace_check(value + length)) break;
+       }
                break;
 
        case PW_TYPE_DATE:
-               {
-                       /*
-                        *      time_t may be 64 bits, whule vp_date
-                        *      MUST be 32-bits.  We need an
-                        *      intermediary variable to handle
-                        *      the conversions.
-                        */
-                       time_t date;
-
-                       if (fr_get_time(value, &date) < 0) {
-                               fr_strerror_printf("failed to parse time string "
-                                          "\"%s\"", value);
-                               return false;
-                       }
+       {
+               /*
+                *      time_t may be 64 bits, whule vp_date
+                *      MUST be 32-bits.  We need an
+                *      intermediary variable to handle
+                *      the conversions.
+                */
+               time_t date;
 
-                       vp->vp_date = date;
+               if (fr_get_time(value, &date) < 0) {
+                       fr_strerror_printf("failed to parse time string "
+                                  "\"%s\"", value);
+                       return -1;
                }
-               vp->length = 4;
-               break;
 
-       case PW_TYPE_ABINARY:
-#ifdef WITH_ASCEND_BINARY
-               if (strncasecmp(value, "0x", 2) == 0) {
-                       goto do_octets;
-               }
+               vp->vp_date = date;
+               vp->length = 4;
+       }
 
-               if (ascend_parse_filter(vp) < 0 ) {
-                       char buffer[256];
+               break;
 
-                       snprintf(buffer, sizeof(buffer), "failed to parse Ascend binary attribute '%s'", fr_strerror());
-                       fr_strerror_printf("%s", buffer);
-                       return false;
+       case PW_TYPE_IFID:
+               if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) {
+                       fr_strerror_printf("Failed to parse interface-id string \"%s\"", value);
+                       return -1;
                }
+               vp->length = 8;
                break;
 
+       case PW_TYPE_ETHERNET:
+       {
+               char const *c1, *c2, *cp;
+               size_t vp_len = 0;
+
                /*
-                *      If Ascend binary is NOT defined,
-                *      then fall through to raw octets, so that
-                *      the user can at least make them by hand...
+                *      Convert things which are obviously integers to Ethernet addresses
+                *
+                *      We assume the number is the bigendian representation of the
+                *      ethernet address.
                 */
-#endif
-       /* raw octets: 0x01020304... */
-       case PW_TYPE_VSA:
-               if (strcmp(value, "ANY") == 0) {
-                       vp->length = 0;
-                       break;
-               } /* else it's hex */
-
-       case PW_TYPE_OCTETS:
-               if (strncasecmp(value, "0x", 2) == 0) {
-                       size_t size;
-                       uint8_t *us;
+               if (is_integer(value)) {
+                       uint64_t integer = htonll(atoll(value));
 
-#ifdef WITH_ASCEND_BINARY
-               do_octets:
-#endif
-                       cp = value + 2;
-                       size = strlen(cp);
-                       vp->length = size >> 1;
-                       us = talloc_array(vp, uint8_t, vp->length);
+                       memcpy(&vp->vp_ether, &integer, sizeof(vp->vp_ether));
+                       break;
+               }
 
-                       /*
-                        *      Invalid.
-                        */
-                       if ((size  & 0x01) != 0) {
-                               fr_strerror_printf("Hex string is not an even length string");
-                               return false;
-                       }
-
-                       if (fr_hex2bin(us, cp, vp->length) != vp->length) {
-                               fr_strerror_printf("Invalid hex data");
-                               return false;
-                       }
-                       vp->vp_octets = us;
-               } else {
-                       pairstrcpy(vp, value);
-               }
-               break;
-
-       case PW_TYPE_IFID:
-               if (ifid_aton(value, (void *) &vp->vp_ifid) == NULL) {
-                       fr_strerror_printf("failed to parse interface-id "
-                                  "string \"%s\"", value);
-                       return false;
-               }
-               vp->length = 8;
-               break;
-
-       case PW_TYPE_IPV6ADDR:
-               {
-                       fr_ipaddr_t ipaddr;
-
-                       if (ip_hton(value, AF_INET6, &ipaddr) < 0) {
-                               char buffer[1024];
-
-                               strlcpy(buffer, fr_strerror(), sizeof(buffer));
-
-                               fr_strerror_printf("failed to parse IPv6 address "
-                                                  "string \"%s\": %s", value, buffer);
-                               return false;
-                       }
-                       vp->vp_ipv6addr = ipaddr.ipaddr.ip6addr;
-                       vp->length = 16; /* length of IPv6 address */
-               }
-               break;
-
-       case PW_TYPE_IPV6PREFIX:
-               p = strchr(value, '/');
-               if (!p || ((p - value) >= 256)) {
-                       fr_strerror_printf("invalid IPv6 prefix "
-                                  "string \"%s\"", value);
-                       return false;
-               } else {
-                       unsigned int prefix;
-                       char buffer[256], *eptr;
-
-                       memcpy(buffer, value, p - value);
-                       buffer[p - value] = '\0';
-
-                       if (inet_pton(AF_INET6, buffer, vp->vp_ipv6prefix + 2) <= 0) {
-                               fr_strerror_printf("failed to parse IPv6 address "
-                                          "string \"%s\"", value);
-                               return false;
-                       }
-
-                       prefix = strtoul(p + 1, &eptr, 10);
-                       if ((prefix > 128) || *eptr) {
-                               fr_strerror_printf("failed to parse IPv6 address "
-                                          "string \"%s\"", value);
-                               return false;
-                       }
-                       vp->vp_ipv6prefix[1] = prefix;
-               }
-               vp->length = 16 + 2;
-               break;
-
-       case PW_TYPE_IPV4PREFIX:
-               p = strchr(value, '/');
-
-               /*
-                *      192.0.2.2 is parsed as if it was /32
-                */
-               if (!p) {
-                       vp->vp_ipv4prefix[1] = 32;
-
-                       if (inet_pton(AF_INET, value, vp->vp_ipv4prefix + 2) <= 0) {
-                               fr_strerror_printf("failed to parse IPv4 address "
-                                          "string \"%s\"", value);
-                               return false;
-                       }
-                       vp->length = sizeof(vp->vp_ipv4prefix);
-                       break;
-               }
-
-               /*
-                *      Otherwise parse the prefix
-                */
-               if ((p - value) >= 256) {
-                       fr_strerror_printf("invalid IPv4 prefix "
-                                  "string \"%s\"", value);
-                       return false;
-               } else {
-                       unsigned int prefix;
-                       char buffer[256], *eptr;
-
-                       memcpy(buffer, value, p - value);
-                       buffer[p - value] = '\0';
-
-                       if (inet_pton(AF_INET, buffer, vp->vp_ipv4prefix + 2) <= 0) {
-                               fr_strerror_printf("failed to parse IPv4 address "
-                                          "string \"%s\"", value);
-                               return false;
-                       }
-
-                       prefix = strtoul(p + 1, &eptr, 10);
-                       if ((prefix > 32) || *eptr) {
-                               fr_strerror_printf("failed to parse IPv4 address "
-                                          "string \"%s\"", value);
-                               return false;
+               cp = value;
+               while (*cp) {
+                       if (cp[1] == ':') {
+                               c1 = hextab;
+                               c2 = memchr(hextab, tolower((int) cp[0]), 16);
+                               cp += 2;
+                       } else if ((cp[1] != '\0') && ((cp[2] == ':') || (cp[2] == '\0'))) {
+                               c1 = memchr(hextab, tolower((int) cp[0]), 16);
+                               c2 = memchr(hextab, tolower((int) cp[1]), 16);
+                               cp += 2;
+                               if (*cp == ':') cp++;
+                       } else {
+                               c1 = c2 = NULL;
                        }
-                       vp->vp_ipv4prefix[1] = prefix;
-
-                       if (prefix < 32) {
-                               uint32_t addr, mask;
-
-                               memcpy(&addr, vp->vp_ipv4prefix + 2, sizeof(addr));
-                               mask = 1;
-                               mask <<= (32 - prefix);
-                               mask--;
-                               mask = ~mask;
-                               mask = htonl(mask);
-                               addr &= mask;
-                               memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr));
+                       if (!c1 || !c2 || (vp_len >= sizeof(vp->vp_ether))) {
+                               fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
+                               return -1;
                        }
+                       vp->vp_ether[vp_len] = ((c1-hextab)<<4) + (c2-hextab);
+                       vp_len++;
                }
-               vp->length = sizeof(vp->vp_ipv4prefix);
-               break;
 
-       case PW_TYPE_ETHERNET:
-               {
-                       char const *c1, *c2;
-
-                       length = 0;
-                       cp = value;
-                       while (*cp) {
-                               if (cp[1] == ':') {
-                                       c1 = hextab;
-                                       c2 = memchr(hextab, tolower((int) cp[0]), 16);
-                                       cp += 2;
-                               } else if ((cp[1] != '\0') &&
-                                          ((cp[2] == ':') ||
-                                           (cp[2] == '\0'))) {
-                                          c1 = memchr(hextab, tolower((int) cp[0]), 16);
-                                          c2 = memchr(hextab, tolower((int) cp[1]), 16);
-                                          cp += 2;
-                                          if (*cp == ':') cp++;
-                               } else {
-                                       c1 = c2 = NULL;
-                               }
-                               if (!c1 || !c2 || (length >= sizeof(vp->vp_ether))) {
-                                       fr_strerror_printf("failed to parse Ethernet address \"%s\"", value);
-                                       return false;
-                               }
-                               vp->vp_ether[length] = ((c1-hextab)<<4) + (c2-hextab);
-                               length++;
-                       }
-               }
                vp->length = 6;
+       }
                break;
 
        /*
@@ -1651,64 +1644,45 @@ bool pairparsevalue(VALUE_PAIR *vp, char const *value)
         *      These are not dynamic da, and will have the same vendor
         *      and attribute as the original.
         */
-       case PW_TYPE_COMBO_IP:
-               {
-                       DICT_ATTR const *da;
-
-                       if (inet_pton(AF_INET6, value, &vp->vp_ipv6addr) > 0) {
-                               da = dict_attrbytype(vp->da->attr, vp->da->vendor,
-                                                    PW_TYPE_IPV6ADDR);
-                               if (!da) {
-                                       return false;
-                               }
+       case PW_TYPE_IP_ADDR:
+       {
+               DICT_ATTR const *da;
 
-                               vp->length = 16; /* length of IPv6 address */
-                       } else {
-                               fr_ipaddr_t ipaddr;
+               if (inet_pton(AF_INET6, value, &vp->vp_ipv6addr) > 0) {
+                       da = dict_attrbytype(vp->da->attr, vp->da->vendor, PW_TYPE_IPV6_ADDR);
+                       if (!da) {
+                               fr_strerror_printf("Cannot find ipv6addr for %s", vp->da->name);
+                               return -1;
+                       }
 
-                               da = dict_attrbytype(vp->da->attr, vp->da->vendor,
-                                                    PW_TYPE_IPADDR);
-                               if (!da) {
-                                       return false;
-                               }
+                       vp->length = 16; /* length of IPv6 address */
+               } else {
+                       fr_ipaddr_t ipaddr;
 
-                               if (ip_hton(value, AF_INET, &ipaddr) < 0) {
-                                       fr_strerror_printf("Failed to find IPv4 address for %s", value);
-                                       return false;
-                               }
+                       da = dict_attrbytype(vp->da->attr, vp->da->vendor,
+                                            PW_TYPE_IPV4_ADDR);
+                       if (!da) {
+                               fr_strerror_printf("Cannot find ipaddr for %s", vp->da->name);
+                               return -1;
+                       }
 
-                               vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
-                               vp->length = 4;
+                       if (ip_hton(&ipaddr, AF_INET, value, false) < 0) {
+                               fr_strerror_printf("Failed to find IPv4 address for %s", value);
+                               return -1;
                        }
 
-                       vp->da = da;
+                       vp->vp_ipaddr = ipaddr.ipaddr.ip4addr.s_addr;
+                       vp->length = 4;
                }
-               break;
 
-       case PW_TYPE_SIGNED: /* Damned code for 1 WiMAX attribute */
-               vp->vp_signed = (int32_t) strtol(value, &p, 10);
-               vp->length = 4;
+               vp->da = da;
+       }
                break;
 
-       case PW_TYPE_TLV: /* don't use this! */
-               if (strncasecmp(value, "0x", 2) != 0) {
-                       fr_strerror_printf("Invalid TLV specification");
-                       return false;
-               }
-               length = strlen(value + 2) / 2;
-               if (vp->length < length) {
-                       TALLOC_FREE(vp->vp_tlv);
-               }
-               vp->vp_tlv = talloc_array(vp, uint8_t, length);
-               if (!vp->vp_tlv) {
-                       fr_strerror_printf("No memory");
-                       return false;
-               }
-               if (fr_hex2bin(vp->vp_tlv, value + 2, length) != length) {
-                       fr_strerror_printf("Invalid hex data in TLV");
-                       return false;
-               }
-               vp->length = length;
+       case PW_TYPE_SIGNED:
+               /* Damned code for 1 WiMAX attribute */
+               vp->vp_signed = (int32_t) strtol(value, NULL, 10);
+               vp->length = 4;
                break;
 
                /*
@@ -1716,14 +1690,74 @@ bool pairparsevalue(VALUE_PAIR *vp, char const *value)
                 */
        default:
                fr_strerror_printf("unknown attribute type %d", vp->da->type);
-               return false;
+               return -1;
        }
 
-       finish:
+finish:
        vp->type = VT_DATA;
-       return true;
+       return 0;
+}
+
+/** Use simple heuristics to create an VALUE_PAIR from an unknown address string
+ *
+ * If a DICT_ATTR is not provided for the address type, parsing will fail with
+ * and error.
+ *
+ * @param ctx to allocate VP in.
+ * @param value IPv4/IPv6 address/prefix string.
+ * @param ipv4 dictionary attribute to use for an IPv4 address.
+ * @param ipv6 dictionary attribute to use for an IPv6 address.
+ * @param ipv4_prefix dictionary attribute to use for an IPv4 prefix.
+ * @param ipv6_prefix dictionary attribute to use for an IPv6 prefix.
+ * @return NULL on error, or new VALUE_PAIR.
+ */
+VALUE_PAIR *pairmake_ip(TALLOC_CTX *ctx, char const *value, DICT_ATTR *ipv4, DICT_ATTR *ipv6,
+                       DICT_ATTR *ipv4_prefix, DICT_ATTR *ipv6_prefix)
+{
+       VALUE_PAIR *vp;
+       DICT_ATTR *da = NULL;
+
+       if (!fr_assert(ipv4 || ipv6 || ipv4_prefix || ipv6_prefix)) {
+               return NULL;
+       }
+
+       /* No point in repeating the work of pairparsevalue */
+       if (strchr(value, ':')) {
+               if (strchr(value, '/')) {
+                       da = ipv6_prefix;
+                       goto finish;
+               }
+
+               da = ipv6;
+               goto finish;
+       }
+
+       if (strchr(value, '/')) {
+               da = ipv4_prefix;
+               goto finish;
+       }
+
+       if (ipv4) {
+               da = ipv4;
+               goto finish;
+       }
+
+       fr_strerror_printf("Invalid IP value specified, allowed types are %s%s%s%s",
+                          ipv4 ? "ipaddr " : "", ipv6 ? "ipv6addr " : "",
+                          ipv4_prefix ? "ipv4prefix " : "", ipv6_prefix ? "ipv6prefix" : "");
+
+finish:
+       vp = pairalloc(ctx, da);
+       if (!vp) return NULL;
+       if (pairparsevalue(vp, value, 0) < 0) {
+               talloc_free(vp);
+               return NULL;
+       }
+
+       return vp;
 }
 
+
 /** Create a valuepair from an ASCII attribute and value
  *
  * Where the attribute name is in the form:
@@ -1814,15 +1848,15 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
        VALUE_PAIR      *vp;
        char            *tc, *ts;
        int8_t          tag;
-       int             found_tag;
+       bool            found_tag;
        char            buffer[256];
        char const      *attrname = attribute;
 
        /*
         *    Check for tags in 'Attribute:Tag' format.
         */
-       found_tag = 0;
-       tag = 0;
+       found_tag = false;
+       tag = TAG_ANY;
 
        ts = strrchr(attribute, ':');
        if (ts && !ts[1]) {
@@ -1840,18 +1874,18 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                 if (ts[1] == '*' && ts[2] == 0) {
                         /* Wildcard tag for check items */
                         tag = TAG_ANY;
-                        *ts = 0;
+                        *ts = '\0';
                 } else if ((ts[1] >= '0') && (ts[1] <= '9')) {
                         /* It's not a wild card tag */
                         tag = strtol(ts + 1, &tc, 0);
                         if (tc && !*tc && TAG_VALID_ZERO(tag))
-                                *ts = 0;
-                        else tag = 0;
+                                *ts = '\0';
+                        else tag = TAG_ANY;
                 } else {
                         fr_strerror_printf("Invalid tag for attribute %s", attribute);
                         return NULL;
                 }
-                found_tag = 1;
+                found_tag = true;
        }
 
        /*
@@ -1894,17 +1928,11 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
        }
 
        vp = pairalloc(ctx, da);
-       if (!vp) {
-               return NULL;
-       }
-
+       if (!vp) return NULL;
        vp->op = (op == 0) ? T_OP_EQ : op;
        vp->tag = tag;
 
        switch (vp->op) {
-       default:
-               break;
-
        case T_OP_CMP_TRUE:
        case T_OP_CMP_FALSE:
                vp->vp_strvalue = NULL;
@@ -1919,6 +1947,10 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                 */
        case T_OP_REG_EQ:       /* =~ */
        case T_OP_REG_NE:       /* !~ */
+       {
+
+               int compare;
+               regex_t reg;
 #ifndef WITH_REGEX
                fr_strerror_printf("Regular expressions are not supported");
                return NULL;
@@ -1932,18 +1964,14 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
 
                talloc_free(vp);
 
-               if (1) {
-                       int compare;
-                       regex_t reg;
-
-                       compare = regcomp(&reg, value, REG_EXTENDED);
-                       if (compare != 0) {
-                               regerror(compare, &reg, buffer, sizeof(buffer));
-                               fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
+               compare = regcomp(&reg, value, REG_EXTENDED);
+               if (compare != 0) {
+                       regerror(compare, &reg, buffer, sizeof(buffer));
+                       fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
                                           attribute, buffer);
-                               return NULL;
-                       }
+                       return NULL;
                }
+               regfree(&reg);
 
                vp = pairmake(ctx, NULL, attribute, NULL, op);
                if (!vp) return NULL;
@@ -1957,6 +1985,9 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                break;
 #endif
        }
+       default:
+               break;
+       }
 
        /*
         *      FIXME: if (strcasecmp(attribute, vp->da->name) != 0)
@@ -1966,7 +1997,7 @@ VALUE_PAIR *pairmake(TALLOC_CTX *ctx, VALUE_PAIR **vps,
         *      We probably want to fix pairparsevalue to accept
         *      octets as values for any attribute.
         */
-       if (value && !pairparsevalue(vp, value)) {
+       if (value && (pairparsevalue(vp, value, 0) < 0)) {
                talloc_free(vp);
                return NULL;
        }
@@ -1995,7 +2026,7 @@ int pairmark_xlat(VALUE_PAIR *vp, char const *value)
                return -1;
        }
 
-       raw = talloc_strdup(vp, value);
+       raw = talloc_typed_strdup(vp, value);
        if (!raw) {
                return -1;
        }
@@ -2107,7 +2138,7 @@ FR_TOKEN pairread(char const **ptr, VALUE_PAIR_RAW *raw)
        *ptr = p;
 
        /* Now we should have an operator here. */
-       raw->op = gettoken(ptr, buf, sizeof(buf));
+       raw->op = gettoken(ptr, buf, sizeof(buf), false);
        if (raw->op  < T_EQSTART || raw->op  > T_EQEND) {
                fr_strerror_printf("Expecting operator");
 
@@ -2117,7 +2148,7 @@ FR_TOKEN pairread(char const **ptr, VALUE_PAIR_RAW *raw)
        /*
         *      Read value.  Note that empty string values are allowed
         */
-       quote = gettoken(ptr, raw->r_opand, sizeof(raw->r_opand));
+       quote = gettoken(ptr, raw->r_opand, sizeof(raw->r_opand), false);
        if (quote == T_EOL) {
                fr_strerror_printf("Failed to get value");
 
@@ -2129,7 +2160,7 @@ FR_TOKEN pairread(char const **ptr, VALUE_PAIR_RAW *raw)
         */
        p = *ptr;
 
-       next = gettoken(&p, buf, sizeof(buf));
+       next = gettoken(&p, buf, sizeof(buf), false);
        switch (next) {
        case T_EOL:
        case T_HASH:
@@ -2194,8 +2225,9 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
        /*
         *      We allow an empty line.
         */
-       if (buffer[0] == 0)
+       if (buffer[0] == 0) {
                return T_EOL;
+       }
 
        head = NULL;
        tail = &head;
@@ -2218,7 +2250,7 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
                        }
                        if (pairmark_xlat(vp, raw.r_opand) < 0) {
                                talloc_free(vp);
-
+                               last_token = T_OP_INVALID;
                                break;
                        }
                } else {
@@ -2254,28 +2286,25 @@ FR_TOKEN userparse(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **list)
 
 /*
  *     Read valuepairs from the fp up to End-Of-File.
- *
- *     Hmm... this function is only used by radclient..
  */
-VALUE_PAIR *readvp2(TALLOC_CTX *ctx, FILE *fp, int *pfiledone, char const *errprefix)
+int readvp2(VALUE_PAIR **out, TALLOC_CTX *ctx, FILE *fp, bool *pfiledone)
 {
        char buf[8192];
        FR_TOKEN last_token = T_EOL;
-       VALUE_PAIR *vp;
-       VALUE_PAIR *list;
-       int error = 0;
 
-       list = NULL;
+       vp_cursor_t cursor;
+
+       VALUE_PAIR *vp = NULL;
+
+       fr_cursor_init(&cursor, out);
 
-       while (!error && fgets(buf, sizeof(buf), fp) != NULL) {
+       while (fgets(buf, sizeof(buf), fp) != NULL) {
                /*
                 *      If we get a '\n' by itself, we assume that's
                 *      the end of that VP
                 */
-               if ((buf[0] == '\n') && (list)) {
-                       return list;
-               }
-               if ((buf[0] == '\n') && (!list)) {
+               if (buf[0] == '\n') {
+                       if (vp) return 0;
                        continue;
                }
 
@@ -2290,23 +2319,155 @@ VALUE_PAIR *readvp2(TALLOC_CTX *ctx, FILE *fp, int *pfiledone, char const *errpr
                vp = NULL;
                last_token = userparse(ctx, buf, &vp);
                if (!vp) {
-                       if (last_token != T_EOL) {
-                               fr_perror("%s", errprefix);
-                               error = 1;
-                               break;
-                       }
+                       if (last_token != T_EOL) goto error;
                        break;
                }
 
-               pairadd(&list, vp);
+               fr_cursor_insert(&cursor, vp);
                buf[0] = '\0';
        }
 
-       if (error) pairfree(&list);
+       *pfiledone = true;
+
+       return 0;
+
+error:
+       vp = fr_cursor_first(&cursor);
+       if (vp) pairfree(&vp);
+
+       return -1;
+}
+
+/** Compare two attribute values
+ *
+ * @param[in] one the first attribute.
+ * @param[in] two the second attribute.
+ * @return -1 if one is less than two, 0 if both are equal, 1 if one is more than two, < -1 on error.
+ */
+int8_t paircmp_value(VALUE_PAIR const *one, VALUE_PAIR const *two)
+{
+       int64_t compare = 0;
+
+       VERIFY_VP(one);
+       VERIFY_VP(two);
+
+       if (one->da->type != two->da->type) {
+               fr_strerror_printf("Can't compare attribute values of different types");
+               return -2;
+       }
+
+       /*
+        *      After doing the previous check for special comparisons,
+        *      do the per-type comparison here.
+        */
+       switch (one->da->type) {
+       case PW_TYPE_ABINARY:
+       case PW_TYPE_OCTETS:
+       {
+               size_t length;
+
+               if (one->length > two->length) {
+                       length = one->length;
+               } else {
+                       length = two->length;
+               }
+
+               if (length) {
+                       compare = memcmp(one->vp_octets, two->vp_octets, length);
+                       if (compare != 0) break;
+               }
+
+               /*
+                *      Contents are the same.  The return code
+                *      is therefore the difference in lengths.
+                *
+                *      i.e. "0x00" is smaller than "0x0000"
+                */
+               compare = one->length - two->length;
+       }
+               break;
+
+       case PW_TYPE_STRING:
+               fr_assert(one->vp_strvalue);
+               fr_assert(two->vp_strvalue);
+               compare = strcmp(one->vp_strvalue, two->vp_strvalue);
+               break;
+
+       case PW_TYPE_BOOLEAN:
+       case PW_TYPE_BYTE:
+       case PW_TYPE_SHORT:
+       case PW_TYPE_INTEGER:
+       case PW_TYPE_DATE:
+               compare = (int64_t) one->vp_integer - (int64_t) two->vp_integer;
+               break;
+
+       case PW_TYPE_SIGNED:
+               compare = one->vp_signed - two->vp_signed;
+               break;
+
+       case PW_TYPE_INTEGER64:
+               /*
+                *      Don't want integer overflow!
+                */
+               if (one->vp_integer64 < two->vp_integer64) {
+                       compare = -1;
+               } else if (one->vp_integer64 > two->vp_integer64) {
+                       compare = 1;
+               }
+               break;
+
+       case PW_TYPE_ETHERNET:
+               compare = memcmp(&one->vp_ether, &two->vp_ether, sizeof(one->vp_ether));
+               break;
 
-       *pfiledone = 1;
+       case PW_TYPE_IPV4_ADDR:
+               compare = (int64_t) ntohl(one->vp_ipaddr) - (int64_t) ntohl(two->vp_ipaddr);
+               break;
 
-       return error ? NULL: list;
+       case PW_TYPE_IPV6_ADDR:
+               compare = memcmp(&one->vp_ipv6addr, &two->vp_ipv6addr, sizeof(one->vp_ipv6addr));
+               break;
+
+       case PW_TYPE_IPV6_PREFIX:
+               compare = memcmp(&one->vp_ipv6prefix, &two->vp_ipv6prefix, sizeof(one->vp_ipv6prefix));
+               break;
+
+       case PW_TYPE_IPV4_PREFIX:
+               compare = memcmp(&one->vp_ipv4prefix, &two->vp_ipv4prefix, sizeof(one->vp_ipv4prefix));
+               break;
+
+       case PW_TYPE_IFID:
+               compare = memcmp(&one->vp_ifid, &two->vp_ifid, sizeof(one->vp_ifid));
+               break;
+
+       /*
+        *      None of the types below should be in the REQUEST
+        */
+       case PW_TYPE_INVALID:           /* We should never see these */
+       case PW_TYPE_IP_ADDR:           /* This should of been converted into IPADDR/IPV6ADDR */
+       case PW_TYPE_IP_PREFIX: /* This should of been converted into IPADDR/IPV6ADDR */
+       case PW_TYPE_TLV:
+       case PW_TYPE_EXTENDED:
+       case PW_TYPE_LONG_EXTENDED:
+       case PW_TYPE_EVS:
+       case PW_TYPE_VSA:
+       case PW_TYPE_TIMEVAL:
+       case PW_TYPE_MAX:
+               fr_assert(0);   /* unknown type */
+               return -2;
+
+       /*
+        *      Do NOT add a default here, as new types are added
+        *      static analysis will warn us they're not handled
+        */
+       }
+
+       if (compare > 0) {
+               return 1;
+       } else if (compare < 0) {
+               return -1;
+       }
+       return 0;
 }
 
 /*
@@ -2315,7 +2476,9 @@ VALUE_PAIR *readvp2(TALLOC_CTX *ctx, FILE *fp, int *pfiledone, char const *errpr
  *
  *     reserved, prefix-len, data...
  */
-static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t const *two)
+static int paircmp_op_cidr(FR_TOKEN op, int bytes,
+                          uint8_t one_net, uint8_t const *one,
+                          uint8_t two_net, uint8_t const *two)
 {
        int i, common;
        uint32_t mask;
@@ -2323,10 +2486,10 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
        /*
         *      Handle the case of netmasks being identical.
         */
-       if (one[1] == two[1]) {
+       if (one_net == two_net) {
                int compare;
 
-               compare = memcmp(one + 2, two + 2, bytes);
+               compare = memcmp(one, two, bytes);
 
                /*
                 *      If they're identical return true for
@@ -2362,14 +2525,14 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
 
        case T_OP_LE:
        case T_OP_LT:   /* 192/8 < 192.168/16 --> false */
-               if (one[1] < two[1]) {
+               if (one_net < two_net) {
                        return false;
                }
                break;
 
        case T_OP_GE:
        case T_OP_GT:   /* 192/16 > 192.168/8 --> false */
-               if (one[1] > two[1]) {
+               if (one_net > two_net) {
                        return false;
                }
                break;
@@ -2378,10 +2541,10 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
                return false;
        }
 
-       if (one[1] < two[1]) {
-               common = one[1];
+       if (one_net < two_net) {
+               common = one_net;
        } else {
-               common = two[1];
+               common = two_net;
        }
 
        /*
@@ -2389,8 +2552,8 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
         *      identical, it MAY be a match.  If they're different,
         *      it is NOT a match.
         */
-       i = 2;
-       while (i < (2 + bytes)) {
+       i = 0;
+       while (i < bytes) {
                /*
                 *      All leading bytes are identical.
                 */
@@ -2420,33 +2583,150 @@ static int paircmp_cidr(FR_TOKEN op, int bytes, uint8_t const *one, uint8_t cons
        return false;
 }
 
-/*
- *     Compare two pairs, using the operator from "one".
+/** Compare two attributes using an operator
+ *
+ * @param[in] a the first attribute
+ * @param[in] op the operator for comparison.
+ * @param[in] b the second attribute
+ * @return 1 if true, 0 if false, -1 on error.
+ */
+int8_t paircmp_op(VALUE_PAIR const *a, FR_TOKEN op, VALUE_PAIR const *b)
+{
+       int compare;
+
+       if (!a || !b) return -1;
+
+       switch (a->da->type) {
+       case PW_TYPE_IPV4_ADDR:
+               switch (b->da->type) {
+               case PW_TYPE_IPV4_ADDR:         /* IPv4 and IPv4 */
+                       goto cmp;
+
+               case PW_TYPE_IPV4_PREFIX:       /* IPv4 and IPv4 Prefix */
+                       return paircmp_op_cidr(op, 4, 32, (uint8_t const *) &a->vp_ipaddr,
+                                              b->vp_ipv4prefix[1], (uint8_t const *) &b->vp_ipv4prefix + 2);
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv4 with IPv6 address");
+                       return -1;
+               }
+               break;
+
+       case PW_TYPE_IPV4_PREFIX:               /* IPv4 and IPv4 Prefix */
+               switch (b->da->type) {
+               case PW_TYPE_IPV4_ADDR:
+                       return paircmp_op_cidr(op, 4, a->vp_ipv4prefix[1],
+                                              (uint8_t const *) &a->vp_ipv4prefix + 2,
+                                              32, (uint8_t const *) &b->vp_ipaddr);
+
+               case PW_TYPE_IPV4_PREFIX:       /* IPv4 Prefix and IPv4 Prefix */
+                       return paircmp_op_cidr(op, 4, a->vp_ipv4prefix[1],
+                                              (uint8_t const *) &a->vp_ipv4prefix + 2,
+                                              b->vp_ipv4prefix[1], (uint8_t const *) &b->vp_ipv4prefix + 2);
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv4 with IPv6 address");
+                       return -1;
+               }
+               break;
+
+       case PW_TYPE_IPV6_ADDR:
+               switch (b->da->type) {
+               case PW_TYPE_IPV6_ADDR:         /* IPv6 and IPv6 */
+                       goto cmp;
+
+               case PW_TYPE_IPV6_PREFIX:       /* IPv6 and IPv6 Preifx */
+                       return paircmp_op_cidr(op, 16, 128, (uint8_t const *) &a->vp_ipv6addr,
+                                              b->vp_ipv6prefix[1], (uint8_t const *) &b->vp_ipv6prefix + 2);
+                       break;
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv6 with IPv4 address");
+                       return -1;
+               }
+               break;
+
+       case PW_TYPE_IPV6_PREFIX:
+               switch (b->da->type) {
+               case PW_TYPE_IPV6_ADDR:         /* IPv6 Prefix and IPv6 */
+                       return paircmp_op_cidr(op, 16, a->vp_ipv6prefix[1],
+                                              (uint8_t const *) &a->vp_ipv6prefix + 2,
+                                              128, (uint8_t const *) &b->vp_ipv6addr);
+
+               case PW_TYPE_IPV6_PREFIX:       /* IPv6 Prefix and IPv6 */
+                       return paircmp_op_cidr(op, 16, a->vp_ipv6prefix[1],
+                                              (uint8_t const *) &a->vp_ipv6prefix + 2,
+                                              b->vp_ipv6prefix[1], (uint8_t const *) &b->vp_ipv6prefix + 2);
+
+               default:
+                       fr_strerror_printf("Cannot compare IPv6 with IPv4 address");
+                       return -1;
+               }
+               break;
+
+       default:
+       cmp:
+               compare = paircmp_value(a, b);
+               if (compare < -1) {     /* comparison error */
+                       return -1;
+               }
+       }
+
+       /*
+        *      Now do the operator comparison.
+        */
+       switch (op) {
+       case T_OP_CMP_EQ:
+               return (compare == 0);
+
+       case T_OP_NE:
+               return (compare != 0);
+
+       case T_OP_LT:
+               return (compare < 0);
+
+       case T_OP_GT:
+               return (compare > 0);
+
+       case T_OP_LE:
+               return (compare <= 0);
+
+       case T_OP_GE:
+               return (compare >= 0);
+
+       default:
+               return 0;
+       }
+}
+
+/** Compare two pairs, using the operator from "a"
  *
  *     i.e. given two attributes, it does:
  *
- *     (two->data) (one->operator) (one->data)
+ *     (b->data) (a->operator) (a->data)
  *
  *     e.g. "foo" != "bar"
  *
- *     Returns true (comparison is true), or false (comparison is not true);
+ * @param[in] a the first attribute
+ * @param[in] b the second attribute
+ * @return 1 if true, 0 if false, -1 on error.
  */
-int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
+int8_t paircmp(VALUE_PAIR *a, VALUE_PAIR *b)
 {
-       int compare;
+       if (!a) return -1;
 
-       VERIFY_VP(one);
-       VERIFY_VP(two);
+       VERIFY_VP(a);
+       if (b) VERIFY_VP(b);
 
-       switch (one->op) {
+       switch (a->op) {
        case T_OP_CMP_TRUE:
-               return (two != NULL);
+               return (b != NULL);
 
        case T_OP_CMP_FALSE:
-               return (two == NULL);
+               return (b == NULL);
 
                /*
-                *      One is a regex, compile it, print two to a string,
+                *      a is a regex, compile it, print b to a string,
                 *      and then do string comparisons.
                 */
        case T_OP_REG_EQ:
@@ -2455,18 +2735,24 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
                return -1;
 #else
                {
+                       int compare;
                        regex_t reg;
                        char buffer[MAX_STRING_LEN * 4 + 1];
 
-                       compare = regcomp(&reg, one->vp_strvalue, REG_EXTENDED);
+                       compare = regcomp(&reg, a->vp_strvalue, REG_EXTENDED);
                        if (compare != 0) {
                                regerror(compare, &reg, buffer, sizeof(buffer));
                                fr_strerror_printf("Illegal regular expression in attribute: %s: %s",
-                                          one->da->name, buffer);
+                                                  a->da->name, buffer);
+                               return -1;
+                       }
+
+                       if (!b) {
+                               regfree(&reg);
                                return -1;
                        }
 
-                       vp_prints_value(buffer, sizeof(buffer), two, 0);
+                       vp_prints_value(buffer, sizeof(buffer), b, 0);
 
                        /*
                         *      Don't care about substring matches,
@@ -2475,7 +2761,10 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
                        compare = regexec(&reg, buffer, 0, NULL, 0);
 
                        regfree(&reg);
-                       if (one->op == T_OP_REG_EQ) return (compare == 0);
+                       if (a->op == T_OP_REG_EQ) {
+                               return (compare == 0);
+                       }
+
                        return (compare != 0);
                }
 #endif
@@ -2484,176 +2773,112 @@ int paircmp(VALUE_PAIR *one, VALUE_PAIR *two)
                break;
        }
 
-       return paircmp_op(two, one->op, one);
+       return paircmp_op(b, a->op, a);
 }
 
-/* Compare two attributes
+/** Determine equality of two lists
+ *
+ * This is useful for comparing lists of attributes inserted into a binary tree.
  *
- * @param[in] one the first attribute
- * @param[in] op the operator for comparison
- * @param[in] two the second attribute
- * @return true if ONE OP TWO is true, else false.
+ * @param a first list of VALUE_PAIRs.
+ * @param b second list of VALUE_PAIRs.
+ * @return -1 if a < b, 0 if the two lists are equal, 1 if a > b, -2 on error.
  */
-int paircmp_op(VALUE_PAIR const *one, FR_TOKEN op, VALUE_PAIR const *two)
+int8_t pairlistcmp(VALUE_PAIR *a, VALUE_PAIR *b)
 {
-       int compare;
-
-       VERIFY_VP(one);
-       VERIFY_VP(two);
-
-       /*
-        *      Can't compare two attributes of differing types
-        *
-        *      FIXME: maybe do checks for IP OP IP/mask ??
-        */
-       if (one->da->type != two->da->type) {
-               return one->da->type - two->da->type;
-       }
-
-       /*
-        *      After doing the previous check for special comparisons,
-        *      do the per-type comparison here.
-        */
-       switch (one->da->type) {
-       case PW_TYPE_ABINARY:
-       case PW_TYPE_OCTETS:
-       {
-               size_t length;
-
-               if (one->length > two->length) {
-                       length = one->length;
-               } else {
-                       length = two->length;
+       vp_cursor_t a_cursor, b_cursor;
+       VALUE_PAIR *a_p, *b_p;
+       int ret;
+
+       for (a_p = fr_cursor_init(&a_cursor, &a), b_p = fr_cursor_init(&b_cursor, &b);
+            a_p && b_p;
+            a_p = fr_cursor_next(&a_cursor), b_p = fr_cursor_next(&b_cursor)) {
+               /* Same VP, no point doing expensive checks */
+               if (a_p == b_p) {
+                       continue;
                }
 
-               if (length) {
-                       compare = memcmp(one->vp_octets, two->vp_octets,
-                                        length);
-                       if (compare != 0) break;
+               if (a_p->da < b_p->da) {
+                       return -1;
                }
-
-               /*
-                *      Contents are the same.  The return code
-                *      is therefore the difference in lengths.
-                *
-                *      i.e. "0x00" is smaller than "0x0000"
-                */
-               compare = one->length - two->length;
-       }
-               break;
-
-       case PW_TYPE_STRING:
-               compare = strcmp(one->vp_strvalue, two->vp_strvalue);
-               break;
-
-       case PW_TYPE_BYTE:
-       case PW_TYPE_SHORT:
-       case PW_TYPE_INTEGER:
-       case PW_TYPE_DATE:
-               if (one->vp_integer < two->vp_integer) {
-                       compare = -1;
-               } else if (one->vp_integer == two->vp_integer) {
-                       compare = 0;
-               } else {
-                       compare = +1;
+               if (a_p->da > b_p->da) {
+                       return 1;
                }
-               break;
 
-       case PW_TYPE_INTEGER64:
-               /*
-                *      Don't want integer overflow!
-                */
-               if (one->vp_integer64 < two->vp_integer64) {
-                       compare = -1;
-               } else if (one->vp_integer64 > two->vp_integer64) {
-                       compare = +1;
-               } else {
-                       compare = 0;
+               if (a_p->tag < b_p->tag) {
+                       return -1;
                }
-               break;
-       case PW_TYPE_IPADDR:
-               if (ntohl(one->vp_ipaddr)  < ntohl(two->vp_ipaddr)) {
-                       compare = -1;
-               } else if (one->vp_ipaddr  == two->vp_ipaddr) {
-                       compare = 0;
-               } else {
-                       compare = +1;
+               if (a_p->tag > b_p->tag) {
+                       return 1;
                }
-               break;
-
-       case PW_TYPE_IPV6ADDR:
-               compare = memcmp(&one->vp_ipv6addr, &two->vp_ipv6addr,
-                                sizeof(one->vp_ipv6addr));
-               break;
 
-       case PW_TYPE_IPV6PREFIX:
-               return paircmp_cidr(op, 16,
-                                   (uint8_t const *) &one->vp_ipv6prefix,
-                                   (uint8_t const *) &two->vp_ipv6prefix);
-
-       case PW_TYPE_IPV4PREFIX:
-               return paircmp_cidr(op, 4,
-                                   (uint8_t const *) &one->vp_ipv4prefix,
-                                   (uint8_t const *) &two->vp_ipv4prefix);
-
-       case PW_TYPE_IFID:
-               compare = memcmp(&one->vp_ifid, &two->vp_ifid,
-                                sizeof(one->vp_ifid));
-               break;
-
-       default:
-               return 0;       /* unknown type */
+               ret = paircmp_value(a_p, b_p);
+               if (ret != 0) {
+                       fr_assert(ret >= -1);   /* Comparison error */
+                       return ret;
+               }
        }
 
-       /*
-        *      Now do the operator comparison.
-        */
-       switch (op) {
-       case T_OP_CMP_EQ:
-               return (compare == 0);
+       if (!a_p && !b_p) {
+               return 0;
+       }
 
-       case T_OP_NE:
-               return (compare != 0);
+       if (!a_p) {
+               return -1;
+       }
 
-       case T_OP_LT:
-               return (compare < 0);
+       /* if(!b_p) */
+       return 1;
+}
 
-       case T_OP_GT:
-               return (compare > 0);
+/** Set the type of the VALUE_PAIR value buffer to match it's DICT_ATTR
+ *
+ * @param vp to fixup.
+ */
+static void pairtypeset(VALUE_PAIR *vp)
+{
+       if (!vp->data.ptr) return;
 
-       case T_OP_LE:
-               return (compare <= 0);
+       switch(vp->da->type) {
+       case PW_TYPE_OCTETS:
+       case PW_TYPE_TLV:
+               talloc_set_type(vp->data.ptr, uint8_t);
+               return;
 
-       case T_OP_GE:
-               return (compare >= 0);
+       case PW_TYPE_STRING:
+               talloc_set_type(vp->data.ptr, char);
+               return;
 
        default:
-               return 0;
+               return;
        }
-
-       return 0;
 }
 
 /** Copy data into an "octets" data type.
  *
  * @param[in,out] vp to update
  * @param[in] src data to copy
- * @param[in] size of the data
+ * @param[in] size of the data, may be 0 in which case previous value will be freed.
  */
 void pairmemcpy(VALUE_PAIR *vp, uint8_t const *src, size_t size)
 {
-       uint8_t *p, *q;
+       uint8_t *p = NULL, *q;
 
        VERIFY_VP(vp);
 
-       p = talloc_memdup(vp, src, size);
-       if (!p) return;
+       if (size > 0) {
+               p = talloc_memdup(vp, src, size);
+               if (!p) return;
+               talloc_set_type(p, uint8_t);
+       }
 
        memcpy(&q, &vp->vp_octets, sizeof(q));
-       talloc_free(q);
+       TALLOC_FREE(q);
 
        vp->vp_octets = p;
        vp->length = size;
+
+       if (size > 0) pairtypeset(vp);
 }
 
 /** Reparent an allocated octet buffer to a VALUE_PAIR
@@ -2661,9 +2886,10 @@ void pairmemcpy(VALUE_PAIR *vp, uint8_t const *src, size_t size)
  * @param[in,out] vp to update
  * @param[in] src buffer to steal.
  */
-void pairmemsteal(VALUE_PAIR *vp, uint8_t *src)
+void pairmemsteal(VALUE_PAIR *vp, uint8_t const *src)
 {
        uint8_t *q;
+
        VERIFY_VP(vp);
 
        memcpy(&q, &vp->vp_octets, sizeof(q));
@@ -2671,7 +2897,8 @@ void pairmemsteal(VALUE_PAIR *vp, uint8_t *src)
 
        vp->vp_octets = talloc_steal(vp, src);
        vp->type = VT_DATA;
-       vp->length = talloc_array_length(vp->vp_octets);
+       vp->length = talloc_array_length(vp->vp_strvalue);
+       pairtypeset(vp);
 }
 
 /** Reparent an allocated char buffer to a VALUE_PAIR
@@ -2679,9 +2906,10 @@ void pairmemsteal(VALUE_PAIR *vp, uint8_t *src)
  * @param[in,out] vp to update
  * @param[in] src buffer to steal.
  */
-void pairstrsteal(VALUE_PAIR *vp, char *src)
+void pairstrsteal(VALUE_PAIR *vp, char const *src)
 {
        uint8_t *q;
+
        VERIFY_VP(vp);
 
        memcpy(&q, &vp->vp_octets, sizeof(q));
@@ -2690,6 +2918,7 @@ void pairstrsteal(VALUE_PAIR *vp, char *src)
        vp->vp_strvalue = talloc_steal(vp, src);
        vp->type = VT_DATA;
        vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+       pairtypeset(vp);
 }
 
 /** Copy data into an "string" data type.
@@ -2704,6 +2933,7 @@ void pairstrcpy(VALUE_PAIR *vp, char const *src)
        VERIFY_VP(vp);
 
        p = talloc_strdup(vp, src);
+
        if (!p) return;
 
        memcpy(&q, &vp->vp_strvalue, sizeof(q));
@@ -2712,8 +2942,35 @@ void pairstrcpy(VALUE_PAIR *vp, char const *src)
        vp->vp_strvalue = p;
        vp->type = VT_DATA;
        vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+       pairtypeset(vp);
 }
 
+/** Copy data into an "string" data type.
+ *
+ * @param[in,out] vp to update.
+ * @param[in] src data to copy.
+ * @param[in] len of data to copy.
+ */
+void pairstrncpy(VALUE_PAIR *vp, char const *src, size_t len)
+{
+       char *p, *q;
+
+       VERIFY_VP(vp);
+
+       p = talloc_array(vp, char, len + 1);
+       if (!p) return;
+
+       memcpy(p, src, len);    /* embdedded \0 safe */
+       p[len] = '\0';
+
+       memcpy(&q, &vp->vp_strvalue, sizeof(q));
+       talloc_free(q);
+
+       vp->vp_strvalue = p;
+       vp->type = VT_DATA;
+       vp->length = len;
+       pairtypeset(vp);
+}
 
 /** Print data into an "string" data type.
  *
@@ -2740,5 +2997,6 @@ void pairsprintf(VALUE_PAIR *vp, char const *fmt, ...)
        vp->type = VT_DATA;
 
        vp->length = talloc_array_length(vp->vp_strvalue) - 1;
+       pairtypeset(vp);
 }
 
diff --git a/src/lib/version.c b/src/lib/version.c
new file mode 100644 (file)
index 0000000..c4dc025
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * version.c   Validate application and library magic numbers.
+ *
+ * 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 1999-2014  The FreeRADIUS server project
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/libradius.h>
+
+static uint64_t libmagic = RADIUSD_MAGIC_NUMBER;
+
+/** Check if the application linking to the library has the correct magic number
+ *
+ * @param magic number as defined by RADIUSD_MAGIC_NUMBER
+ * @returns 0 on success, -1 on prefix mismatch, -2 on version mismatch -3 on commit mismatch.
+ */
+int fr_check_lib_magic(uint64_t magic)
+{
+       if (MAGIC_PREFIX(magic) != MAGIC_PREFIX(libmagic)) {
+               fr_strerror_printf("Application and libfreeradius-radius magic number (prefix) mismatch."
+                                  "  application: %x  library: %x",
+                                  MAGIC_PREFIX(magic), MAGIC_PREFIX(libmagic));
+               return -1;
+       }
+
+       if (MAGIC_VERSION(magic) != MAGIC_VERSION(libmagic)) {
+               fr_strerror_printf("Application and libfreeradius-radius magic number (version) mismatch."
+                                  "  application: %lx library: %lx",
+                                  (unsigned long) MAGIC_VERSION(magic), (unsigned long) MAGIC_VERSION(libmagic));
+               return -2;
+       }
+
+       if (MAGIC_COMMIT(magic) != MAGIC_COMMIT(libmagic)) {
+               fr_strerror_printf("Application and libfreeradius-radius magic number (commit) mismatch."
+                                  "  application: %lx library: %lx",
+                                  (unsigned long) MAGIC_COMMIT(magic), (unsigned long) MAGIC_COMMIT(libmagic));
+               return -3;
+       }
+
+       return 0;
+}
index 6845b23..51d9ec2 100644 (file)
@@ -158,7 +158,7 @@ int rad_accounting(REQUEST *request)
                 */
                case RLM_MODULE_OK:
                case RLM_MODULE_UPDATED:
-                       request->reply->code = PW_ACCOUNTING_RESPONSE;
+                       request->reply->code = PW_CODE_ACCOUNTING_RESPONSE;
                        break;
 
                /*
index 6b8c13c..2517cd2 100644 (file)
@@ -1,3 +1,3 @@
 SUBMAKEFILES := radclient.mk radiusd.mk radsniff.mk radmin.mk radattr.mk \
-       radconf2xml.mk radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk \
+       radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk \
        libfreeradius-server.mk unittest.mk
index 0d11cce..a05fd6a 100644 (file)
@@ -33,17 +33,20 @@ RCSID("$Id$")
  *     Return a short string showing the terminal server, port
  *     and calling station ID.
  */
-char *auth_name(char *buf, size_t buflen, REQUEST *request, int do_cli)
+char *auth_name(char *buf, size_t buflen, REQUEST *request, bool do_cli)
 {
        VALUE_PAIR      *cli;
        VALUE_PAIR      *pair;
-       int             port = 0;
+       uint16_t        port = 0;
        char const      *tls = "";
 
-       if ((cli = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) == NULL)
-               do_cli = 0;
-       if ((pair = pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY)) != NULL)
+       if ((cli = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) == NULL) {
+               do_cli = false;
+       }
+
+       if ((pair = pairfind(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY)) != NULL) {
                port = pair->vp_integer;
+       }
 
        if (request->packet->dst_port == 0) {
                if (pairfind(request->packet->vps, PW_FREERADIUS_PROXIED_TO, 0, TAG_ANY)) {
@@ -166,22 +169,21 @@ static int rad_authlog(char const *msg, REQUEST *request, int goodpass)
  *
  *     NOTE: NOT the same as the RLM_ values !
  */
-static int rad_check_password(REQUEST *request)
+static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
 {
        vp_cursor_t cursor;
        VALUE_PAIR *auth_type_pair;
        int auth_type = -1;
        int result;
        int auth_type_count = 0;
-       result = 0;
 
        /*
         *      Look for matching check items. We skip the whole lot
         *      if the authentication type is PW_AUTHTYPE_ACCEPT or
         *      PW_AUTHTYPE_REJECT.
         */
-       paircursor(&cursor, &request->config_items);
-       while ((auth_type_pair = pairfindnext(&cursor, PW_AUTH_TYPE, 0, TAG_ANY))) {
+       fr_cursor_init(&cursor, &request->config_items);
+       while ((auth_type_pair = fr_cursor_next_by_num(&cursor, PW_AUTH_TYPE, 0, TAG_ANY))) {
                auth_type = auth_type_pair->vp_integer;
                auth_type_count++;
 
@@ -225,19 +227,19 @@ static int rad_check_password(REQUEST *request)
        if (auth_type < 0) {
                if (pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
                        RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Crypt'");
-                       RWDEBUG2("Use the PAP module instead.");
+                       RWDEBUG2("Use the PAP module instead");
                }
                else if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) {
                        RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Local'");
-                       RWDEBUG2("Use the PAP or CHAP modules instead.");
+                       RWDEBUG2("Use the PAP or CHAP modules instead");
                }
 
                /*
-                *      The admin hasn't told us how to
-                *      authenticate the user, so we reject them!
-                *
-                *      This is fail-safe.
-                */
+                *      The admin hasn't told us how to
+                *      authenticate the user, so we reject them!
+                *
+                *      This is fail-safe.
+                */
 
                REDEBUG2("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject");
                return -2;
@@ -308,7 +310,7 @@ int rad_postauth(REQUEST *request)
                case RLM_MODULE_REJECT:
                case RLM_MODULE_USERLOCK:
                default:
-                       request->reply->code = PW_AUTHENTICATION_REJECT;
+                       request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                        result = RLM_MODULE_REJECT;
                        break;
                /*
@@ -351,14 +353,22 @@ int rad_authenticate(REQUEST *request)
        /*
         *      If this request got proxied to another server, we need
         *      to check whether it authenticated the request or not.
+        *
+        *      request->proxy gets set only AFTER authorization, so
+        *      it's safe to check it here.  If it exists, it means
+        *      we're doing a second pass through rad_authenticate().
         */
-       if (request->proxy_reply) {
-               switch (request->proxy_reply->code) {
+       if (request->proxy) {
+               int code = 0;
+
+               if (request->proxy_reply) code = request->proxy_reply->code;
+
+               switch (code) {
                /*
                 *      Reply of ACCEPT means accept, thus set Auth-Type
                 *      accordingly.
                 */
-               case PW_AUTHENTICATION_ACK:
+               case PW_CODE_AUTHENTICATION_ACK:
                        tmp = radius_paircreate(request,
                                                &request->config_items,
                                                PW_AUTH_TYPE, 0);
@@ -369,9 +379,10 @@ int rad_authenticate(REQUEST *request)
                 *      Challenges are punted back to the NAS without any
                 *      further processing.
                 */
-               case PW_ACCESS_CHALLENGE:
-                       request->reply->code = PW_ACCESS_CHALLENGE;
+               case PW_CODE_ACCESS_CHALLENGE:
+                       request->reply->code = PW_CODE_ACCESS_CHALLENGE;
                        return RLM_MODULE_OK;
+
                /*
                 *      ALL other replies mean reject. (this is fail-safe)
                 *
@@ -379,10 +390,10 @@ int rad_authenticate(REQUEST *request)
                 *      are being rejected, so we minimize the amount of work
                 *      done by the server, by rejecting them here.
                 */
-               case PW_AUTHENTICATION_REJECT:
+               case PW_CODE_AUTHENTICATION_REJECT:
                        rad_authlog("Login incorrect (Home Server says so)",
                                    request, 0);
-                       request->reply->code = PW_AUTHENTICATION_REJECT;
+                       request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                        return RLM_MODULE_REJECT;
 
                default:
@@ -428,7 +439,7 @@ autz_redo:
                        } else {
                                rad_authlog("Invalid user", request, 0);
                        }
-                       request->reply->code = PW_AUTHENTICATION_REJECT;
+                       request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                        return result;
        }
        if (!autz_retry) {
@@ -502,8 +513,8 @@ autz_redo:
         *      wants to send back.
         */
        if (result < 0) {
-               RDEBUG2("Failed to authenticate the user.");
-               request->reply->code = PW_AUTHENTICATION_REJECT;
+               RDEBUG2("Failed to authenticate the user");
+               request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
 
                if ((module_msg = pairfind(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL){
                        char msg[MAX_STRING_LEN+19];
@@ -543,7 +554,6 @@ autz_redo:
                int r, session_type = 0;
                char            logstr[1024];
                char            umsg[MAX_STRING_LEN + 1];
-               char const      *user_msg = NULL;
 
                tmp = pairfind(request->config_items, PW_SESSION_TYPE, 0, TAG_ANY);
                if (tmp) {
@@ -572,15 +582,17 @@ autz_redo:
                        }
                        if (!mpp_ok){
                                if (check_item->vp_integer > 1) {
-                               snprintf(umsg, sizeof(umsg),
-                                                       "\r\nYou are already logged in %d times  - access denied\r\n\n",
-                                                       (int)check_item->vp_integer);
-                                       user_msg = umsg;
+                                       snprintf(umsg, sizeof(umsg),
+                                                "\r\n%s (%d)\r\n\n",
+                                                main_config.denied_msg,
+                                                (int)check_item->vp_integer);
                                } else {
-                                       user_msg = "\r\nYou are already logged in - access denied\r\n\n";
+                                       snprintf(umsg, sizeof(umsg),
+                                                "\r\n%s\r\n\n",
+                                                main_config.denied_msg);
                                }
 
-                               request->reply->code = PW_AUTHENTICATION_REJECT;
+                               request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
 
                                /*
                                 *      They're trying to log in too many times.
@@ -588,7 +600,7 @@ autz_redo:
                                 */
                                pairfree(&request->reply->vps);
                                pairmake_reply("Reply-Message",
-                                              user_msg, T_OP_SET);
+                                              umsg, T_OP_SET);
 
                                snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
                                        check_item->vp_integer,
@@ -614,7 +626,7 @@ autz_redo:
         *      been set to something.  (i.e. Access-Challenge)
         */
        if (request->reply->code == 0)
-         request->reply->code = PW_AUTHENTICATION_ACK;
+         request->reply->code = PW_CODE_AUTHENTICATION_ACK;
 
        if ((module_msg = pairfind(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL){
                char msg[MAX_STRING_LEN+12];
@@ -642,17 +654,17 @@ int rad_virtual_server(REQUEST *request)
         *      We currently only handle AUTH packets here.
         *      This could be expanded to handle other packets as well if required.
         */
-       rad_assert(request->packet->code == PW_AUTHENTICATION_REQUEST);
+       rad_assert(request->packet->code == PW_CODE_AUTHENTICATION_REQUEST);
 
        result = rad_authenticate(request);
 
-       if (request->reply->code == PW_AUTHENTICATION_REJECT) {
+       if (request->reply->code == PW_CODE_AUTHENTICATION_REJECT) {
                pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
                vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET);
                if (vp) rad_postauth(request);
        }
 
-       if (request->reply->code == PW_AUTHENTICATION_ACK) {
+       if (request->reply->code == PW_CODE_AUTHENTICATION_ACK) {
                rad_postauth(request);
        }
 
index 3f544bc..c61b737 100644 (file)
@@ -30,51 +30,46 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 void cbtls_info(SSL const *s, int where, int ret)
 {
        char const *str, *state;
-       int w;
        REQUEST *request = SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST);
-       char buffer[1024];
 
-       w = where & ~SSL_ST_MASK;
-       if (w & SSL_ST_CONNECT) str="    TLS_connect";
-       else if (w & SSL_ST_ACCEPT) str="    TLS_accept";
-       else str="    (other)";
+       if ((where & ~SSL_ST_MASK) & SSL_ST_CONNECT) {
+               str="TLS_connect";
+       } else if (((where & ~SSL_ST_MASK)) & SSL_ST_ACCEPT) {
+               str="TLS_accept";
+       } else {
+               str="(other)";
+       }
 
        state = SSL_state_string_long(s);
-       state = state ? state : "NULL";
-       buffer[0] = '\0';
+       state = state ? state : "<none>";
 
-       if (where & SSL_CB_LOOP) {
-               RDEBUG2("%s: %s", str, state);
-       } else if (where & SSL_CB_HANDSHAKE_START) {
-               RDEBUG2("%s: %s", str, state);
-       } else if (where & SSL_CB_HANDSHAKE_DONE) {
+       if ((where & SSL_CB_LOOP) || (where & SSL_CB_HANDSHAKE_START) || (where & SSL_CB_HANDSHAKE_DONE)) {
                RDEBUG2("%s: %s", str, state);
-       } else if (where & SSL_CB_ALERT) {
-               str=(where & SSL_CB_READ)?"read":"write";
-
-               snprintf(buffer, sizeof(buffer), "TLS Alert %s:%s:%s",
-                        str,
-                        SSL_alert_type_string_long(ret),
-                        SSL_alert_desc_string_long(ret));
-       } else if (where & SSL_CB_EXIT) {
+               return;
+       }
+
+       if (where & SSL_CB_ALERT) {
+               if ((ret & 0xff) == SSL_AD_CLOSE_NOTIFY) return;
+
+               RERROR("TLS Alert %s:%s:%s", (where & SSL_CB_READ) ? "read": "write",
+                      SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
+               return;
+       }
+
+       if (where & SSL_CB_EXIT) {
                if (ret == 0) {
-                       snprintf(buffer, sizeof(buffer), "%s: failed in %s",
-                                str, state);
+                       RERROR("%s: Failed in %s", str, state);
+                       return;
+               }
 
-               } else if (ret < 0) {
+               if (ret < 0) {
                        if (SSL_want_read(s)) {
-                               RDEBUG2("%s: Need to read more data: %s",
-                                      str, state);
-                       } else {
-                               snprintf(buffer, sizeof(buffer),
-                                        "%s: error in %s", str, state);
+                               RDEBUG2("%s: Need to read more data: %s", str, state);
+                               return;
                        }
+                       ERROR("tls: %s: Error in %s", str, state);
                }
        }
-
-       if (buffer[0] && request) {
-               REDEBUG("SSL says: %s", buffer);
-       }
 }
 
 /*
@@ -107,6 +102,23 @@ void cbtls_msg(int write_p, int msg_version, int content_type,
                state->info.handshake_type = ((unsigned char const *)buf)[0];
                state->info.alert_level = 0x00;
                state->info.alert_description = 0x00;
+
+#ifdef SSL3_RT_HEARTBEAT
+       } else if (content_type == TLS1_RT_HEARTBEAT) {
+               uint8_t *p = buf;
+
+               if ((len >= 3) && (p[0] == 1)) {
+                       size_t payload_len;
+
+                       payload_len = (p[1] << 8) | p[2];
+
+                       if ((payload_len + 3) > len) {
+                               state->invalid_hb_used = true;
+                               ERROR("OpenSSL Heartbeat attack detected.  Closing connection");
+                               return;
+                       }
+               }
+#endif
        }
        tls_session_information(state);
 }
index a2b3193..2ba40f0 100644 (file)
@@ -455,14 +455,14 @@ sub cisco_snmp {
 
        #       ($login eq $ARGV[3]) ? 1 : 0;
        if($login eq $ARGV[3]) {
-               return 1;
+               return 1;
        }else{
                $out=snmpwalk($ARGV[1],$pass,".iso.org.dod.internet.private.enterprises.9.10.19.1.3.1.1.3");
-               if($out=~/\"$ARGV[3]\"/){
-                       return 1;
-               }else{
-                       return 0;
-               }
+               if($out=~/\"$ARGV[3]\"/){
+                       return 1;
+               }else{
+                       return 0;
+               }
        }
 }
 
@@ -473,16 +473,16 @@ sub cisco_snmp {
 #
 sub juniper_e_snmp {
                #receives acct_session
-                my $temp = $ARGV[4];
-                #removes the leading 0s
-                my $clean_temp = int $temp;
-
-                $out=snmpget($ARGV[1], $cmmty_string, ".1.3.6.1.4.1.4874.2.2.20.1.8.4.1.2.$clean_temp");
-                if($out=~/\"$ARGV[3]\"/){
-                        return 1;
-                }else{
-                        return 0;
-                }
+               my $temp = $ARGV[4];
+               #removes the leading 0s
+               my $clean_temp = int $temp;
+
+               $out=snmpget($ARGV[1], $cmmty_string, ".1.3.6.1.4.1.4874.2.2.20.1.8.4.1.2.$clean_temp");
+               if($out=~/\"$ARGV[3]\"/){
+                       return 1;
+               }else{
+                       return 0;
+               }
 }
 
 #
@@ -494,10 +494,10 @@ $msm    = '.iso.org.dod.internet.private.enterprises.995';
 sub multitech_snmp {
        my $temp = $ARGV[2] + 1;
 
-        $login = snmpget($ARGV[1], "$cmmty_string", "$msm.2.31.1.1.1.$temp");
-        print LOG " user at port S$ARGV[2]: $login\n" if ($debug);
+       $login = snmpget($ARGV[1], "$cmmty_string", "$msm.2.31.1.1.1.$temp");
+       print LOG " user at port S$ARGV[2]: $login\n" if ($debug);
 
-        ($login eq $ARGV[3]) ? 1 : 0;
+       ($login eq $ARGV[3]) ? 1 : 0;
 }
 
 #
@@ -536,7 +536,7 @@ sub computone_finger {
                # Check for known versions
                if ($ver eq '1.7.2' || $ver eq '3.0.4') {
                        if (/^\Q$ARGV[2]\E\s+\S+\s+\S+\s+\S+\s+\Q$trunc\E(\s+|\.\.)/) {
-                               close FD;
+                               close FD;
                                return 1;
                        }
                        next;
@@ -899,7 +899,7 @@ sub cyclades_telnet {
                }
        }
        print LOG "  User '$ARGV[3]' not found on '$ARGV[1]'.\n" if ($debug);
-       0;
+       0;
 }
 
 #
@@ -921,7 +921,7 @@ sub patton_snmp {
    #
    if (snmpget($ARGV[1], "monitor", "$oid") == 0) {
       print LOG "  Session $ARGV[4] still active on NAS " .
-       "$ARGV[1], port $ARGV[2], for user $ARGV[3].\n" if ($debug);
+       "$ARGV[1], port $ARGV[2], for user $ARGV[3].\n" if ($debug);
       return 1;
    }
    0;
@@ -984,7 +984,7 @@ sub usrhiper_snmp {
        my ($login,$password,$oidext);
 
        # Look up community string in naspasswd file.
-        ($login, $password) = naspasswd($ARGV[1], 1);
+       ($login, $password) = naspasswd($ARGV[1], 1);
        if ($login && $login ne 'SNMP') {
                if($debug) {
                        print LOG
@@ -1036,7 +1036,7 @@ sub get_oidext {
     }
     else {
        $oid = 1257 + 256*int(($args{'tty'}-1) / $hiper_density) +
-                            (($args{'tty'}-1) % $hiper_density);
+                            (($args{'tty'}-1) % $hiper_density);
     }
     return($oid);
 }
@@ -1205,7 +1205,7 @@ sub find_l2tp_login
     $sess->getnext($snmp_var);
   } until ($snmp_var->[$SNMP::Varbind::val_f] =~ /$port/) ||
        (!($snmp_var->[$SNMP::Varbind::ref_f] =~ /^$port_oid\.(\d+)\.(\d+)$/)) ||
-       ($sess->{ErrorNum});
+       ($sess->{ErrorNum});
 
   my $val1 = $snmp_var->[$SNMP::Varbind::ref_f];
 
@@ -1295,7 +1295,7 @@ sub mikrotik_telnet {
   # practically this would limit us to a simple only-one user limit for
   # this script to work properly.
   $t = new Net::Telnet (Timeout => 5,
-                        Prompt => '//\[.*@.*\] > /');
+                       Prompt => '//\[.*@.*\] > /');
 
   # Dont just exit when there is error
   $t->errmode('return');
@@ -1305,11 +1305,11 @@ sub mikrotik_telnet {
 
   #Send login and password etc.
   $t->login(Name => $login,
-            Password => $password,
+           Password => $password,
   # We must detect if we are logged in from the login banner.
   # Because if routeros is with a free license the command
   # prompt dont come. Instead it waits us to press "Enter".
-            Prompt => '/MikroTik/');
+           Prompt => '/MikroTik/');
 
   # Just be sure that routeros isn't waiting for us to press "Enter"
   $t->print("");
@@ -1455,7 +1455,7 @@ if ($ARGV[0] eq 'livingston') {
 } elsif ($ARGV[0] eq 'cvx') {
        $ret = &cvx_snmp;
 } elsif ($ARGV[0] eq 'juniper') {
-        $ret = &juniper_e_snmp;
+       $ret = &juniper_e_snmp;
 } elsif ($ARGV[0] eq 'multitech') {
        $ret = &multitech_snmp;
 } elsif ($ARGV[0] eq 'computone') {
index 4c46efd..5176f41 100644 (file)
@@ -43,7 +43,7 @@ struct radclient_list {
         *      FIXME: One set of trees for IPv4, and another for IPv6?
         */
        rbtree_t        *trees[129]; /* for 0..128, inclusive. */
-       int             min_prefix;
+       uint32_t        min_prefix;
 };
 
 
@@ -142,6 +142,7 @@ void clients_free(RADCLIENT_LIST *clients)
        int i;
 
        if (!clients) clients = root_clients;
+       if (!clients) return;   /* Clients may not have been initialised yet */
 
        for (i = 0; i <= 128; i++) {
                if (clients->trees[i]) rbtree_free(clients->trees[i]);
@@ -180,93 +181,27 @@ RADCLIENT_LIST *clients_init(CONF_SECTION *cs)
        return clients;
 }
 
-
-/*
- *     Sanity check a client.
- */
-static int client_sane(RADCLIENT *client)
-{
-       switch (client->ipaddr.af) {
-       case AF_INET:
-               if (client->prefix > 32) {
-                       return 0;
-               }
-
-               /*
-                *      Zero out the subnet bits.
-                */
-               if (client->prefix == 0) {
-                       memset(&client->ipaddr.ipaddr.ip4addr, 0,
-                              sizeof(client->ipaddr.ipaddr.ip4addr));
-
-               } else if (client->prefix < 32) {
-                       uint32_t mask = ~0;
-
-                       mask <<= (32 - client->prefix);
-                       client->ipaddr.ipaddr.ip4addr.s_addr &= htonl(mask);
-               }
-               break;
-
-       case AF_INET6:
-               if (client->prefix > 128) return 0;
-
-               if (client->prefix == 0) {
-                       memset(&client->ipaddr.ipaddr.ip6addr, 0,
-                              sizeof(client->ipaddr.ipaddr.ip6addr));
-
-               } else if (client->prefix < 128) {
-                       uint32_t mask, *addr;
-
-                       addr = (uint32_t *) &client->ipaddr.ipaddr.ip6addr;
-
-                       if ((client->prefix & 0x1f) == 0) {
-                               mask = 0;
-                       } else {
-                               mask = ~ ((uint32_t) 0);
-                               mask <<= (32 - (client->prefix & 0x1f));
-                               mask = htonl(mask);
-                       }
-
-                       switch (client->prefix >> 5) {
-                       case 0:
-                               addr[0] &= mask;
-                               mask = 0;
-                               /* FALL-THROUGH */
-                       case 1:
-                               addr[1] &= mask;
-                               mask = 0;
-                               /* FALL-THROUGH */
-                       case 2:
-                               addr[2] &= mask;
-                               mask = 0;
-                               /* FALL-THROUGH */
-                       case 3:
-                               addr[3] &= mask;
-                         break;
-                       }
-               }
-               break;
-
-       default:
-               return 0;
-       }
-
-       return 1;
-}
-
-
 /*
  *     Add a client to the tree.
  */
 int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
 {
        RADCLIENT *old;
+       char buffer[INET6_ADDRSTRLEN + 3];
 
        if (!client) {
                return 0;
        }
 
        /*
+        *      Hack to fixup wildcard clients
+        */
+       if (is_wildcard(&client->ipaddr)) client->ipaddr.prefix = 0;
+
+       fr_ntop(buffer, sizeof(buffer), &client->ipaddr);
+       DEBUG3("Adding client %s (%s) to prefix tree %i", buffer, client->longname, client->ipaddr.prefix);
+
+       /*
         *      If "clients" is NULL, it means add to the global list.
         */
        if (!clients) {
@@ -280,19 +215,12 @@ int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
                clients = root_clients;
        }
 
-       if ((client->prefix < 0) || (client->prefix > 128)) {
-               return 0;
-       }
-
-       if (!client_sane(client)) return 0;
-
        /*
         *      Create a tree for it.
         */
-       if (!clients->trees[client->prefix]) {
-               clients->trees[client->prefix] = rbtree_create(client_ipaddr_cmp,
-                                                              NULL, 0);
-               if (!clients->trees[client->prefix]) {
+       if (!clients->trees[client->ipaddr.prefix]) {
+               clients->trees[client->ipaddr.prefix] = rbtree_create(client_ipaddr_cmp, NULL, 0);
+               if (!clients->trees[client->ipaddr.prefix]) {
                        return 0;
                }
        }
@@ -302,14 +230,14 @@ int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
        /*
         *      Cannot insert the same client twice.
         */
-       old = rbtree_finddata(clients->trees[client->prefix], client);
+       old = rbtree_finddata(clients->trees[client->ipaddr.prefix], client);
        if (old) {
                /*
                 *      If it's a complete duplicate, then free the new
                 *      one, and return "OK".
                 */
                if ((fr_ipaddr_cmp(&old->ipaddr, &client->ipaddr) == 0) &&
-                   (old->prefix == client->prefix) &&
+                   (old->ipaddr.prefix == client->ipaddr.prefix) &&
                    namecmp(longname) && namecmp(secret) &&
                    namecmp(shortname) && namecmp(nas_type) &&
                    namecmp(login) && namecmp(password) && namecmp(server) &&
@@ -323,7 +251,7 @@ int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
                    (old->coa_pool == client->coa_pool) &&
 #endif
                    (old->message_authenticator == client->message_authenticator)) {
-                       WDEBUG("Ignoring duplicate client %s", client->longname);
+                       WARN("Ignoring duplicate client %s", client->longname);
                        client_free(client);
                        return 1;
                }
@@ -337,7 +265,7 @@ int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
        /*
         *      Other error adding client: likely is fatal.
         */
-       if (!rbtree_insert(clients->trees[client->prefix], client)) {
+       if (!rbtree_insert(clients->trees[client->ipaddr.prefix], client)) {
                return 0;
        }
 
@@ -373,8 +301,8 @@ int client_add(RADCLIENT_LIST *clients, RADCLIENT *client)
        if (tree_num) rbtree_insert(tree_num, client);
 #endif
 
-       if (client->prefix < clients->min_prefix) {
-               clients->min_prefix = client->prefix;
+       if (client->ipaddr.prefix < clients->min_prefix) {
+               clients->min_prefix = client->ipaddr.prefix;
        }
 
        (void) talloc_steal(clients, client); /* reparent it */
@@ -392,14 +320,14 @@ void client_delete(RADCLIENT_LIST *clients, RADCLIENT *client)
 
        if (!client->dynamic) return;
 
-       rad_assert((client->prefix >= 0) && (client->prefix <= 128));
+       rad_assert(client->ipaddr.prefix <= 128);
 
        client->dynamic = 2;    /* signal to client_free */
 
 #ifdef WITH_STATS
        rbtree_deletebydata(tree_num, client);
 #endif
-       rbtree_deletebydata(clients->trees[client->prefix], client);
+       rbtree_deletebydata(clients->trees[client->ipaddr.prefix], client);
 }
 #endif
 
@@ -439,7 +367,7 @@ RADCLIENT *client_findbynumber(UNUSED const RADCLIENT_LIST *clients, UNUSED int
  */
 RADCLIENT *client_find(RADCLIENT_LIST const *clients, fr_ipaddr_t const *ipaddr, int proto)
 {
-       int i, max_prefix;
+       uint32_t i, max_prefix;
        RADCLIENT myclient;
 
        if (!clients) clients = root_clients;
@@ -462,23 +390,19 @@ RADCLIENT *client_find(RADCLIENT_LIST const *clients, fr_ipaddr_t const *ipaddr,
        for (i = max_prefix; i >= clients->min_prefix; i--) {
                void *data;
 
-               myclient.prefix = i;
                myclient.ipaddr = *ipaddr;
                myclient.proto = proto;
-               client_sane(&myclient); /* clean up the ipaddress */
+               fr_ipaddr_mask(&myclient.ipaddr, i);
 
                if (!clients->trees[i]) continue;
 
                data = rbtree_finddata(clients->trees[i], &myclient);
-               if (data) {
-                       return data;
-               }
+               if (data) return data;
        }
 
        return NULL;
 }
 
-
 /*
  *     Old wrapper for client_find
  */
@@ -487,76 +411,57 @@ RADCLIENT *client_find_old(fr_ipaddr_t const *ipaddr)
        return client_find(root_clients, ipaddr, IPPROTO_UDP);
 }
 
-static struct in_addr cl_ip4addr;
-static struct in6_addr cl_ip6addr;
-static char *cl_srcipaddr = NULL;
+static fr_ipaddr_t cl_ipaddr;
+static char const *cl_srcipaddr = NULL;
+static uint32_t cl_prefix;
 #ifdef WITH_TCP
-static char *hs_proto = NULL;
+static char const *hs_proto = NULL;
 #endif
 
 #ifdef WITH_TCP
 static CONF_PARSER limit_config[] = {
-       { "max_connections", PW_TYPE_INTEGER,
-         offsetof(RADCLIENT, limit.max_connections), NULL,   "16" },
+       { "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.max_connections),   "16" },
 
-       { "lifetime", PW_TYPE_INTEGER,
-         offsetof(RADCLIENT, limit.lifetime), NULL,   "0" },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.lifetime),   "0" },
 
-       { "idle_timeout", PW_TYPE_INTEGER,
-         offsetof(RADCLIENT, limit.idle_timeout), NULL,   "30" },
+       { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, limit.idle_timeout), "30" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 #endif
 
 static const CONF_PARSER client_config[] = {
-       { "ipaddr",  PW_TYPE_IPADDR,
-         0, &cl_ip4addr,  NULL },
-       { "ipv6addr",  PW_TYPE_IPV6ADDR,
-         0, &cl_ip6addr, NULL },
-       { "netmask",  PW_TYPE_INTEGER,
-         offsetof(RADCLIENT, prefix), 0, NULL },
-
-       { "src_ipaddr",  PW_TYPE_STRING_PTR,
-         0, &cl_srcipaddr,  NULL },
-
-       { "require_message_authenticator",  PW_TYPE_BOOLEAN,
-         offsetof(RADCLIENT, message_authenticator), 0, "no" },
-
-       { "secret",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, secret), 0, NULL },
-       { "shortname",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, shortname), 0, NULL },
-       { "nastype",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(RADCLIENT, nas_type), 0, NULL },
-       { "nas_type",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, nas_type), 0, NULL },
-       { "login",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, login), 0, NULL },
-       { "password",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, password), 0, NULL },
-       { "virtual_server",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, server), 0, NULL },
+       { "ipaddr", FR_CONF_POINTER(PW_TYPE_IP_PREFIX, &cl_ipaddr), NULL },
+       { "ipv4addr", FR_CONF_POINTER(PW_TYPE_IPV4_PREFIX, &cl_ipaddr), NULL },
+       { "ipv6addr", FR_CONF_POINTER(PW_TYPE_IPV6_PREFIX, &cl_ipaddr), NULL },
+       { "netmask", FR_CONF_POINTER(PW_TYPE_INTEGER, &cl_prefix), NULL },
 
-#ifdef WITH_TCP
-       { "proto",  PW_TYPE_STRING_PTR,
-         0, &hs_proto, NULL },
+       { "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL },
 
-       { "limit", PW_TYPE_SUBSECTION, 0, NULL, (void const *) limit_config },
+       { "require_message_authenticator",  FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" },
+
+       { "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, RADCLIENT, secret), NULL },
+       { "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), NULL },
+       { "nastype", FR_CONF_OFFSET(PW_TYPE_DEPRECATED | PW_TYPE_STRING, RADCLIENT, nas_type), NULL },
+       { "nas_type", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, nas_type), NULL },
+       { "login", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, login), NULL },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, password), NULL },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, server), NULL },
+       { "response_window", FR_CONF_OFFSET(PW_TYPE_TIMEVAL, RADCLIENT, response_window), NULL },
+
+#ifdef WITH_TCP
+       { "proto", FR_CONF_POINTER(PW_TYPE_STRING, &hs_proto), NULL },
+       { "limit", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) limit_config },
 #endif
 
 #ifdef WITH_DYNAMIC_CLIENTS
-       { "dynamic_clients",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, client_server), 0, NULL },
-       { "lifetime",  PW_TYPE_INTEGER,
-         offsetof(RADCLIENT, lifetime), 0, NULL },
-       { "rate_limit",  PW_TYPE_BOOLEAN,
-         offsetof(RADCLIENT, rate_limit), 0, NULL },
+       { "dynamic_clients", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, client_server), NULL },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, RADCLIENT, lifetime), NULL },
+       { "rate_limit", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, rate_limit), NULL },
 #endif
 
 #ifdef WITH_COA
-       { "coa_server",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, coa_name), 0, NULL },
+       { "coa_server", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, coa_name), NULL },
 #endif
 
        { NULL, -1, 0, NULL, NULL }
@@ -575,17 +480,15 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
        }
 
        /*
-        * The size is fine.. Let's create the buffer
+        *      The size is fine.. Let's create the buffer
         */
        c = talloc_zero(cs, RADCLIENT);
        c->cs = cs;
 
-       memset(&cl_ip4addr, 0, sizeof(cl_ip4addr));
-       memset(&cl_ip6addr, 0, sizeof(cl_ip6addr));
-       c->prefix = -1;
-
+       memset(&cl_ipaddr, 0, sizeof(cl_ipaddr));
+       cl_prefix = 256;
        if (cf_section_parse(cs, c, client_config) < 0) {
-               cf_log_err_cs(cs, "Error parsing client section.");
+               cf_log_err_cs(cs, "Error parsing client section");
        error:
                client_free(c);
 #ifdef WITH_TCP
@@ -597,124 +500,117 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
        }
 
        /*
-        *      Global clients can set servers to use,
-        *      per-server clients cannot.
+        *      Global clients can set servers to use per-server clients cannot.
         */
        if (in_server && c->server) {
-               cf_log_err_cs(cs,
-                          "Clients inside of an server section cannot point to a server.");
+               cf_log_err_cs(cs, "Clients inside of an server section cannot point to a server");
                goto error;
        }
 
        /*
-        *      No "ipaddr" or "ipv6addr", use old-style
-        *      "client <ipaddr> {" syntax.
+        *      Newer style client definitions with either ipaddr or ipaddr6
+        *      config items.
         */
-       if (!cf_pair_find(cs, "ipaddr") &&
-           !cf_pair_find(cs, "ipv6addr")) {
-               char *prefix_ptr;
-
-               prefix_ptr = strchr(name2, '/');
+       if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) {
+               char buffer[128];
 
                /*
-                *      Look for prefixes.
+                *      Sets ipv4/ipv6 address and prefix.
                 */
-               if (prefix_ptr) {
-                       c->prefix = atoi(prefix_ptr + 1);
-                       if ((c->prefix < 0) || (c->prefix > 128)) {
-                               cf_log_err_cs(cs, "Invalid Prefix value '%s' for IP.", prefix_ptr + 1);
-                               goto error;
-                       }
-                       /* Replace '/' with '\0' */
-                       *prefix_ptr = '\0';
-               }
+               c->ipaddr = cl_ipaddr;
 
                /*
-                *      Always get the numeric representation of IP
+                *      Set the long name to be the result of a reverse lookup on the IP address.
                 */
-               if (ip_hton(name2, AF_UNSPEC, &c->ipaddr) < 0) {
-                       cf_log_err_cs(cs,
-                                  "Failed to look up hostname %s: %s",
-                                  name2, fr_strerror());
-                       goto error;
-               }
-
-               if (prefix_ptr) *prefix_ptr = '/';
-               c->longname = talloc_strdup(c, name2);
-
-               if (!c->shortname) c->shortname = talloc_strdup(c, c->longname);
-
-       } else {
-               char buffer[1024];
+               ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
+               c->longname = talloc_typed_strdup(c, buffer);
 
                /*
-                *      Figure out which one to use.
+                *      Set the short name to the name2.
                 */
-               if (cf_pair_find(cs, "ipaddr")) {
-                       c->ipaddr.af = AF_INET;
-                       c->ipaddr.ipaddr.ip4addr = cl_ip4addr;
+               if (!c->shortname) c->shortname = talloc_typed_strdup(c, name2);
+       /*
+        *      No "ipaddr" or "ipv6addr", use old-style "client <ipaddr> {" syntax.
+        */
+       } else {
+               WARN("No 'ipaddr' or 'ipv4addr' or 'ipv6addr' field found in client %s.  Please fix your configuration",
+                    name2);
+               WARN("Support for old-style clients will be removed in a future release");
 
-                       if ((c->prefix < -1) || (c->prefix > 32)) {
-                               cf_log_err_cs(cs, "Netmask must be between 0 and 32");
+#ifdef WITH_TCP
+               if (cf_pair_find(cs, "proto") != NULL) {
+                       cf_log_err_cs(cs, "Cannot use 'proto' inside of old-style client definition");
+                       goto error;
+               }
+#endif
+               if (fr_pton(&c->ipaddr, name2, 0, true) < 0) {
+                       cf_log_err_cs(cs, "Failed parsing client name \"%s\" as ip address or hostname: %s", name2,
+                                     fr_strerror());
+                       goto error;
+               }
+
+               c->longname = talloc_typed_strdup(c, name2);
+               if (!c->shortname) c->shortname = talloc_typed_strdup(c, c->longname);
+       }
 
+       /*
+        *      Prefix override (this needs to die)
+        */
+       if (cl_prefix != 256) {
+               WARN("'netmask' field found in client %s is deprecated, use CIDR notation instead.  "
+                    "Please fix your configuration", name2);
+               WARN("Support for 'netmask' will be removed in a future release");
+
+               switch (c->ipaddr.af) {
+               case AF_INET:
+                       if (cl_prefix > 32) {
+                               cf_log_err_cs(cs, "Invalid IPv4 mask length \"%i\".  Should be between 0-32",
+                                             cl_prefix);
                                goto error;
                        }
+                       break;
 
-               } else if (cf_pair_find(cs, "ipv6addr")) {
-                       c->ipaddr.af = AF_INET6;
-                       c->ipaddr.ipaddr.ip6addr = cl_ip6addr;
-
-                       if ((c->prefix < -1) || (c->prefix > 128)) {
-                               cf_log_err_cs(cs,
-                                          "Netmask must be between 0 and 128");
+               case AF_INET6:
+                       if (cl_prefix > 128) {
+                               cf_log_err_cs(cs, "Invalid IPv6 mask length \"%i\".  Should be between 0-128",
+                                             cl_prefix);
                                goto error;
                        }
-               } else {
-                       cf_log_err_cs(cs,
-                                  "No IP address defined for the client");
-                       goto error;
-               }
-
-               ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
-               c->longname = talloc_strdup(c, buffer);
+                       break;
 
-               /*
-                *      Set the short name to the name2
-                */
-               if (!c->shortname) c->shortname = talloc_strdup(c, name2);
+               default:
+                       rad_assert(0);
+               }
+               fr_ipaddr_mask(&c->ipaddr, cl_prefix);
+       }
 
-               c->proto = IPPROTO_UDP;
-               if (hs_proto) {
-                       if (strcmp(hs_proto, "udp") == 0) {
-                               hs_proto = NULL;
+       c->proto = IPPROTO_UDP;
+       if (hs_proto) {
+               if (strcmp(hs_proto, "udp") == 0) {
+                       hs_proto = NULL;
 
 #ifdef WITH_TCP
-                       } else if (strcmp(hs_proto, "tcp") == 0) {
-                               hs_proto = NULL;
-                               c->proto = IPPROTO_TCP;
-
-#ifdef WITH_TLS
-                       } else if (strcmp(hs_proto, "tls") == 0) {
-                               hs_proto = NULL;
-                               c->proto = IPPROTO_TCP;
-                               c->tls_required = true;
-
-                       } else if (strcmp(hs_proto, "radsec") == 0) {
-                               hs_proto = NULL;
-                               c->proto = IPPROTO_TCP;
-                               c->tls_required = true;
-#endif
-
-                       } else if (strcmp(hs_proto, "*") == 0) {
-                               hs_proto = NULL;
-                               c->proto = IPPROTO_IP; /* fake for dual */
+               } else if (strcmp(hs_proto, "tcp") == 0) {
+                       hs_proto = NULL;
+                       c->proto = IPPROTO_TCP;
+#  ifdef WITH_TLS
+               } else if (strcmp(hs_proto, "tls") == 0) {
+                       hs_proto = NULL;
+                       c->proto = IPPROTO_TCP;
+                       c->tls_required = true;
+
+               } else if (strcmp(hs_proto, "radsec") == 0) {
+                       hs_proto = NULL;
+                       c->proto = IPPROTO_TCP;
+                       c->tls_required = true;
+#  endif
+               } else if (strcmp(hs_proto, "*") == 0) {
+                       hs_proto = NULL;
+                       c->proto = IPPROTO_IP; /* fake for dual */
 #endif
-
-                       } else {
-                               cf_log_err_cs(cs,
-                                          "Unknown proto \"%s\".", hs_proto);
-                               goto error;
-                       }
+               } else {
+                       cf_log_err_cs(cs, "Unknown proto \"%s\".", hs_proto);
+                       goto error;
                }
        }
 
@@ -725,38 +621,40 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
         */
        if (cl_srcipaddr) {
 #ifdef WITH_UDPFROMTO
-               if (ip_hton(cl_srcipaddr, c->ipaddr.af, &c->src_ipaddr) < 0) {
-                       cf_log_err_cs(cs, "Failed parsing src_ipaddr");
-                       goto error;
+               switch (c->ipaddr.af) {
+               case AF_INET:
+                       if (fr_pton4(&c->src_ipaddr, cl_srcipaddr, 0, true, false) < 0) {
+                               cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror());
+                               goto error;
+                       }
+                       break;
+
+               case AF_INET6:
+                       if (fr_pton6(&c->src_ipaddr, cl_srcipaddr, 0, true, false) < 0) {
+                               cf_log_err_cs(cs, "Failed parsing src_ipaddr: %s", fr_strerror());
+                               goto error;
+                       }
+                       break;
+               default:
+                       rad_assert(0);
                }
 #else
-               WDEBUG("Server not build with udpfromto, ignoring client src_ipaddr");
+               WARN("Server not built with udpfromto, ignoring client src_ipaddr");
 #endif
-
                cl_srcipaddr = NULL;
        }
 
-       if (c->prefix < 0) switch (c->ipaddr.af) {
-       case AF_INET:
-               c->prefix = 32;
-               break;
-       case AF_INET6:
-               c->prefix = 128;
-               break;
-       default:
-               break;
-       }
+       FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, >=, 0, 1000);
+       FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, 60, 0);
+       FR_TIMEVAL_BOUND_CHECK("response_window", &c->response_window, <=, main_config.max_request_time, 0);
 
 #ifdef WITH_DYNAMIC_CLIENTS
        if (c->client_server) {
-               c->secret = talloc_strdup(c, "testing123");
+               c->secret = talloc_typed_strdup(c, "testing123");
 
-               if (((c->ipaddr.af == AF_INET) &&
-                    (c->prefix == 32)) ||
-                   ((c->ipaddr.af == AF_INET6) &&
-                    (c->prefix == 128))) {
-                       cf_log_err_cs(cs,
-                                  "Dynamic clients MUST be a network, not a single IP address.");
+               if (((c->ipaddr.af == AF_INET) && (c->ipaddr.prefix == 32)) ||
+                   ((c->ipaddr.af == AF_INET6) && (c->ipaddr.prefix == 128))) {
+                       cf_log_err_cs(cs, "Dynamic clients MUST be a network, not a single IP address");
                        goto error;
                }
 
@@ -764,7 +662,7 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
        }
 #endif
 
-       if (!c->secret || !*c->secret) {
+       if (!c->secret || (c->secret[0] == '\0')) {
 #ifdef WITH_DHCP
                char const *value = NULL;
                CONF_PAIR *cp = cf_pair_find(cs, "dhcp");
@@ -775,7 +673,6 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
                 *      Secrets aren't needed for DHCP.
                 */
                if (value && (strcmp(value, "yes") == 0)) return c;
-
 #endif
 
 #ifdef WITH_TLS
@@ -785,7 +682,7 @@ static RADCLIENT *client_parse(CONF_SECTION *cs, int in_server)
                 *      "radsec".  See RFC 6614.
                 */
                if (c->tls_required) {
-                       c->secret = talloc_strdup(cs, "radsec");
+                       c->secret = talloc_typed_strdup(cs, "radsec");
                } else
 #endif
 
@@ -841,7 +738,7 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, bool tls_required)
 RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_required)
 #endif
 {
-       int             global = false, in_server = false;
+       bool            global = false, in_server = false;
        CONF_SECTION    *cs;
        RADCLIENT       *c;
        RADCLIENT_LIST  *clients;
@@ -885,7 +782,10 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
                 *      non-TLS clients CANNOT use TLS listeners.
                 */
                if (tls_required != c->tls_required) {
-                       WARN("Security mismatch (TLS / non-TLS) between client and socket.");
+                       cf_log_err_cs(cs, "Client does not have the same TLS configuration as the listener");
+                       client_free(c);
+                       clients_free(clients);
+                       return NULL;
                }
 #endif
 
@@ -923,7 +823,7 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
 
                        dir = opendir(value);
                        if (!dir) {
-                               cf_log_err_cs(cs, "Error reading directory %s: %s", value, strerror(errno));
+                               cf_log_err_cs(cs, "Error reading directory %s: %s", value, fr_syserror(errno));
                                client_free(c);
                                return NULL;
                        }
@@ -977,9 +877,9 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
                        closedir(dir);
                }
 #endif /* HAVE_DIRENT_H */
-#endif /* WITH_DYNAMIC_CLIENTS */
 
        add_client:
+#endif /* WITH_DYNAMIC_CLIENTS */
                if (!client_add(clients, c)) {
                        cf_log_err_cs(cs,
                                   "Failed to add client %s",
@@ -1007,26 +907,19 @@ RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_req
  *     We overload this structure a lot.
  */
 static const CONF_PARSER dynamic_config[] = {
-       { "FreeRADIUS-Client-IP-Address",  PW_TYPE_IPADDR,
-         offsetof(RADCLIENT, ipaddr), 0, NULL },
-       { "FreeRADIUS-Client-IPv6-Address",  PW_TYPE_IPV6ADDR,
-         offsetof(RADCLIENT, ipaddr), 0, NULL },
-       { "FreeRADIUS-Client-Src-IP-Address",  PW_TYPE_IPADDR,
-         offsetof(RADCLIENT, src_ipaddr), 0, NULL },
-       { "FreeRADIUS-Client-Src-IPv6-Address",  PW_TYPE_IPV6ADDR,
-         offsetof(RADCLIENT, src_ipaddr), 0, NULL },
-
-       { "FreeRADIUS-Client-Require-MA",  PW_TYPE_BOOLEAN,
-         offsetof(RADCLIENT, message_authenticator), NULL, NULL },
-
-       { "FreeRADIUS-Client-Secret",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, secret), 0, "" },
-       { "FreeRADIUS-Client-Shortname",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, shortname), 0, "" },
-       { "FreeRADIUS-Client-NAS-Type",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, nas_type), 0, NULL },
-       { "FreeRADIUS-Client-Virtual-Server",  PW_TYPE_STRING_PTR,
-         offsetof(RADCLIENT, server), 0, NULL },
+       { "FreeRADIUS-Client-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, ipaddr), NULL },
+       { "FreeRADIUS-Client-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, ipaddr), NULL },
+       { "FreeRADIUS-Client-IP-Prefix", FR_CONF_OFFSET(PW_TYPE_IPV4_PREFIX, RADCLIENT, ipaddr), NULL },
+       { "FreeRADIUS-Client-IPv6-Prefix", FR_CONF_OFFSET(PW_TYPE_IPV6_PREFIX, RADCLIENT, ipaddr), NULL },
+       { "FreeRADIUS-Client-Src-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, src_ipaddr), NULL },
+       { "FreeRADIUS-Client-Src-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, src_ipaddr), NULL },
+
+       { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), NULL },
+
+       { "FreeRADIUS-Client-Secret",  FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, secret), "" },
+       { "FreeRADIUS-Client-Shortname",  FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), "" },
+       { "FreeRADIUS-Client-NAS-Type",  FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, nas_type), NULL },
+       { "FreeRADIUS-Client-Virtual-Server",  FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, server), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -1041,7 +934,7 @@ bool client_validate(RADCLIENT_LIST *clients, RADCLIENT *master, RADCLIENT *c)
         *      definition.
         */
        if (master->server && !c->server) {
-               c->server = talloc_strdup(c, master->server);
+               c->server = talloc_typed_strdup(c, master->server);
        }
 
        /*
@@ -1066,26 +959,26 @@ bool client_validate(RADCLIENT_LIST *clients, RADCLIENT *master, RADCLIENT *c)
        /*
         *      Initialize the remaining fields.
         */
-       c->dynamic = 1;
+       c->dynamic = true;
        c->lifetime = master->lifetime;
        c->created = time(NULL);
-       c->longname = talloc_strdup(c, c->shortname);
+       c->longname = talloc_typed_strdup(c, c->shortname);
 
        DEBUG("- Added client %s with shared secret %s",
              ip_ntoh(&c->ipaddr, buffer, sizeof(buffer)),
              c->secret);
 
-       return 1;
+       return true;
 
 error:
        client_free(c);
-       return 0;
+       return false;
 }
 
 /** Add a client from a result set (LDAP, SQL, et al)
  *
  * @param ctx Talloc context.
- * @param identifier Client IP Address / IPv4 subnet / FQDN.
+ * @param identifier Client IP Address / IPv4 subnet / IPv6 subnet / FQDN.
  * @param secret Client secret.
  * @param shortname Client friendly name.
  * @param type NAS-Type.
@@ -1097,82 +990,33 @@ RADCLIENT *client_from_query(TALLOC_CTX *ctx, char const *identifier, char const
                             char const *type, char const *server, bool require_ma)
 {
        RADCLIENT *c;
-       char *id;
-       char *prefix;
+       char buffer[128];
 
        rad_assert(identifier);
        rad_assert(secret);
 
        c = talloc_zero(ctx, RADCLIENT);
 
-#ifdef WITH_DYNAMIC_CLIENTS
-       c->dynamic = 1;
-#endif
-
-       id = talloc_strdup(c, identifier);
-
-       /*
-        *      Look for prefixes
-        */
-       c->prefix = -1;
-       prefix = strchr(id, '/');
-       if (prefix) {
-               c->prefix = atoi(prefix + 1);
-               if ((c->prefix < 0) || (c->prefix > 128)) {
-                       ERROR("Invalid Prefix value '%s' for IP.", prefix + 1);
-                       talloc_free(c);
-
-                       return NULL;
-               }
-
-               /* Replace '/' with '\0' */
-               *prefix = '\0';
-       }
-
-       /*
-        *      Always get the numeric representation of IP
-        */
-       if (ip_hton(id, AF_UNSPEC, &c->ipaddr) < 0) {
-               ERROR("Failed to look up hostname %s: %s", id, fr_strerror());
+       if (fr_pton(&c->ipaddr, identifier, 0, true) < 0) {
+               ERROR("%s", fr_strerror());
                talloc_free(c);
 
                return NULL;
        }
 
-       {
-               char buffer[256];
-               ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
-               c->longname = talloc_strdup(c, buffer);
-       }
-
-       if (c->prefix < 0) switch (c->ipaddr.af) {
-       case AF_INET:
-               c->prefix = 32;
-               break;
-       case AF_INET6:
-               c->prefix = 128;
-               break;
-       default:
-               break;
-       }
+#ifdef WITH_DYNAMIC_CLIENTS
+       c->dynamic = true;
+#endif
+       ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
+       c->longname = talloc_typed_strdup(c, buffer);
 
        /*
         *      Other values (secret, shortname, nas_type, virtual_server)
         */
-       c->secret = talloc_strdup(c, secret);
-
-       if (shortname) {
-               c->shortname = talloc_strdup(c, shortname);
-       }
-
-       if (type) {
-               c->nas_type = talloc_strdup(c, type);
-       }
-
-       if (server) {
-               c->server = talloc_strdup(c, server);
-       }
-
+       c->secret = talloc_typed_strdup(c, secret);
+       if (shortname) c->shortname = talloc_typed_strdup(c, shortname);
+       if (type) c->nas_type = talloc_typed_strdup(c, type);
+       if (server) c->server = talloc_typed_strdup(c, server);
        c->message_authenticator = require_ma;
 
        return c;
@@ -1222,43 +1066,61 @@ RADCLIENT *client_from_request(RADCLIENT_LIST *clients, REQUEST *request)
                }
 
                switch (dynamic_config[i].type) {
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        if (da->attr == PW_FREERADIUS_CLIENT_IP_ADDRESS) {
                                c->ipaddr.af = AF_INET;
                                c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
-                               c->prefix = 32;
+                               c->ipaddr.prefix = 32;
                        } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS) {
 #ifdef WITH_UDPFROMTO
                                c->src_ipaddr.af = AF_INET;
                                c->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
 #else
-                               WDEBUG("Server not build with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address.");
+                               WARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address");
 #endif
                        }
 
                        break;
 
-               case PW_TYPE_IPV6ADDR:
-                       if (da->attr == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) {
+               case PW_TYPE_IPV6_ADDR:
+                       if (da->attr == PW_FREERADIUS_CLIENT_IPV6_ADDRESS) {
                                c->ipaddr.af = AF_INET6;
                                c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
-                               c->prefix = 128;
+                               c->ipaddr.prefix = 128;
                        } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) {
 #ifdef WITH_UDPFROMTO
                                c->src_ipaddr.af = AF_INET6;
                                c->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
 #else
-                               WDEBUG("Server not build with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address.");
+                               WARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address");
 #endif
                        }
 
                        break;
 
-               case PW_TYPE_STRING_PTR:
+               case PW_TYPE_IPV4_PREFIX:
+                       if (da->attr == PW_FREERADIUS_CLIENT_IP_PREFIX) {
+                               c->ipaddr.af = AF_INET;
+                               memcpy(&c->ipaddr.ipaddr.ip4addr.s_addr, &(vp->vp_ipv4prefix[2]), sizeof(c->ipaddr.ipaddr.ip4addr.s_addr));
+                               fr_ipaddr_mask(&c->ipaddr, (vp->vp_ipv4prefix[1] & 0x3f));
+                       }
+
+                       break;
+
+               case PW_TYPE_IPV6_PREFIX:
+                       if (da->attr == PW_FREERADIUS_CLIENT_IPV6_PREFIX) {
+                               c->ipaddr.af = AF_INET6;
+                               memcpy(&c->ipaddr.ipaddr.ip6addr, &(vp->vp_ipv6prefix[2]), sizeof(c->ipaddr.ipaddr.ip6addr));
+                               fr_ipaddr_mask(&c->ipaddr, vp->vp_ipv6prefix[1]);
+                       }
+
+                       break;
+
+               case PW_TYPE_STRING:
                        p = (char **) ((char *) c + dynamic_config[i].offset);
                        if (*p) talloc_free(*p);
                        if (vp->vp_strvalue[0]) {
-                               *p = talloc_strdup(c->cs, vp->vp_strvalue);
+                               *p = talloc_typed_strdup(c->cs, vp->vp_strvalue);
                        } else {
                                *p = NULL;
                        }
@@ -1282,15 +1144,24 @@ RADCLIENT *client_from_request(RADCLIENT_LIST *clients, REQUEST *request)
                goto error;
        }
 
-       if (fr_ipaddr_cmp(&request->packet->src_ipaddr, &c->ipaddr) != 0) {
-               char buf2[128];
+       {
+               fr_ipaddr_t addr;
 
-               DEBUG("- Cannot add client %s: IP address %s do not match",
-                     ip_ntoh(&request->packet->src_ipaddr,
-                             buffer, sizeof(buffer)),
-                     ip_ntoh(&c->ipaddr,
-                             buf2, sizeof(buf2)));
-               goto error;
+               /*
+                *      Need to apply the same mask as we set for the client
+                *      else clients created with FreeRADIUS-Client-IPv6-Prefix
+                *      or FreeRADIUS-Client-IPv4-Prefix will fail this check.
+                */
+               addr = request->packet->src_ipaddr;
+               fr_ipaddr_mask(&addr, c->ipaddr.prefix);
+               if (fr_ipaddr_cmp(&addr, &c->ipaddr) != 0) {
+                       char buf2[128];
+
+                       DEBUG("- Cannot add client %s: IP address %s do not match",
+                             ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)),
+                             ip_ntoh(&c->ipaddr, buf2, sizeof(buf2)));
+                       goto error;
+               }
        }
 
        if (!c->secret || !*c->secret) {
@@ -1361,3 +1232,4 @@ RADCLIENT *client_read(char const *filename, int in_server, int flag)
        return c;
 }
 #endif
+
diff --git a/src/main/collectd.c b/src/main/collectd.c
new file mode 100644 (file)
index 0000000..1310e04
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file collectd.c
+ * @brief Helper functions to enabled radsniff to talk to collectd
+ *
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+#ifdef HAVE_COLLECTDC_H
+#include <assert.h>
+#include <ctype.h>
+
+#include <collectd/client.h>
+#include <freeradius-devel/radsniff.h>
+
+/** Copy a 64bit unsigned integer into a double
+ *
+ */
+/*
+static void _copy_uint64_to_double(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
+{
+       assert(tmpl->src);
+       assert(tmpl->dst);
+
+       *((double *) tmpl->dst) = *((uint64_t *) tmpl->src);
+}
+*/
+
+/*
+static void _copy_uint64_to_uint64(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
+{
+       assert(tmpl->src);
+       assert(tmpl->dst);
+
+       *((uint64_t *) tmpl->dst) = *((uint64_t *) tmpl->src);
+}
+*/
+
+static void _copy_double_to_double(UNUSED rs_t *conf, rs_stats_value_tmpl_t *tmpl)
+{
+       assert(tmpl->src);
+       assert(tmpl->dst);
+
+       *((double *) tmpl->dst) = *((double*) tmpl->src);
+}
+
+
+/** Allocates a stats template which describes a single guage/counter
+ *
+ * This is just intended to simplify allocating a fairly complex memory structure
+ * src and dst pointers must be set
+ *
+ * @param ctx Context to allocate collectd struct in.
+ * @param conf Radsniff configuration.
+ * @param plugin_instance usually the type of packet (in our case).
+ * @param type string, the name of a collection of stats e.g. exchange
+ * @param type_instance the name of the counter/guage within the collection e.g. latency.
+ * @param stats structure to derive statistics from.
+ * @param values Value templates used to populate lcc_value_list.
+ * @return a new rs_stats_tmpl_t on success or NULL on failure.
+ */
+static rs_stats_tmpl_t *rs_stats_collectd_init(TALLOC_CTX *ctx, rs_t *conf,
+                                              char const *plugin_instance,
+                                              char const *type, char const *type_instance,
+                                              void *stats,
+                                              rs_stats_value_tmpl_t const *values)
+{
+       static char hostname[255];
+       static char fqdn[LCC_NAME_LEN];
+
+       size_t len;
+       int i;
+       char *p;
+
+       rs_stats_tmpl_t *tmpl;
+       lcc_value_list_t *value;
+
+       assert(conf);
+       assert(type);
+       assert(type_instance);
+
+       for (len = 0; values[len].src; len++) {} ;
+       assert(len > 0);
+
+       /*
+        *      Initialise hostname once so we don't call gethostname every time
+        */
+       if (*fqdn == '\0') {
+               int ret;
+               struct addrinfo hints, *info = NULL;
+
+               if (gethostname(hostname, sizeof(hostname)) < 0) {
+                       ERROR("Error getting hostname: %s", fr_syserror(errno));
+
+                       return NULL;
+               }
+
+               memset(&hints, 0, sizeof hints);
+               hints.ai_family = AF_UNSPEC; /*either IPV4 or IPV6*/
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_flags = AI_CANONNAME;
+
+               if ((ret = getaddrinfo(hostname, "radius", &hints, &info)) != 0) {
+                       ERROR("Error getting hostname: %s", gai_strerror(ret));
+                       return NULL;
+               }
+
+               strlcpy(fqdn, info->ai_canonname, sizeof(fqdn));
+
+               freeaddrinfo(info);
+       }
+
+       tmpl = talloc_zero(ctx, rs_stats_tmpl_t);
+       if (!tmpl) {
+               return NULL;
+       }
+
+       tmpl->value_tmpl = talloc_zero_array(tmpl, rs_stats_value_tmpl_t, len);
+       if (!tmpl->value_tmpl) {
+               goto error;
+       }
+
+       tmpl->stats = stats;
+
+       value = talloc_zero(tmpl, lcc_value_list_t);
+       if (!value) {
+               goto error;
+       }
+       tmpl->value = value;
+
+       value->interval = conf->stats.interval;
+       value->values_len = len;
+
+       value->values_types = talloc_zero_array(value, int, len);
+       if (!value->values_types) {
+               goto error;
+       }
+
+       value->values = talloc_zero_array(value, value_t, len);
+       if (!value->values) {
+               goto error;
+       }
+
+       for (i = 0; i < (int) len; i++) {
+               assert(values[i].src);
+               assert(values[i].cb);
+
+               tmpl->value_tmpl[i] = values[i];
+               switch (tmpl->value_tmpl[i].type) {
+                       case LCC_TYPE_COUNTER:
+                               tmpl->value_tmpl[i].dst = &value->values[i].counter;
+                               break;
+
+                       case LCC_TYPE_GAUGE:
+                               tmpl->value_tmpl[i].dst = &value->values[i].gauge;
+                               break;
+
+                       case LCC_TYPE_DERIVE:
+                               tmpl->value_tmpl[i].dst = &value->values[i].derive;
+                               break;
+
+                       case LCC_TYPE_ABSOLUTE:
+                               tmpl->value_tmpl[i].dst = &value->values[i].absolute;
+                               break;
+                       default:
+                               assert(0);
+               }
+               value->values_types[i] = tmpl->value_tmpl[i].type;
+       }
+
+       /*
+        *      These should be OK as is
+        */
+       strlcpy(value->identifier.host, fqdn, sizeof(value->identifier.host));
+
+       /*
+        *      Plugin is ASCII only and no '/'
+        */
+       fr_print_string(conf->stats.prefix, strlen(conf->stats.prefix),
+                       value->identifier.plugin, sizeof(value->identifier.plugin));
+       for (p = value->identifier.plugin; *p; ++p) {
+               if ((*p == '-') || (*p == '/'))*p = '_';
+       }
+
+       /*
+        *      Plugin instance is ASCII only (assuming printable only) and no '/'
+        */
+       fr_print_string(plugin_instance, strlen(plugin_instance),
+                       value->identifier.plugin_instance, sizeof(value->identifier.plugin_instance));
+       for (p = value->identifier.plugin_instance; *p; ++p) {
+               if ((*p == '-') || (*p == '/')) *p = '_';
+       }
+
+       /*
+        *      Type is ASCII only (assuming printable only) and no '/' or '-'
+        */
+       fr_print_string(type, strlen(type),
+                       value->identifier.type, sizeof(value->identifier.type));
+       for (p = value->identifier.type; *p; ++p) {
+               if ((*p == '-') || (*p == '/')) *p = '_';
+       }
+
+       fr_print_string(type_instance, strlen(type_instance),
+                       value->identifier.type_instance, sizeof(value->identifier.type_instance));
+       for (p = value->identifier.type_instance; *p; ++p) {
+               if ((*p == '-') || (*p == '/')) *p = '_';
+       }
+
+
+       return tmpl;
+
+       error:
+       talloc_free(tmpl);
+       return NULL;
+}
+
+
+/** Setup stats templates for latency
+ *
+ */
+rs_stats_tmpl_t *rs_stats_collectd_init_latency(TALLOC_CTX *ctx, rs_stats_tmpl_t **out, rs_t *conf,
+                                               char const *type, rs_latency_t *stats, PW_CODE code)
+{
+       rs_stats_tmpl_t **tmpl, *last;
+       char *p;
+       char buffer[LCC_NAME_LEN];
+       tmpl = out;
+
+       rs_stats_value_tmpl_t rtx[(RS_RETRANSMIT_MAX + 1) + 1 + 1];     // RTX bins + 0 bin + lost + NULL
+       int i;
+
+       /* not static so were thread safe */
+       rs_stats_value_tmpl_t const _packet_count[] = {
+               { &stats->interval.received, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { &stats->interval.linked, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { &stats->interval.unlinked, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { &stats->interval.reused, LCC_TYPE_GAUGE,  _copy_double_to_double, NULL },
+               { NULL, 0, NULL, NULL }
+       };
+
+       rs_stats_value_tmpl_t const _latency[] = {
+               { &stats->latency_smoothed, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { &stats->interval.latency_average, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { &stats->interval.latency_high, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { &stats->interval.latency_low, LCC_TYPE_GAUGE, _copy_double_to_double, NULL },
+               { NULL, 0, NULL, NULL }
+       };
+
+#define INIT_STATS(_ti, _v) do {\
+               strlcpy(buffer, fr_packet_codes[code], sizeof(buffer)); \
+               for (p = buffer; *p; ++p) *p = tolower(*p);\
+               last = *tmpl = rs_stats_collectd_init(ctx, conf, type, _ti, buffer, stats, _v);\
+               if (!*tmpl) {\
+                       TALLOC_FREE(*out);\
+                       return NULL;\
+               }\
+               tmpl = &(*tmpl)->next;\
+               ctx = *tmpl;\
+               } while (0)
+
+
+       INIT_STATS("radius_count", _packet_count);
+       INIT_STATS("radius_latency", _latency);
+
+       for (i = 0; i < (RS_RETRANSMIT_MAX + 1); i++) {
+               rtx[i].src = &stats->interval.rt[i];
+               rtx[i].type = LCC_TYPE_GAUGE;
+               rtx[i].cb = _copy_double_to_double;
+               rtx[i].dst = NULL;
+       }
+
+       rtx[i].src = &stats->interval.lost;
+       rtx[i].type = LCC_TYPE_GAUGE;
+       rtx[i].cb = _copy_double_to_double;
+       rtx[i].dst = NULL;
+
+       memset(&rtx[++i], 0, sizeof(rs_stats_value_tmpl_t));
+
+       INIT_STATS("radius_rtx", rtx);
+
+       return last;
+}
+
+/** Refresh and send the stats to the collectd server
+ *
+ */
+void rs_stats_collectd_do_stats(rs_t *conf, rs_stats_tmpl_t *tmpls, struct timeval *now)
+{
+       rs_stats_tmpl_t *tmpl = tmpls;
+       char identifier[6 * LCC_NAME_LEN];
+       int i;
+
+       while (tmpl) {
+               /*
+                *      Refresh the value of whatever were sending
+                */
+               for (i = 0; i < (int) tmpl->value->values_len; i++) {
+                       tmpl->value_tmpl[i].cb(conf, &tmpl->value_tmpl[i]);
+               }
+
+               tmpl->value->time = now->tv_sec;
+
+               lcc_identifier_to_string(conf->stats.handle, identifier, sizeof(identifier), &tmpl->value->identifier);
+
+               if (lcc_putval(conf->stats.handle, tmpl->value) < 0) {
+                       char const *error;
+
+                       error = lcc_strerror(conf->stats.handle);
+                       ERROR("Failed PUTVAL \"%s\" interval=%i %" PRIu64 " : %s",
+                             identifier,
+                             (int) tmpl->value->interval,
+                             (uint64_t) tmpl->value->time,
+                             error ? error : "unknown error");
+               }
+
+               tmpl = tmpl->next;
+       }
+}
+
+/** Connect to a collectd server for stats output
+ *
+ * @param[in,out] conf radsniff configuration, we write the generated handle here.
+ * @return 0 on success -1 on failure.
+ */
+int rs_stats_collectd_open(rs_t *conf)
+{
+       assert(conf->stats.collectd);
+
+       /*
+        *      Tear down stale connections gracefully.
+        */
+       rs_stats_collectd_close(conf);
+
+       /*
+        *      There's no way to get the error from the connection handle
+        *      because it's freed on failure, before lcc returns.
+        */
+       if (lcc_connect(conf->stats.collectd, &conf->stats.handle) < 0) {
+               ERROR("Failed opening connection to collectd: %s", fr_syserror(errno));
+               return -1;
+       }
+       DEBUG2("Connected to \"%s\"", conf->stats.collectd);
+
+       assert(conf->stats.handle);
+       return 0;
+}
+
+/** Close connection
+ *
+ * @param[in,out] conf radsniff configuration.
+ * @return 0 on success -1 on failure.
+ */
+int rs_stats_collectd_close(rs_t *conf)
+{
+       assert(conf->stats.collectd);
+
+       int ret = 0;
+
+       if (conf->stats.handle) {
+               ret = lcc_disconnect(conf->stats.handle);
+               conf->stats.handle = NULL;
+       }
+
+       return ret;
+}
+#endif
index 81c4335..61ec2e1 100644 (file)
@@ -23,7 +23,6 @@
 
 #ifdef WITH_COMMAND_SOCKET
 
-#include <freeradius-devel/modpriv.h>
 #include <freeradius-devel/parser.h>
 
 #ifdef HAVE_INTTYPES_H
@@ -67,23 +66,23 @@ struct fr_command_table_t {
 #define COMMAND_BUFFER_SIZE (1024)
 
 typedef struct fr_cs_buffer_t {
-       int     auth;
-       int     mode;
-       ssize_t offset;
-       ssize_t next;
-       char buffer[COMMAND_BUFFER_SIZE];
+       int             auth;
+       int             mode;
+       ssize_t         offset;
+       ssize_t         next;
+       char            buffer[COMMAND_BUFFER_SIZE];
 } fr_cs_buffer_t;
 
 #define COMMAND_SOCKET_MAGIC (0xffdeadee)
 typedef struct fr_command_socket_t {
-       uint32_t magic;
-       char    *path;
-       char    *copy;          /* <sigh> */
-       uid_t   uid;
-       gid_t   gid;
-       char    *uid_name;
-       char    *gid_name;
-       char    *mode_name;
+       uint32_t        magic;
+       char const      *path;
+       char            *copy;          /* <sigh> */
+       uid_t           uid;
+       gid_t           gid;
+       char const      *uid_name;
+       char const      *gid_name;
+       char const      *mode_name;
        char user[256];
 
        /*
@@ -92,7 +91,7 @@ typedef struct fr_command_socket_t {
         */
        fr_ipaddr_t     src_ipaddr; /* src_port is always 0 */
        fr_ipaddr_t     dst_ipaddr;
-       int             dst_port;
+       uint16_t        dst_port;
        rad_listen_t    *inject_listener;
        RADCLIENT       *inject_client;
 
@@ -100,16 +99,12 @@ typedef struct fr_command_socket_t {
 } fr_command_socket_t;
 
 static const CONF_PARSER command_config[] = {
-  { "socket",  PW_TYPE_STRING_PTR,
-    offsetof(fr_command_socket_t, path), NULL, "${run_dir}/radiusd.sock"},
-  { "uid",  PW_TYPE_STRING_PTR,
-    offsetof(fr_command_socket_t, uid_name), NULL, NULL},
-  { "gid",  PW_TYPE_STRING_PTR,
-    offsetof(fr_command_socket_t, gid_name), NULL, NULL},
-  { "mode",  PW_TYPE_STRING_PTR,
-    offsetof(fr_command_socket_t, mode_name), NULL, NULL},
-
-  { NULL, -1, 0, NULL, NULL }          /* end the list */
+       { "socket", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, path), "${run_dir}/radiusd.sock" },
+       { "uid", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, uid_name), NULL },
+       { "gid", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, gid_name), NULL },
+       { "mode", FR_CONF_OFFSET(PW_TYPE_STRING, fr_command_socket_t, mode_name), NULL },
+
+       { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
 static FR_NAME_NUMBER mode_names[] = {
@@ -120,12 +115,7 @@ static FR_NAME_NUMBER mode_names[] = {
        { NULL, 0 }
 };
 
-
-static ssize_t cprintf(rad_listen_t *listener, char const *fmt, ...)
-#ifdef __GNUC__
-               __attribute__ ((format (printf, 2, 3)))
-#endif
-;
+extern const FR_NAME_NUMBER mod_rcode_table[];
 
 #ifndef HAVE_GETPEEREID
 static int getpeereid(int s, uid_t *euid, gid_t *egid)
@@ -157,19 +147,19 @@ static int fr_server_domain_socket(char const *path)
        struct stat buf;
 
        if (!path) {
-               ERROR("No path provided, was NULL.");
+               ERROR("No path provided, was NULL");
                return -1;
        }
 
        len = strlen(path);
        if (len >= sizeof(salocal.sun_path)) {
-               ERROR("Path too long in socket filename.");
+               ERROR("Path too long in socket filename");
                return -1;
        }
 
        if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
                ERROR("Failed creating socket: %s",
-                       strerror(errno));
+                       fr_syserror(errno));
                return -1;
        }
 
@@ -185,7 +175,7 @@ static int fr_server_domain_socket(char const *path)
        if (stat(path, &buf) < 0) {
                if (errno != ENOENT) {
                        ERROR("Failed to stat %s: %s",
-                              path, strerror(errno));
+                              path, fr_syserror(errno));
                        close(sockfd);
                        return -1;
                }
@@ -214,16 +204,16 @@ static int fr_server_domain_socket(char const *path)
                }
 
                if (unlink(path) < 0) {
-                       ERROR("Failed to delete %s: %s",
-                              path, strerror(errno));
-                       close(sockfd);
-                       return -1;
+                      ERROR("Failed to delete %s: %s",
+                            path, fr_syserror(errno));
+                      close(sockfd);
+                      return -1;
                }
        }
 
        if (bind(sockfd, (struct sockaddr *)&salocal, socklen) < 0) {
                ERROR("Failed binding to %s: %s",
-                       path, strerror(errno));
+                       path, fr_syserror(errno));
                close(sockfd);
                return -1;
        }
@@ -234,14 +224,14 @@ static int fr_server_domain_socket(char const *path)
         */
        if (chmod(path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) < 0) {
                ERROR("Failed setting permissions on %s: %s",
-                      path, strerror(errno));
+                      path, fr_syserror(errno));
                close(sockfd);
                return -1;
        }
 
        if (listen(sockfd, 8) < 0) {
                ERROR("Failed listening to %s: %s",
-                       path, strerror(errno));
+                       path, fr_syserror(errno));
                close(sockfd);
                return -1;
        }
@@ -252,7 +242,7 @@ static int fr_server_domain_socket(char const *path)
 
                if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
                        ERROR("Failure getting socket flags: %s",
-                               strerror(errno));
+                               fr_syserror(errno));
                        close(sockfd);
                        return -1;
                }
@@ -260,7 +250,7 @@ static int fr_server_domain_socket(char const *path)
                flags |= O_NONBLOCK;
                if( fcntl(sockfd, F_SETFL, flags) < 0) {
                        ERROR("Failure setting socket flags: %s",
-                               strerror(errno));
+                               fr_syserror(errno));
                        close(sockfd);
                        return -1;
                }
@@ -279,11 +269,10 @@ static void command_close_socket(rad_listen_t *this)
         *      This removes the socket from the event fd, so no one
         *      will be calling us any more.
         */
-       event_new_fd(this);
+       radius_update_listener(this);
 }
 
-
-static ssize_t cprintf(rad_listen_t *listener, char const *fmt, ...)
+static ssize_t CC_HINT(format (printf, 2, 3)) cprintf(rad_listen_t *listener, char const *fmt, ...)
 {
        ssize_t len;
        va_list ap;
@@ -382,7 +371,7 @@ static int command_show_config(rad_listen_t *listener, int argc, char *argv[])
                return 0;
        }
 
-       ci = cf_reference_item(mainconfig.config, mainconfig.config, argv[0]);
+       ci = cf_reference_item(main_config.config, main_config.config, argv[0]);
        if (!ci) return 0;
 
        if (!cf_item_is_pair(ci)) return 0;
@@ -406,11 +395,9 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
 
 {
        int i;
-       void const *data;
        char const *name1 = cf_section_name1(cs);
        char const *name2 = cf_section_name2(cs);
        CONF_PARSER const *variables = cf_section_parse_table(cs);
-       char buffer[256];
 
        if (name2) {
                cprintf(listener, "%.*s%s %s {\n", indent, tabs, name1, name2);
@@ -424,6 +411,9 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
         *      Print
         */
        if (variables) for (i = 0; variables[i].name != NULL; i++) {
+               void const *data;
+               char buffer[256];
+
                /*
                 *      No base struct offset, data must be the pointer.
                 *      If data doesn't exist, ignore the entry, there
@@ -454,11 +444,11 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
                                variables[i].name, *(int const *) data);
                        break;
 
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        inet_ntop(AF_INET, data, buffer, sizeof(buffer));
                        break;
 
-               case PW_TYPE_IPV6ADDR:
+               case PW_TYPE_IPV6_ADDR:
                        inet_ntop(AF_INET6, data, buffer, sizeof(buffer));
                        break;
 
@@ -468,7 +458,7 @@ static void cprint_conf_parser(rad_listen_t *listener, int indent, CONF_SECTION
                                ((*(bool const *) data) == false) ? "no" : "yes");
                        break;
 
-               case PW_TYPE_STRING_PTR:
+               case PW_TYPE_STRING:
                case PW_TYPE_FILE_INPUT:
                case PW_TYPE_FILE_OUTPUT:
                        /*
@@ -584,8 +574,8 @@ static int command_show_module_flags(rad_listen_t *listener, int argc, char *arg
                cprintf(listener, "\tthread-unsafe\n");
 
 
-       if ((mod->type & RLM_TYPE_CHECK_CONFIG_SAFE) != 0)
-               cprintf(listener, "\twill-check-config\n");
+       if ((mod->type & RLM_TYPE_CHECK_CONFIG_UNSAFE) != 0)
+               cprintf(listener, "\tno-check-config\n");
 
 
        if ((mod->type & RLM_TYPE_HUP_SAFE) != 0)
@@ -594,6 +584,35 @@ static int command_show_module_flags(rad_listen_t *listener, int argc, char *arg
        return 1;               /* success */
 }
 
+static int command_show_module_status(rad_listen_t *listener, int argc, char *argv[])
+{
+       CONF_SECTION *cs;
+       const module_instance_t *mi;
+
+       if (argc != 1) {
+               cprintf(listener, "ERROR: No module name was given\n");
+               return 0;
+       }
+
+       cs = cf_section_find("modules");
+       if (!cs) return 0;
+
+       mi = find_module_instance(cs, argv[0], 0);
+       if (!mi) {
+               cprintf(listener, "ERROR: No such module \"%s\"\n", argv[0]);
+               return 0;
+       }
+
+       if (!mi->force) {
+               cprintf(listener, "alive\n");
+       } else {
+               cprintf(listener, "%s\n", fr_int2str(mod_rcode_table, mi->code, "<invalid>"));
+       }
+
+
+       return 1;               /* success */
+}
+
 
 /*
  *     Show all loaded modules
@@ -632,7 +651,7 @@ static int command_show_modules(rad_listen_t *listener, UNUSED int argc, UNUSED
 static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
 {
        int i;
-       home_server *home;
+       home_server_t *home;
        char const *type, *state, *proto;
 
        char buffer[256];
@@ -688,11 +707,11 @@ static int command_show_home_servers(rad_listen_t *listener, UNUSED int argc, UN
                         *      The *reported* state changes because
                         *      the internal state machine NEEDS THE
                         *      RIGHT STATE.  However, reporting that
-                        *      to the admin will confuse him.  So...
-                        *      we lie.  Yes, that dress doesn't make
-                        *      you look fat.
+                        *      to the admin will confuse them.
+                        *      So... we lie.  No, that dress doesn't
+                        *      make you look fat...
                         */
-                       if ((home->last_packet_recv + home->ping_interval) >= now) {
+                       if ((home->last_packet_recv + (int)home->ping_interval) >= now) {
                                state = "alive";
                        } else {
                                state = "unknown";
@@ -723,10 +742,10 @@ static int command_show_clients(rad_listen_t *listener, UNUSED int argc, UNUSED
                ip_ntoh(&client->ipaddr, buffer, sizeof(buffer));
 
                if (((client->ipaddr.af == AF_INET) &&
-                    (client->prefix != 32)) ||
+                    (client->ipaddr.prefix != 32)) ||
                    ((client->ipaddr.af == AF_INET6) &&
-                    (client->prefix != 128))) {
-                       cprintf(listener, "\t%s/%d\n", buffer, client->prefix);
+                    (client->ipaddr.prefix != 128))) {
+                       cprintf(listener, "\t%s/%d\n", buffer, client->ipaddr.prefix);
                } else {
                        cprintf(listener, "\t%s\n", buffer);
                }
@@ -736,51 +755,6 @@ static int command_show_clients(rad_listen_t *listener, UNUSED int argc, UNUSED
 }
 
 
-static int command_show_xml(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
-{
-       int fd;
-       CONF_ITEM *ci;
-       FILE *fp;
-
-       fd = dup(listener->fd);
-       if (fd < 0) return 0;
-
-       fp = fdopen(fd, "a");
-       if (!fp) {
-               cprintf(listener, "ERROR: Can't dup %s\n", strerror(errno));
-               return 0;
-       }
-
-       if (argc == 0) {
-               cprintf(listener, "ERROR: <reference> is required\n");
-               fclose(fp);
-               return 0;
-       }
-
-       ci = cf_reference_item(mainconfig.config, mainconfig.config, argv[0]);
-       if (!ci) {
-               cprintf(listener, "ERROR: No such item <reference>\n");
-               fclose(fp);
-               return 0;
-       }
-
-       if (cf_item_is_section(ci)) {
-               cf_section2xml(fp, cf_itemtosection(ci));
-
-       } else if (cf_item_is_pair(ci)) {
-               cf_pair2xml(fp, cf_itemtopair(ci));
-
-       } else {
-               cprintf(listener, "ERROR: No such item <reference>\n");
-               fclose(fp);
-               return 0;
-       }
-
-       fclose(fp);
-
-       return 1;               /* success */
-}
-
 static int command_show_version(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
 {
        cprintf(listener, "%s\n", radiusd_version);
@@ -811,7 +785,7 @@ static char debug_log_file_buffer[1024];
 
 static int command_debug_file(rad_listen_t *listener, int argc, char *argv[])
 {
-       if (debug_flag && default_log.dest == L_DST_STDOUT) {
+       if (debug_flag && default_log.dst == L_DST_STDOUT) {
                cprintf(listener, "ERROR: Cannot redirect debug logs to a file when already in debugging mode.\n");
                return -1;
        }
@@ -844,28 +818,84 @@ static int command_debug_file(rad_listen_t *listener, int argc, char *argv[])
 extern fr_cond_t *debug_condition;
 static int command_debug_condition(UNUSED rad_listen_t *listener, int argc, char *argv[])
 {
+       int i;
        char const *error;
-
-       /*
-        *      Delete old condition.
-        *
-        *      This is thread-safe because the condition is evaluated
-        *      in the main server thread, along with code.
-        */
-       talloc_free(debug_condition);
-       debug_condition = NULL;
+       fr_cond_t *new_condition = NULL;
+       char *p, buffer[1024];
 
        /*
         *      Disable it.
         */
        if (argc == 0) {
+               talloc_free(debug_condition);
+               debug_condition = NULL;
                return 0;
        }
 
-       if (fr_condition_tokenize(NULL, NULL, argv[0], &debug_condition, &error, FR_COND_ONE_PASS) < 0) {
-               ERROR("Failed parsing condition '%s': %s", argv[0], error);
+       if (!((argc == 1) &&
+             ((argv[0][0] == '"') || (argv[0][0] == '\'')))) {
+               p = buffer;
+               *p = '\0';
+               for (i = 0; i < argc; i++) {
+                       size_t len;
+
+                       len = strlcpy(p, argv[i], buffer + sizeof(buffer) - p);
+                       p += len;
+                       *(p++) = ' ';
+                       *p = '\0';
+               }
+
+       } else {
+               /*
+                *      Backwards compatibility.  De-escape the string.
+                */
+               char quote;
+               char *q;
+
+               p = argv[0];
+               q = buffer;
+
+               quote = *(p++);
+
+               while (true) {
+                       if (!*p) {
+                               ERROR("Failed parsing condition '%s': Unexpected end of string", argv[0]);
+                               return 0;
+                       }
+
+                       if (*p == quote) {
+                               if (p[1]) {
+                                       ERROR("Failed parsing condition '%s': Unexpected text after end of string", argv[0]);
+                                       return 0;
+                               }
+                               *q = '\0';
+                               break;
+                       }
+
+                       if (*p == '\\') {
+                               *(q++) = p[1];
+                               p += 2;
+                               continue;
+                       }
+
+                       *(q++) = *(p++);
+               }
        }
 
+       if (fr_condition_tokenize(NULL, NULL, buffer, &new_condition, &error, FR_COND_ONE_PASS) < 0) {
+               ERROR("Failed parsing condition '%s': %s", buffer, error);
+               return 0;
+       }
+
+       /*
+        *      Delete old condition.
+        *
+        *      This is thread-safe because the condition is evaluated
+        *      in the main server thread, along with this code.
+        */
+       talloc_free(debug_condition);
+       debug_condition = new_condition;
+
        return 0;
 }
 
@@ -912,7 +942,7 @@ static RADCLIENT *get_client(rad_listen_t *listener, int argc, char *argv[])
                return NULL;
        }
 
-       if (ip_hton(argv[0], AF_UNSPEC, &ipaddr) < 0) {
+       if (ip_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) {
                cprintf(listener, "ERROR: Failed parsing IP address; %s\n",
                        fr_strerror());
                return NULL;
@@ -943,40 +973,12 @@ static RADCLIENT *get_client(rad_listen_t *listener, int argc, char *argv[])
        return client;
 }
 
-
-static int command_show_client_config(rad_listen_t *listener, int argc, char *argv[])
-{
-       int fd;
-       RADCLIENT *client;
-       FILE *fp;
-
-       client = get_client(listener, argc, argv);
-       if (!client) {
-               return 0;
-       }
-
-       if (!client->cs) return 1;
-       fd = dup(listener->fd);
-       if (fd < 0) return 0;
-
-       fp = fdopen(fd, "a");
-       if (!fp) {
-               cprintf(listener, "ERROR: Can't dup %s\n", strerror(errno));
-               return 0;
-       }
-
-       cf_section2file(fp, client->cs);
-       fclose(fp);
-
-       return 1;
-}
-
 #ifdef WITH_PROXY
-static home_server *get_home_server(rad_listen_t *listener, int argc,
+static home_server_t *get_home_server(rad_listen_t *listener, int argc,
                                    char *argv[], int *last)
 {
-       home_server *home;
-       int port;
+       home_server_t *home;
+       uint16_t port;
        int proto = IPPROTO_UDP;
        fr_ipaddr_t ipaddr;
 
@@ -985,7 +987,7 @@ static home_server *get_home_server(rad_listen_t *listener, int argc,
                return NULL;
        }
 
-       if (ip_hton(argv[0], AF_UNSPEC, &ipaddr) < 0) {
+       if (ip_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) {
                cprintf(listener, "ERROR: Failed parsing IP address; %s\n",
                        fr_strerror());
                return NULL;
@@ -1016,37 +1018,10 @@ static home_server *get_home_server(rad_listen_t *listener, int argc,
        return home;
 }
 
-static int command_show_home_server_config(rad_listen_t *listener, int argc, char *argv[])
-{
-       int fd;
-       home_server *home;
-       FILE *fp;
-
-       home = get_home_server(listener, argc, argv, NULL);
-       if (!home) {
-               return 0;
-       }
-
-       if (!home->cs) return 1;
-       fd = dup(listener->fd);
-       if (fd < 0) return 0;
-
-       fp = fdopen(fd, "a");
-       if (!fp) {
-               cprintf(listener, "ERROR: Can't dup %s\n", strerror(errno));
-               return 0;
-       }
-
-       cf_section2file(fp, home->cs);
-       fclose(fp);
-
-       return 1;
-}
-
 static int command_set_home_server_state(rad_listen_t *listener, int argc, char *argv[])
 {
        int last;
-       home_server *home;
+       home_server_t *home;
 
        if (argc < 3) {
                cprintf(listener, "ERROR: Must specify <ipaddr> <port> [proto] <state>\n");
@@ -1077,7 +1052,7 @@ static int command_set_home_server_state(rad_listen_t *listener, int argc, char
 
 static int command_show_home_server_state(rad_listen_t *listener, int argc, char *argv[])
 {
-       home_server *home;
+       home_server_t *home;
 
        home = get_home_server(listener, argc, argv, NULL);
        if (!home) {
@@ -1123,7 +1098,6 @@ static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request)
        vp_cursor_t cursor;
        char *output_file;
        FILE *fp;
-       VALUE_PAIR *vp;
 
        output_file = request_data_reference(request, null_socket_send, 0);
        if (!output_file) {
@@ -1134,12 +1108,13 @@ static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request)
        fp = fopen(output_file, "w");
        if (!fp) {
                ERROR("Failed to send injected file to %s: %s",
-                      output_file, strerror(errno));
+                      output_file, fr_syserror(errno));
                return 0;
        }
 
        if (request->reply->code != 0) {
                char const *what = "reply";
+               VALUE_PAIR *vp;
                char buffer[1024];
 
                if (request->reply->code < FR_MAX_PACKET_CODE) {
@@ -1156,9 +1131,9 @@ static int null_socket_send(UNUSED rad_listen_t *listener, REQUEST *request)
                                         request->reply->code, request->reply->id);
                }
 
-               for (vp = paircursor(&cursor, &request->reply->vps);
+               for (vp = fr_cursor_init(&cursor, &request->reply->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        vp_prints(buffer, sizeof(buffer), vp);
                        fprintf(fp, "%s\n", buffer);
                        if (debug_flag) {
@@ -1175,7 +1150,7 @@ static rad_listen_t *get_socket(rad_listen_t *listener, int argc,
                               char *argv[], int *last)
 {
        rad_listen_t *sock;
-       int port;
+       uint16_t port;
        int proto = IPPROTO_UDP;
        fr_ipaddr_t ipaddr;
 
@@ -1184,7 +1159,7 @@ static rad_listen_t *get_socket(rad_listen_t *listener, int argc,
                return NULL;
        }
 
-       if (ip_hton(argv[0], AF_UNSPEC, &ipaddr) < 0) {
+       if (ip_hton(&ipaddr, AF_UNSPEC, argv[0], false) < 0) {
                cprintf(listener, "ERROR: Failed parsing IP address; %s\n",
                        fr_strerror());
                return NULL;
@@ -1251,7 +1226,7 @@ static int command_inject_from(rad_listen_t *listener, int argc, char *argv[])
        }
 
        sock->src_ipaddr.af = AF_UNSPEC;
-       if (ip_hton(argv[0], AF_UNSPEC, &sock->src_ipaddr) < 0) {
+       if (ip_hton(&sock->src_ipaddr, AF_UNSPEC, argv[0], false) < 0) {
                cprintf(listener, "ERROR: Failed parsing IP address; %s\n",
                        fr_strerror());
                return 0;
@@ -1271,7 +1246,8 @@ static int command_inject_from(rad_listen_t *listener, int argc, char *argv[])
 static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
 {
        static int inject_id = 0;
-       int filedone;
+       int ret;
+       bool filedone;
        fr_command_socket_t *sock = listener->data;
        rad_listen_t *fake;
        RADIUS_PACKET *packet;
@@ -1304,13 +1280,13 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
        fp = fopen(argv[0], "r");
        if (!fp ) {
                cprintf(listener, "ERROR: Failed opening %s: %s\n",
-                       argv[0], strerror(errno));
+                       argv[0], fr_syserror(errno));
                return 0;
        }
 
-       vp = readvp2(NULL, fp, &filedone, "");
+       ret = readvp2(&vp, NULL, fp, &filedone);
        fclose(fp);
-       if (!vp) {
+       if (ret < 0) {
                cprintf(listener, "ERROR: Failed reading attributes from %s: %s\n",
                        argv[0], fr_strerror());
                return 0;
@@ -1336,12 +1312,12 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
        packet->id = inject_id++;
 
        if (fake->type == RAD_LISTEN_AUTH) {
-               packet->code = PW_AUTHENTICATION_REQUEST;
+               packet->code = PW_CODE_AUTHENTICATION_REQUEST;
                fun = rad_authenticate;
 
        } else {
 #ifdef WITH_ACCOUNTING
-               packet->code = PW_ACCOUNTING_REQUEST;
+               packet->code = PW_CODE_ACCOUNTING_REQUEST;
                fun = rad_accounting;
 #else
                cprintf(listener, "ERROR: This server was built without accounting support.\n");
@@ -1359,14 +1335,14 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
                                          buffer, sizeof(buffer)),
                                packet->code, packet->id);
 
-               for (vp = paircursor(&cursor, &packet->vps);
+               for (vp = fr_cursor_init(&cursor, &packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        vp_prints(buffer, sizeof(buffer), vp);
                        DEBUG("\t%s", buffer);
                }
 
-               WDEBUG("INJECTION IS LEAKING MEMORY!");
+               WARN("INJECTION IS LEAKING MEMORY!");
        }
 
        if (!request_receive(fake, packet, sock->inject_client, fun)) {
@@ -1381,7 +1357,7 @@ static int command_inject_file(rad_listen_t *listener, int argc, char *argv[])
         *      Remember what the output file is, and remember to
         *      delete the fake listener when done.
         */
-       request_data_add(request, null_socket_send, 0, talloc_strdup(NULL, buffer), true);
+       request_data_add(request, null_socket_send, 0, talloc_typed_strdup(NULL, buffer), true);
        request_data_add(request, null_socket_send, 1, fake, true);
 
 #endif
@@ -1451,18 +1427,14 @@ static fr_command_table_t command_table_show_module[] = {
        { "methods", FR_READ,
          "show module methods <module> - show sections where <module> may be used",
          command_show_module_methods, NULL },
+       { "status", FR_READ,
+         "show module status <module> - show the module status",
+         command_show_module_status, NULL },
 
        { NULL, 0, NULL, NULL, NULL }
 };
 
 static fr_command_table_t command_table_show_client[] = {
-       { "config", FR_READ,
-         "show client config <ipaddr> "
-#ifdef WITH_TCP
-         "[proto] "
-#endif
-         "- show configuration for given client",
-         command_show_client_config, NULL },
        { "list", FR_READ,
          "show client list - shows list of global clients",
          command_show_clients, NULL },
@@ -1472,9 +1444,6 @@ static fr_command_table_t command_table_show_client[] = {
 
 #ifdef WITH_PROXY
 static fr_command_table_t command_table_show_home[] = {
-       { "config", FR_READ,
-         "show home_server config <ipaddr> <port> [proto] - show configuration for given home server",
-         command_show_home_server_config, NULL },
        { "list", FR_READ,
          "show home_server list - shows list of home servers",
          command_show_home_servers, NULL },
@@ -1511,9 +1480,6 @@ static fr_command_table_t command_table_show[] = {
        { "version", FR_READ,
          "show version - Prints version of the running server",
          command_show_version, NULL },
-       { "xml", FR_READ,
-         "show xml <reference> - Prints out configuration as XML",
-         command_show_xml, NULL },
        { NULL, 0, NULL, NULL, NULL }
 };
 
@@ -1596,8 +1562,7 @@ static int command_set_module_config(rad_listen_t *listener, int argc, char *arg
         */
        cf_pair_replace(mi->cs, cp, argv[2]);
 
-       rcode = cf_item_parse(mi->cs, argv[1], variables[i].type,
-                             data, argv[2]);
+       rcode = cf_item_parse(mi->cs, argv[1], variables[i].type, data, argv[2]);
        if (rcode < 0) {
                cprintf(listener, "ERROR: Failed to parse value\n");
                return 0;
@@ -1627,14 +1592,23 @@ static int command_set_module_status(rad_listen_t *listener, int argc, char *arg
 
 
        if (strcmp(argv[1], "alive") == 0) {
-               mi->dead = false;
+               mi->force = false;
 
        } else if (strcmp(argv[1], "dead") == 0) {
-               mi->dead = true;
+               mi->code = RLM_MODULE_FAIL;
+               mi->force = true;
 
        } else {
-               cprintf(listener, "ERROR: Unknown status \"%s\"\n", argv[2]);
-               return 0;
+               int rcode;
+
+               rcode = fr_str2int(mod_rcode_table, argv[1], -1);
+               if (rcode < 0) {
+                       cprintf(listener, "ERROR: Unknown status \"%s\"\n", argv[1]);
+                       return 0;
+               }
+
+               mi->code = rcode;
+               mi->force = true;
        }
 
        return 1;               /* success */
@@ -1723,7 +1697,7 @@ static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
        }
 
        data = NULL;
-       for (this = mainconfig.listen; this != NULL; this = this->next) {
+       for (this = main_config.listen; this != NULL; this = this->next) {
                if (this->type != RAD_LISTEN_DETAIL) continue;
 
                data = this->data;
@@ -1768,7 +1742,7 @@ static int command_stats_detail(rad_listen_t *listener, int argc, char *argv[])
 #ifdef WITH_PROXY
 static int command_stats_home_server(rad_listen_t *listener, int argc, char *argv[])
 {
-       home_server *home;
+       home_server_t *home;
 
        if (argc == 0) {
                cprintf(listener, "ERROR: Must specify [auth/acct] OR <ipaddr> <port>\n");
@@ -1805,7 +1779,7 @@ static int command_stats_home_server(rad_listen_t *listener, int argc, char *arg
 
 static int command_stats_client(rad_listen_t *listener, int argc, char *argv[])
 {
-       int auth = true;
+       bool auth = true;
        fr_stats_t *stats;
        RADCLIENT *client, fake;
 
@@ -1893,7 +1867,7 @@ static int command_stats_client(rad_listen_t *listener, int argc, char *argv[])
 
 static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[])
 {
-       int auth = true;
+       bool auth = true;
        rad_listen_t *sock;
 
        sock = get_socket(listener, argc, argv, NULL);
@@ -1908,6 +1882,7 @@ static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[])
 #endif /* WITH_STATS */
 
 
+#ifdef WITH_DYNAMIC_CLIENTS
 static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[])
 {
        RADCLIENT *c;
@@ -1938,7 +1913,6 @@ static int command_add_client_file(rad_listen_t *listener, int argc, char *argv[
 
 static int command_del_client(rad_listen_t *listener, int argc, char *argv[])
 {
-#ifdef WITH_DYNAMIC_CLIENTS
        RADCLIENT *client;
 
        client = get_client(listener, argc, argv);
@@ -1958,9 +1932,6 @@ static int command_del_client(rad_listen_t *listener, int argc, char *argv[])
         *      structure will stick around for a while.  Oh well...
         */
        client->lifetime = 1;
-#else
-       cprintf(listener, "ERROR: Dynamic clients are not supported.\n");
-#endif
 
        return 1;
 }
@@ -2000,7 +1971,7 @@ static fr_command_table_t command_table_add[] = {
 
        { NULL, 0, NULL, NULL, NULL }
 };
-
+#endif
 
 #ifdef WITH_PROXY
 static fr_command_table_t command_table_set_home[] = {
@@ -2018,7 +1989,7 @@ static fr_command_table_t command_table_set_module[] = {
          command_set_module_config, NULL },
 
        { "status", FR_WRITE,
-         "set module status [alive|dead] - set the module to be alive or dead (always return \"fail\")",
+         "set module status <module> [alive|...] - set the module status to be alive (operating normally), or force a particular code (ok,fail, etc.)",
          command_set_module_status, NULL },
 
        { NULL, 0, NULL, NULL, NULL }
@@ -2074,11 +2045,15 @@ static fr_command_table_t command_table_stats[] = {
 #endif
 
 static fr_command_table_t command_table[] = {
+#ifdef WITH_DYNAMIC_CLIENTS
        { "add", FR_WRITE, NULL, NULL, command_table_add },
+#endif
        { "debug", FR_WRITE,
          "debug <command> - debugging commands",
          NULL, command_table_debug },
+#ifdef WITH_DYNAMIC_CLIENTS
        { "del", FR_WRITE, NULL, NULL, command_table_del },
+#endif
        { "hup", FR_WRITE,
          "hup [module] - sends a HUP signal to the server, or optionally to one module",
          command_hup, NULL },
@@ -2136,7 +2111,7 @@ static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this)
 
        sock->magic = COMMAND_SOCKET_MAGIC;
        sock->copy = NULL;
-       if (sock->path) sock->copy = talloc_strdup(sock, sock->path);
+       if (sock->path) sock->copy = talloc_typed_strdup(sock, sock->path);
 
 #if defined(HAVE_GETPEEREID) || defined (SO_PEERCRED)
        if (sock->uid_name) {
@@ -2145,7 +2120,7 @@ static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this)
                pw = getpwnam(sock->uid_name);
                if (!pw) {
                        ERROR("Failed getting uid for %s: %s",
-                              sock->uid_name, strerror(errno));
+                              sock->uid_name, fr_syserror(errno));
                        return -1;
                }
 
@@ -2160,7 +2135,7 @@ static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this)
                gr = getgrnam(sock->gid_name);
                if (!gr) {
                        ERROR("Failed getting gid for %s: %s",
-                              sock->gid_name, strerror(errno));
+                              sock->gid_name, fr_syserror(errno));
                        return -1;
                }
                sock->gid = gr->gr_gid;
@@ -2207,7 +2182,7 @@ static int command_socket_parse_unix(CONF_SECTION *cs, rad_listen_t *this)
                fr_suid_up();
                if (fchown(this->fd, sock->uid, sock->gid) < 0) {
                        ERROR("Failed setting ownership of %s: %s",
-                              sock->path, strerror(errno));
+                              sock->path, fr_syserror(errno));
                        fr_suid_down();
                        return -1;
                }
@@ -2268,8 +2243,6 @@ static int command_socket_print(rad_listen_t const *this, char *buffer, size_t b
 static int str2argvX(char *str, char **argv, int max_argc)
 {
        int argc = 0;
-       size_t len;
-       char buffer[1024];
 
        while (*str) {
                if (argc >= max_argc) return argc;
@@ -2289,29 +2262,31 @@ static int str2argvX(char *str, char **argv, int max_argc)
 
                if (!*str) return argc;
 
+               argv[argc++] = str;
+
                if ((*str == '\'') || (*str == '"')) {
-                       char const *p = str;
-                       FR_TOKEN token;
+                       char quote = *str;
+                       char *p = str + 1;
 
-                       token = gettoken(&p, buffer, sizeof(buffer));
-                       if ((token != T_SINGLE_QUOTED_STRING) &&
-                           (token != T_DOUBLE_QUOTED_STRING)) {
-                               return -1;
-                       }
+                       while (true) {
+                               if (!*p) return -1;
 
-                       len = strlen(buffer);
-                       if (len >= (size_t) (p - str)) {
-                               return -1;
-                       }
+                               if (*p == quote) {
+                                       str = p + 1;
+                                       break;
+                               }
 
-                       memcpy(str, buffer, len + 1);
-                       argv[argc] = str;
+                               /*
+                                *      Handle \" and nothing else.
+                                */
+                               if (*p == '\\') {
+                                       p += 2;
+                                       continue;
+                               }
 
-                       memcpy(&str, &p, sizeof(str));
-               } else {
-                       argv[argc] = str;
+                               p++;
+                       }
                }
-               argc++;
 
                while (*str &&
                       (*str != ' ') &&
@@ -2491,8 +2466,7 @@ static int command_domain_recv_co(rad_listen_t *listener, fr_cs_buffer_t *co)
                        }
 
                        len = 1;
-                       rcode = table[i].func(listener,
-                                             argc - 1, argv + 1);
+                       table[i].func(listener, argc - 1, argv + 1);
                        break;
                }
        }
@@ -2545,7 +2519,7 @@ static int command_write_magic(int newfd, listen_socket_t *sock)
        magic = htonl(0xf7eead15);
        if (write(newfd, &magic, 4) < 0) {
                ERROR("Failed writing initial data to socket: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                return -1;
        }
 
@@ -2555,7 +2529,7 @@ static int command_write_magic(int newfd, listen_socket_t *sock)
                magic = htonl(1);
        }
        if (write(newfd, &magic, 4) < 0) {
-               ERROR("Failed writing initial data to socket: %s", strerror(errno));
+               ERROR("Failed writing initial data to socket: %s", fr_syserror(errno));
                return -1;
        }
 
@@ -2577,7 +2551,7 @@ static int command_write_magic(int newfd, listen_socket_t *sock)
                 *      FIXME: EINTR, etc.
                 */
                if (write(newfd, co->buffer, 16) < 0) {
-                       ERROR("Failed writing version data to socket: %s", strerror(errno));
+                       ERROR("Failed writing version data to socket: %s", fr_syserror(errno));
                        return -1;
                }
        }
@@ -2617,7 +2591,7 @@ static int command_tcp_recv(rad_listen_t *this)
                                if (errno == EINTR) return 0;
 
                                ERROR("Failed reading from control socket; %s",
-                                      strerror(errno));
+                                      fr_syserror(errno));
                                goto close_socket;
                        }
 
@@ -2668,7 +2642,7 @@ static int command_domain_accept(rad_listen_t *listener)
 
        salen = sizeof(src);
 
-       DEBUG2(" ... new connection request on command socket.");
+       DEBUG2(" ... new connection request on command socket");
 
        newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
        if (newfd < 0) {
@@ -2679,7 +2653,7 @@ static int command_domain_accept(rad_listen_t *listener)
                        return 0;
                }
 
-               DEBUG2(" ... failed to accept connection.");
+               DEBUG2(" ... failed to accept connection");
                return 0;
        }
 
@@ -2693,7 +2667,7 @@ static int command_domain_accept(rad_listen_t *listener)
 
                if (getpeereid(newfd, &uid, &gid) < 0) {
                        ERROR("Failed getting peer credentials for %s: %s",
-                              sock->path, strerror(errno));
+                              sock->path, fr_syserror(errno));
                        close(newfd);
                        return 0;
                }
@@ -2760,7 +2734,7 @@ static int command_domain_accept(rad_listen_t *listener)
        /*
         *      Tell the event loop that we have a new FD
         */
-       event_new_fd(this);
+       radius_update_listener(this);
 
        return 0;
 }
index a6b1934..1aa61f7 100644 (file)
@@ -80,6 +80,7 @@ struct conf_part {
        CONF_ITEM item;
        char const *name1;
        char const *name2;
+       FR_TOKEN    name2_type;
        struct conf_item *children;
        struct conf_item *tail; /* for speed */
        CONF_SECTION    *template;
@@ -88,7 +89,7 @@ struct conf_part {
        rbtree_t        *name2_tree; /* for sections of the same name2 */
        rbtree_t        *data_tree;
        void            *base;
-       int depth;
+       int             depth;
        CONF_PARSER const *variables;
 };
 
@@ -115,6 +116,7 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
                                       CONF_SECTION *outercs,
                                       char *output, size_t outsize,
                                       char const *input);
+static CONF_SECTION *cf_template_copy(CONF_SECTION *parent, CONF_SECTION const *template);
 
 /*
  *     Isolate the scary casts in these tiny provably-safe functions
@@ -197,7 +199,7 @@ static CONF_PAIR *cf_pair_alloc(CONF_SECTION *parent, char const *attr,
        cp->value_type = value_type;
        cp->op = op;
 
-       cp->attr = talloc_strdup(cp, attr);
+       cp->attr = talloc_typed_strdup(cp, attr);
        if (!cp->attr) {
        error:
                talloc_free(cp);
@@ -205,7 +207,7 @@ static CONF_PAIR *cf_pair_alloc(CONF_SECTION *parent, char const *attr,
        }
 
        if (value) {
-               cp->value = talloc_strdup(cp, value);
+               cp->value = talloc_typed_strdup(cp, value);
                if (!cp->value) goto error;
        }
 
@@ -261,8 +263,8 @@ static int name2_cmp(void const *a, void const *b)
        rad_assert(strcmp(one->name1, two->name1) == 0);
 
        if (!one->name2 && !two->name2) return 0;
-       if (!one->name2) return -1;
-       if (!two->name2) return +1;
+       if (one->name2 && !two->name2) return -1;
+       if (!one->name2 && two->name2) return +1;
 
        return strcmp(one->name2, two->name2);
 }
@@ -345,7 +347,7 @@ CONF_SECTION *cf_section_alloc(CONF_SECTION *parent, char const *name1,
        cs->item.type = CONF_ITEM_SECTION;
        cs->item.parent = parent;
 
-       cs->name1 = talloc_strdup(cs, name1);
+       cs->name1 = talloc_typed_strdup(cs, name1);
        if (!cs->name1) {
        error:
                talloc_free(cs);
@@ -353,7 +355,7 @@ CONF_SECTION *cf_section_alloc(CONF_SECTION *parent, char const *name1,
        }
 
        if (name2 && *name2) {
-               cs->name2 = talloc_strdup(cs, name2);
+               cs->name2 = talloc_typed_strdup(cs, name2);
                if (!cs->name2) goto error;
        }
 
@@ -439,11 +441,17 @@ static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci)
                 */
                switch (ci->type) {
                        case CONF_ITEM_PAIR:
-                               rbtree_insert(cs->pair_tree, ci);
+                               if (!rbtree_insert(cs->pair_tree, ci)) {
+                                       CONF_PAIR *cp = cf_itemtopair(ci);
+
+                                       if (strcmp(cp->attr, "confdir") == 0) break;
+                                       if (!cp->value) break; /* module name, "ok", etc. */
+                               }
                                break;
 
                        case CONF_ITEM_SECTION: {
                                CONF_SECTION *cs_new = cf_itemtosection(ci);
+                               CONF_SECTION *name1_cs;
 
                                if (!cs->section_tree) {
                                        cs->section_tree = rbtree_create(section_cmp, NULL, 0);
@@ -453,32 +461,59 @@ static void cf_item_add(CONF_SECTION *cs, CONF_ITEM *ci)
                                        }
                                }
 
-                               rbtree_insert(cs->section_tree, cs_new);
+                               name1_cs = rbtree_finddata(cs->section_tree, cs_new);
+                               if (!name1_cs) {
+                                       if (!rbtree_insert(cs->section_tree, cs_new)) {
+                                               ERROR("Failed inserting section into tree");
+                                               fr_exit_now(1);
+                                       }
+                                       break;
+                               }
 
+#if 0
                                /*
-                                *      Two names: find the named instance.
+                                *      We'll ignore these checks for
+                                *      now.  Various sections can be
+                                *      duplicated, such as "listen",
+                                *      "update", "if", "else", etc.
                                 */
-                               {
-                                       CONF_SECTION *old_cs;
+                               if (!name1_cs->name2 && !cs_new->name2) {
+                                       WARN("%s[%d] Duplicate configuration section \"%s { ...}\" %s %d",
+                                            ci->filename, ci->lineno, cs_new->name1, name1_cs->item.filename, name1_cs->item.lineno);
+                                       break;
+                               }
 
-                                       /*
-                                        *      Find the FIRST
-                                        *      CONF_SECTION having
-                                        *      the given name1, and
-                                        *      create a new tree
-                                        *      under it.
-                                        */
-                                       old_cs = rbtree_finddata(cs->section_tree, cs_new);
-                                       if (!old_cs) return; /* this is a bad error! */
+                               if ((name1_cs->name2 && cs_new->name2) &&
+                                   (strcmp(name1_cs->name2, cs_new->name2) == 0)) {
+                                       WARN("%s[%d] Duplicate configuration section \"%s %s { ...}\"",
+                                            ci->filename, ci->lineno, cs_new->name1, cs_new->name2);
+                                       break;
+                               }
+#endif
 
-                                       if (!old_cs->name2_tree) {
-                                               old_cs->name2_tree = rbtree_create(name2_cmp,
-                                                                                  NULL, 0);
-                                       }
-                                       if (old_cs->name2_tree) {
-                                               rbtree_insert(old_cs->name2_tree, cs_new);
+                               /*
+                                *      We already have a section of
+                                *      this "name1".  Add a new
+                                *      sub-section based on name2.
+                                */
+                               if (!name1_cs->name2_tree) {
+                                       name1_cs->name2_tree = rbtree_create(name2_cmp,
+                                                                            NULL, 0);
+                                       if (!name1_cs->name2_tree) {
+                                               ERROR("Out of memory");
+                                               fr_exit_now(1);
                                        }
-                               } /* had a name2 */
+                               }
+
+                               /*
+                                *      We don't care if this fails.
+                                *      If the user tries to create
+                                *      two sections of the same
+                                *      name1/name2, the duplicate
+                                *      section is just silently
+                                *      ignored.
+                                */
+                               rbtree_insert(name1_cs->name2_tree, cs_new);
                                break;
                        } /* was a section */
 
@@ -625,7 +660,7 @@ CONF_ITEM *cf_reference_item(CONF_SECTION const *parentcs,
        }
 
 no_such_item:
-       WDEBUG2("No such configuration item %s", ptr);
+       WARN("No such configuration item %s", ptr);
        return NULL;
 }
 
@@ -657,7 +692,7 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
 
        /*
         *      Find the master parent conf section.
-        *      We can't use mainconfig.config, because we're in the
+        *      We can't use main_config.config, because we're in the
         *      process of re-building it, and it isn't set up yet...
         */
        parentcs = cf_top_section(outercs);
@@ -767,6 +802,8 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
                                ptr = end + 1;
 
                        } else if (ci->type == CONF_ITEM_SECTION) {
+                               CONF_SECTION *subcs;
+
                                /*
                                 *      Adding an entry again to a
                                 *      section is wrong.  We don't
@@ -776,8 +813,21 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
                                        ERROR("%s[%d]: Cannot reference different item in same section", cf, *lineno);
                                        return NULL;
                                }
-                               cf_item_add(outercs, ci);
-                               (void) talloc_reference(outercs, ci);
+
+                               /*
+                                *      Copy the section instead of
+                                *      referencing it.
+                                */
+                               subcs = cf_template_copy(outercs, cf_itemtosection(ci));
+                               if (!subcs) {
+                                       ERROR("%s[%d]: Failed copying reference %s", cf, *lineno, name);
+                                       return NULL;
+                               }
+
+                               subcs->item.filename = ci->filename;
+                               subcs->item.lineno = ci->lineno;
+                               cf_item_add(outercs, &(subcs->item));
+
                                ptr = end + 1;
 
                        } else {
@@ -857,6 +907,53 @@ static char const *cf_expand_variables(char const *cf, int *lineno,
 
 static char const *parse_spaces = "                                                                                                                                                                                                                                                                ";
 
+/** Validation function for ipaddr conffile types
+ *
+ */
+static inline int fr_item_validate_ipaddr(CONF_SECTION *cs, char const *name, PW_TYPE type, char const *value,
+                                         fr_ipaddr_t *ipaddr)
+{
+       char ipbuf[128];
+
+       if (strcmp(value, "*") == 0) {
+               cf_log_info(cs, "%.*s\t%s = *", cs->depth, parse_spaces, name);
+       } else if (strspn(value, ".0123456789abdefABCDEF:%[]/") == strlen(value)) {
+               cf_log_info(cs, "%.*s\t%s = %s", cs->depth, parse_spaces, name, value);
+       } else {
+               cf_log_info(cs, "%.*s\t%s = %s IPv%s address [%s]", cs->depth, parse_spaces, name, value,
+                           (ipaddr->af == AF_INET ? "4" : " 6"), ip_ntoh(ipaddr, ipbuf, sizeof(ipbuf)));
+       }
+
+       switch (type) {
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IP_ADDR:
+               switch (ipaddr->af) {
+               case AF_INET:
+               if (ipaddr->prefix != 32) {
+                       ERROR("Invalid IPv4 mask length \"/%i\".  Only \"/32\" permitted for non-prefix types",
+                             ipaddr->prefix);
+
+                       return -1;
+               }
+                       break;
+
+               case AF_INET6:
+               if (ipaddr->prefix != 128) {
+                       ERROR("Invalid IPv6 mask length \"/%i\".  Only \"/128\" permitted for non-prefix types",
+                             ipaddr->prefix);
+
+                       return -1;
+               }
+                       break;
+
+               default:
+                       return -1;
+               }
+       default:
+               return 0;
+       }
+}
 
 /*
  *     Parses an item (not a CONF_ITEM) into the specified format,
@@ -869,24 +966,25 @@ static char const *parse_spaces = "
 int cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char const *dflt)
 {
        int rcode;
-       bool deprecated, required, attribute;
+       bool deprecated, required, attribute, secret;
        char **q;
        char const *value;
-       fr_ipaddr_t ipaddr;
        CONF_PAIR const *cp = NULL;
-       char ipbuf[128];
+       fr_ipaddr_t *ipaddr;
+       char buffer[8192];
 
        if (!cs) return -1;
 
        deprecated = (type & PW_TYPE_DEPRECATED);
        required = (type & PW_TYPE_REQUIRED);
        attribute = (type & PW_TYPE_ATTRIBUTE);
+       secret = (type & PW_TYPE_SECRET);
 
        type &= 0xff;           /* normal types are small */
        rcode = 0;
 
        if (attribute) {
-               required = 1;
+               required = true;
        }
 
        cp = cf_pair_find(cs, name);
@@ -943,12 +1041,52 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char
                break;
 
        case PW_TYPE_INTEGER:
-               *(int *)data = strtol(value, 0, 0);
-               cf_log_info(cs, "%.*s\t%s = %d",
-                           cs->depth, parse_spaces, name, *(int *)data);
+       {
+               unsigned long v = strtoul(value, 0, 0);
+
+               /*
+                *      Restrict integer values to 0-INT32_MAX, this means
+                *      it will always be safe to cast them to a signed type
+                *      for comparisons, and imposes the same range limit as
+                *      before we switched to using an unsigned type to
+                *      represent config item integers.
+                */
+               if (v > INT32_MAX) {
+                       cf_log_err(&(cs->item), "Invalid value \"%s\" for variable %s, must be between 0-%u", value,
+                                  name, INT32_MAX);
+                       return -1;
+               }
+
+               *(uint32_t *)data = v;
+               cf_log_info(cs, "%.*s\t%s = %u", cs->depth, parse_spaces, name, *(uint32_t *)data);
+       }
                break;
 
-       case PW_TYPE_STRING_PTR:
+       case PW_TYPE_SHORT:
+       {
+               unsigned long v = strtoul(value, 0, 0);
+
+               if (v > UINT16_MAX) {
+                       cf_log_err(&(cs->item), "Invalid value \"%s\" for variable %s, must be between 0-%u", value,
+                                  name, UINT16_MAX);
+                       return -1;
+               }
+               *(uint16_t *)data = (uint16_t) v;
+               cf_log_info(cs, "%.*s\t%s = %u", cs->depth, parse_spaces, name, *(uint16_t *)data);
+       }
+               break;
+
+       case PW_TYPE_INTEGER64:
+               *(uint64_t *)data = strtoull(value, 0, 0);
+               cf_log_info(cs, "%.*s\t%s = %" PRIu64, cs->depth, parse_spaces, name, *(uint64_t *)data);
+               break;
+
+       case PW_TYPE_SIGNED:
+               *(int32_t *)data = strtol(value, 0, 0);
+               cf_log_info(cs, "%.*s\t%s = %d", cs->depth, parse_spaces, name, *(int32_t *)data);
+               break;
+
+       case PW_TYPE_STRING:
                q = (char **) data;
                if (*q != NULL) {
                        talloc_free(*q);
@@ -960,15 +1098,10 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char
                 *      file was read.
                 */
                if (value == dflt) {
-                       char buffer[8192];
-
                        int lineno = 0;
 
                        lineno = cs->item.lineno;
 
-                       /*
-                        *      FIXME: sizeof(buffer)?
-                        */
                        value = cf_expand_variables("<internal>",
                                                    &lineno,
                                                    cs, buffer, sizeof(buffer),
@@ -994,13 +1127,21 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char
                        }
                }
 
-               cf_log_info(cs, "%.*s\t%s = \"%s\"",
-                           cs->depth, parse_spaces, name, value ? value : "(null)");
-               *q = value ? talloc_strdup(cs, value) : NULL;
+               /*
+                *      Hide secrets when using "radiusd -X".
+                */
+               if (secret && (debug_flag <= 2)) {
+                       cf_log_info(cs, "%.*s\t%s = <<< secret >>>",
+                                   cs->depth, parse_spaces, name);
+               } else {
+                       cf_log_info(cs, "%.*s\t%s = \"%s\"",
+                                   cs->depth, parse_spaces, name, value ? value : "(null)");
+               }
+               *q = value ? talloc_typed_strdup(cs, value) : NULL;
                break;
 
                /*
-                *      This is the same as PW_TYPE_STRING_PTR,
+                *      This is the same as PW_TYPE_STRING,
                 *      except that we also "stat" the file, and
                 *      cache the result.
                 */
@@ -1017,13 +1158,8 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char
                 *      file was read.
                 */
                if ((value == dflt) && cs) {
-                       char buffer[8192];
-
                        int lineno = 0;
 
-                       /*
-                        *      FIXME: sizeof(buffer)?
-                        */
                        value = cf_expand_variables("?",
                                                    &lineno,
                                                    cs, buffer, sizeof(buffer),
@@ -1035,67 +1171,82 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, int type, void *data, char
 
                cf_log_info(cs, "%.*s\t%s = \"%s\"",
                            cs->depth, parse_spaces, name, value);
-               *q = value ? talloc_strdup(cs, value) : NULL;
+               *q = value ? talloc_typed_strdup(cs, value) : NULL;
 
                /*
-                *      And now we "stat" the file.
+                *      If the filename exists and we're supposed to
+                *      read it, check it.
                 */
-               if (*q) {
+               if (*q && (type == PW_TYPE_FILE_INPUT)) {
                        struct stat buf;
 
-                       if (stat(*q, &buf) == 0) {
-                               time_t *mtime = talloc(cs, time_t);
-
-                               *mtime = buf.st_mtime;
-                               /* FIXME: error? */
-                               cf_data_add_internal(cs, *q, mtime, NULL, type);
-                       /*
-                        *      We were expecting the file to exist...
-                        */
-                       } else if (type == PW_TYPE_FILE_INPUT) {
-                               ERROR("File \"%s\" does not exist", value);
+                       if (stat(*q, &buf) < 0) {
+                               ERROR("Unable to open file \"%s\": %s",
+                                     value, fr_syserror(errno));
                                return -1;
                        }
-
                }
                break;
 
-       case PW_TYPE_IPADDR:
-               /*
-                *      Allow '*' as any address
-                */
-               if (strcmp(value, "*") == 0) {
-                       *(uint32_t *) data = htonl(INADDR_ANY);
-                       cf_log_info(cs, "%.*s\t%s = *",
-                                   cs->depth, parse_spaces, name);
-                       break;
-               }
-               if (ip_hton(value, AF_INET, &ipaddr) < 0) {
-                       ERROR("Can't find IP address for host %s", value);
+       case PW_TYPE_IPV4_ADDR:
+       case PW_TYPE_IPV4_PREFIX:
+               ipaddr = data;
+
+               if (fr_pton4(ipaddr, value, 0, true, false) < 0) {
+                       ERROR("%s", fr_strerror());
                        return -1;
                }
+               if (fr_item_validate_ipaddr(cs, name, type, value, ipaddr) < 0) return -1;
+               break;
 
-               if (strspn(value, "0123456789.") == strlen(value)) {
-                       cf_log_info(cs, "%.*s\t%s = %s",
-                                   cs->depth, parse_spaces, name, value);
-               } else {
-                       cf_log_info(cs, "%.*s\t%s = %s IP address [%s]",
-                                   cs->depth, parse_spaces, name, value,
-                              ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
+       case PW_TYPE_IPV6_ADDR:
+       case PW_TYPE_IPV6_PREFIX:
+               ipaddr = data;
+
+               if (fr_pton6(ipaddr, value, 0, true, false) < 0) {
+                       ERROR("%s", fr_strerror());
+                       return -1;
                }
-               *(uint32_t *) data = ipaddr.ipaddr.ip4addr.s_addr;
+               if (fr_item_validate_ipaddr(cs, name, type, value, ipaddr) < 0) return -1;
                break;
 
-       case PW_TYPE_IPV6ADDR:
-               if (ip_hton(value, AF_INET6, &ipaddr) < 0) {
-                       ERROR("Can't find IPv6 address for host %s", value);
+       case PW_TYPE_IP_ADDR:
+       case PW_TYPE_IP_PREFIX:
+               ipaddr = data;
+
+               if (fr_pton(ipaddr, value, 0, true) < 0) {
+                       ERROR("%s", fr_strerror());
                        return -1;
                }
-               cf_log_info(cs, "%.*s\t%s = %s IPv6 address [%s]",
-                           cs->depth, parse_spaces, name, value,
-                           ip_ntoh(&ipaddr, ipbuf, sizeof(ipbuf)));
-               memcpy(data, &ipaddr.ipaddr.ip6addr,
-                      sizeof(ipaddr.ipaddr.ip6addr));
+               if (fr_item_validate_ipaddr(cs, name, type, value, ipaddr) < 0) return -1;
+               break;
+
+       case PW_TYPE_TIMEVAL: {
+               int sec;
+               char *end;
+               struct timeval tv;
+
+               sec = strtoul(value, &end, 10);
+               tv.tv_sec = sec;
+               tv.tv_usec = 0;
+               if (*end == '.') {
+                       sec = strlen(end + 1);
+
+                       if (sec > 6) {
+                               ERROR("Too much precision for timeval");
+                               return -1;
+                       }
+
+                       strcpy(buffer, "000000");
+                       memcpy(buffer, end + 1, sec);
+
+                       sec = strtoul(buffer, NULL, 10);
+                       tv.tv_usec = sec;
+               }
+               cf_log_info(cs, "%.*s\t%s = %d.%06d",
+                           cs->depth, parse_spaces, name, (int) tv.tv_sec, (int) tv.tv_usec);
+               memcpy(data, &tv, sizeof(tv));
+               }
                break;
 
        default:
@@ -1154,9 +1305,11 @@ static void cf_section_parse_init(CONF_SECTION *cs, void *base,
                        if (!subcs) {
                                subcs = cf_section_alloc(cs, variables[i].name,
                                                         NULL);
-                               cf_item_add(cs, &(subcs->item));
+                               if (!subcs) return;
+
                                subcs->item.filename = cs->item.filename;
                                subcs->item.lineno = cs->item.lineno;
+                               cf_item_add(cs, &(subcs->item));
                        }
 
                        cf_section_parse_init(subcs, base,
@@ -1164,7 +1317,7 @@ static void cf_section_parse_init(CONF_SECTION *cs, void *base,
                        continue;
                }
 
-               if ((variables[i].type != PW_TYPE_STRING_PTR) &&
+               if ((variables[i].type != PW_TYPE_STRING) &&
                    (variables[i].type != PW_TYPE_FILE_INPUT) &&
                    (variables[i].type != PW_TYPE_FILE_OUTPUT)) {
                        continue;
@@ -1268,6 +1421,129 @@ int cf_section_parse(CONF_SECTION *cs, void *base,
 }
 
 
+static CONF_SECTION *cf_template_copy(CONF_SECTION *parent, CONF_SECTION const *template)
+{
+       CONF_ITEM *ci;
+       CONF_SECTION *cs;
+
+       cs = cf_section_alloc(parent, template->name1, template->name2);
+       if (!cs) return NULL;
+
+       for (ci = template->children; ci; ci = ci->next) {
+               if (ci->type == CONF_ITEM_PAIR) {
+                       CONF_PAIR *cp1, *cp2;
+
+                       cp1 = cf_itemtopair(ci);
+                       cp2 = cf_pair_alloc(cs, cp1->attr, cp1->value, cp1->op, cp1->value_type);
+                       if (!cp2) return false;
+
+                       cp2->item.filename = cp1->item.filename;
+                       cp2->item.lineno = cp1->item.lineno;
+
+                       cf_item_add(cs, &(cp2->item));
+                       continue;
+               }
+
+               if (ci->type == CONF_ITEM_SECTION) {
+                       CONF_SECTION *subcs1, *subcs2;
+
+                       subcs1 = cf_itemtosection(ci);
+                       subcs2 = cf_template_copy(cs, subcs1);
+
+                       subcs2->item.filename = subcs1->item.filename;
+                       subcs2->item.lineno = subcs1->item.lineno;
+
+                       cf_item_add(cs, &(subcs2->item));
+                       continue;
+               }
+
+               /* ignore everything else */
+       }
+
+       return cs;
+}
+
+
+/*
+ *     Merge the template so everyting else "just works".
+ */
+static bool cf_template_merge(CONF_SECTION *cs, CONF_SECTION const *template)
+{
+       CONF_ITEM *ci;
+
+       if (!cs || !template) return true;
+
+       cs->template = NULL;
+
+       /*
+        *      Walk over the template, adding its' entries to the
+        *      current section.  But only if the entries don't
+        *      already exist in the current section.
+        */
+       for (ci = template->children; ci; ci = ci->next) {
+               if (ci->type == CONF_ITEM_PAIR) {
+                       CONF_PAIR *cp1, *cp2;
+
+                       /*
+                        *      It exists, don't over-write it.
+                        */
+                       cp1 = cf_itemtopair(ci);
+                       if (cf_pair_find(cs, cp1->attr)) {
+                               continue;
+                       }
+
+                       /*
+                        *      Create a new pair with all of the data
+                        *      of the old one.
+                        */
+                       cp2 = cf_pair_alloc(cs, cp1->attr, cp1->value, cp1->op, cp1->value_type);
+                       if (!cp2) return false;
+
+                       cp2->item.filename = cp1->item.filename;
+                       cp2->item.lineno = cp1->item.lineno;
+
+                       cf_item_add(cs, &(cp2->item));
+                       continue;
+               }
+
+               if (ci->type == CONF_ITEM_SECTION) {
+                       CONF_SECTION *subcs1, *subcs2;
+
+                       subcs1 = cf_itemtosection(ci);
+                       rad_assert(subcs1 != NULL);
+
+                       subcs2 = cf_section_sub_find_name2(cs, subcs1->name1, subcs1->name2);
+                       if (subcs2) {
+                               /*
+                                *      sub-sections get merged.
+                                */
+                               if (!cf_template_merge(subcs2, subcs1)) {
+                                       return false;
+                               }
+                               continue;
+                       }
+
+                       /*
+                        *      Our section doesn't have a matching
+                        *      sub-section.  Copy it verbatim from
+                        *      the template.
+                        */
+                       subcs2 = cf_template_copy(cs, subcs1);
+                       if (!subcs2) return false;
+
+                       subcs2->item.filename = subcs1->item.filename;
+                       subcs2->item.lineno = subcs1->item.lineno;
+
+                       cf_item_add(cs, &(subcs2->item));
+                       continue;
+               }
+
+               /* ignore everything else */
+       }
+
+       return true;
+}
+
 static char const *cf_local_file(char const *base, char const *filename,
                                 char *buffer, size_t bufsize)
 {
@@ -1323,7 +1599,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                           CONF_SECTION *current)
 
 {
-       CONF_SECTION *this, *css;
+       CONF_SECTION *this, *css, *nextcs;
        CONF_PAIR *cpn;
        char const *ptr, *start;
        char const *value;
@@ -1331,8 +1607,8 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
        char buf1[8192];
        char buf2[8192];
        char buf3[8192];
-       int t1, t2, t3;
-       int spaces = false;
+       FR_TOKEN t1, t2, t3;
+       bool spaces = false;
        char *cbuf = buf;
        size_t len;
        fr_cond_t *cond = NULL;
@@ -1344,6 +1620,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
         */
        for (;;) {
                int at_eof;
+               nextcs = NULL;
 
                /*
                 *      Get data, and remember if we are at EOF.
@@ -1438,10 +1715,9 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                return -1;
                        }
 
-                       t1 = T_BARE_WORD;
                        ptr += hack;
 
-                       t2 = gettoken(&ptr, buf2, sizeof(buf2));
+                       t2 = gettoken(&ptr, buf2, sizeof(buf2), true);
                        switch (t2) {
                        case T_EOL:
                        case T_HASH:
@@ -1453,7 +1729,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                return -1;
                        }
                } else {
-                       t1 = gettoken(&ptr, buf1, sizeof(buf1));
+                       t1 = gettoken(&ptr, buf1, sizeof(buf1), true);
                }
 
                /*
@@ -1467,8 +1743,18 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                               ERROR("%s[%d]: Too many closing braces",
                                      filename, *lineno);
                               return -1;
+                      }
 
+                      /*
+                       *       Merge the template into the existing
+                       *       section.  This uses more memory, but
+                       *       means that templates now work with
+                       *       sub-sections, etc.
+                       */
+                      if (!cf_template_merge(this, this->template)) {
+                              return -1;
                       }
+
                       this = this->item.parent;
                       if (seen_too_much(filename, *lineno, ptr)) return -1;
                       continue;
@@ -1482,16 +1768,21 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                 */
               if ((strcasecmp(buf1, "$INCLUDE") == 0) ||
                   (strcasecmp(buf1, "$-INCLUDE") == 0)) {
-                      int relative = 1;
+                       bool relative = true;
 
-                       t2 = getword(&ptr, buf2, sizeof(buf2));
+                       t2 = getword(&ptr, buf2, sizeof(buf2), true);
+                       if (t2 != T_EOL) {
+                              ERROR("%s[%d]: Unexpected text after $INCLUDE",
+                                    filename, *lineno);
+                              return -1;
+                       }
 
-                       if (buf2[0] == '$') relative = 0;
+                       if (buf2[0] == '$') relative = false;
 
                        value = cf_expand_variables(filename, lineno, this, buf, sizeof(buf), buf2);
                        if (!value) return -1;
 
-                       if (!FR_DIR_IS_RELATIVE(value)) relative = 0;
+                       if (!FR_DIR_IS_RELATIVE(value)) relative = false;
 
                        if (relative) {
                                value = cf_local_file(filename, value, buf3,
@@ -1524,7 +1815,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                if (stat(value, &stat_buf) < 0) {
                                        ERROR("%s[%d]: Failed reading directory %s: %s",
                                               filename, *lineno,
-                                              value, strerror(errno));
+                                              value, fr_syserror(errno));
                                        return -1;
                                }
 
@@ -1538,7 +1829,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                if (!dir) {
                                        ERROR("%s[%d]: Error reading directory %s: %s",
                                               filename, *lineno, value,
-                                              strerror(errno));
+                                              fr_syserror(errno));
                                        return -1;
                                }
 
@@ -1584,7 +1875,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                        struct stat statbuf;
 
                                        if (stat(value, &statbuf) < 0) {
-                                               WDEBUG("Not including file %s: %s", value, strerror(errno));
+                                               WARN("Not including file %s: %s", value, fr_syserror(errno));
                                                continue;
                                        }
                                }
@@ -1599,7 +1890,13 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
               if (strcasecmp(buf1, "$template") == 0) {
                       CONF_ITEM *ci;
                       CONF_SECTION *parentcs, *templatecs;
-                      t2 = getword(&ptr, buf2, sizeof(buf2));
+                      t2 = getword(&ptr, buf2, sizeof(buf2), true);
+
+                      if (t2 != T_EOL) {
+                              ERROR("%s[%d]: Unexpected text after $TEMPLATE",
+                                     filename, *lineno);
+                              return -1;
+                      }
 
                       parentcs = cf_top_section(current);
 
@@ -1617,6 +1914,12 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                return -1;
                       }
 
+                      if (!this) {
+                               ERROR("%s[%d]: Internal sanity check error in template reference",
+                                      filename, *lineno);
+                               return -1;
+                      }
+
                       if (this->template) {
                                ERROR("%s[%d]: Section already has a template",
                                       filename, *lineno);
@@ -1685,7 +1988,16 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                if (!server) goto invalid_location;
                        }
 
-                       slen = fr_condition_tokenize(this, cf_sectiontoitem(this), ptr, &cond, &error, FR_COND_TWO_PASS);
+                       nextcs = cf_section_alloc(this, buf1, ptr);
+                       if (!nextcs) {
+                               ERROR("%s[%d]: Failed allocating memory for section",
+                                     filename, *lineno);
+                               return -1;
+                       }
+                       nextcs->item.filename = talloc_strdup(nextcs, filename);
+                       nextcs->item.lineno = *lineno;
+
+                       slen = fr_condition_tokenize(nextcs, cf_sectiontoitem(nextcs), ptr, &cond, &error, FR_COND_TWO_PASS);
                        if (p) *p = '{';
 
                        if (slen < 0) {
@@ -1702,15 +2014,16 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                ERROR("%s[%d]: Parse error in condition",
                                       filename, *lineno);
 
-                               EDEBUG("%s", start);
-                               EDEBUG("%.*s^%s", (int) offset, spbuf, error);
+                               ERROR("%s", start);
+                               ERROR("%.*s^ %s", (int) offset, spbuf, error);
+                               talloc_free(nextcs);
                                free(spbuf);
                                return -1;
                        }
 
                        if ((size_t) slen >= (sizeof(buf2) - 1)) {
-                               talloc_free(cond);
-                               EDEBUG("%s[%d]: Condition is too large after \"%s\"",
+                               talloc_free(nextcs);
+                               ERROR("%s[%d]: Condition is too large after \"%s\"",
                                       filename, *lineno, buf1);
                                return -1;
                        }
@@ -1720,20 +2033,28 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                        ptr += slen;
                        t2 = T_BARE_WORD;
 
-                       if (gettoken(&ptr, buf3, sizeof(buf3)) != T_LCBRACE) {
-                               talloc_free(cond);
-                               EDEBUG("%s[%d]: Expected '{'",
+                       if (gettoken(&ptr, buf3, sizeof(buf3), true) != T_LCBRACE) {
+                               talloc_free(nextcs);
+                               ERROR("%s[%d]: Expected '{'",
                                       filename, *lineno);
                                return -1;
                        }
 
+                       /*
+                        *      Swap the condition with trailing stuff for
+                        *      the final condition.
+                        */
+                       memcpy(&p, &nextcs->name2, sizeof(nextcs->name2));
+                       talloc_free(p);
+                       nextcs->name2 = talloc_typed_strdup(nextcs, buf2);
+
                        goto section_alloc;
                }
 
                /*
                 *      Grab the next token.
                 */
-               t2 = gettoken(&ptr, buf2, sizeof(buf2));
+               t2 = gettoken(&ptr, buf2, sizeof(buf2), true);
                switch (t2) {
                case T_EOL:
                case T_HASH:
@@ -1759,7 +2080,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
 
                case T_OP_EQ:
                case T_OP_SET:
-                       t3 = getstring(&ptr, buf3, sizeof(buf3));
+                       t3 = getstring(&ptr, buf3, sizeof(buf3), true);
                        if (t3 == T_OP_INVALID) {
                                ERROR("%s[%d]: Parse error: %s",
                                       filename, *lineno,
@@ -1803,7 +2124,8 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                         */
                do_set:
                        cpn = cf_pair_alloc(this, buf1, value, t2, t3);
-                       cpn->item.filename = filename;
+                       if (!cpn) return -1;
+                       cpn->item.filename = talloc_strdup(cpn, filename);
                        cpn->item.lineno = *lineno;
                        cf_item_add(this, &(cpn->item));
                        continue;
@@ -1814,7 +2136,7 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                case T_BARE_WORD:
                case T_DOUBLE_QUOTED_STRING:
                case T_SINGLE_QUOTED_STRING:
-                       t3 = gettoken(&ptr, buf3, sizeof(buf3));
+                       t3 = gettoken(&ptr, buf3, sizeof(buf3), true);
                        if (t3 != T_LCBRACE) {
                                ERROR("%s[%d]: Expecting section start brace '{' after \"%s %s\"",
                                       filename, *lineno, buf1, buf2);
@@ -1829,34 +2151,44 @@ static int cf_section_read(char const *filename, int *lineno, FILE *fp,
                                return -1;
                        }
 
-                       css = cf_section_alloc(this, buf1,
-                                              t2 == T_LCBRACE ? NULL : buf2);
-                       if (!css) {
-                               ERROR("%s[%d]: Failed allocating memory for section",
-                                               filename, *lineno);
-                               return -1;
-                       }
-                       cf_item_add(this, &(css->item));
-                       css->item.filename = filename;
-                       css->item.lineno = *lineno;
+                       if (!cond) {
+                               css = cf_section_alloc(this, buf1,
+                                                      t2 == T_LCBRACE ? NULL : buf2);
+                               if (!css) {
+                                       ERROR("%s[%d]: Failed allocating memory for section",
+                                             filename, *lineno);
+                                       return -1;
+                               }
 
-                       if (cond) {
-                               /*
-                                *      FIXME: talloc_steal cond to css
-                                *      set "ci" in all of "cond" to css
-                                *
-                                *      <sigh>
-                                */
+                               css->item.filename = talloc_strdup(css, filename);
+                               css->item.lineno = *lineno;
+                               cf_item_add(this, &(css->item));
+
+                       } else {
+                               css = nextcs;
+                               nextcs = NULL;
+
+                               cf_item_add(this, &(css->item));
                                cf_data_add_internal(css, "if", cond, NULL, false);
                                cond = NULL; /* eaten by the above line */
                        }
 
                        /*
+                        *      There may not be a name2
+                        */
+                       css->name2_type = (t2 == T_LCBRACE) ? T_OP_INVALID : t2;
+
+                       /*
                         *      The current section is now the child section.
                         */
                        this = css;
                        continue;
 
+               case T_OP_INVALID:
+                       ERROR("%s[%d]: Syntax error in '%s': %s",
+                             filename, *lineno, ptr, fr_strerror());
+                       return -1;
+
                default:
                        ERROR("%s[%d]: Parse error after \"%s\": unexpected token \"%s\"",
                              filename, *lineno, buf1, fr_int2str(fr_tokens, t2, "<INVALID>"));
@@ -1893,7 +2225,7 @@ int cf_file_include(CONF_SECTION *cs, char const *filename)
        fp = fopen(filename, "r");
        if (!fp) {
                ERROR("Unable to open file \"%s\": %s",
-                      filename, strerror(errno));
+                      filename, fr_syserror(errno));
                return -1;
        }
 
@@ -1945,7 +2277,7 @@ int cf_file_include(CONF_SECTION *cs, char const *filename)
                return -1;
        }
 
-       if (!cs->item.filename) cs->item.filename = filename;
+       if (!cs->item.filename) cs->item.filename = talloc_strdup(cs, filename);
 
        /*
         *      Read the section.  It's OK to have EOF without a
@@ -1979,7 +2311,7 @@ CONF_SECTION *cf_file_read(char const *filename)
        if (p) *p = '\0';
 
        cp->item.filename = "internal";
-       cp->item.lineno = 0;
+       cp->item.lineno = -1;
        cf_item_add(cs, &(cp->item));
 
        if (cf_file_include(cs, filename) < 0) {
@@ -2002,33 +2334,17 @@ void cf_file_free(CONF_SECTION *cs)
  */
 CONF_PAIR *cf_pair_find(CONF_SECTION const *cs, char const *name)
 {
-       CONF_ITEM       *ci;
-       CONF_PAIR       *cp = NULL;
+       CONF_PAIR *cp, mycp;
 
-       if (!cs) return NULL;
-
-       /*
-        *      Find the name in the tree, for speed.
-        */
-       if (name) {
-               CONF_PAIR mycp;
+       if (!cs || !name) return NULL;
 
-               mycp.attr = name;
-               cp = rbtree_finddata(cs->pair_tree, &mycp);
-       } else {
-               /*
-                *      Else find the first one that matches
-                */
-               for (ci = cs->children; ci; ci = ci->next) {
-                       if (ci->type == CONF_ITEM_PAIR) {
-                               return cf_itemtopair(ci);
-                       }
-               }
-       }
+       mycp.attr = name;
+       cp = rbtree_finddata(cs->pair_tree, &mycp);
+       if (cp) return cp;
 
-       if (cp || !cs->template) return cp;
+       if (!cs->template) return NULL;
 
-       return cf_pair_find(cs->template, name);
+       return rbtree_finddata(cs->template->pair_tree, &mycp);
 }
 
 /*
@@ -2065,11 +2381,6 @@ FR_TOKEN cf_pair_value_type(CONF_PAIR const *pair)
 }
 
 /*
- *     Copied here for error reporting.
- */
-extern void fr_strerror_printf(char const *, ...);
-
-/*
  * Turn a CONF_PAIR into a VALUE_PAIR
  * For now, ignore the "value_type" field...
  */
@@ -2092,7 +2403,7 @@ VALUE_PAIR *cf_pairtovp(CONF_PAIR *pair)
        if ((pair->op != T_OP_CMP_FALSE) &&
            ((pair->value_type == T_DOUBLE_QUOTED_STRING) ||
             (pair->value_type == T_BACK_QUOTED_STRING))) {
-               VALUE_PAIR *vp;
+               VALUE_PAIR *vp;
 
                vp = pairmake(pair, NULL, pair->attr, NULL, pair->op);
                if (!vp) {
@@ -2213,33 +2524,24 @@ CONF_SECTION *cf_section_find(char const *name)
 }
 
 /** Find a sub-section in a section
+ *
+ *     This finds ANY section having the same first name.
+ *     The second name is ignored.
  */
 CONF_SECTION *cf_section_sub_find(CONF_SECTION const *cs, char const *name)
 {
-       CONF_ITEM *ci;
+       CONF_SECTION mycs;
 
-       if (!name) return NULL; /* can't find an un-named section */
+       if (!cs || !name) return NULL;  /* can't find an un-named section */
 
        /*
-        *      Do the fast lookup if possible.
+        *      No sub-sections have been defined, so none exist.
         */
-       if (cs->section_tree) {
-               CONF_SECTION mycs;
-
-               mycs.name1 = name;
-               mycs.name2 = NULL;
-               return rbtree_finddata(cs->section_tree, &mycs);
-       }
-
-       for (ci = cs->children; ci; ci = ci->next) {
-               if (ci->type != CONF_ITEM_SECTION)
-                       continue;
-               if (strcmp(cf_itemtosection(ci)->name1, name) == 0)
-                       break;
-       }
-
-       return cf_itemtosection(ci);
+       if (!cs->section_tree) return NULL;
 
+       mycs.name1 = name;
+       mycs.name2 = NULL;
+       return rbtree_finddata(cs->section_tree, &mycs);
 }
 
 
@@ -2252,17 +2554,44 @@ CONF_SECTION *cf_section_sub_find_name2(CONF_SECTION const *cs,
        CONF_ITEM    *ci;
 
        if (!cs) cs = root_config;
+       if (!cs) return NULL;
 
-       if (name1 && (cs->section_tree)) {
+       if (name1) {
                CONF_SECTION mycs, *master_cs;
 
+               if (!cs->section_tree) return NULL;
+
                mycs.name1 = name1;
                mycs.name2 = name2;
 
                master_cs = rbtree_finddata(cs->section_tree, &mycs);
-               if (master_cs) {
-                       return rbtree_finddata(master_cs->name2_tree, &mycs);
+               if (!master_cs) return NULL;
+
+               /*
+                *      Look it up in the name2 tree.  If it's there,
+                *      return it.
+                */
+               if (master_cs->name2_tree) {
+                       CONF_SECTION *subcs;
+
+                       subcs = rbtree_finddata(master_cs->name2_tree, &mycs);
+                       if (subcs) return subcs;
+               }
+
+               /*
+                *      We don't insert ourselves into the name2 tree.
+                *      So if there's nothing in the name2 tree, maybe
+                *      *we* are the answer.
+                */
+               if (!master_cs->name2 && name2) return NULL;
+               if (master_cs->name2 && !name2) return NULL;
+               if (!master_cs->name2 && !name2) return master_cs;
+
+               if (strcmp(master_cs->name2, name2) == 0) {
+                       return master_cs;
                }
+
+               return NULL;
        }
 
        /*
@@ -2275,19 +2604,11 @@ CONF_SECTION *cf_section_sub_find_name2(CONF_SECTION const *cs,
                        continue;
 
                subcs = cf_itemtosection(ci);
-               if (!name1) {
-                       if (!subcs->name2) {
-                               if (strcmp(subcs->name1, name2) == 0) break;
-                       } else {
-                               if (strcmp(subcs->name2, name2) == 0) break;
-                       }
-                       continue; /* don't do the string comparisons below */
+               if (!subcs->name2) {
+                       if (strcmp(subcs->name1, name2) == 0) break;
+               } else {
+                       if (strcmp(subcs->name2, name2) == 0) break;
                }
-
-               if ((strcmp(subcs->name1, name1) == 0) &&
-                   (subcs->name2 != NULL) &&
-                   (strcmp(subcs->name2, name2) == 0))
-                       break;
        }
 
        return cf_itemtosection(ci);
@@ -2414,8 +2735,8 @@ static CONF_DATA *cf_data_alloc(CONF_SECTION *parent, char const *name,
 
        cd->item.type = CONF_ITEM_DATA;
        cd->item.parent = parent;
-       cd->name = talloc_strdup(cd, name);
-       if (!cd) {
+       cd->name = talloc_typed_strdup(cd, name);
+       if (!cd->name) {
                talloc_free(cd);
                return NULL;
        }
@@ -2496,216 +2817,6 @@ int cf_data_add(CONF_SECTION *cs, char const *name,
        return cf_data_add_internal(cs, name, data, data_free, 0);
 }
 
-#if 0
-/*
- *     Copy CONF_DATA from src to dst
- */
-static void cf_section_copy_data(CONF_SECTION *s, CONF_SECTION *d)
-{
-
-       CONF_ITEM *cd, *next, **last;
-
-       /*
-        *      Don't check if s->data_tree is NULL.  It's child
-        *      sections may have data, even if this section doesn't.
-        */
-
-       rad_assert(d->data_tree == NULL);
-       d->data_tree = s->data_tree;
-       s->data_tree = NULL;
-
-       /*
-        *      Walk through src, moving CONF_ITEM_DATA
-        *      to dst, by hand.
-        */
-       last = &(s->children);
-       for (cd = s->children; cd != NULL; cd = next) {
-               next = cd->next;
-
-               /*
-                *      Recursively copy data from child sections.
-                */
-               if (cd->type == CONF_ITEM_SECTION) {
-                       CONF_SECTION *s1, *d1;
-
-                       s1 = cf_itemtosection(cd);
-                       d1 = cf_section_sub_find_name2(d, s1->name1, s1->name2);
-                       if (d1) {
-                               cf_section_copy_data(s1, d1);
-                       }
-                       last = &(cd->next);
-                       continue;
-               }
-
-               /*
-                *      Not conf data, remember last ptr.
-                */
-               if (cd->type != CONF_ITEM_DATA) {
-                       last = &(cd->next);
-                       continue;
-               }
-
-               /*
-                *      Remove it from the src list
-                */
-               *last = cd->next;
-               cd->next = NULL;
-
-               /*
-                *      Add it to the dst list
-                */
-               if (!d->children) {
-                       rad_assert(d->tail == NULL);
-                       d->children = cd;
-               } else {
-                       rad_assert(d->tail != NULL);
-                       d->tail->next = cd;
-               }
-               d->tail = cd;
-       }
-}
-
-/*
- *     For a CONF_DATA element, stat the filename, if necessary.
- */
-static int filename_stat(UNUSED void *context, void *data)
-{
-       struct stat buf;
-       CONF_DATA *cd = data;
-
-       if (cd->flag != PW_TYPE_FILE_INPUT) return 0;
-
-       if (stat(cd->name, &buf) < 0) return -1;
-
-       if (buf.st_mtime != *(time_t *) cd->data) return -1;
-
-       return 0;
-}
-
-
-/*
- *     Compare two CONF_SECTIONS.  The items MUST be in the same
- *     order.
- */
-static int cf_section_cmp(CONF_SECTION *a, CONF_SECTION *b)
-{
-       CONF_ITEM *ca = a->children;
-       CONF_ITEM *cb = b->children;
-
-       while (1) {
-               CONF_PAIR *pa, *pb;
-
-               /*
-                *      Done.  Stop.
-                */
-               if (!ca && !cb) break;
-
-               /*
-                *      Skip CONF_DATA.
-                */
-               if (ca && ca->type == CONF_ITEM_DATA) {
-                       ca = ca->next;
-                       continue;
-               }
-               if (cb && cb->type == CONF_ITEM_DATA) {
-                       cb = cb->next;
-                       continue;
-               }
-
-               /*
-                *      One is smaller than the other.  Exit.
-                */
-               if (!ca || !cb) return 0;
-
-               if (ca->type != cb->type) return 0;
-
-               /*
-                *      Deal with subsections.
-                */
-               if (ca->type == CONF_ITEM_SECTION) {
-                       CONF_SECTION *sa = cf_itemtosection(ca);
-                       CONF_SECTION *sb = cf_itemtosection(cb);
-
-                       if (!cf_section_cmp(sa, sb)) return 0;
-                       goto next;
-               }
-
-               rad_assert(ca->type == CONF_ITEM_PAIR);
-
-               pa = cf_itemtopair(ca);
-               pb = cf_itemtopair(cb);
-
-               /*
-                *      Different attr and/or value, Exit.
-                */
-               if ((strcmp(pa->attr, pb->attr) != 0) ||
-                   (strcmp(pa->value, pb->value) != 0)) return 0;
-
-
-               /*
-                *      And go to the next element.
-                */
-       next:
-               ca = ca->next;
-               cb = cb->next;
-       }
-
-       /*
-        *      Walk over the CONF_DATA, stat'ing PW_TYPE_FILE_INPUT.
-        */
-       if (a->data_tree &&
-           (rbtree_walk(a->data_tree, InOrder, filename_stat, NULL) != 0)) {
-               return 0;
-       }
-
-       /*
-        *      They must be the same, say so.
-        */
-       return 1;
-}
-
-
-/*
- *     Migrate CONF_DATA from one section to another.
- */
-int cf_section_migrate(CONF_SECTION *dst, CONF_SECTION *src)
-{
-       CONF_ITEM *ci;
-       CONF_SECTION *s, *d;
-
-       for (ci = src->children; ci != NULL; ci = ci->next) {
-               if (ci->type != CONF_ITEM_SECTION)
-                       continue;
-
-               s = cf_itemtosection(ci);
-               d = cf_section_sub_find_name2(dst, s->name1, s->name2);
-
-               if (!d) continue; /* not in new one, don't migrate it */
-
-               /*
-                *      A section of the same name is in BOTH src & dst,
-                *      compare the CONF_PAIR's.  If they're all the same,
-                *      then copy the CONF_DATA from one to the other.
-                */
-               if (cf_section_cmp(s, d)) {
-                       cf_section_copy_data(s, d);
-               }
-       }
-
-       return 1;               /* rcode means anything? */
-}
-#endif
-
-int cf_section_template(CONF_SECTION *cs, CONF_SECTION *template)
-{
-       if (!cs || !template || cs->template || template->template) return -1;
-
-       cs->template = template;
-
-       return 0;
-}
-
-
 /*
  *     This is here to make the rest of the code easier to read.  It
  *     ties conffile.c to log.c, but it means we don't have to
@@ -2798,209 +2909,12 @@ const CONF_PARSER *cf_section_parse_table(CONF_SECTION *cs)
        return cs->variables;
 }
 
-#if 0
 /*
- * JMG dump_config tries to dump the config structure in a readable format
- *
-*/
-
-static int dump_config_section(CONF_SECTION const *cs, int indent)
-{
-       CONF_SECTION    *scs;
-       CONF_PAIR       *cp;
-       CONF_ITEM       *ci;
-
-       /* The DEBUG macro doesn't let me
-        *   for(i=0;i<indent;++i) debugputchar('\t');
-        * so I had to get creative. --Pac. */
-
-       for (ci = cs->children; ci; ci = ci->next) {
-               switch (ci->type) {
-               case CONF_ITEM_PAIR:
-                       cp=cf_itemtopair(ci);
-                       DEBUG("%.*s%s = %s",
-                               indent, "\t\t\t\t\t\t\t\t\t\t\t",
-                               cp->attr, cp->value);
-                       break;
-
-               case CONF_ITEM_SECTION:
-                       scs=cf_itemtosection(ci);
-                       DEBUG("%.*s%s %s%s{",
-                               indent, "\t\t\t\t\t\t\t\t\t\t\t",
-                               scs->name1,
-                               scs->name2 ? scs->name2 : "",
-                               scs->name2 ?  " " : "");
-                       dump_config_section(scs, indent+1);
-                       DEBUG("%.*s}",
-                               indent, "\t\t\t\t\t\t\t\t\t\t\t");
-                       break;
-
-               default:        /* FIXME: Do more! */
-                       break;
-               }
-       }
-
-       return 0;
-}
-
-int dump_config(CONF_SECTION *cs)
-{
-       return dump_config_section(cs, 0);
-}
-#endif
-
-static char const *cf_pair_print_value(CONF_PAIR const *cp,
-                                      char *buffer, size_t buflen)
-{
-       char *p;
-
-       if (!cp->value) return "";
-
-       switch (cp->value_type) {
-       default:
-       case T_BARE_WORD:
-               snprintf(buffer, buflen, "%s", cp->value);
-               break;
-
-       case T_SINGLE_QUOTED_STRING:
-               snprintf(buffer, buflen, "'%s'", cp->value);
-               break;
-
-       case T_DOUBLE_QUOTED_STRING:
-               buffer[0] = '"';
-               fr_print_string(cp->value, strlen(cp->value),
-                               buffer + 1, buflen - 3);
-               p = buffer + strlen(buffer); /* yuck... */
-               p[0] = '"';
-               p[1] = '\0';
-               break;
-       }
-
-       return buffer;
-}
-
-
-int cf_pair2xml(FILE *fp, CONF_PAIR const *cp)
-{
-       fprintf(fp, "<%s>", cp->attr);
-       if (cp->value) {
-               char buffer[2048];
-
-               char *p = buffer;
-               char const *q = cp->value;
-
-               while (*q && (p < (buffer + sizeof(buffer) - 1))) {
-                       if (q[0] == '&') {
-                               memcpy(p, "&amp;", 4);
-                               p += 5;
-
-                       } else if (q[0] == '<') {
-                               memcpy(p, "&lt;", 4);
-                               p += 4;
-
-                       } else if (q[0] == '>') {
-                               memcpy(p, "&gt;", 4);
-                               p += 4;
-
-                       } else {
-                               *(p++) = *q;
-                       }
-                       q++;
-               }
-
-               *p = '\0';
-               fprintf(fp, "%s", buffer);
-       }
-
-       fprintf(fp, "</%s>\n", cp->attr);
-
-       return 1;
-}
-
-int cf_section2xml(FILE *fp, CONF_SECTION const *cs)
-{
-       CONF_ITEM *ci, *next;
-
-       /*
-        *      Section header
-        */
-       fprintf(fp, "<%s>\n", cs->name1);
-       if (cs->name2) {
-               fprintf(fp, "<_name2>%s</_name2>\n", cs->name2);
-       }
-
-       /*
-        *      Loop over contents.
-        */
-       for (ci = cs->children; ci; ci = next) {
-               next = ci->next;
-
-               switch (ci->type) {
-               case CONF_ITEM_PAIR:
-                       if (!cf_pair2xml(fp, (CONF_PAIR *) ci)) return 0;
-                       break;
-
-               case CONF_ITEM_SECTION:
-                       if (!cf_section2xml(fp, (CONF_SECTION *) ci)) return 0;
-                       break;
-
-               default:        /* should really be an error. */
-                       break;
-
-               }
-       }
-
-       fprintf(fp, "</%s>\n", cs->name1);
-
-       return 1;               /* success */
-}
-
-int cf_pair2file(FILE *fp, CONF_PAIR const *cp)
-{
-       char buffer[2048];
-
-       fprintf(fp, "\t%s = %s\n", cp->attr,
-               cf_pair_print_value(cp, buffer, sizeof(buffer)));
-
-       return 1;
-}
-
-int cf_section2file(FILE *fp, CONF_SECTION const *cs)
+ *     For "switch" and "case" statements.
+ */
+FR_TOKEN cf_section_name2_type(CONF_SECTION const *cs)
 {
-       CONF_ITEM const *ci, *next;
-
-       /*
-        *      Section header
-        */
-       if (!cs->name2) {
-               fprintf(fp, "%s {\n", cs->name1);
-       } else {
-               fprintf(fp, "%s %s {\n",
-                       cs->name1, cs->name2);
-       }
-
-       /*
-        *      Loop over contents.
-        */
-       for (ci = cs->children; ci; ci = next) {
-               next = ci->next;
-
-               switch (ci->type) {
-               case CONF_ITEM_PAIR:
-                       if (!cf_pair2file(fp, (CONF_PAIR const *) ci)) return 0;
-                       break;
-
-               case CONF_ITEM_SECTION:
-                       if (!cf_section2file(fp, (CONF_SECTION const *) ci)) return 0;
-                       break;
-
-               default:        /* should really be an error. */
-                       break;
-
-               }
-       }
-
-       fprintf(fp, "}\n");
+       if (!cs) return T_OP_INVALID;
 
-       return 1;               /* success */
+       return cs->name2_type;
 }
index 665b5c2..3c9e586 100644 (file)
@@ -32,6 +32,14 @@ typedef struct fr_connection fr_connection_t;
 
 static int fr_connection_pool_check(fr_connection_pool_t *pool);
 
+extern bool check_config;
+
+#ifndef NDEBUG
+#ifdef HAVE_PTHREAD_H
+/* #define PTHREAD_DEBUG (1) */
+#endif
+#endif
+
 /** An individual connection within the connection pool
  *
  * Defines connection counters, timestamps, and holds a pointer to the
@@ -47,16 +55,19 @@ struct fr_connection {
        time_t          last_used;      //!< Last time the connection was
                                        //!< reserved.
 
-       uint64_t        num_uses;       //!< Number of times the connection
+       uint32_t        num_uses;       //!< Number of times the connection
                                        //!< has been reserved.
-       int             in_use;         //!< Whether the connection is currently
-                                       //!< reserved.
        uint64_t        number;         //!< Unique ID assigned when the
                                        //!< connection is created, these will
                                        //!< monotonically increase over the
                                        //!< lifetime of the connection pool.
        void            *connection;    //!< Pointer to whatever the module
                                        //!< uses for a connection handle.
+       bool            in_use;         //!< Whether the connection is currently
+                                       //!< reserved.
+#ifdef PTHREAD_DEBUG
+       pthread_t       pthread_id;     //!< When 'in_use == true'
+#endif
 };
 
 /** A connection pool
@@ -69,23 +80,33 @@ struct fr_connection {
  * @see fr_connection
  */
 struct fr_connection_pool_t {
-       int             start;          //!< Number of initial connections
-       int             min;            //!< Minimum number of concurrent
+       uint32_t        start;          //!< Number of initial connections
+       uint32_t        min;            //!< Minimum number of concurrent
                                        //!< connections to keep open.
-       int             max;            //!< Maximum number of concurrent
+       uint32_t        max;            //!< Maximum number of concurrent
                                        //!< connections to allow.
-       int             spare;          //!< Number of spare connections to try
-                                       //!< and maintain.
-       int             cleanup_delay;  //!< How long a connection can go unused
-                                       //!< for before it's closed
-                                       //!< (0 is infinite).
+       uint32_t        spare;          //!< Number of spare connections to try
+       uint32_t        retry_delay;    //!< seconds to delay re-open
+                                       //!< after a failed open.
+       uint32_t        cleanup_interval; //!< Initial timer for how
+                                         //!< often we sweep the pool
+                                         //!< for free connections.
+                                         //!< (0 is infinite).
+       int             delay_interval;  //!< When we next do a
+                                       //!< cleanup.  Initialized to
+                                       //!< cleanup_interval, and increase
+                                       //!< from there based on the delay.
+       int             next_delay;     //!< The next delay time.
+                                       //!< cleanup.  Initialized to
+                                       //!< cleanup_interval, and decays
+                                       //!< from there.
        uint64_t        max_uses;       //!< Maximum number of times a
                                        //!< connection can be used before being
                                        //!< closed.
-       int             lifetime;       //!< How long a connection can be open
+       uint32_t        lifetime;       //!< How long a connection can be open
                                        //!< before being closed (irrespective
                                        //!< of whether it's idle or not).
-       int             idle_timeout;   //!< How long a connection can be idle
+       uint32_t        idle_timeout;   //!< How long a connection can be idle
                                        //!< before being closed.
 
        bool            trigger;        //!< If true execute connection triggers
@@ -102,8 +123,6 @@ struct fr_connection_pool_t {
        time_t          last_spawned;   //!< Last time we spawned a connection.
        time_t          last_failed;    //!< Last time we tried to spawn a
                                        //!< a connection but failed.
-       time_t          last_complained;//!< Last time we complained about
-                                       //!< configuration parameters.
        time_t          last_throttled; //!< Last time we refused to spawn a
                                        //!< connection because the last
                                        //!< connection failed, or we were
@@ -113,7 +132,7 @@ struct fr_connection_pool_t {
 
        uint64_t        count;          //!< Number of connections spawned over
                                        //!< the lifetime of the pool.
-       int             num;            //!< Number of connections in the pool.
+       uint32_t        num;            //!< Number of connections in the pool.
        int             active;         //!< Number of currently reserved
                                        //!< connections.
 
@@ -154,24 +173,17 @@ struct fr_connection_pool_t {
 #endif
 
 static const CONF_PARSER connection_config[] = {
-       { "start",    PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, start),
-         0, "5" },
-       { "min",      PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, min),
-         0, "5" },
-       { "max",      PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, max),
-         0, "10" },
-       { "spare",    PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, spare),
-         0, "3" },
-       { "uses",     PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, max_uses),
-         0, "0" },
-       { "lifetime", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, lifetime),
-         0, "0" },
-       { "cleanup_delay", PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, cleanup_delay),
-         0, "5" },
-       { "idle_timeout",  PW_TYPE_INTEGER, offsetof(fr_connection_pool_t, idle_timeout),
-         0, "60" },
-       { "spread", PW_TYPE_BOOLEAN, offsetof(fr_connection_pool_t, spread),
-         0, "no" },
+       { "start", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, start), "5" },
+       { "min", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, min), "5" },
+       { "max", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, max), "10" },
+       { "spare", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, spare), "3" },
+       { "uses", FR_CONF_OFFSET(PW_TYPE_INTEGER64, fr_connection_pool_t, max_uses), "0" },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, lifetime), "0" },
+       { "cleanup_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, cleanup_interval), NULL},
+       { "cleanup_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, cleanup_interval), "30" },
+       { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, idle_timeout), "60" },
+       { "retry_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_connection_pool_t, retry_delay), "1" },
+       { "spread", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_connection_pool_t, spread), "no" },
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -211,7 +223,7 @@ static void fr_connection_unlink(fr_connection_pool_t *pool,
  * @param[in] this Connection to add.
  */
 static void fr_connection_link_head(fr_connection_pool_t *pool,
-                                   fr_connection_t *this)
+                                   fr_connection_t *this)
 {
        rad_assert(pool != NULL);
        rad_assert(this != NULL);
@@ -241,7 +253,7 @@ static void fr_connection_link_head(fr_connection_pool_t *pool,
  * @param[in] this Connection to add.
  */
 static void fr_connection_link_tail(fr_connection_pool_t *pool,
-                                   fr_connection_t *this)
+                                   fr_connection_t *this)
 {
        rad_assert(pool != NULL);
        rad_assert(this != NULL);
@@ -273,10 +285,11 @@ static void fr_connection_link_tail(fr_connection_pool_t *pool,
  *
  * @param[in] pool to modify.
  * @param[in] now Current time.
+ * @param[in] in_use whether the new connection should be "in_use" or not
  * @return the new connection struct or NULL on error.
  */
 static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
-                                           time_t now)
+                                           time_t now, bool in_use)
 {
        fr_connection_t *this;
        void *conn;
@@ -294,8 +307,24 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
        pthread_mutex_lock(&pool->mutex);
        rad_assert(pool->num <= pool->max);
 
-       if ((pool->last_failed == now) || pool->spawning) {
-               int complain = false;
+       /*
+        *      Don't spawn multiple connections at the same time.
+        */
+       if (pool->spawning) {
+               pthread_mutex_unlock(&pool->mutex);
+
+               ERROR("%s: Cannot open new connection, "
+                     "connection spawning already in "
+                     "progress", pool->log_prefix);
+               return NULL;
+       }
+
+       /*
+        *      If the last attempt failed, wait a bit before
+        *      retrying.
+        */
+       if (pool->last_failed && ((pool->last_failed + pool->retry_delay) > now)) {
+               bool complain = false;
 
                if (pool->last_throttled != now) {
                        complain = true;
@@ -305,16 +334,9 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
 
                pthread_mutex_unlock(&pool->mutex);
 
-               if (complain) {
-                       if (pool->spawning) {
-                               ERROR("%s: Cannot open new connection, "
-                                      "connection spawning already in "
-                                      "progress", pool->log_prefix);
-                       } else {
-                               ERROR("%s: Last connection failed, "
-                                      "throttling connection spawn",
-                                      pool->log_prefix);
-                       }
+               if (!RATE_LIMIT_ENABLED || complain) {
+                       ERROR("%s: Last connection attempt failed, waiting %d seconds before retrying",
+                             pool->log_prefix, pool->retry_delay);
                }
 
                return NULL;
@@ -333,9 +355,6 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
 
        INFO("%s: Opening additional connection (%" PRIu64 ")", pool->log_prefix, pool->count);
 
-       this = rad_malloc(sizeof(*this));
-       memset(this, 0, sizeof(*this));
-
        /*
         *      This may take a long time, which prevents other
         *      threads from releasing connections.  We don't care
@@ -347,88 +366,41 @@ static fr_connection_t *fr_connection_spawn(fr_connection_pool_t *pool,
                ERROR("%s: Opening connection failed (%" PRIu64 ")", pool->log_prefix, pool->count);
 
                pool->last_failed = now;
-               free(this);
                pool->spawning = false; /* atomic, so no lock is needed */
                return NULL;
        }
 
-       this->created = now;
-       this->connection = conn;
-
        /*
         *      And lock the mutex again while we link the new
         *      connection back into the pool.
         */
        pthread_mutex_lock(&pool->mutex);
 
-       this->number = pool->count++;
-       this->last_used = now;
-       fr_connection_link_head(pool, this);
-       pool->num++;
-       pool->spawning = false;
-       pool->last_spawned = time(NULL);
-
-       pthread_mutex_unlock(&pool->mutex);
-
-       if (pool->trigger) exec_trigger(NULL, pool->cs, "open", true);
-
-       return this;
-}
-
-/** Add a new connection to the pool
- *
- * If conn is not NULL will attempt to add that connection handle to the pool.
- * If conn is NULL will attempt to spawn a new connection using the create
- * callback.
- *
- * @note Will call the 'open' trigger.
- *
- * @param[in,out] pool to add connection to.
- * @param[in] conn to add.
- * @return 0 if the connection wasn't added else 1.
- */
-int fr_connection_add(fr_connection_pool_t *pool, void *conn)
-{
-       fr_connection_t *this;
-
-       if (!pool) return 0;
-
-       pthread_mutex_lock(&pool->mutex);
-
-       if (!conn) {
-               conn = pool->create(pool->ctx);
-               if (!conn) {
-                       pthread_mutex_unlock(&pool->mutex);
-                       return 0;
-               }
-
-               INFO("%s: Opening connection successful (%" PRIu64 ")", pool->log_prefix, pool->count);
-       }
-
-       /*
-        *      Too many connections: can't add it.
-        */
-       if (pool->num >= pool->max) {
+       this = talloc_zero(pool, fr_connection_t);
+       if (!this) {
                pthread_mutex_unlock(&pool->mutex);
-               return 0;
+               return NULL;
        }
 
-       this = rad_malloc(sizeof(*this));
-       memset(this, 0, sizeof(*this));
-
-       this->created = time(NULL);
+       this->created = now;
        this->connection = conn;
+       this->in_use = in_use;
 
        this->number = pool->count++;
-       this->last_used = time(NULL);
+       this->last_used = now;
        fr_connection_link_head(pool, this);
        pool->num++;
+       pool->spawning = false;
+       pool->last_spawned = time(NULL);
+       pool->delay_interval = pool->cleanup_interval;
+       pool->next_delay = pool->cleanup_interval;
+       pool->last_failed = 0;
 
        pthread_mutex_unlock(&pool->mutex);
 
        if (pool->trigger) exec_trigger(NULL, pool->cs, "open", true);
 
-       return 1;
+       return this;
 }
 
 /** Close an existing connection.
@@ -446,15 +418,27 @@ int fr_connection_add(fr_connection_pool_t *pool, void *conn)
 static void fr_connection_close(fr_connection_pool_t *pool,
                                fr_connection_t *this)
 {
-       if (pool->trigger) exec_trigger(NULL, pool->cs, "close", true);
+       /*
+        *      If it's in use, release it.
+        */
+       if (this->in_use) {
+#ifdef PTHREAD_DEBUG
+               pthread_t pthread_id = pthread_self();
+               rad_assert(pthread_equal(this->pthread_id, pthread_id) != 0);
+#endif
+               this->in_use = false;
 
-       rad_assert(this->in_use == false);
+               rad_assert(pool->active > 0);
+               pool->active--;
+       }
+
+       if (pool->trigger) exec_trigger(NULL, pool->cs, "close", true);
 
        fr_connection_unlink(pool, this);
        pool->delete(pool->ctx, this->connection);
        rad_assert(pool->num > 0);
        pool->num--;
-       free(this);
+       talloc_free(this);
 }
 
 /** Find a connection handle in the connection list
@@ -485,7 +469,17 @@ static fr_connection_t *fr_connection_find(fr_connection_pool_t *pool, void *con
         *      order to find top of the parent structure.
         */
        for (this = pool->head; this != NULL; this = this->next) {
-               if (this->connection == conn) return this;
+               if (this->connection == conn) {
+#ifdef PTHREAD_DEBUG
+                       pthread_t pthread_id;
+
+                       pthread_id = pthread_self();
+                       rad_assert(pthread_equal(this->pthread_id, pthread_id) != 0);
+#endif
+
+                       rad_assert(this->in_use == true);
+                       return this;
+               }
        }
 
        pthread_mutex_unlock(&pool->mutex);
@@ -510,17 +504,6 @@ int fr_connection_del(fr_connection_pool_t *pool, void *conn)
        this = fr_connection_find(pool, conn);
        if (!this) return 0;
 
-       /*
-        *      If it's in use, release it.
-        */
-       if (this->in_use) {
-               rad_assert(this->in_use == true);
-               this->in_use = false;
-
-               rad_assert(pool->active > 0);
-               pool->active--;
-       }
-
        INFO("%s: Deleting connection (%" PRIu64 ")", pool->log_prefix, this->number);
 
        fr_connection_close(pool, this);
@@ -562,8 +545,7 @@ void fr_connection_pool_delete(fr_connection_pool_t *pool)
        rad_assert(pool->tail == NULL);
        rad_assert(pool->num == 0);
 
-       free(pool->log_prefix);
-       free(pool);
+       talloc_free(pool);
 }
 
 /** Create a new connection pool
@@ -590,9 +572,9 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
                                              fr_connection_create_t c,
                                              fr_connection_alive_t a,
                                              fr_connection_delete_t d,
-                                             char *prefix)
+                                             char const *prefix)
 {
-       int i, lp_len;
+       uint32_t i;
        fr_connection_pool_t *pool;
        fr_connection_t *this;
        CONF_SECTION *modules;
@@ -605,8 +587,12 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
        cs = cf_section_sub_find(parent, "pool");
        if (!cs) cs = cf_section_sub_find(parent, "limit");
 
-       pool = rad_malloc(sizeof(*pool));
-       memset(pool, 0, sizeof(*pool));
+       if (cs) {
+               pool = talloc_zero(cs, fr_connection_pool_t);
+       } else {
+               pool = talloc_zero(parent, fr_connection_pool_t);
+       }
+       if (!pool) return NULL;
 
        pool->cs = cs;
        pool->ctx = ctx;
@@ -631,18 +617,16 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
                                        cs_name2 = cs_name1;
                                }
 
-                               lp_len = (sizeof(LOG_PREFIX) - 4) + strlen(cs_name1) + strlen(cs_name2);
-                               pool->log_prefix = rad_malloc(lp_len);
-                               snprintf(pool->log_prefix, lp_len, LOG_PREFIX, cs_name1,
-                                        cs_name2);
+                               pool->log_prefix = talloc_typed_asprintf(pool, LOG_PREFIX, cs_name1,
+                                                                  cs_name2);
                        }
                } else {                /* not a module configuration */
                        cs_name1 = cf_section_name1(parent);
 
-                       pool->log_prefix = strdup(cs_name1);
+                       pool->log_prefix = talloc_typed_strdup(pool, cs_name1);
                }
        } else {
-               pool->log_prefix = strdup(prefix);
+               pool->log_prefix = talloc_typed_strdup(pool, prefix);
        }
 
        DEBUG("%s: Initialising connection pool", pool->log_prefix);
@@ -658,7 +642,7 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
                pool->min = 5;
                pool->max = 10;
                pool->spare = 3;
-               pool->cleanup_delay = 5;
+               pool->cleanup_interval = 30;
                pool->idle_timeout = 60;
        }
 
@@ -683,12 +667,26 @@ fr_connection_pool_t *fr_connection_pool_init(CONF_SECTION *parent,
                pool->idle_timeout = 0;
        }
 
+       if ((pool->idle_timeout > 0) && (pool->cleanup_interval > pool->idle_timeout)) {
+               pool->cleanup_interval = pool->idle_timeout;
+       }
+
+       /*
+        *      Don't open any connections.  Instead, force the limits
+        *      to only 1 connection.
+        *
+        */
+       if (check_config) {
+               pool->start = pool->min = pool->max = 1;
+               return pool;
+       }
+
        /*
         *      Create all of the connections, unless the admin says
         *      not to.
         */
        for (i = 0; i < pool->start; i++) {
-               this = fr_connection_spawn(pool, now);
+               this = fr_connection_spawn(pool, now, false);
                if (!this) {
                error:
                        fr_connection_pool_delete(pool);
@@ -732,11 +730,8 @@ static int fr_connection_manage(fr_connection_pool_t *pool,
                DEBUG("%s: Closing expired connection (%" PRIu64 "): Hit max_uses limit", pool->log_prefix,
                      this->number);
        do_delete:
-               if ((pool->num <= pool->min) &&
-                   (pool->last_complained < now)) {
-                       WARN("%s: You probably need to lower \"min\"", pool->log_prefix);
-
-                       pool->last_complained = now;
+               if (pool->num <= pool->min) {
+                       RATE_LIMIT(WARN("%s: You probably need to lower \"min\"", pool->log_prefix));
                }
                fr_connection_close(pool, this);
                return 0;
@@ -775,7 +770,7 @@ static int fr_connection_manage(fr_connection_pool_t *pool,
  */
 static int fr_connection_pool_check(fr_connection_pool_t *pool)
 {
-       int spare, spawn;
+       uint32_t spawn, idle, extra;
        time_t now = time(NULL);
        fr_connection_t *this, *next;
 
@@ -784,46 +779,109 @@ static int fr_connection_pool_check(fr_connection_pool_t *pool)
                return 1;
        }
 
-       spare = pool->num - pool->active;
+       /*
+        *      Some idle connections are OK, if they're within the
+        *      configured "spare" range.  Any extra connections
+        *      outside of that range can be closed.
+        */
+       idle = pool->num - pool->active;
+       if (idle <= pool->spare) {
+               extra = 0;
+       } else {
+               extra = idle - pool->spare;
+       }
 
-       if ((pool->num < pool->max) && (spare < pool->spare)) {
-               spawn = pool->spare - spare;
-               if ((spawn + pool->num) > pool->max) {
-                       spawn = pool->max - pool->num;
+       /*
+        *      The other end can close connections.  If so, we'll
+        *      have fewer than "min".  When that happens, open more
+        *      connections to enforce "min".
+        */
+       if (pool->num <= pool->min) {
+               if (pool->spawning) {
+                       spawn = 0;
+               } else {
+                       spawn = pool->min - pool->num;
                }
-               if (pool->spawning) spawn = 0;
+               extra = 0;
+
+       } else if (pool->num >= pool->max) {
+               /*
+                *      Ensure we don't spawn more connections.  If
+                *      there are extra idle connections, we can
+                *      delete all of them.
+                */
+               spawn = 0;
+               /* leave extra alone from above */
+
+       } else if (idle <= pool->spare) {
+               /*
+                *      Not enough spare connections.  Spawn a few.
+                *      But cap the pool size at "max"
+                */
+               spawn = pool->spare - idle;
+               extra = 0;
 
-               if (spawn) {
-                       pthread_mutex_unlock(&pool->mutex);
-                       fr_connection_spawn(pool, now); /* ignore return code */
-                       pthread_mutex_lock(&pool->mutex);
+               if ((pool->num + spawn) > pool->max) {
+                       spawn = pool->max - pool->num;
                }
+
+       } else if ((pool->min + extra) >= pool->num) {
+               /*
+                *      If closing the extra connections would take us
+                *      below "min", then don't do that.  Cap the
+                *      spare connections at the ones which will take
+                *      us exactly to "min".
+                */
+               spawn = 0;
+               extra = pool->num - pool->min;
+
+       } else {
+               /*
+                *      Closing the "extra" connections won't take us
+                *      below "min".  It's therefore safe to close
+                *      them all.
+                */
+               spawn = 0;
+               /* leave extra alone from above */
+       }
+
+       if (spawn) {
+               pthread_mutex_unlock(&pool->mutex);
+               fr_connection_spawn(pool, now, false); /* ignore return code */
+               pthread_mutex_lock(&pool->mutex);
        }
 
        /*
         *      We haven't spawned connections in a while, and there
         *      are too many spare ones.  Close the one which has been
-        *      idle for the longest.
+        *      unused for the longest.
         */
-       if ((now >= (pool->last_spawned + pool->cleanup_delay)) &&
-           (spare > pool->spare)) {
-               fr_connection_t *idle;
+       if (extra && (now >= (pool->last_spawned + pool->delay_interval))) {
+               fr_connection_t *found;
 
-               idle = NULL;
+               found = NULL;
                for (this = pool->tail; this != NULL; this = this->prev) {
                        if (this->in_use) continue;
 
-                       if (!idle ||
-                          (this->last_used < idle->last_used)) {
-                               idle = this;
+                       if (!found ||
+                          (this->last_used < found->last_used)) {
+                               found = this;
                        }
                }
 
-               rad_assert(idle != NULL);
+               rad_assert(found != NULL);
 
-               INFO("%s: Closing connection (%" PRIu64 "): Too many free connections (%d > %d)", pool->log_prefix,
-                    idle->number, spare, pool->spare);
-               fr_connection_close(pool, idle);
+               INFO("%s: Closing connection (%" PRIu64 "), from %d unused connections", pool->log_prefix,
+                    found->number, extra);
+               fr_connection_close(pool, found);
+
+               /*
+                *      Decrease the delay for the next time we clean
+                *      up.
+                */
+               pool->next_delay >>= 1;
+               if (pool->next_delay == 0) pool->next_delay = 1;
+               pool->delay_interval += pool->next_delay;
        }
 
        /*
@@ -841,62 +899,13 @@ static int fr_connection_pool_check(fr_connection_pool_t *pool)
        return 1;
 }
 
-/** Trigger connection check for a given connection or all connections
- *
- * If conn is not NULL then we call fr_connection_manage on the connection.
- * If conn is NULL we call fr_connection_pool_check on the pool.
- *
- * @note Only connections that are not in use will be closed.
- *
- * @see fr_connection_manage
- * @see fr_connection_pool_check
- * @param[in,out] pool to manage.
- * @param[in,out] conn to check.
- * @return 0 if the connection was closed, else 1.
- */
-int fr_connection_check(fr_connection_pool_t *pool, void *conn)
-{
-       fr_connection_t *this;
-       time_t now;
-       int ret = 1;
-
-       if (!pool) return 1;
-
-       now = time(NULL);
-       pthread_mutex_lock(&pool->mutex);
-
-       if (!conn) return fr_connection_pool_check(pool);
-
-       for (this = pool->head; this != NULL; this = this->next) {
-               if (this->connection == conn) {
-                       ret = fr_connection_manage(pool, conn, now);
-                       break;
-               }
-       }
-
-       pthread_mutex_unlock(&pool->mutex);
-
-       return ret;
-}
-
-/** Reserve a connection in the connection pool
+/** Get a connection from the connection pool
  *
- * Will attempt to find an unused connection in the connection pool, if one is
- * found, will mark it as in in use increment the number of active connections
- * and return the connection handle.
- *
- * If no free connections are found will attempt to spawn a new one, conditional
- * on a connection spawning not already being in progress, and not being at the
- * 'max' connection limit.
- *
- * @note fr_connection_release must be called once the caller has finished
- * using the connection.
- *
- * @see fr_connection_release
  * @param[in,out] pool to reserve the connection from.
+ * @param[in] spawn whether to spawn a new connection
  * @return a pointer to the connection handle, or NULL on error.
  */
-void *fr_connection_get(fr_connection_pool_t *pool)
+static void *fr_connection_get_internal(fr_connection_pool_t *pool, int spawn)
 {
        time_t now;
        fr_connection_t *this, *next;
@@ -913,7 +922,7 @@ void *fr_connection_get(fr_connection_pool_t *pool)
        }
 
        if (pool->num == pool->max) {
-               int complain = false;
+               bool complain = false;
 
                /*
                 *      Rate-limit complaints.
@@ -925,7 +934,7 @@ void *fr_connection_get(fr_connection_pool_t *pool)
 
                pthread_mutex_unlock(&pool->mutex);
 
-               if (complain) {
+               if (!RATE_LIMIT_ENABLED || complain) {
                        ERROR("%s: No connections available and at max connection limit", pool->log_prefix);
                }
 
@@ -933,7 +942,10 @@ void *fr_connection_get(fr_connection_pool_t *pool)
        }
 
        pthread_mutex_unlock(&pool->mutex);
-       this = fr_connection_spawn(pool, now);
+
+       if (!spawn) return NULL;
+
+       this = fr_connection_spawn(pool, now, true); /* MY connection! */
        if (!this) return NULL;
        pthread_mutex_lock(&pool->mutex);
 
@@ -943,6 +955,9 @@ do_return:
        this->last_used = now;
        this->in_use = true;
 
+#ifdef PTHREAD_DEBUG
+       this->pthread_id = pthread_self();
+#endif
        pthread_mutex_unlock(&pool->mutex);
 
        DEBUG("%s: Reserved connection (%" PRIu64 ")", pool->log_prefix, this->number);
@@ -950,6 +965,39 @@ do_return:
        return this->connection;
 }
 
+
+/** Reserve a connection in the connection pool
+ *
+ * Will attempt to find an unused connection in the connection pool, if one is
+ * found, will mark it as in in use increment the number of active connections
+ * and return the connection handle.
+ *
+ * If no free connections are found will attempt to spawn a new one, conditional
+ * on a connection spawning not already being in progress, and not being at the
+ * 'max' connection limit.
+ *
+ * @note fr_connection_release must be called once the caller has finished
+ * using the connection.
+ *
+ * @see fr_connection_release
+ * @param[in,out] pool to reserve the connection from.
+ * @return a pointer to the connection handle, or NULL on error.
+ */
+void *fr_connection_get(fr_connection_pool_t *pool)
+{
+       return fr_connection_get_internal(pool, true);
+}
+
+/** Get the number of connections currently in the pool
+ *
+ * @param pool to count connections for.
+ * @return the number of connections in the pool
+ */
+int fr_connection_get_num(fr_connection_pool_t *pool)
+{
+       return pool->num;
+}
+
 /** Release a connection
  *
  * Will mark a connection as unused and decrement the number of active
@@ -966,7 +1014,6 @@ void fr_connection_release(fr_connection_pool_t *pool, void *conn)
        this = fr_connection_find(pool, conn);
        if (!this) return;
 
-       rad_assert(this->in_use == true);
        this->in_use = false;
 
        /*
@@ -1030,7 +1077,7 @@ void fr_connection_release(fr_connection_pool_t *pool, void *conn)
  * @see fr_connection_get
  * @param[in,out] pool to reconnect the connection in.
  * @param[in,out] conn to reconnect.
- * @return ew connection handle if successful else NULL.
+ * @return new connection handle if successful else NULL.
  */
 void *fr_connection_reconnect(fr_connection_pool_t *pool, void *conn)
 {
@@ -1043,45 +1090,35 @@ void *fr_connection_reconnect(fr_connection_pool_t *pool, void *conn)
        this = fr_connection_find(pool, conn);
        if (!this) return NULL;
 
+       /*
+        *      The pool is now locked.
+        */
        conn_number = this->number;
 
-       rad_assert(this->in_use == true);
-
        DEBUG("%s: Reconnecting (%" PRIu64 ")", pool->log_prefix, conn_number);
 
        new_conn = pool->create(pool->ctx);
        if (!new_conn) {
-               time_t now = time(NULL);
-
-               if (pool->last_complained == now) {
-                       now = 0;
-               } else {
-                       pool->last_complained = now;
-               }
-
-               this->in_use = false;
-
-               rad_assert(pool->active > 0);
-               pool->active--;
-
+               /*
+                *      We can't create a new connection, so close
+                *      this one.
+                */
                fr_connection_close(pool, this);
-               pthread_mutex_unlock(&pool->mutex);
 
                /*
-                *      Can't create a new socket.
-                *      Try grabbing a pre-existing one.
+                *      Maybe there's a connection which is unused and
+                *      available.  If so, return it.
                 */
-               new_conn = fr_connection_get(pool);
+               pthread_mutex_unlock(&pool->mutex);
+               new_conn = fr_connection_get_internal(pool, false);
                if (new_conn) return new_conn;
 
-               if (!now) return NULL;
-
-               ERROR("%s: Failed to reconnect (%" PRIu64 "), and no other connections available", pool->log_prefix,
-                     conn_number);
-
+               RATE_LIMIT(ERROR("%s: Failed to reconnect (%" PRIu64 "), no free connections are available", pool->log_prefix,
+                                conn_number));
                return NULL;
        }
 
+       if (pool->trigger) exec_trigger(NULL, pool->cs, "close", true);
        pool->delete(pool->ctx, conn);
        this->connection = new_conn;
        pthread_mutex_unlock(&pool->mutex);
index b8bba35..99c66d8 100644 (file)
@@ -32,7 +32,7 @@ RCSID("$Id$")
 /*
  *  No pthreads, no mutex.
  */
-static int fr_crypt_init = 0;
+static bool fr_crypt_init = false;
 static pthread_mutex_t fr_crypt_mutex;
 #endif
 
@@ -53,9 +53,9 @@ int fr_crypt_check(char const *key, char const *crypted)
        /*
         *      Ensure we're thread-safe, as crypt() isn't.
         */
-       if (fr_crypt_init == 0) {
+       if (fr_crypt_init == false) {
                pthread_mutex_init(&fr_crypt_mutex, NULL);
-               fr_crypt_init = 1;
+               fr_crypt_init = true;
        }
 
        pthread_mutex_lock(&fr_crypt_mutex);
index 08c4412..47c3053 100644 (file)
@@ -41,6 +41,8 @@ RCSID("$Id$")
 
 #ifdef WITH_DETAIL
 
+extern bool check_config;
+
 #define USEC (1000000)
 
 static FR_NAME_NUMBER state_names[] = {
@@ -56,14 +58,16 @@ static FR_NAME_NUMBER state_names[] = {
        { NULL, 0 }
 };
 
+
 /*
  *     If we're limiting outstanding packets, then mark the response
  *     as being sent.
  */
 int detail_send(rad_listen_t *listener, REQUEST *request)
 {
-       int rtt;
-       struct timeval now;
+#ifdef WITH_DETAIL_THREAD
+       char c = 0;
+#endif
        listen_detail_t *data = listener->data;
 
        rad_assert(request->listener == listener);
@@ -80,85 +84,92 @@ int detail_send(rad_listen_t *listener, REQUEST *request)
 
                RDEBUG("Detail - No response configured for request %d.  Will retry in %d seconds",
                       request->number, data->retry_interval);
+       } else {
+               int rtt;
+               struct timeval now;
+               /*
+                *      We call gettimeofday a lot.  But it should be OK,
+                *      because there's nothing else to do.
+                */
+               gettimeofday(&now, NULL);
 
-               radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL);
-               return 0;
-       }
-
-       /*
-        *      We call gettimeofday a lot.  But it should be OK,
-        *      because there's nothing else to do.
-        */
-       gettimeofday(&now, NULL);
+               /*
+                *      If we haven't sent a packet in the last second, reset
+                *      the RTT.
+                */
+               now.tv_sec -= 1;
+               if (timercmp(&data->last_packet, &now, <)) {
+                       data->has_rtt = false;
+               }
+               now.tv_sec += 1;
 
-       /*
-        *      If we haven't sent a packet in the last second, reset
-        *      the RTT.
-        */
-       now.tv_sec -= 1;
-       if (timercmp(&data->last_packet, &now, <)) {
-               data->has_rtt = false;
-       }
-       now.tv_sec += 1;
+               /*
+                *      Only one detail packet may be outstanding at a time,
+                *      so it's safe to update some entries in the detail
+                *      structure.
+                *
+                *      We keep smoothed round trip time (SRTT), but not round
+                *      trip timeout (RTO).  We use SRTT to calculate a rough
+                *      load factor.
+                */
+               rtt = now.tv_sec - request->packet->timestamp.tv_sec;
+               rtt *= USEC;
+               rtt += now.tv_usec;
+               rtt -= request->packet->timestamp.tv_usec;
 
-       /*
-        *      Only one detail packet may be outstanding at a time,
-        *      so it's safe to update some entries in the detail
-        *      structure.
-        *
-        *      We keep smoothed round trip time (SRTT), but not round
-        *      trip timeout (RTO).  We use SRTT to calculate a rough
-        *      load factor.
-        */
-       rtt = now.tv_sec - request->packet->timestamp.tv_sec;
-       rtt *= USEC;
-       rtt += now.tv_usec;
-       rtt -= request->packet->timestamp.tv_usec;
+               /*
+                *      If we're proxying, the RTT is our processing time,
+                *      plus the network delay there and back, plus the time
+                *      on the other end to process the packet.  Ideally, we
+                *      should remove the network delays from the RTT, but we
+                *      don't know what they are.
+                *
+                *      So, to be safe, we over-estimate the total cost of
+                *      processing the packet.
+                */
+               if (!data->has_rtt) {
+                       data->has_rtt = true;
+                       data->srtt = rtt;
+                       data->rttvar = rtt / 2;
 
-       /*
-        *      If we're proxying, the RTT is our processing time,
-        *      plus the network delay there and back, plus the time
-        *      on the other end to process the packet.  Ideally, we
-        *      should remove the network delays from the RTT, but we
-        *      don't know what they are.
-        *
-        *      So, to be safe, we over-estimate the total cost of
-        *      processing the packet.
-        */
-       if (!data->has_rtt) {
-               data->has_rtt = true;
-               data->srtt = rtt;
-               data->rttvar = rtt / 2;
+               } else {
+                       data->rttvar -= data->rttvar >> 2;
+                       data->rttvar += (data->srtt - rtt);
+                       data->srtt -= data->srtt >> 3;
+                       data->srtt += rtt >> 3;
+               }
 
-       } else {
-               data->rttvar -= data->rttvar >> 2;
-               data->rttvar += (data->srtt - rtt);
-               data->srtt -= data->srtt >> 3;
-               data->srtt += rtt >> 3;
-       }
+               /*
+                *      Calculate the time we wait before sending the next
+                *      packet.
+                *
+                *      rtt / (rtt + delay) = load_factor / 100
+                */
+               data->delay_time = (data->srtt * (100 - data->load_factor)) / (data->load_factor);
 
-       /*
-        *      Calculate the time we wait before sending the next
-        *      packet.
-        *
-        *      rtt / (rtt + delay) = load_factor / 100
-        */
-       data->delay_time = (data->srtt * (100 - data->load_factor)) / (data->load_factor);
+               /*
+                *      Cap delay at no less than 4 packets/s.  If the
+                *      end system can't handle this, then it's very
+                *      broken.
+                */
+               if (data->delay_time > (USEC / 4)) data->delay_time= USEC / 4;
 
-       /*
-        *      Cap delay at 4 packets/s.  If the end system can't
-        *      handle this, then it's very broken.
-        */
-       if (data->delay_time > (USEC / 4)) data->delay_time= USEC / 4;
+               RDEBUG3("Received response for request %d.  Will read the next packet in %d seconds",
+                       request->number, data->delay_time / USEC);
 
-       RDEBUG3("Received response for request %d.  Will read the next packet in %d seconds",
-               request->number, data->delay_time / USEC);
+               data->last_packet = now;
+               data->signal = 1;
+               data->state = STATE_REPLIED;
+               data->counter++;
+       }
 
-       data->last_packet = now;
-       data->signal = 1;
-       data->state = STATE_REPLIED;
-       data->counter++;
+#ifdef WITH_DETAIL_THREAD
+       if (write(data->child_pipe[1], &c, 1) < 0) {
+               ERROR("Failed writing ack to reader thread: %s", fr_syserror(errno));
+       }
+#else
        radius_signal_self(RADIUS_SIGNAL_SELF_DETAIL);
+#endif
 
        return 0;
 }
@@ -192,88 +203,63 @@ static int detail_open(rad_listen_t *this)
         *      this file will be read && processed before the
         *      file globbing is done.
         */
-       this->fd = open(data->filename_work, O_RDWR);
-       if (this->fd < 0) {
-               bool free_filename = false;
-               char *filename = data->filename;
-
-               DEBUG2("Polling for detail file %s", filename);
-
-               /*
-                *      Try reading the detail file.  If it
-                *      doesn't exist, we can't do anything.
-                *
-                *      Doing the stat will tell us if the file
-                *      exists, even if we don't have permissions
-                *      to read it.
-                */
-               if (stat(filename, &st) < 0) {
+       data->work_fd = open(data->filename_work, O_RDWR);
+       if (data->work_fd < 0) {
 #ifndef HAVE_GLOB_H
-                       return 0;
+               return 0;
 #else
-                       unsigned int i;
-                       int found;
-                       time_t chtime;
-                       glob_t files;
-
-                       memset(&files, 0, sizeof(files));
-                       if (glob(filename, 0, NULL, &files) != 0) {
-                               globfree(&files);
-                               return 0;
-                       }
-
-                       chtime = 0;
-                       found = -1;
-                       for (i = 0; i < files.gl_pathc; i++) {
-                               if (stat(files.gl_pathv[i], &st) < 0) continue;
-
-                               if ((i == 0) ||
-                                   (st.st_ctime < chtime)) {
-                                       chtime = st.st_ctime;
-                                       found = i;
-                               }
-                       }
+               unsigned int    i;
+               int             found;
+               time_t          chtime;
+               char const      *filename;
+               glob_t          files;
 
-                       if (found < 0) {
-                               globfree(&files);
-                               return 0;
-                       }
+               DEBUG2("Polling for detail file %s", data->filename);
 
-                       filename = strdup(files.gl_pathv[found]);
-                       free_filename = true;
+               memset(&files, 0, sizeof(files));
+               if (glob(data->filename, 0, NULL, &files) != 0) {
+               noop:
                        globfree(&files);
-#endif
+                       return 0;
                }
 
                /*
-                *      Open it BEFORE we rename it, just to
-                *      be safe...
+                *      Loop over the glob'd files, looking for the
+                *      oldest one.
                 */
-               this->fd = open(filename, O_RDWR);
-               if (this->fd < 0) {
-                       ERROR("Detail - Failed to open %s: %s",
-                              filename, strerror(errno));
-                       if (free_filename) free(filename);
-                       return 0;
+               chtime = 0;
+               found = -1;
+               for (i = 0; i < files.gl_pathc; i++) {
+                       if (stat(files.gl_pathv[i], &st) < 0) continue;
+
+                       if ((i == 0) || (st.st_ctime < chtime)) {
+                               chtime = st.st_ctime;
+                               found = i;
+                       }
                }
 
+               if (found < 0) goto noop;
+
                /*
                 *      Rename detail to detail.work
                 */
+               filename = files.gl_pathv[found];
+
                DEBUG("Detail - Renaming %s -> %s", filename, data->filename_work);
                if (rename(filename, data->filename_work) < 0) {
                        ERROR("Detail - Failed renaming %s to %s: %s",
-                             filename, data->filename_work, strerror(errno));
-                       if (free_filename) free(filename);
-                       close(this->fd);
-                       this->fd = -1;
-                       return 0;
+                             filename, data->filename_work, fr_syserror(errno));
+                       goto noop;
                }
 
+               globfree(&files);       /* Shouldn't be using anything in files now */
+
                /*
-                *      Ensure we don't leak memory.
+                *      And try to open the filename.
                 */
-               if (free_filename) free(filename);
+               data->work_fd = open(data->filename_work, O_RDWR);
+               if (data->work_fd < 0) return 0;
+#endif
        } /* else detail.work existed, and we opened it */
 
        rad_assert(data->vps == NULL);
@@ -306,13 +292,12 @@ static int detail_open(rad_listen_t *this)
  *     t_rtt + t_delay wait for signal that the server is idle.
  *
  */
+#ifndef WITH_DETAIL_THREAD
+static RADIUS_PACKET *detail_poll(rad_listen_t *listener);
+
 int detail_recv(rad_listen_t *listener)
 {
-       char            key[256], op[8], value[1024];
-       vp_cursor_t     cursor;
-       VALUE_PAIR      *vp;
-       RADIUS_PACKET   *packet;
-       char            buffer[2048];
+       RADIUS_PACKET *packet;
        listen_detail_t *data = listener->data;
 
        /*
@@ -321,15 +306,71 @@ int detail_recv(rad_listen_t *listener)
         */
        if (data->signal) return 0;
 
+       packet = detail_poll(listener);
+       if (!packet) return -1;
+
+       /*
+        *      Don't bother doing limit checks, etc.
+        */
+       if (!request_receive(listener, packet, &data->detail_client,
+                            rad_accounting)) {
+               rad_free(&packet);
+               data->state = STATE_NO_REPLY;   /* try again later */
+               return 0;
+       }
+
+       return 1;
+}
+#else
+int detail_recv(rad_listen_t *listener)
+{
+       ssize_t rcode;
+       RADIUS_PACKET *packet;
+       listen_detail_t *data = listener->data;
+
+       /*
+        *      Block until there's a packet ready.
+        */
+       rcode = read(data->master_pipe[0], &packet, sizeof(packet));
+       if (rcode <= 0) return rcode;
+
+       rad_assert(packet != NULL);
+
+       if (!request_receive(listener, packet, &data->detail_client,
+                                    rad_accounting)) {
+               char c = 0;
+               rad_free(&packet);
+               data->state = STATE_NO_REPLY;   /* try again later */
+               if (write(data->child_pipe[1], &c, 1) < 0) {
+                       ERROR("Failed writing ack to reader thread: %s", fr_syserror(errno));
+               }
+       }
+
+       /*
+        *      Wait for the child thread to write an answer to the pipe
+        */
+       return 0;
+}
+#endif
+
+static RADIUS_PACKET *detail_poll(rad_listen_t *listener)
+{
+       char            key[256], op[8], value[1024];
+       vp_cursor_t     cursor;
+       VALUE_PAIR      *vp;
+       RADIUS_PACKET   *packet;
+       char            buffer[2048];
+       listen_detail_t *data = listener->data;
+
        switch (data->state) {
                case STATE_UNOPENED:
        open_file:
-                       rad_assert(listener->fd < 0);
+                       rad_assert(data->work_fd < 0);
 
-                       if (!detail_open(listener)) return 0;
+                       if (!detail_open(listener)) return NULL;
 
                        rad_assert(data->state == STATE_UNLOCKED);
-                       rad_assert(listener->fd >= 0);
+                       rad_assert(data->work_fd >= 0);
 
                        /* FALL-THROUGH */
 
@@ -351,22 +392,22 @@ int detail_recv(rad_listen_t *listener)
                         *      "ping-pongs" between radiusd &
                         *      radrelay.
                         */
-                       if (rad_lockfd_nonblock(listener->fd, 0) < 0) {
+                       if (rad_lockfd_nonblock(data->work_fd, 0) < 0) {
                                /*
                                 *      Close the FD.  The main loop
                                 *      will wake up in a second and
                                 *      try again.
                                 */
-                               close(listener->fd);
-                               listener->fd = -1;
+                               close(data->work_fd);
+                               data->work_fd = -1;
                                data->state = STATE_UNOPENED;
-                               return 0;
+                               return NULL;
                        }
 
-                       data->fp = fdopen(listener->fd, "r");
+                       data->fp = fdopen(data->work_fd, "r");
                        if (!data->fp) {
                                ERROR("FATAL: Failed to re-open detail file %s: %s",
-                                      data->filename, strerror(errno));
+                                      data->filename, fr_syserror(errno));
                                fr_exit(1);
                        }
 
@@ -390,13 +431,13 @@ int detail_recv(rad_listen_t *listener)
                        {
                                struct stat buf;
 
-                               if (fstat(listener->fd, &buf) < 0) {
+                               if (fstat(data->work_fd, &buf) < 0) {
                                        ERROR("Failed to stat "
                                               "detail file %s: %s",
-                                               data->filename,
-                                               strerror(errno));
+                                               data->filename,
+                                               fr_syserror(errno));
 
-                                       goto cleanup;
+                                       goto cleanup;
                                }
                                if (((off_t) ftell(data->fp)) == buf.st_size) {
                                        goto cleanup;
@@ -414,7 +455,7 @@ int detail_recv(rad_listen_t *listener)
                                unlink(data->filename_work);
                                if (data->fp) fclose(data->fp);
                                data->fp = NULL;
-                               listener->fd = -1;
+                               data->work_fd = -1;
                                data->state = STATE_UNOPENED;
                                rad_assert(data->vps == NULL);
 
@@ -423,7 +464,7 @@ int detail_recv(rad_listen_t *listener)
                                        radius_signal_self(RADIUS_SIGNAL_SELF_EXIT);
                                }
 
-                               return 0;
+                               return NULL;
                        }
 
                        /*
@@ -451,12 +492,11 @@ int detail_recv(rad_listen_t *listener)
                         *      retry it.
                         */
                case STATE_RUNNING:
-                       if (time(NULL) < (data->running + data->retry_interval)) {
-                               return 0;
+                       if (time(NULL) < (data->running + (int)data->retry_interval)) {
+                               return NULL;
                        }
 
                        DEBUG("No response to detail request.  Retrying");
-                       data->state = STATE_NO_REPLY;
                        /* FALL-THROUGH */
 
                        /*
@@ -478,7 +518,7 @@ int detail_recv(rad_listen_t *listener)
                        goto do_header;
        }
 
-       paircursor(&cursor, &data->vps);
+       fr_cursor_init(&cursor, &data->vps);
 
        /*
         *      Read a header, OR a value-pair.
@@ -526,8 +566,8 @@ int detail_recv(rad_listen_t *listener)
                 *
                 *      FIXME: print an error for badly formatted attributes?
                 */
-               if (sscanf(buffer, "%255s %8s %1023s", key, op, value) != 3) {
-                       WDEBUG2("Skipping badly formatted line %s",
+               if (sscanf(buffer, "%255s %7s %1023s", key, op, value) != 3) {
+                       WARN("Skipping badly formatted line %s",
                               buffer);
                        continue;
                }
@@ -551,7 +591,7 @@ int detail_recv(rad_listen_t *listener)
                 */
                if (!strcasecmp(key, "Client-IP-Address")) {
                        data->client_ip.af = AF_INET;
-                       if (ip_hton(value, AF_INET, &data->client_ip) < 0) {
+                       if (ip_hton(&data->client_ip, AF_INET, value, false) < 0) {
                                ERROR("Failed parsing Client-IP-Address");
 
                                pairfree(&data->vps);
@@ -572,7 +612,7 @@ int detail_recv(rad_listen_t *listener)
                        if (vp) {
                                vp->vp_date = (uint32_t) data->timestamp;
                                vp->type = VT_DATA;
-                               pairinsert(&cursor, vp);
+                               fr_cursor_insert(&cursor, vp);
                        }
                        continue;
                }
@@ -586,7 +626,7 @@ int detail_recv(rad_listen_t *listener)
                vp = NULL;
                if ((userparse(data, buffer, &vp) > 0) &&
                    (vp != NULL)) {
-                       pairinsert(&cursor, vp);
+                       fr_cursor_insert(&cursor, vp);
                }
        }
 
@@ -625,7 +665,7 @@ int detail_recv(rad_listen_t *listener)
        if (!data->vps) {
                data->state = STATE_HEADER;
                if (!data->fp || feof(data->fp)) goto cleanup;
-               return 0;
+               return NULL;
        }
 
        /*
@@ -643,7 +683,7 @@ int detail_recv(rad_listen_t *listener)
        packet->sockfd = -1;
        packet->src_ipaddr.af = AF_INET;
        packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-       packet->code = PW_ACCOUNTING_REQUEST;
+       packet->code = PW_CODE_ACCOUNTING_REQUEST;
        gettimeofday(&packet->timestamp, NULL);
 
        /*
@@ -735,30 +775,19 @@ int detail_recv(rad_listen_t *listener)
 
        if (debug_flag) {
                fr_printf_log("detail_recv: Read packet from %s\n", data->filename_work);
-               for (vp = paircursor(&cursor, &packet->vps);
+               for (vp = fr_cursor_init(&cursor, &packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        debug_pair(vp);
                }
        }
 
-       /*
-        *      Don't bother doing limit checks, etc.
-        */
-       if (!request_receive(listener, packet, &data->detail_client,
-                            rad_accounting)) {
-               rad_free(&packet);
-               data->state = STATE_NO_REPLY;   /* try again later */
-               return 0;
-       }
-
        data->state = STATE_RUNNING;
        data->running = packet->timestamp.tv_sec;
 
-       return 1;
+       return packet;
 }
 
-
 /*
  *     Free detail-specific stuff.
  */
@@ -766,9 +795,40 @@ void detail_free(rad_listen_t *this)
 {
        listen_detail_t *data = this->data;
 
-       talloc_free(data->filename);
-       data->filename = NULL;
-       pairfree(&data->vps);
+#ifdef WITH_DETAIL_THREAD
+       if (!check_config) {
+               ssize_t ret;
+               void *arg = NULL;
+
+               /*
+                *      Mark the child pipes as unusable
+                */
+               close(data->child_pipe[0]);
+               close(data->child_pipe[1]);
+               data->child_pipe[0] = -1;
+
+               /*
+                *      Tell it to stop (interrupting it's sleep)
+                */
+               pthread_kill(data->pthread_id, SIGTERM);
+
+               /*
+                *      Wait for it to acknowledge that it's stopped.
+                */
+               ret = read(data->master_pipe[0], &arg, sizeof(arg));
+               if (ret < 0) {
+                       ERROR("Reader thread exited without informing the master: %s", fr_syserror(errno));
+               } else if (ret != sizeof(arg)) {
+                       ERROR("Invalid thread pointer received from reader thread during exit");
+                       ERROR("Expected %zu bytes, got %zi bytes", sizeof(arg), ret);
+               }
+
+               close(data->master_pipe[0]);
+               close(data->master_pipe[1]);
+
+               if (arg) pthread_join(data->pthread_id, &arg);
+       }
+#endif
 
        if (data->fp != NULL) {
                fclose(data->fp);
@@ -789,32 +849,42 @@ int detail_print(rad_listen_t const *this, char *buffer, size_t bufsize)
                        this->server);
 }
 
+
 /*
- *     Overloaded to return delay times.
+ *     Delay while waiting for a file to be ready
  */
-int detail_encode(rad_listen_t *this, UNUSED REQUEST *request)
+static int detail_delay(listen_detail_t *data)
 {
-       listen_detail_t *data = this->data;
+       int delay = (data->poll_interval - 1) * USEC;
 
        /*
-        *      We haven't sent a packet... delay things a bit.
+        *      Add +/- 0.25s of jitter
         */
-       if (!data->signal) {
-               int delay = (data->poll_interval - 1) * USEC;
+       delay += (USEC * 3) / 4;
+       delay += fr_rand() % (USEC / 2);
 
-               /*
-                *      Add +/- 0.25s of jitter
-                */
-               delay += (USEC * 3) / 4;
-               delay += fr_rand() % (USEC / 2);
+       DEBUG2("Detail listener %s state %s waiting %d.%06d sec",
+              data->filename,
+              fr_int2str(state_names, data->state, "?"),
+              (delay / USEC), delay % USEC);
 
-               DEBUG2("Detail listener %s state %s signalled %d waiting %d.%06d sec",
-                      data->filename,
-                      fr_int2str(state_names, data->state, "?"), data->signal,
-                      (delay / USEC), delay % USEC);
+       return delay;
+}
 
-               return delay;
-       }
+/*
+ *     Overloaded to return delay times.
+ */
+int detail_encode(UNUSED rad_listen_t *this, UNUSED REQUEST *request)
+{
+#ifdef WITH_DETAIL_THREAD
+       return 0;
+#else
+       listen_detail_t *data = this->data;
+
+       /*
+        *      We haven't sent a packet... delay things a bit.
+        */
+       if (!data->signal) return detail_delay(data);
 
        data->signal = 0;
 
@@ -825,41 +895,86 @@ int detail_encode(rad_listen_t *this, UNUSED REQUEST *request)
               data->delay_time % USEC);
 
        return data->delay_time;
+#endif
 }
 
-
 /*
  *     Overloaded to return "should we fix delay times"
  */
-int detail_decode(rad_listen_t *this, UNUSED REQUEST *request)
+int detail_decode(UNUSED rad_listen_t *this, UNUSED REQUEST *request)
 {
+#ifdef WITH_DETAIL_THREAD
+       return 0;
+#else
        listen_detail_t *data = this->data;
 
        return data->signal;
+#endif
 }
 
 
+#ifdef WITH_DETAIL_THREAD
+static void *detail_handler_thread(void *arg)
+{
+       char c;
+       rad_listen_t *this = arg;
+       listen_detail_t *data = this->data;
+
+       while (true) {
+               RADIUS_PACKET *packet;
+
+               while ((packet = detail_poll(this)) == NULL) {
+                       usleep(detail_delay(data));
+
+                       /*
+                        *      If we're supposed to exit then tell
+                        *      the master thread we've exited.
+                        */
+                       if (data->child_pipe[0] < 0) {
+                               packet = NULL;
+                               if (write(data->master_pipe[1], &packet, sizeof(packet)) < 0) {
+                                       ERROR("Failed writing exit status to master: %s", fr_syserror(errno));
+                               }
+                               return NULL;
+                       }
+               }
+
+               /*
+                *      Keep retrying forever.
+                *
+                *      FIXME: cap the retries.
+                */
+               do {
+                       if (write(data->master_pipe[1], &packet, sizeof(packet)) < 0) {
+                               ERROR("Failed passing detail packet pointer to master: %s", fr_syserror(errno));
+                       }
+
+                       if (read(data->child_pipe[0], &c, 1) < 0) {
+                               ERROR("Failed getting detail packet ack from master: %s", fr_syserror(errno));
+                               break;
+                       }
+
+                       if (data->delay_time > 0) usleep(data->delay_time);
+               } while (data->state != STATE_REPLIED);
+       }
+
+       return NULL;
+}
+#endif
+
+
 static const CONF_PARSER detail_config[] = {
-       { "detail",   PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED,
-         offsetof(listen_detail_t, filename), NULL,  NULL },
-       { "filename",   PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED,
-         offsetof(listen_detail_t, filename), NULL,  NULL },
-       { "load_factor",   PW_TYPE_INTEGER,
-         offsetof(listen_detail_t, load_factor), NULL, STRINGIFY(10)},
-       { "poll_interval",   PW_TYPE_INTEGER,
-         offsetof(listen_detail_t, poll_interval), NULL, STRINGIFY(1)},
-       { "retry_interval",   PW_TYPE_INTEGER,
-         offsetof(listen_detail_t, retry_interval), NULL, STRINGIFY(30)},
-       { "one_shot",   PW_TYPE_BOOLEAN,
-         offsetof(listen_detail_t, one_shot), NULL, NULL},
-       { "max_outstanding",   PW_TYPE_INTEGER,
-         offsetof(listen_detail_t, load_factor), NULL, NULL},
+       { "detail", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, listen_detail_t, filename), NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, listen_detail_t, filename), NULL },
+       { "load_factor", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_detail_t, load_factor), STRINGIFY(10) },
+       { "poll_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_detail_t, poll_interval), STRINGIFY(1) },
+       { "retry_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_detail_t, retry_interval), STRINGIFY(30) },
+       { "one_shot", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, listen_detail_t, one_shot), NULL },
+       { "max_outstanding", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_detail_t, load_factor), NULL },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
-extern bool check_config;
-
 /*
  *     Parse a detail section.
  */
@@ -870,8 +985,6 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
        RADCLIENT       *client;
        char buffer[2048];
 
-       if (check_config) return 0;
-
        data = this->data;
 
        rcode = cf_section_parse(cs, data, detail_config);
@@ -901,6 +1014,8 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
                return -1;
        }
 
+       if (check_config) return 0;
+
        if (data->max_outstanding == 0) data->max_outstanding = 1;
 
        /*
@@ -929,9 +1044,9 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
                snprintf(buffer, sizeof(buffer), "%s.work", data->filename);
        }
 
-       free(data->filename_work);
-       data->filename_work = strdup(buffer); /* FIXME: leaked */
+       data->filename_work = talloc_strdup(data, buffer);
 
+       data->work_fd = -1;
        data->vps = NULL;
        data->fp = NULL;
        data->state = STATE_UNOPENED;
@@ -945,10 +1060,31 @@ int detail_parse(CONF_SECTION *cs, rad_listen_t *this)
        memset(client, 0, sizeof(*client));
        client->ipaddr.af = AF_INET;
        client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
-       client->prefix = 0;
+       client->ipaddr.prefix = 0;
        client->longname = client->shortname = data->filename;
        client->secret = client->shortname;
-       client->nas_type = strdup("none");
+       client->nas_type = talloc_strdup(data, "none"); /* Part of 'data' not dynamically allocated */
+
+#ifdef WITH_DETAIL_THREAD
+       /*
+        *      Create the communication pipes.
+        */
+       if (pipe(data->master_pipe) < 0) {
+               ERROR("radiusd: Error opening internal pipe: %s",
+                     fr_syserror(errno));
+               fr_exit(1);
+       }
+
+       if (pipe(data->child_pipe) < 0) {
+               ERROR("radiusd: Error opening internal pipe: %s",
+                     fr_syserror(errno));
+               fr_exit(1);
+       }
+
+       pthread_create(&data->pthread_id, NULL, detail_handler_thread, this);
+
+       this->fd = data->master_pipe[0];
+#endif
 
        return 0;
 }
index 77a1b8c..b8334a7 100644 (file)
@@ -34,11 +34,24 @@ RCSID("$Id$")
 
 #ifdef WITH_EVAL_DEBUG
 #define EVAL_DEBUG(fmt, ...) printf("EVAL: ");printf(fmt, ## __VA_ARGS__);printf("\n");fflush(stdout)
+
+static FR_NAME_NUMBER const template_names[] = {
+       { "literal",    VPT_TYPE_LITERAL },
+       { "xlat",       VPT_TYPE_XLAT },
+       { "attr",       VPT_TYPE_ATTR },
+       { "list",       VPT_TYPE_LIST },
+       { "regex",      VPT_TYPE_REGEX },
+       { "exec",       VPT_TYPE_EXEC },
+       { "data",       VPT_TYPE_DATA },
+       { "xlat",       VPT_TYPE_XLAT_STRUCT },
+       { "regex",      VPT_TYPE_REGEX_STRUCT },
+       { NULL, 0 }
+};
 #else
 #define EVAL_DEBUG(...)
 #endif
 
-static const FR_NAME_NUMBER modreturn_table[] = {
+FR_NAME_NUMBER const modreturn_table[] = {
        { "reject",     RLM_MODULE_REJECT       },
        { "fail",       RLM_MODULE_FAIL         },
        { "ok",         RLM_MODULE_OK           },
@@ -56,6 +69,8 @@ static int all_digits(char const *string)
 {
        char const *p = string;
 
+       rad_assert(p != NULL);
+
        if (*p == '-') p++;
 
        while (isdigit((int) *p)) p++;
@@ -75,6 +90,7 @@ static int all_digits(char const *string)
 static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt)
 {
        VALUE_PAIR *vp;
+       int ret;
        *out = NULL;
 
        rad_assert(vpt->type != VPT_TYPE_LIST);
@@ -82,7 +98,7 @@ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t co
        switch (vpt->type) {
        case VPT_TYPE_LITERAL:
                EVAL_DEBUG("TMPL LITERAL");
-               *out = talloc_strdup(request, vpt->name);
+               *out = talloc_typed_strdup(request, vpt->name);
                break;
 
        case VPT_TYPE_EXEC:
@@ -96,11 +112,12 @@ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t co
 
        case VPT_TYPE_REGEX:
                EVAL_DEBUG("TMPL REGEX");
-               if (strchr(vpt->name, '%') == NULL) {
-                       *out = talloc_strdup(request, vpt->name);
-                       break;
+               /* Error in expansion, this is distinct from zero length expansion */
+               if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) {
+                       rad_assert(!*out);
+                       return -1;
                }
-               /* FALL-THROUGH */
+               break;
 
        case VPT_TYPE_XLAT:
                EVAL_DEBUG("TMPL XLAT");
@@ -111,19 +128,30 @@ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t co
                }
                break;
 
-       case VPT_TYPE_ATTR:
-               EVAL_DEBUG("TMPL ATTR");
-               vp = radius_vpt_get_vp(request, vpt);
-               if (!vp) {
+       case VPT_TYPE_XLAT_STRUCT:
+               EVAL_DEBUG("TMPL XLAT_STRUCT");
+               /* Error in expansion, this is distinct from zero length expansion */
+               if (radius_axlat_struct(out, request, vpt->vpt_xlat, NULL, NULL) < 0) {
+                       rad_assert(!*out);
                        return -1;
                }
-               *out = vp_aprint(request, vp);
+               RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */
+               RDEBUG2("   --> %s", *out);
+               break;
+
+       case VPT_TYPE_ATTR:
+               EVAL_DEBUG("TMPL ATTR");
+               ret = radius_tmpl_get_vp(&vp, request, vpt);
+               if (ret < 0) return ret;
+
+               *out = vp_aprint_value(request, vp);
                if (!*out) {
                        return -1;
                }
                break;
 
        case VPT_TYPE_DATA:
+       case VPT_TYPE_REGEX_STRUCT:
                rad_assert(0 == 1);
                /* FALL-THROUGH */
 
@@ -172,10 +200,10 @@ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
 
        case VPT_TYPE_ATTR:
        case VPT_TYPE_LIST:
-               if (radius_vpt_get_vp(request, vpt) != NULL) {
-                       rcode = true;
-               } else {
+               if (radius_tmpl_get_vp(NULL, request, vpt) < 0) {
                        rcode = false;
+               } else {
+                       rcode = true;
                }
                break;
 
@@ -183,6 +211,7 @@ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
                 *      FIXME: expand the strings
                 *      if not empty, return!
                 */
+       case VPT_TYPE_XLAT_STRUCT:
        case VPT_TYPE_XLAT:
        case VPT_TYPE_EXEC:
                if (!*vpt->name) return false;
@@ -199,6 +228,7 @@ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
                 *      Can't have a bare ... (/foo/) ...
                 */
        case VPT_TYPE_REGEX:
+       case VPT_TYPE_REGEX_STRUCT:
                EVAL_DEBUG("FAIL %d", __LINE__);
                rad_assert(0 == 1);
                /* FALL-THROUGH */
@@ -212,36 +242,73 @@ int radius_evaluate_tmpl(REQUEST *request, int modreturn, UNUSED int depth,
 }
 
 
-static int do_regex(REQUEST *request, char const *lhs, char const *rhs, bool iflag)
+static int do_regex(REQUEST *request, value_pair_map_t const *map)
 {
-       int compare;
-       int cflags = REG_EXTENDED;
-       regex_t reg;
+       int compare, rcode, ret;
+       regex_t reg, *preg;
+       char *lhs, *rhs;
        regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
 
-       if (iflag) cflags |= REG_ICASE;
-
        /*
-        *      Include substring matches.
+        *  Expand and then compile it.
         */
-       compare = regcomp(&reg, rhs, cflags);
-       if (compare != 0) {
-               if (debug_flag) {
-                       char errbuf[128];
+       switch (map->src->type) {
+       case VPT_TYPE_REGEX:
+               rcode = radius_expand_tmpl(&rhs, request, map->src);
+               if (rcode < 0) {
+                       EVAL_DEBUG("FAIL %d", __LINE__);
+                       return -1;
+               }
+               rad_assert(rhs != NULL);
 
-                       regerror(compare, &reg, errbuf, sizeof(errbuf));
-                       EDEBUG("Failed compiling regular expression: %s", errbuf);
+               compare = regcomp(&reg, rhs, REG_EXTENDED | (map->src->vpt_iflag ? REG_ICASE : 0));
+               if (compare != 0) {
+                       if (debug_flag) {
+                               char errbuf[128];
+
+                               regerror(compare, &reg, errbuf, sizeof(errbuf));
+                               ERROR("Failed compiling regular expression: %s", errbuf);
+                       }
+                       EVAL_DEBUG("FAIL %d", __LINE__);
+                       return -1;
                }
-               EVAL_DEBUG("FAIL %d", __LINE__);
+
+               preg = &reg;
+               break;
+
+       case VPT_TYPE_REGEX_STRUCT:
+               preg = map->src->vpt_preg;
+               break;
+
+       default:
+               rad_assert(0);
                return -1;
        }
 
-       memset(&rxmatch, 0, sizeof(rxmatch));   /* regexec does not seem to initialise unused elements */
-       compare = regexec(&reg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
-       regfree(&reg);
+       rcode = radius_expand_tmpl(&lhs, request, map->dst);
+       if (rcode < 0) {
+               EVAL_DEBUG("FAIL %d", __LINE__);
+               ret = -1;
+               goto finish;
+       }
+       rad_assert(lhs != NULL);
+
+       /*
+        *  regexec doesn't initialise unused elements
+        */
+       memset(&rxmatch, 0, sizeof(rxmatch));
+       compare = regexec(preg, lhs, REQUEST_MAX_REGEX + 1, rxmatch, 0);
        rad_regcapture(request, compare, lhs, rxmatch);
+       ret = (compare == 0);
 
-       return (compare == 0);
+finish:
+       /*
+        *  regcomp allocs extra memory for the expression, so if the
+        *  result wasn't cached we need to free it here.
+        */
+       if (preg == &reg) regfree(&reg);
+
+       return ret;
 }
 
 /*
@@ -258,9 +325,9 @@ static VALUE_PAIR *get_cast_vp(REQUEST *request, value_pair_tmpl_t const *vpt, D
        if (!vp) return NULL;
 
        if (vpt->type == VPT_TYPE_DATA) {
-               rad_assert(vp->da->type == vpt->da->type);
-               memcpy(&vp->data, vpt->vpd, sizeof(vp->data));
-               vp->length = vpt->length;
+               rad_assert(vp->da->type == vpt->vpt_da->type);
+               memcpy(&vp->data, vpt->vpt_value, sizeof(vp->data));
+               vp->length = vpt->vpt_length;
                return vp;
        }
 
@@ -270,7 +337,7 @@ static VALUE_PAIR *get_cast_vp(REQUEST *request, value_pair_tmpl_t const *vpt, D
                return NULL;
        }
 
-       if (!pairparsevalue(vp, str)) {
+       if ((pairparsevalue(vp, str, 0) < 0)) {
                talloc_free(str);
                pairfree(&vp);
                return NULL;
@@ -279,6 +346,116 @@ static VALUE_PAIR *get_cast_vp(REQUEST *request, value_pair_tmpl_t const *vpt, D
        return vp;
 }
 
+/*
+ *     Copy data from src to dst, where the attributes are of
+ *     different type.
+ */
+static int do_cast_copy(VALUE_PAIR *dst, VALUE_PAIR const *src)
+{
+       rad_assert(dst->da->type != src->da->type);
+
+       if (dst->da->type == PW_TYPE_STRING) {
+               dst->vp_strvalue = vp_aprint_value(dst, src);
+               dst->length = strlen(dst->vp_strvalue);
+               return 0;
+       }
+
+       if (dst->da->type == PW_TYPE_OCTETS) {
+               if (src->da->type == PW_TYPE_STRING) {
+                       pairmemcpy(dst, src->vp_octets, src->length);   /* Copy embedded NULLs */
+               } else {
+                       pairmemcpy(dst, (uint8_t const *) &src->data, src->length);
+               }
+               return 0;
+       }
+
+       if (src->da->type == PW_TYPE_STRING) {
+               return pairparsevalue(dst, src->vp_strvalue, 0);
+       }
+
+       if ((src->da->type == PW_TYPE_INTEGER64) &&
+           (dst->da->type == PW_TYPE_ETHERNET)) {
+               uint8_t array[8];
+               uint64_t i;
+
+               i = htonll(src->vp_integer64);
+               memcpy(array, &i, 8);
+
+               /*
+                *      For OUIs in the DB.
+                */
+               if ((array[0] != 0) || (array[1] != 0)) return -1;
+
+               memcpy(&dst->vp_ether, &array[2], 6);
+               dst->length = 6;
+               return 0;
+       }
+
+       /*
+        *      The attribute we've found has to have a size which is
+        *      compatible with the type of the destination cast.
+        */
+       if ((src->length < dict_attr_sizes[dst->da->type][0]) ||
+           (src->length > dict_attr_sizes[dst->da->type][1])) {
+               EVAL_DEBUG("Casted attribute is wrong size (%u)", (unsigned int) src->length);
+               return -1;
+       }
+
+       if (src->da->type == PW_TYPE_OCTETS) {
+               switch (dst->da->type) {
+               case PW_TYPE_INTEGER64:
+                       dst->vp_integer = ntohll(*(uint64_t const *) src->vp_octets);
+                       break;
+
+
+               case PW_TYPE_INTEGER:
+               case PW_TYPE_DATE:
+               case PW_TYPE_SIGNED:
+                       dst->vp_integer = ntohl(*(uint32_t const *) src->vp_octets);
+                       break;
+
+               case PW_TYPE_SHORT:
+                       dst->vp_integer = ntohs(*(uint16_t const *) src->vp_octets);
+                       break;
+
+               case PW_TYPE_BYTE:
+                       dst->vp_integer = src->vp_octets[0];
+                       break;
+
+               default:
+                       memcpy(&dst->data, src->vp_octets, src->length);
+                       break;
+               }
+
+               dst->length = src->length;
+               return 0;
+       }
+
+       /*
+        *      Convert host order to network byte order.
+        */
+       if ((dst->da->type == PW_TYPE_IPV4_ADDR) &&
+           ((src->da->type == PW_TYPE_INTEGER) ||
+            (src->da->type == PW_TYPE_DATE) ||
+            (src->da->type == PW_TYPE_SIGNED))) {
+               dst->vp_ipaddr = htonl(src->vp_integer);
+
+       } else if ((src->da->type == PW_TYPE_IPV4_ADDR) &&
+                  ((dst->da->type == PW_TYPE_INTEGER) ||
+                   (dst->da->type == PW_TYPE_DATE) ||
+                   (dst->da->type == PW_TYPE_SIGNED))) {
+               dst->vp_integer = htonl(src->vp_ipaddr);
+
+       } else {                /* they're of the same byte order */
+               memcpy(&dst->data, &src->data, src->length);
+       }
+
+       dst->length = src->length;
+
+       return 0;
+}
+
+
 /** Evaluate a map
  *
  * @param[in] request the REQUEST
@@ -302,11 +479,17 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
        rad_assert(map->dst->type != VPT_TYPE_LIST);
        rad_assert(map->src->type != VPT_TYPE_LIST);
        rad_assert(map->dst->type != VPT_TYPE_REGEX);
+       rad_assert(map->dst->type != VPT_TYPE_REGEX_STRUCT);
+
+       EVAL_DEBUG("Map %s ? %s",
+                  fr_int2str(template_names, map->dst->type, "???"),
+                  fr_int2str(template_names, map->src->type, "???"));
 
        /*
         *      Verify regexes.
         */
-       if (map->src->type == VPT_TYPE_REGEX) {
+       if ((map->src->type == VPT_TYPE_REGEX) ||
+           (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
                rad_assert(map->op == T_OP_REG_EQ);
        } else {
                rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)));
@@ -321,10 +504,8 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                VALUE_PAIR *lhs_vp, *rhs_vp;
 
                EVAL_DEBUG("ATTR to ATTR");
-               lhs_vp = radius_vpt_get_vp(request, map->dst);
-               rhs_vp = radius_vpt_get_vp(request, map->src);
-
-               if (!lhs_vp || !rhs_vp) return false;
+               if ((radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) ||
+                   (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0)) return false;
 
                return paircmp_op(lhs_vp, map->op, rhs_vp);
        }
@@ -336,7 +517,30 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
        if (c->cast) {
                VALUE_PAIR *lhs_vp, *rhs_vp;
 
-               lhs_vp = get_cast_vp(request, map->dst, c->cast);
+               /*
+                *      Try to copy data from the VP which is being
+                *      casted, instead of printing it to a string and
+                *      then re-parsing it.
+                */
+               if (map->dst->type == VPT_TYPE_ATTR) {
+                       VALUE_PAIR *cast_vp;
+
+                       if (radius_tmpl_get_vp(&cast_vp, request, map->dst) < 0) return false;
+
+                       lhs_vp = pairalloc(request, c->cast);
+                       if (!lhs_vp) return false;
+
+                       /*
+                        *      In a separate function for clarity
+                        */
+                       if (do_cast_copy(lhs_vp, cast_vp) < 0) {
+                               talloc_free(lhs_vp);
+                               return false;
+                       }
+
+               } else {
+                       lhs_vp = get_cast_vp(request, map->dst, c->cast);
+               }
                if (!lhs_vp) return false;
 
                /*
@@ -344,14 +548,16 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                 *      VP, and return that.
                 */
                if (map->src->type == VPT_TYPE_ATTR) {
-                       rhs_vp = radius_vpt_get_vp(request, map->src);
+                       if (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0) return false;
                } else {
                        rhs_vp = get_cast_vp(request, map->src, c->cast);
                }
 
                if (!rhs_vp) return false;
 
-               EVAL_DEBUG("CAST to ...");
+               EVAL_DEBUG("CAST to %s",
+                          fr_int2str(dict_attr_types,
+                                     c->cast->type, "?Unknown?"));
 
                rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
                pairfree(&lhs_vp);
@@ -366,13 +572,14 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
         */
        if ((map->dst->type == VPT_TYPE_ATTR) &&
            (map->src->type != VPT_TYPE_REGEX) &&
+           (map->src->type != VPT_TYPE_REGEX_STRUCT) &&
            (c->pass2_fixup == PASS2_PAIRCOMPARE)) {
-               int ret;
+               int ret;
                VALUE_PAIR *lhs_vp;
 
                EVAL_DEBUG("virtual ATTR to DATA");
 
-               lhs_vp = get_cast_vp(request, map->src, map->dst->da);
+               lhs_vp = get_cast_vp(request, map->src, map->dst->vpt_da);
                if (!lhs_vp) return false;
 
                /*
@@ -399,10 +606,9 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
 
                EVAL_DEBUG("ATTR to DATA");
 
-               lhs_vp = radius_vpt_get_vp(request, map->dst);
-               if (!lhs_vp) return false;
+               if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) return false;
 
-               rhs_vp = get_cast_vp(request, map->src, map->dst->da);
+               rhs_vp = get_cast_vp(request, map->src, map->dst->vpt_da);
                if (!rhs_vp) return false;
 
 #ifdef WITH_EVAL_DEBUG
@@ -418,6 +624,16 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
        rad_assert(map->src->type != VPT_TYPE_DATA);
        rad_assert(map->dst->type != VPT_TYPE_DATA);
 
+#ifdef HAVE_REGEX_H
+       /*
+        *      Parse regular expressions.
+        */
+       if ((map->src->type == VPT_TYPE_REGEX) ||
+           (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
+               return do_regex(request, map);
+       }
+#endif
+
        /*
         *      The RHS now needs to be expanded into a string.
         */
@@ -426,6 +642,7 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                EVAL_DEBUG("FAIL %d", __LINE__);
                return -1;
        }
+       rad_assert(rhs != NULL);
 
        /*
         *      User-Name == FOO
@@ -436,8 +653,7 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
         *
         *      The LHS may be a virtual attribute, too.
         */
-       if ((map->dst->type == VPT_TYPE_ATTR) &&
-           (map->src->type != VPT_TYPE_REGEX)) {
+       if (map->dst->type == VPT_TYPE_ATTR) {
                VALUE_PAIR *lhs_vp, *rhs_vp;
 
                EVAL_DEBUG("ATTR to non-REGEX");
@@ -445,17 +661,16 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                /*
                 *      No LHS means no match
                 */
-               lhs_vp = radius_vpt_get_vp(request, map->dst);
-               if (!lhs_vp) {
+               if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) {
                        /*
                         *      Not a real attr: might be a dynamic comparison.
                         */
                        if ((map->dst->type == VPT_TYPE_ATTR) &&
-                           (map->dst->da->vendor == 0) &&
-                           radius_find_compare(map->dst->da)) {
-                               rhs_vp = pairalloc(request, map->dst->da);
-                               rad_assert(rhs_vp != NULL);             
-                               if (!pairparsevalue(rhs_vp, rhs)) {
+                           (map->dst->vpt_da->vendor == 0) &&
+                           radius_find_compare(map->dst->vpt_da)) {
+                               rhs_vp = pairalloc(request, map->dst->vpt_da);
+                               rad_assert(rhs_vp != NULL);
+                               if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
                                        talloc_free(rhs);
                                        EVAL_DEBUG("FAIL %d", __LINE__);
                                        return -1;
@@ -473,9 +688,9 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                /*
                 *      Get VP for RHS
                 */
-               rhs_vp = pairalloc(request, map->dst->da);
+               rhs_vp = pairalloc(request, map->dst->vpt_da);
                rad_assert(rhs_vp != NULL);
-               if (!pairparsevalue(rhs_vp, rhs)) {
+               if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
                        talloc_free(rhs);
                        pairfree(&rhs_vp);
                        EVAL_DEBUG("FAIL %d", __LINE__);
@@ -496,17 +711,11 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                EVAL_DEBUG("FAIL %d", __LINE__);
                return -1;
        }
+       rad_assert(lhs != NULL);
 
        EVAL_DEBUG("LHS is %s", lhs);
 
        /*
-        *      Compile  the RHS to a regex, and do regex stuff
-        */
-       if (map->src->type == VPT_TYPE_REGEX) {
-               return do_regex(request, lhs, rhs, c->regex_i);
-       }
-
-       /*
         *      Loop over the string, doing comparisons
         */
        if (all_digits(lhs) && all_digits(rhs)) {
@@ -541,6 +750,9 @@ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth
                }
 
        } else {
+               rad_assert(lhs != NULL);
+               rad_assert(rhs != NULL);
+
                rcode = strcmp(lhs, rhs);
                talloc_free(lhs);
                talloc_free(rhs);
@@ -586,6 +798,12 @@ int radius_evaluate_cond(REQUEST *request, int modreturn, int depth,
                         fr_cond_t const *c)
 {
        int rcode = -1;
+#ifdef WITH_EVAL_DEBUG
+       char buffer[1024];
+
+       fr_cond_sprint(buffer, sizeof(buffer), c);
+       EVAL_DEBUG("%s", buffer);
+#endif
 
        while (c) {
                switch (c->type) {
@@ -648,7 +866,7 @@ int radius_evaluate_cond(REQUEST *request, int modreturn, int depth,
  *     only paircopy() those attributes that we're really going to
  *     use.
  */
-void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
+void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from, bool do_xlat)
 {
        int i, j, count, from_count, to_count, tailto;
        vp_cursor_t cursor;
@@ -657,6 +875,8 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
        int *edited = NULL;
        REQUEST *fixup = NULL;
 
+       if (!request) return;
+
        /*
         *      Set up arrays for editing, to remove some of the
         *      O(N^2) dependencies.  This also makes it easier to
@@ -679,10 +899,10 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
         *      the matching attributes are deleted.
         */
        count = 0;
-       for (vp = paircursor(&cursor, &from); vp; vp = pairnext(&cursor)) count++;
+       for (vp = fr_cursor_init(&cursor, &from); vp; vp = fr_cursor_next(&cursor)) count++;
        from_list = rad_malloc(sizeof(*from_list) * count);
 
-       for (vp = paircursor(&cursor, to); vp; vp = pairnext(&cursor)) count++;
+       for (vp = fr_cursor_init(&cursor, to); vp; vp = fr_cursor_next(&cursor)) count++;
        to_list = rad_malloc(sizeof(*to_list) * count);
 
        /*
@@ -717,6 +937,8 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
 
                RDEBUG4("::: Examining %s", from_list[i]->da->name);
 
+               if (do_xlat) radius_xlat_do(request, from_list[i]);
+
                /*
                 *      Attribute should be appended, OR the "to" list
                 *      is empty, and we're supposed to replace or
@@ -934,10 +1156,8 @@ void radius_pairmove(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR *from)
 
                *last = vp;
                last = &(*last)->next;
-               (void) talloc_steal(request, vp);
        }
 
-       rad_assert(request != NULL);
        rad_assert(request->packet != NULL);
 
        free(to_list);
index 91f932d..904f2ea 100644 (file)
@@ -32,7 +32,6 @@ RCSID("$Id$")
 
 #include <fcntl.h>
 #include <ctype.h>
-#include <signal.h>
 
 #ifdef HAVE_SYS_WAIT_H
 #      include <sys/wait.h>
@@ -82,12 +81,9 @@ static void tv_sub(struct timeval *end, struct timeval *start,
  * @param shell_escape values before passing them as arguments.
  * @return PID of the child process, -1 on error.
  */
-pid_t radius_start_program(char const *cmd, REQUEST *request,
-                       int exec_wait,
-                       int *input_fd,
-                       int *output_fd,
-                       VALUE_PAIR *input_pairs,
-                       int shell_escape)
+pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait,
+                          int *input_fd, int *output_fd,
+                          VALUE_PAIR *input_pairs, bool shell_escape)
 {
 #ifndef __MINGW32__
        char *p;
@@ -103,6 +99,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
        char argv_buf[4096];
 #define MAX_ENVP 1024
        char *envp[MAX_ENVP];
+       int envlen = 0;
 
        argc = rad_expand_xlat(request, cmd, MAX_ARGV, argv, true, sizeof(argv_buf), argv_buf);
        if (argc <= 0) {
@@ -127,13 +124,13 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
        if (exec_wait) {
                if (input_fd) {
                        if (pipe(to_child) != 0) {
-                               RDEBUG("Couldn't open pipe to child: %s", strerror(errno));
+                               RDEBUG("Couldn't open pipe to child: %s", fr_syserror(errno));
                                return -1;
                        }
                }
                if (output_fd) {
                        if (pipe(from_child) != 0) {
-                               RDEBUG("Couldn't open pipe from child: %s", strerror(errno));
+                               RDEBUG("Couldn't open pipe from child: %s", fr_syserror(errno));
                                /* safe because these either need closing or are == -1 */
                                close(to_child[0]);
                                close(to_child[1]);
@@ -146,7 +143,6 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
 
        if (input_pairs) {
                vp_cursor_t cursor;
-               int envlen;
                char buffer[1024];
 
                /*
@@ -155,9 +151,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
                 *      hold mutexes.  They might be locked when we fork,
                 *      and will remain locked in the child.
                 */
-               envlen = 0;
-
-               for (vp = paircursor(&cursor, &input_pairs); vp; vp = pairnext(&cursor)) {
+               for (vp = fr_cursor_init(&cursor, &input_pairs); vp; vp = fr_cursor_next(&cursor)) {
                        /*
                         *      Hmm... maybe we shouldn't pass the
                         *      user's password in an environment
@@ -175,7 +169,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
                        }
 
                        n = strlen(buffer);
-                       vp_prints_value(buffer+n, sizeof(buffer) - n, vp, shell_escape ? '"' : 0);
+                       vp_prints_value(buffer + n, sizeof(buffer) - n, vp, shell_escape ? '"' : 0);
 
                        envp[envlen++] = strdup(buffer);
 
@@ -183,8 +177,12 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
                         *      Don't add too many attributes.
                         */
                        if (envlen == (MAX_ENVP - 1)) break;
+
+                       /*
+                        *      NULL terminate for execve
+                        */
+                       envp[envlen] = NULL;
                }
-               envp[envlen] = NULL;
        }
 
        if (exec_wait) {
@@ -208,7 +206,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
                 */
                devnull = open("/dev/null", O_RDWR);
                if (devnull < 0) {
-                       RDEBUG("Failed opening /dev/null: %s\n", strerror(errno));
+                       RDEBUG("Failed opening /dev/null: %s\n", fr_syserror(errno));
 
                        /*
                         *      Where the status code is interpreted as a module rcode
@@ -224,7 +222,6 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
                 *      has created them.
                 */
                if (exec_wait) {
-
                        if (input_fd) {
                                close(to_child[1]);
                                dup2(to_child[0], STDIN_FILENO);
@@ -268,7 +265,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
                 *      I swear the signature for execve is wrong and should take 'char const * const argv[]'.
                 */
                execve(argv[0], argv, envp);
-               printf("Failed to execute \"%s\": %s", argv[0], strerror(errno)); /* fork output will be captured */
+               printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */
 
                /*
                 *      Where the status code is interpreted as a module rcode
@@ -282,7 +279,7 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
        /*
         *      Free child environment variables
         */
-       for (i = 0; envp[i] != NULL; i++) {
+       for (i = 0; i < envlen; i++) {
                free(envp[i]);
        }
 
@@ -290,13 +287,13 @@ pid_t radius_start_program(char const *cmd, REQUEST *request,
         *      Parent process.
         */
        if (pid < 0) {
-               RDEBUG("Couldn't fork %s: %s", argv[0], strerror(errno));
+               RDEBUG("Couldn't fork %s: %s", argv[0], fr_syserror(errno));
                if (exec_wait) {
                        /* safe because these either need closing or are == -1 */
                        close(to_child[0]);
                        close(to_child[1]);
                        close(from_child[0]);
-                       close(from_child[0]);
+                       close(from_child[1]);
                }
                return -1;
        }
@@ -377,7 +374,7 @@ int radius_readfrom_program(REQUEST *request, int fd, pid_t pid, int timeout,
        int status;
        struct timeval start;
 #ifdef O_NONBLOCK
-       int nonblock = true;
+       bool nonblock = true;
 #endif
 
 #ifdef O_NONBLOCK
@@ -485,12 +482,18 @@ int radius_readfrom_program(REQUEST *request, int fd, pid_t pid, int timeout,
                if (left <= 0) break;
        }
 #endif /* __MINGW32__ */
+
+       /* Strip trailing new lines */
+       while ((done > 0) && (answer[done - 1] == '\n')) {
+               answer[--done] = '\0';
+       }
+
        return done;
 }
 
 /** Execute a program.
  *
- * @param[in] request Current request.
+ * @param[in] request Current request (may be NULL).
  * @param[in] cmd Command to execute. This is parsed into argv[] parts, then each individual argv part
  *     is xlat'ed.
  * @param[in] exec_wait set to 1 if you want to read from or write to child.
@@ -511,15 +514,15 @@ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool
        pid_t pid;
        int from_child;
 #ifndef __MINGW32__
-       VALUE_PAIR *vp;
        char *p;
        pid_t child_pid;
        int comma = 0;
-       int status;
-       int n, done;
+       int status, ret = 0;
+       ssize_t len;
        char answer[4096];
 #endif
-       RDEBUG2("Executing: \"%s\"", cmd);
+
+       DEBUG2("Executing: %s:", cmd);
 
        if (user_msg) *user_msg = '\0';
 
@@ -533,17 +536,17 @@ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool
        }
 
 #ifndef __MINGW32__
-       done = radius_readfrom_program(request, from_child, pid, timeout, answer, sizeof(answer));
-       if (done < 0) {
+       len = radius_readfrom_program(request, from_child, pid, timeout, answer, sizeof(answer));
+       if (len < 0) {
                /*
-                * failure - radius_readfrom_program will
-                * have called close(from_child) for us
+                *      Failure - radius_readfrom_program will
+                *      have called close(from_child) for us
                 */
                DEBUG("Failed to read from child output");
                return -1;
 
        }
-       answer[done] = '\0';
+       answer[len] = '\0';
 
        /*
         *      Make sure that the writer can't block while writing to
@@ -551,71 +554,61 @@ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool
         */
        close(from_child);
 
+       if (len == 0) {
+               goto wait;
+       }
+
        /*
         *      Parse the output, if any.
         */
-       if (done) {
-               n = T_OP_INVALID;
-               if (output_pairs) {
-                       /*
-                        *      For backwards compatibility, first check
-                        *      for plain text (user_msg).
-                        */
-                       vp = NULL;
-                       n = userparse(request, answer, &vp);
-                       if (vp) {
-                               pairfree(&vp);
-                       }
-               }
-
-               if (n == T_OP_INVALID) {
-                       if (user_msg) {
-                               strlcpy(user_msg, answer, msg_len);
+       if (output_pairs) {
+               /*
+                *      HACK: Replace '\n' with ',' so that
+                *      userparse() can parse the buffer in
+                *      one go (the proper way would be to
+                *      fix userparse(), but oh well).
+                */
+               for (p = answer; *p; p++) {
+                       if (*p == '\n') {
+                               *p = comma ? ' ' : ',';
+                               p++;
+                               comma = 0;
                        }
-               } else {
-                       /*
-                        *      HACK: Replace '\n' with ',' so that
-                        *      userparse() can parse the buffer in
-                        *      one go (the proper way would be to
-                        *      fix userparse(), but oh well).
-                        */
-                       for (p = answer; *p; p++) {
-                               if (*p == '\n') {
-                                       *p = comma ? ' ' : ',';
-                                       p++;
-                                       comma = 0;
-                               }
-                               if (*p == ',') comma++;
+                       if (*p == ',') {
+                               comma++;
                        }
+               }
 
-                       /*
-                        *      Replace any trailing comma by a NUL.
-                        */
-                       if (answer[strlen(answer) - 1] == ',') {
-                               answer[strlen(answer) - 1] = '\0';
-                       }
+               /*
+                *      Replace any trailing comma by a NUL.
+                */
+               if (answer[len - 1] == ',') {
+                       answer[--len] = '\0';
+               }
 
-                       if (userparse(request, answer, &vp) == T_OP_INVALID) {
-                               REDEBUG("Unparsable reply from '%s'", cmd);
+               if (userparse(request, answer, output_pairs) == T_OP_INVALID) {
+                       ERROR("Failed parsing output from: %s: %s", cmd, fr_strerror());
+                       strlcpy(user_msg, answer, len);
+                       ret = -1;
+               }
+       /*
+        *      We've not been told to extract output pairs,
+        *      just copy the programs output to the user_msg
+        *      buffer.
+        */
 
-                               return -1;
-                       } else {
-                               /*
-                                *      Tell the caller about the value
-                                *      pairs.
-                                */
-                               *output_pairs = vp;
-                       }
-               } /* else the answer was a set of VP's, not a text message */
-       } /* else we didn't read anything from the child */
+       } else if (user_msg) {
+               strlcpy(user_msg, answer, msg_len);
+       }
 
        /*
         *      Call rad_waitpid (should map to waitpid on non-threaded
         *      or single-server systems).
         */
+wait:
        child_pid = rad_waitpid(pid, &status);
        if (child_pid == 0) {
-               REDEBUG("Timeout waiting for child");
+               ERROR("Timeout waiting for child");
 
                return -2;
        }
@@ -623,14 +616,17 @@ int radius_exec_program(REQUEST *request, char const *cmd, bool exec_wait, bool
        if (child_pid == pid) {
                if (WIFEXITED(status)) {
                        status = WEXITSTATUS(status);
+                       if ((status != 0) || (ret < 0)) {
+                               ERROR("Program returned code (%d) and output '%s'", status, answer);
+                       } else {
+                               ERROR("Program returned code (%d) and output '%s'", status, answer);
+                       }
 
-                       RDEBUG("Program returned code (%d): %s", status, answer);
-
-                       return status;
+                       return ret < 0 ? ret : status;
                }
        }
 
-       REDEBUG("Abnormal child exit: %s", strerror(errno));
+       ERROR("Abnormal child exit: %s", fr_syserror(errno));
 #endif /* __MINGW32__ */
 
        return -1;
index 6b0c766..7b5dda6 100644 (file)
@@ -75,7 +75,7 @@ int pairlist_read(TALLOC_CTX *ctx, char const *file, PAIR_LIST **list, int compl
                if (!complain)
                        return -1;
                ERROR("Couldn't open %s for reading: %s",
-                               file, strerror(errno));
+                               file, fr_syserror(errno));
                return -1;
        }
 
@@ -118,7 +118,7 @@ parse_again:
                        }
 
                        ptr = buffer;
-                       getword(&ptr, entry, sizeof(entry));
+                       getword(&ptr, entry, sizeof(entry), false);
 
                        /*
                         *      Include another file if we see
@@ -146,11 +146,9 @@ parse_again:
                                                p = newfile + strlen(newfile);
                                                *p = FR_DIR_SEP;
                                        }
-                                       getword(&ptr, p + 1,
-                                               sizeof(newfile) - 1 - (p - newfile));
+                                       getword(&ptr, p + 1, sizeof(newfile) - 1 - (p - newfile), false);
                                } else {
-                                       getword(&ptr, newfile,
-                                               sizeof(newfile));
+                                       getword(&ptr, newfile, sizeof(newfile), false);
                                }
 
                                t = NULL;
@@ -158,7 +156,7 @@ parse_again:
                                if (pairlist_read(ctx, newfile, &t, 0) != 0) {
                                        pairlist_free(&pl);
                                        ERROR("%s[%d]: Could not open included file %s: %s",
-                                              file, lineno, newfile, strerror(errno));
+                                              file, lineno, newfile, fr_syserror(errno));
                                        fclose(fp);
                                        return -1;
                                }
@@ -230,7 +228,7 @@ parse_again:
                                check_tmp = NULL;
                                reply_tmp = NULL;
 
-                               t->name = talloc_strdup(t, entry);
+                               t->name = talloc_typed_strdup(t, entry);
 
                                *last = t;
                                last = &(t->next);
index f0d8b30..951569e 100644 (file)
@@ -28,6 +28,7 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 #include <freeradius-devel/process.h>
 #include <freeradius-devel/protocol.h>
+#include <freeradius-devel/modpriv.h>
 
 #include <freeradius-devel/detail.h>
 
@@ -85,7 +86,7 @@ static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
 
        if (!fmt || !out || (outlen < 1)) return 0;
 
-       if (!request || !request->listener) {
+       if (!request->listener) {
                RWDEBUG("No listener associated with this request");
                *out = '\0';
                return 0;
@@ -107,7 +108,7 @@ static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request,
  *     Find a per-socket client.
  */
 RADCLIENT *client_listener_find(rad_listen_t *listener,
-                               fr_ipaddr_t const *ipaddr, int src_port)
+                               fr_ipaddr_t const *ipaddr, uint16_t src_port)
 {
 #ifdef WITH_DYNAMIC_CLIENTS
        int rcode;
@@ -255,7 +256,7 @@ RADCLIENT *client_listener_find(rad_listen_t *listener,
        request->number = 0;
        request->priority = listener->type;
        request->server = client->client_server;
-       request->root = &mainconfig;
+       request->root = &main_config;
 
        /*
         *      Run a fake request through the given virtual server.
@@ -330,7 +331,7 @@ int rad_status_server(REQUEST *request)
                switch (rcode) {
                case RLM_MODULE_OK:
                case RLM_MODULE_UPDATED:
-                       request->reply->code = PW_AUTHENTICATION_ACK;
+                       request->reply->code = PW_CODE_AUTHENTICATION_ACK;
                        break;
 
                case RLM_MODULE_FAIL:
@@ -340,7 +341,7 @@ int rad_status_server(REQUEST *request)
 
                default:
                case RLM_MODULE_REJECT:
-                       request->reply->code = PW_AUTHENTICATION_REJECT;
+                       request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                        break;
                }
                break;
@@ -357,7 +358,7 @@ int rad_status_server(REQUEST *request)
                switch (rcode) {
                case RLM_MODULE_OK:
                case RLM_MODULE_UPDATED:
-                       request->reply->code = PW_ACCOUNTING_RESPONSE;
+                       request->reply->code = PW_CODE_ACCOUNTING_RESPONSE;
                        break;
 
                default:
@@ -384,7 +385,7 @@ int rad_status_server(REQUEST *request)
                switch (rcode) {
                case RLM_MODULE_OK:
                case RLM_MODULE_UPDATED:
-                       request->reply->code = PW_COA_ACK;
+                       request->reply->code = PW_CODE_COA_ACK;
                        break;
 
                default:
@@ -436,6 +437,7 @@ static int dual_tcp_recv(rad_listen_t *listener)
                sock->packet->src_port = sock->other_port;
                sock->packet->dst_ipaddr = sock->my_ipaddr;
                sock->packet->dst_port = sock->my_port;
+               sock->packet->proto = sock->proto;
        }
 
        /*
@@ -456,9 +458,9 @@ static int dual_tcp_recv(rad_listen_t *listener)
        if (rcode == -1) {      /* error reading packet */
                char buffer[256];
 
-               ERROR("Invalid packet from %s port %d: closing socket",
+               ERROR("Invalid packet from %s port %d, closing socket: %s",
                       ip_ntoh(&packet->src_ipaddr, buffer, sizeof(buffer)),
-                      packet->src_port);
+                      packet->src_port, fr_strerror());
        }
 
        if (rcode < 0) {        /* error or connection reset */
@@ -468,7 +470,7 @@ static int dual_tcp_recv(rad_listen_t *listener)
                 *      Tell the event handler that an FD has disappeared.
                 */
                DEBUG("Client has closed connection");
-               event_new_fd(listener);
+               radius_update_listener(listener);
 
                /*
                 *      Do NOT free the listener here.  It's in use by
@@ -484,24 +486,24 @@ static int dual_tcp_recv(rad_listen_t *listener)
         *      Some sanity checks, based on the packet code.
         */
        switch(packet->code) {
-       case PW_AUTHENTICATION_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
                if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
                FR_STATS_INC(auth, total_requests);
                fun = rad_authenticate;
                break;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
                if (listener->type != RAD_LISTEN_ACCT) goto bad_packet;
                FR_STATS_INC(acct, total_requests);
                fun = rad_accounting;
                break;
 #endif
 
-       case PW_STATUS_SERVER:
-               if (!mainconfig.status_server) {
+       case PW_CODE_STATUS_SERVER:
+               if (!main_config.status_server) {
                        FR_STATS_INC(auth, total_unknown_types);
-                       WDEBUG("Ignoring Status-Server request due to security configuration");
+                       WARN("Ignoring Status-Server request due to security configuration");
                        rad_free(&sock->packet);
                        return 0;
                }
@@ -530,7 +532,8 @@ static int dual_tcp_recv(rad_listen_t *listener)
 
 static int dual_tcp_accept(rad_listen_t *listener)
 {
-       int newfd, src_port;
+       int newfd;
+       uint16_t src_port;
        rad_listen_t *this;
        socklen_t salen;
        struct sockaddr_storage src;
@@ -540,7 +543,7 @@ static int dual_tcp_accept(rad_listen_t *listener)
 
        salen = sizeof(src);
 
-       DEBUG2(" ... new connection request on TCP socket.");
+       DEBUG2(" ... new connection request on TCP socket");
 
        newfd = accept(listener->fd, (struct sockaddr *) &src, &salen);
        if (newfd < 0) {
@@ -553,13 +556,13 @@ static int dual_tcp_accept(rad_listen_t *listener)
                }
 #endif
 
-               DEBUG2(" ... failed to accept connection.");
+               DEBUG2(" ... failed to accept connection");
                return -1;
        }
 
        if (!fr_sockaddr2ipaddr(&src, salen, &src_ipaddr, &src_port)) {
                close(newfd);
-               DEBUG2(" ... unknown address family.");
+               DEBUG2(" ... unknown address family");
                return 0;
        }
 
@@ -690,7 +693,7 @@ static int dual_tcp_accept(rad_listen_t *listener)
         *      Tell the event loop that we have a new FD.
         *      This can be called from a child thread...
         */
-       event_new_fd(this);
+       radius_update_listener(this);
 
        return 0;
 }
@@ -699,24 +702,25 @@ static int dual_tcp_accept(rad_listen_t *listener)
 /*
  *     Ensure that we always keep the correct counters.
  */
-#ifdef WITH_TLS
+#ifdef WITH_TCP
 static void common_socket_free(rad_listen_t *this)
 {
        listen_socket_t *sock = this->data;
 
        if (sock->proto != IPPROTO_TCP) return;
 
-       if (!sock->parent) return;
-
        /*
         *      Decrement the number of connections.
         */
-       if (sock->parent->limit.num_connections > 0) {
+       if (sock->parent && (sock->parent->limit.num_connections > 0)) {
                sock->parent->limit.num_connections--;
        }
-       if (sock->client->limit.num_connections > 0) {
+       if (sock->client && sock->client->limit.num_connections > 0) {
                sock->client->limit.num_connections--;
        }
+       if (sock->home && sock->home->limit.num_connections > 0) {
+               sock->home->limit.num_connections--;
+       }
 }
 #else
 static void common_socket_free(UNUSED rad_listen_t *this)
@@ -860,32 +864,23 @@ int common_socket_print(rad_listen_t const *this, char *buffer, size_t bufsize)
 extern bool check_config;      /* radiusd.c */
 
 static CONF_PARSER performance_config[] = {
-       { "skip_duplicate_checks", PW_TYPE_BOOLEAN,
-         offsetof(rad_listen_t, nodup), NULL,   NULL },
+       { "skip_duplicate_checks", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rad_listen_t, nodup), NULL },
 
-       { "synchronous", PW_TYPE_BOOLEAN,
-         offsetof(rad_listen_t, synchronous), NULL,   NULL },
+       { "synchronous", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rad_listen_t, synchronous), NULL },
 
-       { "workers", PW_TYPE_INTEGER,
-         offsetof(rad_listen_t, workers), NULL,   NULL },
+       { "workers", FR_CONF_OFFSET(PW_TYPE_INTEGER, rad_listen_t, workers), NULL },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
 
 static CONF_PARSER limit_config[] = {
-       { "max_pps", PW_TYPE_INTEGER,
-         offsetof(listen_socket_t, max_rate), NULL,   NULL },
+       { "max_pps", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, max_rate), NULL },
 
 #ifdef WITH_TCP
-       { "max_connections", PW_TYPE_INTEGER,
-         offsetof(listen_socket_t, limit.max_connections), NULL,   "16" },
-
-       { "lifetime", PW_TYPE_INTEGER,
-         offsetof(listen_socket_t, limit.lifetime), NULL,   "0" },
-
-       { "idle_timeout", PW_TYPE_INTEGER,
-         offsetof(listen_socket_t, limit.idle_timeout), NULL,   "30" },
+       { "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.max_connections), "16" },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.lifetime), "0" },
+       { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.idle_timeout), STRINGIFY(30) },
 #endif
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
@@ -897,10 +892,10 @@ static CONF_PARSER limit_config[] = {
 int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 {
        int             rcode;
-       int             listen_port;
+       uint16_t        listen_port;
        fr_ipaddr_t     ipaddr;
        listen_socket_t *sock = this->data;
-       char            *section_name = NULL;
+       char const      *section_name = NULL;
        CONF_SECTION    *client_cs, *parentcs;
        CONF_SECTION    *subcs;
 
@@ -911,51 +906,35 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
         */
        memset(&ipaddr, 0, sizeof(ipaddr));
        ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-       rcode = cf_item_parse(cs, "ipaddr", PW_TYPE_IPADDR,
-                             &ipaddr.ipaddr.ip4addr, NULL);
-       if (rcode < 0) return -1;
-
-       if (rcode == 0) { /* successfully parsed IPv4 */
-               ipaddr.af = AF_INET;
-
-       } else {        /* maybe IPv6? */
-               rcode = cf_item_parse(cs, "ipv6addr", PW_TYPE_IPV6ADDR,
-                                     &ipaddr.ipaddr.ip6addr, NULL);
-               if (rcode < 0) return -1;
 
-               if (rcode == 1) {
-                       cf_log_err_cs(cs,
-                                  "No address specified in listen section");
-                       return -1;
-               }
-               ipaddr.af = AF_INET6;
+       rcode = cf_item_parse(cs, "ipaddr", FR_ITEM_POINTER(PW_TYPE_IP_ADDR, &ipaddr), NULL);
+       if (rcode < 0) return -1;
+       if (rcode != 0) rcode = cf_item_parse(cs, "ipv4addr", FR_ITEM_POINTER(PW_TYPE_IPV4_ADDR, &ipaddr), NULL);
+       if (rcode < 0) return -1;
+       if (rcode != 0) rcode = cf_item_parse(cs, "ipv6addr", FR_ITEM_POINTER(PW_TYPE_IPV6_ADDR, &ipaddr), NULL);
+       if (rcode < 0) return -1;
+       if (rcode != 0) {
+               cf_log_err_cs(cs, "No address specified in listen section");
+               return -1;
        }
 
-       rcode = cf_item_parse(cs, "port", PW_TYPE_INTEGER,
-                             &listen_port, "0");
+       rcode = cf_item_parse(cs, "port", FR_ITEM_POINTER(PW_TYPE_SHORT, &listen_port), "0");
        if (rcode < 0) return -1;
 
-       if ((listen_port < 0) || (listen_port > 65535)) {
-                       cf_log_err_cs(cs,
-                                  "Invalid value for \"port\"");
-                       return -1;
-       }
-
        sock->proto = IPPROTO_UDP;
 
        if (cf_pair_find(cs, "proto")) {
 #ifndef WITH_TCP
                cf_log_err_cs(cs,
-                          "System does not support the TCP protocol.  Delete this line from the configuration file.");
+                          "System does not support the TCP protocol.  Delete this line from the configuration file");
                return -1;
 #else
-               char *proto = NULL;
+               char const *proto = NULL;
 #ifdef WITH_TLS
                CONF_SECTION *tls;
 #endif
 
-               rcode = cf_item_parse(cs, "proto", PW_TYPE_STRING_PTR,
-                                     &proto, "udp");
+               rcode = cf_item_parse(cs, "proto", FR_ITEM_POINTER(PW_TYPE_STRING, &proto), "udp");
                if (rcode < 0) return -1;
 
                if (strcmp(proto, "udp") == 0) {
@@ -985,20 +964,20 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 #ifdef WITH_TLS
                tls = cf_section_sub_find(cs, "tls");
 
-               /*
-                *      Don't allow TLS configurations for UDP sockets.
-                */
-               if (sock->proto != IPPROTO_TCP) {
-                       cf_log_err_cs(cs,
-                                  "TLS transport is not available for UDP sockets.");
-                       return -1;
-               }
-
                if (tls) {
                        /*
-                        *      FIXME: Make this better.
+                        *      Don't allow TLS configurations for UDP sockets.
+                        */
+                       if (sock->proto != IPPROTO_TCP) {
+                               cf_log_err_cs(cs,
+                                             "TLS transport is not available for UDP sockets");
+                               return -1;
+                       }
+
+                       /*
+                        *      If unset, set to default.
                         */
-                       if (listen_port == 0) listen_port = 2083;
+                       if (listen_port == 0) listen_port = PW_RADIUS_TLS_PORT;
 
                        this->tls = tls_server_conf_parse(tls);
                        if (!this->tls) {
@@ -1020,7 +999,7 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                 */
                if (cf_section_sub_find(cs, "tls")) {
                        cf_log_err_cs(cs,
-                                  "TLS transport is not available in this executable.");
+                                  "TLS transport is not available in this executable");
                        return -1;
                }
 #endif /* WITH_TLS */
@@ -1032,7 +1011,7 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
                 */
        } else if (cf_section_sub_find(cs, "tls")) {
                cf_log_err_cs(cs,
-                          "TLS transport is not available in this \"listen\" section.");
+                          "TLS transport is not available in this \"listen\" section");
                return -1;
        }
 
@@ -1104,15 +1083,15 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 #ifdef WITH_PROXY
        if (check_config) {
                /*
-                *      Until there is a side effects free way of forwarding a
-                *      request to another virtual server, this check is invalid,
-                *      and should be left disabled.
-                */
+                *      Until there is a side effects free way of forwarding a
+                *      request to another virtual server, this check is invalid,
+                *      and should be left disabled.
+                */
 #if 0
                if (home_server_find(&sock->my_ipaddr, sock->my_port, sock->proto)) {
                                char buffer[128];
 
-                               EDEBUG("We have been asked to listen on %s port %d, which is also listed as a "
+                               ERROR("We have been asked to listen on %s port %d, which is also listed as a "
                                       "home server.  This can create a proxy loop",
                                       ip_ntoh(&sock->my_ipaddr, buffer, sizeof(buffer)), sock->my_port);
                                return -1;
@@ -1147,7 +1126,7 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        if (cf_pair_find(cs, "broadcast")) {
 #ifndef SO_BROADCAST
                cf_log_err_cs(cs,
-                          "System does not support broadcast sockets.  Delete this line from the configuration file.");
+                          "System does not support broadcast sockets.  Delete this line from the configuration file");
                return -1;
 #else
                char const *value;
@@ -1155,7 +1134,7 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
 
                if (this->type != RAD_LISTEN_DHCP) {
                        cf_log_err_cp(cp,
-                                  "Broadcast can only be set for DHCP listeners.  Delete this line from the configuration file.");
+                                  "Broadcast can only be set for DHCP listeners.  Delete this line from the configuration file");
                        return -1;
                }
 
@@ -1200,16 +1179,13 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
         */
        client_cs = NULL;
        parentcs = cf_top_section(cs);
-       rcode = cf_item_parse(cs, "clients", PW_TYPE_STRING_PTR,
-                             &section_name, NULL);
+       rcode = cf_item_parse(cs, "clients", FR_ITEM_POINTER(PW_TYPE_STRING, &section_name), NULL);
        if (rcode < 0) return -1; /* bad string */
        if (rcode == 0) {
                /*
                 *      Explicit list given: use it.
                 */
-               client_cs = cf_section_sub_find_name2(parentcs,
-                                                     "clients",
-                                                     section_name);
+               client_cs = cf_section_sub_find_name2(parentcs, "clients", section_name);
                if (!client_cs) {
                        client_cs = cf_section_find(section_name);
                }
@@ -1274,6 +1250,8 @@ static int auth_socket_send(rad_listen_t *listener, REQUEST *request)
        rad_assert(request->listener == listener);
        rad_assert(listener->send == auth_socket_send);
 
+       if (request->reply->code == 0) return 0;
+
 #ifdef WITH_UDPFROMTO
        /*
         *      Overwrite the src ip address on the outbound packet
@@ -1369,7 +1347,8 @@ static int proxy_socket_send(rad_listen_t *listener, REQUEST *request)
 static int stats_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
-       int             code, src_port;
+       int             code;
+       uint16_t        src_port;
        RADIUS_PACKET   *packet;
        RADCLIENT       *client = NULL;
        fr_ipaddr_t     src_ipaddr;
@@ -1396,7 +1375,7 @@ static int stats_socket_recv(rad_listen_t *listener)
        /*
         *      We only understand Status-Server on this socket.
         */
-       if (code != PW_STATUS_SERVER) {
+       if (code != PW_CODE_STATUS_SERVER) {
                DEBUG("Ignoring packet code %d sent to Status-Server port",
                      code);
                rad_recv_discard(listener->fd);
@@ -1435,7 +1414,8 @@ static int stats_socket_recv(rad_listen_t *listener)
 static int auth_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
-       int             code, src_port;
+       int             code;
+       uint16_t        src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
        RADCLIENT       *client = NULL;
@@ -1464,15 +1444,15 @@ static int auth_socket_recv(rad_listen_t *listener)
         *      Some sanity checks, based on the packet code.
         */
        switch(code) {
-       case PW_AUTHENTICATION_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
                fun = rad_authenticate;
                break;
 
-       case PW_STATUS_SERVER:
-               if (!mainconfig.status_server) {
+       case PW_CODE_STATUS_SERVER:
+               if (!main_config.status_server) {
                        rad_recv_discard(listener->fd);
                        FR_STATS_INC(auth, total_unknown_types);
-                       WDEBUG("Ignoring Status-Server request due to security configuration");
+                       WARN("Ignoring Status-Server request due to security configuration");
                        return 0;
                }
                fun = rad_status_server;
@@ -1541,7 +1521,8 @@ static int auth_socket_recv(rad_listen_t *listener)
 static int acct_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
-       int             code, src_port;
+       int             code;
+       uint16_t        src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
        RADCLIENT       *client = NULL;
@@ -1570,16 +1551,16 @@ static int acct_socket_recv(rad_listen_t *listener)
         *      Some sanity checks, based on the packet code.
         */
        switch(code) {
-       case PW_ACCOUNTING_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
                fun = rad_accounting;
                break;
 
-       case PW_STATUS_SERVER:
-               if (!mainconfig.status_server) {
+       case PW_CODE_STATUS_SERVER:
+               if (!main_config.status_server) {
                        rad_recv_discard(listener->fd);
                        FR_STATS_INC(acct, total_unknown_types);
 
-                       WDEBUG("Ignoring Status-Server request due to security configuration");
+                       WARN("Ignoring Status-Server request due to security configuration");
                        return 0;
                }
                fun = rad_status_server;
@@ -1654,14 +1635,14 @@ static int rad_coa_recv(REQUEST *request)
         *      Get the correct response
         */
        switch (request->packet->code) {
-       case PW_COA_REQUEST:
-               ack = PW_COA_ACK;
-               nak = PW_COA_NAK;
+       case PW_CODE_COA_REQUEST:
+               ack = PW_CODE_COA_ACK;
+               nak = PW_CODE_COA_NAK;
                break;
 
-       case PW_DISCONNECT_REQUEST:
-               ack = PW_DISCONNECT_ACK;
-               nak = PW_DISCONNECT_NAK;
+       case PW_CODE_DISCONNECT_REQUEST:
+               ack = PW_CODE_DISCONNECT_ACK;
+               nak = PW_CODE_DISCONNECT_NAK;
                break;
 
        default:                /* shouldn't happen */
@@ -1681,12 +1662,12 @@ static int rad_coa_recv(REQUEST *request)
                 *      have a State attribute in it.
                 */
                vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY);
-               if (request->packet->code == PW_COA_REQUEST) {
+               if (request->packet->code == PW_CODE_COA_REQUEST) {
                        if (vp && (vp->vp_integer == 17)) {
                                vp = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
                                if (!vp || (vp->length == 0)) {
                                        REDEBUG("CoA-Request with Service-Type = Authorize-Only MUST contain a State attribute");
-                                       request->reply->code = PW_COA_NAK;
+                                       request->reply->code = PW_CODE_COA_NAK;
                                        return RLM_MODULE_FAIL;
                                }
                        }
@@ -1695,7 +1676,7 @@ static int rad_coa_recv(REQUEST *request)
                         *      RFC 5176, Section 3.2.
                         */
                        REDEBUG("Disconnect-Request MUST NOT contain a Service-Type attribute");
-                       request->reply->code = PW_DISCONNECT_NAK;
+                       request->reply->code = PW_CODE_DISCONNECT_NAK;
                        return RLM_MODULE_FAIL;
                }
 
@@ -1790,7 +1771,8 @@ static int rad_coa_recv(REQUEST *request)
 static int coa_socket_recv(rad_listen_t *listener)
 {
        ssize_t         rcode;
-       int             code, src_port;
+       int             code;
+       uint16_t        src_port;
        RADIUS_PACKET   *packet;
        RAD_REQUEST_FUNP fun = NULL;
        RADCLIENT       *client = NULL;
@@ -1817,12 +1799,12 @@ static int coa_socket_recv(rad_listen_t *listener)
         *      Some sanity checks, based on the packet code.
         */
        switch(code) {
-       case PW_COA_REQUEST:
+       case PW_CODE_COA_REQUEST:
                FR_STATS_INC(coa, total_requests);
                fun = rad_coa_recv;
                break;
 
-       case PW_DISCONNECT_REQUEST:
+       case PW_CODE_DISCONNECT_REQUEST:
                FR_STATS_INC(dsc, total_requests);
                fun = rad_coa_recv;
                break;
@@ -1875,21 +1857,21 @@ static int proxy_socket_recv(rad_listen_t *listener)
         *      FIXME: Client MIB updates?
         */
        switch(packet->code) {
-       case PW_AUTHENTICATION_ACK:
-       case PW_ACCESS_CHALLENGE:
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_ACCESS_CHALLENGE:
+       case PW_CODE_AUTHENTICATION_REJECT:
                break;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_RESPONSE:
+       case PW_CODE_ACCOUNTING_RESPONSE:
                break;
 #endif
 
 #ifdef WITH_COA
-       case PW_DISCONNECT_ACK:
-       case PW_DISCONNECT_NAK:
-       case PW_COA_ACK:
-       case PW_COA_NAK:
+       case PW_CODE_DISCONNECT_ACK:
+       case PW_CODE_DISCONNECT_NAK:
+       case PW_CODE_COA_ACK:
+       case PW_CODE_COA_NAK:
                break;
 #endif
 
@@ -1929,7 +1911,7 @@ static int proxy_socket_tcp_recv(rad_listen_t *listener)
        packet = fr_tcp_recv(listener->fd, 0);
        if (!packet) {
                listener->status = RAD_LISTEN_STATUS_EOL;
-               event_new_fd(listener);
+               radius_update_listener(listener);
                return 0;
        }
 
@@ -1937,13 +1919,13 @@ static int proxy_socket_tcp_recv(rad_listen_t *listener)
         *      FIXME: Client MIB updates?
         */
        switch(packet->code) {
-       case PW_AUTHENTICATION_ACK:
-       case PW_ACCESS_CHALLENGE:
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_ACCESS_CHALLENGE:
+       case PW_CODE_AUTHENTICATION_REJECT:
                break;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_RESPONSE:
+       case PW_CODE_ACCOUNTING_RESPONSE:
                break;
 #endif
 
@@ -2214,7 +2196,7 @@ static int listen_bind(rad_listen_t *this)
 #endif
 
                default:
-                       WDEBUG("Internal sanity check failed in binding to socket.  Ignoring problem.");
+                       WARN("Internal sanity check failed in binding to socket.  Ignoring problem");
                        return -1;
                }
        }
@@ -2236,7 +2218,7 @@ static int listen_bind(rad_listen_t *this)
 
                this->print(this, buffer, sizeof(buffer));
 
-               ERROR("Failed opening %s: %s", buffer, strerror(errno));
+               ERROR("Failed opening %s: %s", buffer, fr_syserror(errno));
                return -1;
        }
 
@@ -2249,7 +2231,7 @@ static int listen_bind(rad_listen_t *this)
        if (rcode >= 0) {
                if (fcntl(this->fd, F_SETFD, rcode | FD_CLOEXEC) < 0) {
                        close(this->fd);
-                       ERROR("Failed setting close on exec: %s", strerror(errno));
+                       ERROR("Failed setting close on exec: %s", fr_syserror(errno));
                        return -1;
                }
        }
@@ -2272,7 +2254,7 @@ static int listen_bind(rad_listen_t *this)
                if (rcode < 0) {
                        close(this->fd);
                        ERROR("Failed binding to interface %s: %s",
-                              sock->interface, strerror(errno));
+                              sock->interface, fr_syserror(errno));
                        return -1;
                } /* else it worked. */
 #else
@@ -2294,7 +2276,7 @@ static int listen_bind(rad_listen_t *this)
                                if (sock->my_ipaddr.scope == 0) {
                                        close(this->fd);
                                        ERROR("Failed finding interface %s: %s",
-                                              sock->interface, strerror(errno));
+                                              sock->interface, fr_syserror(errno));
                                        return -1;
                                }
                        } /* else scope was defined: we're OK. */
@@ -2319,7 +2301,7 @@ static int listen_bind(rad_listen_t *this)
 
                if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
                        close(this->fd);
-                       ERROR("Failed to reuse address: %s", strerror(errno));
+                       ERROR("Failed to reuse address: %s", fr_syserror(errno));
                        return -1;
                }
        }
@@ -2335,7 +2317,7 @@ static int listen_bind(rad_listen_t *this)
         */
        if (udpfromto_init(this->fd) != 0) {
                ERROR("Failed initializing udpfromto: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                close(this->fd);
                return -1;
        }
@@ -2365,9 +2347,9 @@ static int listen_bind(rad_listen_t *this)
                        if (setsockopt(this->fd, IPPROTO_IPV6, IPV6_V6ONLY,
                                       (char *)&on, sizeof(on)) < 0) {
                                ERROR("Failed setting socket to IPv6 "
-                                      "only: %s", strerror(errno));
+                                      "only: %s", fr_syserror(errno));
 
-                               close(this->fd);
+                               close(this->fd);
                                return -1;
                        }
                }
@@ -2388,7 +2370,7 @@ static int listen_bind(rad_listen_t *this)
                if (setsockopt(this->fd, IPPROTO_IP, IP_MTU_DISCOVER,
                               &flag, sizeof(flag)) < 0) {
                        ERROR("Failed disabling PMTU discovery: %s",
-                              strerror(errno));
+                              fr_syserror(errno));
 
                        close(this->fd);
                        return -1;
@@ -2403,7 +2385,7 @@ static int listen_bind(rad_listen_t *this)
                if (setsockopt(this->fd, IPPROTO_IP, IP_DONTFRAG,
                               &flag, sizeof(flag)) < 0) {
                        ERROR("Failed setting don't fragment flag: %s",
-                              strerror(errno));
+                              fr_syserror(errno));
 
                        close(this->fd);
                        return -1;
@@ -2418,7 +2400,7 @@ static int listen_bind(rad_listen_t *this)
 
                if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
                        ERROR("Can't set broadcast option: %s",
-                              strerror(errno));
+                              fr_syserror(errno));
                        return -1;
                }
        }
@@ -2429,16 +2411,6 @@ static int listen_bind(rad_listen_t *this)
         *      May be binding to priviledged ports.
         */
        if (sock->my_port != 0) {
-#ifdef SO_REUSEADDR
-               int on = 1;
-
-               if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
-                       ERROR("Can't set re-use address option: %s",
-                              strerror(errno));
-                       return -1;
-               }
-#endif
-
                fr_suid_up();
                rcode = bind(this->fd, (struct sockaddr *) &salocal, salen);
                fr_suid_down();
@@ -2448,7 +2420,7 @@ static int listen_bind(rad_listen_t *this)
 
                        this->print(this, buffer, sizeof(buffer));
                        ERROR("Failed binding to %s: %s\n",
-                              buffer, strerror(errno));
+                              buffer, fr_syserror(errno));
                        return -1;
                }
 
@@ -2465,7 +2437,7 @@ static int listen_bind(rad_listen_t *this)
                        if (getsockname(this->fd, (struct sockaddr *) &src,
                                        &sizeof_src) < 0) {
                                ERROR("Failed getting socket name: %s",
-                                      strerror(errno));
+                                      fr_syserror(errno));
                                return -1;
                        }
 
@@ -2480,23 +2452,35 @@ static int listen_bind(rad_listen_t *this)
 #ifdef WITH_TCP
        if (sock->proto == IPPROTO_TCP) {
                /*
-                *      Allow a backlog of 8 listeners
+                *      If there are hard-coded worker threads, OR
+                *      it's a TLS connection, it's blocking.
+                *
+                *      Otherwise, they're non-blocking.
                 */
-               if (listen(this->fd, 8) < 0) {
-                       close(this->fd);
-                       ERROR("Failed in listen(): %s", strerror(errno));
-                       return -1;
+               if (!this->workers
+#ifdef WITH_PROXY
+#ifdef WITH_TLS
+                   && (this->type == RAD_LISTEN_PROXY) && !this->tls
+#endif
+#endif
+                       ) {
+                       if (fr_nonblock(this->fd) < 0) {
+                               close(this->fd);
+                               ERROR("Failed setting non-blocking on socket: %s",
+                                     fr_syserror(errno));
+                               return -1;
+                       }
                }
 
                /*
-                *      If there are hard-coded worker threads, they're blocking.
-                *
-                *      Otherwise, they're non-blocking.
+                *      Allow a backlog of 8 listeners, but only for incoming interfaces.
                 */
-               if (!this->workers && fr_nonblock(this->fd) < 0) {
+#ifdef WITH_PROXY
+               if (this->type != RAD_LISTEN_PROXY)
+#endif
+               if (listen(this->fd, 8) < 0) {
                        close(this->fd);
-                       ERROR("Failed setting non-blocking on socket: %s",
-                             strerror(errno));
+                       ERROR("Failed in listen(): %s", fr_syserror(errno));
                        return -1;
                }
        }
@@ -2547,19 +2531,24 @@ static int listener_free(void *ctx)
                ) {
                listen_socket_t *sock = this->data;
 
-#ifdef WITH_TLS
-               if (sock->request) {
-#  ifdef HAVE_PTHREAD_H
-                       pthread_mutex_destroy(&(sock->mutex));
-#  endif
-                       request_free(&sock->request);
-                       sock->packet = NULL;
+               rad_free(&sock->packet);
+
+               rad_assert(sock->ev == NULL);
 
+#ifdef WITH_TLS
+               /*
+                *      Note that we do NOT free this->tls, as the
+                *      pointer is parented by its CONF_SECTION.  It
+                *      may be used by multiple listeners.
+                */
+               if (this->tls) {
                        if (sock->ssn) session_free(sock->ssn);
                        request_free(&sock->request);
-               } else
+#ifdef HAVE_PTHREAD_H
+                       pthread_mutex_destroy(&(sock->mutex));
 #endif
-                       rad_free(&sock->packet);
+               }
+#endif /* WITH_TLS */
        }
 #endif                         /* WITH_TCP */
 
@@ -2596,22 +2585,31 @@ static rad_listen_t *listen_alloc(TALLOC_CTX *ctx, RAD_LISTEN_TYPE type)
  *     Not thread-safe, but all calls to it are protected by the
  *     proxy mutex in event.c
  */
-rad_listen_t *proxy_new_listener(home_server *home, int src_port)
+rad_listen_t *proxy_new_listener(home_server_t *home, uint16_t src_port)
 {
+       time_t now;
        rad_listen_t *this;
        listen_socket_t *sock;
        char buffer[256];
 
        if (!home) return NULL;
 
+       rad_assert(home->server == NULL); /* we only open real sockets */
+
        if ((home->limit.max_connections > 0) &&
            (home->limit.num_connections >= home->limit.max_connections)) {
-               WDEBUG("Home server has too many open connections (%d)",
-                     home->limit.max_connections);
+               RATE_LIMIT(INFO("Home server %s has too many open connections (%d)",
+                               home->name, home->limit.max_connections));
+               return NULL;
+       }
+
+       now = time(NULL);
+       if (home->last_failed_open == now) {
+               WARN("Suppressing attempt to open socket to 'down' home server");
                return NULL;
        }
 
-       this = listen_alloc(mainconfig.config, RAD_LISTEN_PROXY);
+       this = listen_alloc(main_config.config, RAD_LISTEN_PROXY);
 
        sock = this->data;
        sock->other_ipaddr = home->ipaddr;
@@ -2622,13 +2620,17 @@ rad_listen_t *proxy_new_listener(home_server *home, int src_port)
        sock->my_port = src_port;
        sock->proto = home->proto;
 
+       /*
+        *      For error messages.
+        */
+       this->print(this, buffer, sizeof(buffer));
+
        if (debug_flag >= 2) {
-               this->print(this, buffer, sizeof(buffer));
-               DEBUG("Opening new %s", buffer);
+               DEBUG("Opening new proxy socket '%s'", buffer);
        }
 
 #ifdef WITH_TCP
-       sock->opened = sock->last_packet = time(NULL);
+       sock->opened = sock->last_packet = now;
 
        if (home->proto == IPPROTO_TCP) {
                this->recv = proxy_socket_tcp_recv;
@@ -2642,31 +2644,37 @@ rad_listen_t *proxy_new_listener(home_server *home, int src_port)
                 */
                this->fd = fr_tcp_client_socket(&home->src_ipaddr,
                                                &home->ipaddr, home->port);
-#ifdef WITH_TLS
-               if (home->tls) {
-                       DEBUG("Trying SSL to port %d\n", home->port);
-                       sock->ssn = tls_new_client_session(home->tls, this->fd);
-                       if (!sock->ssn) {
-                               listen_free(&this);
-                               return NULL;
-                       }
-
-                       this->recv = proxy_tls_recv;
-                       this->send = proxy_tls_send;
-               }
-#endif
        } else
 #endif
                this->fd = fr_socket(&home->src_ipaddr, src_port);
 
        if (this->fd < 0) {
                this->print(this, buffer,sizeof(buffer));
-               DEBUG("Failed opening client socket ::%s:: : %s",
+               ERROR("Failed opening proxy socket '%s' : %s",
                      buffer, fr_strerror());
+               home->last_failed_open = now;
                listen_free(&this);
                return NULL;
        }
 
+
+#ifdef WITH_TCP
+#ifdef WITH_TLS
+       if ((home->proto == IPPROTO_TCP) && home->tls) {
+               DEBUG("Trying SSL to port %d\n", home->port);
+               sock->ssn = tls_new_client_session(home->tls, this->fd);
+               if (!sock->ssn) {
+                       ERROR("Failed starting SSL to '%s'", buffer);
+                       home->last_failed_open = now;
+                       listen_free(&this);
+                       return NULL;
+               }
+
+               this->recv = proxy_tls_recv;
+               this->send = proxy_tls_send;
+       }
+#endif
+#endif
        /*
         *      Figure out which port we were bound to.
         */
@@ -2677,25 +2685,28 @@ rad_listen_t *proxy_new_listener(home_server *home, int src_port)
                memset(&src, 0, sizeof_src);
                if (getsockname(this->fd, (struct sockaddr *) &src,
                                &sizeof_src) < 0) {
-                       ERROR("Failed getting socket name: %s",
-                              strerror(errno));
+                       ERROR("Failed getting socket name for '%s': %s",
+                             buffer, fr_syserror(errno));
+                       home->last_failed_open = now;
                        listen_free(&this);
                        return NULL;
                }
 
                if (!fr_sockaddr2ipaddr(&src, sizeof_src,
                                        &sock->my_ipaddr, &sock->my_port)) {
-                       ERROR("Socket has unsupported address family");
+                       ERROR("Socket has unsupported address family for '%s'", buffer);
+                       home->last_failed_open = now;
                        listen_free(&this);
                        return NULL;
                }
        }
 
+       home->limit.num_connections++;
+
        return this;
 }
 #endif
 
-
 static const FR_NAME_NUMBER listen_compare[] = {
 #ifdef WITH_STATS
        { "status",     RAD_LISTEN_NONE },
@@ -2726,11 +2737,16 @@ static const FR_NAME_NUMBER listen_compare[] = {
        { NULL, 0 },
 };
 
+static int _free_proto_handle(lt_dlhandle *handle)
+{
+       dlclose(*handle);
+       return 0;
+}
 
 static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
 {
        int             type, rcode;
-       char            *listen_type;
+       char const      *listen_type;
        rad_listen_t    *this;
        CONF_PAIR       *cp;
        char const      *value;
@@ -2755,6 +2771,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
        handle = lt_dlopenext(buffer);
        if (handle) {
                fr_protocol_t   *proto;
+               lt_dlhandle     *marker;
 
                proto = dlsym(handle, buffer);
                if (!proto) {
@@ -2771,9 +2788,11 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
                memcpy(&master_listen[type], proto, sizeof(*proto));
 
                /*
-                *      And throw away the handle.
-                *      @todo: fix it later
+                *      Ensure handle gets closed if config section gets freed
                 */
+               marker = talloc(cs, lt_dlhandle);
+               *marker = handle;
+               talloc_set_destructor(marker, _free_proto_handle);
 
                if (master_listen[type].magic !=  RLM_MODULE_INIT) {
                        ERROR("Failed to load protocol '%s' due to internal sanity check problem",
@@ -2785,8 +2804,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
        cf_log_info(cs, "listen {");
 
        listen_type = NULL;
-       rcode = cf_item_parse(cs, "type", PW_TYPE_STRING_PTR,
-                             &listen_type, "");
+       rcode = cf_item_parse(cs, "type", FR_ITEM_POINTER(PW_TYPE_STRING, &listen_type), "");
        if (rcode < 0) return NULL;
        if (rcode == 1) {
                cf_log_err_cs(cs,
@@ -2807,12 +2825,7 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
         *      refer to a server.
         */
        if (!server) {
-               rcode = cf_item_parse(cs, "virtual_server", PW_TYPE_STRING_PTR,
-                                     &server, NULL);
-               if (rcode == 1) { /* compatiblity with 2.0-pre */
-                       rcode = cf_item_parse(cs, "server", PW_TYPE_STRING_PTR,
-                                             &server, NULL);
-               }
+               rcode = cf_item_parse(cs, "virtual_server", FR_ITEM_POINTER(PW_TYPE_STRING, &server), NULL);
                if (rcode < 0) return NULL;
        }
 
@@ -2857,29 +2870,6 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
        return this;
 }
 
-#ifdef WITH_PROXY
-static int is_loopback(fr_ipaddr_t const *ipaddr)
-{
-       /*
-        *      We shouldn't proxy on loopback.
-        */
-       if ((ipaddr->af == AF_INET) &&
-           (ipaddr->ipaddr.ip4addr.s_addr == htonl(INADDR_LOOPBACK))) {
-               return 1;
-       }
-
-#ifdef HAVE_STRUCT_SOCKADDR_IN6
-       if ((ipaddr->af == AF_INET6) &&
-           (IN6_IS_ADDR_LINKLOCAL(&ipaddr->ipaddr.ip6addr))) {
-               return 1;
-       }
-#endif
-
-       return 0;
-}
-#endif
-
-
 #ifdef HAVE_PTHREAD_H
 /*
  *     A child thread which does NOTHING other than read and process
@@ -2905,21 +2895,21 @@ static void *recv_thread(void *arg)
  */
 int listen_init(CONF_SECTION *config, rad_listen_t **head,
 #ifdef WITH_TLS
-               int spawn_flag
+               bool spawn_flag
 #else
-               UNUSED int spawn_flag
+               UNUSED bool spawn_flag
 #endif
-               )
+               )
 
 {
-       int             override = false;
+       bool            override = false;
        CONF_SECTION    *cs = NULL;
        rad_listen_t    **last;
        rad_listen_t    *this;
        fr_ipaddr_t     server_ipaddr;
-       int             auth_port = 0;
+       uint16_t        auth_port = 0;
 #ifdef WITH_PROXY
-       int             defined_proxy = 0;
+       bool            defined_proxy = false;
 #endif
 
        /*
@@ -2938,16 +2928,15 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
         *
         *      FIXME: If argv[0] == "vmpsd", then don't listen on auth/acct!
         */
-       if (mainconfig.port >= 0) {
-               auth_port = mainconfig.port;
+       if (main_config.port > 0) {
+               auth_port = main_config.port;
 
                /*
                 *      -p X but no -i Y on the command-line.
                 */
-               if ((mainconfig.port > 0) &&
-                   (mainconfig.myip.af == AF_UNSPEC)) {
+               if (main_config.myip.af == AF_UNSPEC) {
                        ERROR("The command-line says \"-p %d\", but there is no associated IP address to use",
-                              mainconfig.port);
+                             main_config.port);
                        return -1;
                }
        }
@@ -2956,10 +2945,10 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
         *      If the IP address was configured on the command-line,
         *      use that as the "bind_address"
         */
-       if (mainconfig.myip.af != AF_UNSPEC) {
+       if (main_config.myip.af != AF_UNSPEC) {
                listen_socket_t *sock;
 
-               memcpy(&server_ipaddr, &mainconfig.myip,
+               memcpy(&server_ipaddr, &main_config.myip,
                       sizeof(server_ipaddr));
                override = true;
 
@@ -2993,8 +2982,8 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
                auth_port = sock->my_port;      /* may have been updated in listen_bind */
                if (override) {
                        cs = cf_section_sub_find_name2(config, "server",
-                                                      mainconfig.name);
-                       if (cs) this->server = mainconfig.name;
+                                                      main_config.name);
+                       if (cs) this->server = main_config.name;
                }
 
                *last = this;
@@ -3042,8 +3031,8 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
 
                if (override) {
                        cs = cf_section_sub_find_name2(config, "server",
-                                                      mainconfig.name);
-                       if (cs) this->server = mainconfig.name;
+                                                      main_config.name);
+                       if (cs) this->server = main_config.name;
                }
 
                *last = this;
@@ -3055,12 +3044,12 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head,
         *      They specified an IP on the command-line, ignore
         *      all listen sections except the one in '-n'.
         */
-       if (mainconfig.myip.af != AF_UNSPEC) {
+       if (main_config.myip.af != AF_UNSPEC) {
                CONF_SECTION *subcs;
                char const *name2 = cf_section_name2(cs);
 
                cs = cf_section_sub_find_name2(config, "server",
-                                              mainconfig.name);
+                                              main_config.name);
                if (!cs) goto add_sockets;
 
                /*
@@ -3129,7 +3118,7 @@ add_sockets:
         *      proxying is pointless.
         */
        if (!*head) {
-               ERROR("The server is not configured to listen on any ports.  Cannot start.");
+               ERROR("The server is not configured to listen on any ports.  Cannot start");
                return -1;
        }
 
@@ -3140,15 +3129,16 @@ add_sockets:
        for (this = *head; this != NULL; this = this->next) {
 #ifdef WITH_PROXY
                if (this->type == RAD_LISTEN_PROXY) {
-                       defined_proxy = 1;
+                       defined_proxy = true;
                }
 
 #endif
 
 #ifdef WITH_TLS
                if (!check_config && !spawn_flag && this->tls) {
-                       cf_log_err_cs(this->cs, "Threading must be enabled for TLS sockets to function properly.");
-                       cf_log_err_cs(this->cs, "You probably need to do 'radiusd -fxx -l stdout' for debugging");
+                       cf_log_err_cs(this->cs, "Threading must be enabled for TLS sockets to function properly");
+                       cf_log_err_cs(this->cs, "You probably need to do '%s -fxx -l stdout' for debugging",
+                                     progname);
                        return -1;
                }
 #endif
@@ -3159,11 +3149,9 @@ add_sockets:
                        }
 
                        if (this->workers) {
-#ifndef HAVE_PTHREAD_H
-                               WARN("Setting 'workers' requires 'synchronous'.  Disabling 'workers'");
-                               this->workers = 0;
-#else
-                               int i, rcode;
+#ifdef HAVE_PTHREAD_H
+                               int rcode;
+                               uint32_t i;
                                char buffer[256];
 
                                this->print(this, buffer, sizeof(buffer));
@@ -3177,15 +3165,19 @@ add_sockets:
                                        rcode = pthread_create(&id, 0, recv_thread, this);
                                        if (rcode != 0) {
                                                ERROR("Thread create failed: %s",
-                                                     strerror(rcode));
+                                                     fr_syserror(rcode));
                                                fr_exit(1);
                                        }
 
                                        DEBUG("Thread %d for %s\n", i, buffer);
                                }
+#else
+                               WARN("Setting 'workers' requires 'synchronous'.  Disabling 'workers'");
+                               this->workers = 0;
 #endif
+
                        } else {
-                               event_new_fd(this);
+                               radius_update_listener(this);
                        }
 
                }
@@ -3196,12 +3188,11 @@ add_sockets:
         *      Otherwise, don't do anything.
         */
 #ifdef WITH_PROXY
-       if ((mainconfig.proxy_requests == true) &&
+       if ((main_config.proxy_requests == true) &&
            !check_config &&
            (*head != NULL) && !defined_proxy) {
-               listen_socket_t *sock = NULL;
-               int             port = 0;
-               home_server     home;
+               uint16_t        port = 0;
+               home_server_t   home;
 
                memset(&home, 0, sizeof(home));
 
@@ -3210,39 +3201,7 @@ add_sockets:
                 */
                home.proto = IPPROTO_UDP;
                home.src_ipaddr = server_ipaddr;
-
-               /*
-                *      Find the first authentication port,
-                *      and use it
-                */
-               for (this = *head; this != NULL; this = this->next) {
-                       switch (this->type) {
-                       case RAD_LISTEN_AUTH:
-                               sock = this->data;
-
-                               if (is_loopback(&sock->my_ipaddr)) continue;
-
-                               if (home.src_ipaddr.af == AF_UNSPEC) {
-                                       home.src_ipaddr = sock->my_ipaddr;
-                               }
-                               port = sock->my_port + 2;
-                               break;
-#ifdef WITH_ACCT
-                       case RAD_LISTEN_ACCT:
-                               sock = this->data;
-
-                               if (is_loopback(&sock->my_ipaddr)) continue;
-
-                               if (home.src_ipaddr.af == AF_UNSPEC) {
-                                       home.src_ipaddr = sock->my_ipaddr;
-                               }
-                               port = sock->my_port + 1;
-                               break;
-#endif
-                       default:
-                               break;
-                       }
-               }
+               port = 0;
 
                /*
                 *      Address is still unspecified, use IPv4.
@@ -3261,11 +3220,7 @@ add_sockets:
                        return -1;
                }
 
-               if (!event_new_fd(this)) {
-                       listen_free(&this);
-                       listen_free(head);
-                       return -1;
-               }
+               radius_update_listener(this);
        }
 #endif
 
@@ -3299,12 +3254,11 @@ void listen_free(rad_listen_t **head)
 }
 
 #ifdef WITH_STATS
-RADCLIENT_LIST *listener_find_client_list(fr_ipaddr_t const *ipaddr,
-                                         int port)
+RADCLIENT_LIST *listener_find_client_list(fr_ipaddr_t const *ipaddr, uint16_t port)
 {
        rad_listen_t *this;
 
-       for (this = mainconfig.listen; this != NULL; this = this->next) {
+       for (this = main_config.listen; this != NULL; this = this->next) {
                listen_socket_t *sock;
 
                if ((this->type != RAD_LISTEN_AUTH)
@@ -3325,11 +3279,11 @@ RADCLIENT_LIST *listener_find_client_list(fr_ipaddr_t const *ipaddr,
 }
 #endif
 
-rad_listen_t *listener_find_byipaddr(fr_ipaddr_t const *ipaddr, int port, int proto)
+rad_listen_t *listener_find_byipaddr(fr_ipaddr_t const *ipaddr, uint16_t port, int proto)
 {
        rad_listen_t *this;
 
-       for (this = mainconfig.listen; this != NULL; this = this->next) {
+       for (this = main_config.listen; this != NULL; this = this->next) {
                listen_socket_t *sock;
 
                sock = this->data;
@@ -3344,7 +3298,7 @@ rad_listen_t *listener_find_byipaddr(fr_ipaddr_t const *ipaddr, int port, int pr
        /*
         *      Failed to find a specific one.  Find INADDR_ANY
         */
-       for (this = mainconfig.listen; this != NULL; this = this->next) {
+       for (this = main_config.listen; this != NULL; this = this->next) {
                listen_socket_t *sock;
 
                sock = this->data;
index 18a408e..d224dba 100644 (file)
 RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
 
 #ifdef HAVE_SYS_STAT_H
-#include <sys/stat.h>
+#  include <sys/stat.h>
 #endif
 
 #ifdef HAVE_SYSLOG_H
-#      include <syslog.h>
+#  include <syslog.h>
 #endif
 
+#include <sys/file.h>
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+bool rate_limit = true;
+
 /*
  * Logging facility names
  */
@@ -47,8 +56,8 @@ static const FR_NAME_NUMBER levels[] = {
        { ": Error: ",          L_ERR           },
        { ": WARNING: ",        L_DBG_WARN      },
        { ": ERROR: ",          L_DBG_ERR       },
-       { ": WARNING: ",        L_DBG_WARN2     },
-       { ": ERROR: ",          L_DBG_ERR2      },
+       { ": WARNING: ",        L_DBG_WARN_REQ  },
+       { ": ERROR: ",          L_DBG_ERR_REQ   },
        { NULL, 0 }
 };
 
@@ -67,8 +76,8 @@ static const FR_NAME_NUMBER colours[] = {
        { VTC_BOLD VTC_YELLOW,  L_WARN          },
        { VTC_BOLD VTC_RED,     L_DBG_ERR       },
        { VTC_BOLD VTC_YELLOW,  L_DBG_WARN      },
-       { VTC_BOLD VTC_RED,     L_DBG_ERR2      },
-       { VTC_BOLD VTC_YELLOW,  L_DBG_WARN2     },
+       { VTC_BOLD VTC_RED,     L_DBG_ERR_REQ   },
+       { VTC_BOLD VTC_YELLOW,  L_DBG_WARN_REQ  },
        { NULL, 0 }
 };
 
@@ -147,24 +156,173 @@ const FR_NAME_NUMBER log_str2dst[] = {
 
 bool log_dates_utc = false;
 
-
 fr_log_t default_log = {
        .colourise = true,
        .fd = STDOUT_FILENO,
-       .dest = L_DST_STDOUT,
+       .dst = L_DST_STDOUT,
        .file = NULL,
        .debug_file = NULL,
 };
 
+static int stderr_fd = -1;     //!< The original unmolested stderr file descriptor
+static int stdout_fd = -1;     //!< The original unmolested stdout file descriptor
+
+static char const spaces[] = "                                                                                                                        ";
+
+/** On fault, reset STDOUT and STDERR to something useful.
+ *
+ * @return 0
+ */
+static int _restore_std(UNUSED int sig)
+{
+       if ((stderr_fd > 0) && (stdout_fd > 0)) {
+               dup2(stderr_fd, STDOUT_FILENO);
+               dup2(stdout_fd, STDERR_FILENO);
+               return 0;
+       }
+
+       if (default_log.fd > 0) {
+               dup2(default_log.fd, STDOUT_FILENO);
+               dup2(default_log.fd, STDERR_FILENO);
+               return 0;
+       }
+
+       return 0;
+}
+
+/** Pass debug logging through to vradlog
+ *
+ */
+static void CC_HINT(format (printf, 1, 2)) _radlog_info(char const *msg, ...)
+{
+       va_list ap;
+
+       va_start(ap, msg);
+       vradlog(L_INFO, msg, ap);
+       va_end(ap);
+}
+
+/** Initialise file descriptors based on logging destination
+ *
+ * @param log Logger to manipulate.
+ * @param daemonize Whether the server is starting as a daemon.
+ * @return 0 on success -1 on failure.
+ */
+int radlog_init(fr_log_t *log, bool daemonize)
+{
+       int devnull;
+
+       rate_limit = daemonize;
+
+       /*
+        *      If we're running in foreground mode, save STDIN /
+        *      STDERR as higher FDs, which won't get used by anyone
+        *      else.  When we fork/exec a program, it's STD FDs will
+        *      get set to pipes.  We later set STDOUT / STDERR to
+        *      /dev/null, so that any library trying to write to them
+        *      doesn't screw anything up.
+        *
+        *      Then, when something goes wrong, restore them so that
+        *      any debugger called from the panic action has access
+        *      to STDOUT / STDERR.
+        */
+       if (!daemonize) {
+               fr_fault_set_cb(_restore_std);
+
+               stdout_fd = dup(STDOUT_FILENO);
+               stderr_fd = dup(STDERR_FILENO);
+       }
+
+       devnull = open("/dev/null", O_RDWR);
+       if (devnull < 0) {
+               fr_strerror_printf("Error opening /dev/null: %s", fr_syserror(errno));
+               return -1;
+       }
+
+       /*
+        *      STDOUT & STDERR go to /dev/null, unless we have "-x",
+        *      then STDOUT & STDERR go to the "-l log" destination.
+        *
+        *      The complexity here is because "-l log" can go to
+        *      STDOUT or STDERR, too.
+        */
+       if (log->dst == L_DST_STDOUT) {
+               setlinebuf(stdout);
+               log->fd = STDOUT_FILENO;
+
+               /*
+                *      If we're debugging, allow STDERR to go to
+                *      STDOUT too, for executed programs,
+                */
+               if (debug_flag) {
+                       dup2(STDOUT_FILENO, STDERR_FILENO);
+               } else {
+                       dup2(devnull, STDERR_FILENO);
+               }
+
+       } else if (log->dst == L_DST_STDERR) {
+               setlinebuf(stderr);
+               log->fd = STDERR_FILENO;
+
+               /*
+                *      If we're debugging, allow STDOUT to go to
+                *      STDERR too, for executed programs,
+                */
+               if (debug_flag) {
+                       dup2(STDERR_FILENO, STDOUT_FILENO);
+               } else {
+                       dup2(devnull, STDOUT_FILENO);
+               }
+
+       } else if (log->dst == L_DST_SYSLOG) {
+               /*
+                *      Discard STDOUT and STDERR no matter what the
+                *      status of debugging.  Syslog isn't a file
+                *      descriptor, so we can't use it.
+                */
+               dup2(devnull, STDOUT_FILENO);
+               dup2(devnull, STDERR_FILENO);
+
+       } else if (debug_flag) {
+               /*
+                *      If we're debugging, allow STDOUT and STDERR to
+                *      go to the log file.
+                */
+               dup2(log->fd, STDOUT_FILENO);
+               dup2(log->fd, STDERR_FILENO);
+
+       } else {
+               /*
+                *      Not debugging, and the log isn't STDOUT or
+                *      STDERR.  Ensure that we move both of them to
+                *      /dev/null, so that the calling terminal can
+                *      exit, and the output from executed programs
+                *      doesn't pollute STDOUT / STDERR.
+                */
+               dup2(devnull, STDOUT_FILENO);
+               dup2(devnull, STDERR_FILENO);
+       }
+
+       close(devnull);
+
+       /*
+        *      This handles setting up all the talloc logging
+        *      and callbacks too.
+        */
+       fr_fault_set_log_fn(_radlog_info);
+       fr_fault_set_log_fd(log->fd);
+
+       return 0;
+}
+
 /*
  *     Log the message to the logfile. Include the severity and
  *     a time stamp.
  */
-DIAG_OFF(format-nonliteral)
 int vradlog(log_type_t type, char const *fmt, va_list ap)
 {
        unsigned char *p;
-       char buffer[8192];
+       char buffer[10240];     /* The largest config item size, then extra for prefixes and suffixes */
        char *unsan;
        size_t len;
        int colourise = default_log.colourise;
@@ -182,7 +340,7 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
         *      If we don't want any messages, then
         *      throw them away.
         */
-       if (default_log.dest == L_DST_NULL) {
+       if (default_log.dst == L_DST_NULL) {
                return 0;
        }
 
@@ -190,9 +348,10 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
        len = 0;
 
        if (colourise) {
-               len += strlcpy(buffer + len, fr_int2str(colours, type, ""),
-                              sizeof(buffer) - len) ;
-               if (len == 0) colourise = false;
+               len += strlcpy(buffer + len, fr_int2str(colours, type, ""), sizeof(buffer) - len) ;
+               if (len == 0) {
+                       colourise = false;
+               }
        }
 
        /*
@@ -202,36 +361,36 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
 
        /*
         *      Don't print timestamps to syslog, it does that for us.
-        *      Don't print timestamps for low levels of debugging.
+        *      Don't print timestamps and error types for low levels
+        *      of debugging.
         *
         *      Print timestamps for non-debugging, and for high levels
         *      of debugging.
         */
-       if ((default_log.dest != L_DST_SYSLOG) &&
-           (debug_flag != 1) && (debug_flag != 2)) {
-               time_t timeval;
-
-               timeval = time(NULL);
-               CTIME_R(&timeval, buffer + len, sizeof(buffer) - len - 1);
-
-               len = strlen(buffer);
-
-               len += strlcpy(buffer + len,
-                              fr_int2str(levels, type, ": "),
-                              sizeof(buffer) - len);
-       }
-
-       switch (type) {
-       case L_DBG_WARN:
-               len += strlcpy(buffer + len, "WARNING: ", sizeof(buffer) - len);
-               break;
+       if (default_log.dst != L_DST_SYSLOG) {
+               if ((debug_flag != 1) && (debug_flag != 2)) {
+                       time_t timeval;
 
-       case L_DBG_ERR:
-               len += strlcpy(buffer + len, "ERROR: ", sizeof(buffer) - len);
-               break;
+                       timeval = time(NULL);
+                       CTIME_R(&timeval, buffer + len, sizeof(buffer) - len - 1);
 
-       default:
-               break;
+                       len = strlen(buffer);
+                       len += strlcpy(buffer + len, fr_int2str(levels, type, ": "), sizeof(buffer) - len);
+               } else goto add_prefix;
+       } else {
+       add_prefix:
+               if (len < sizeof(buffer)) switch (type) {
+               case L_DBG_WARN:
+                       len += strlcpy(buffer + len, "WARNING: ", sizeof(buffer) - len);
+                       break;
+
+               case L_DBG_ERR:
+                       len += strlcpy(buffer + len, "ERROR: ", sizeof(buffer) - len);
+                       break;
+
+               default:
+                       break;
+               }
        }
 
        if (len < sizeof(buffer)) {
@@ -239,14 +398,29 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
        }
 
        /*
-        *      Filter out characters not in Latin-1.
+        *      Filter out control chars and non UTF8 chars
         */
        for (p = (unsigned char *)unsan; *p != '\0'; p++) {
-               if (*p == '\r' || *p == '\n')
+               int clen;
+
+               switch (*p) {
+               case '\r':
+               case '\n':
                        *p = ' ';
-               else if (*p == '\t') continue;
-               else if (*p < 32 || (*p >= 128 && *p <= 160))
-                       *p = '?';
+                       break;
+
+               case '\t':
+                       continue;
+
+               default:
+                       clen = fr_utf8_char(p);
+                       if (!clen) {
+                               *p = '?';
+                               continue;
+                       }
+                       p += (clen - 1);
+                       break;
+               }
        }
 
        if (colourise && (len < sizeof(buffer))) {
@@ -261,30 +435,30 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
                buffer[sizeof(buffer) - 1] = '\0';
        }
 
-       switch (default_log.dest) {
+       switch (default_log.dst) {
 
 #ifdef HAVE_SYSLOG_H
        case L_DST_SYSLOG:
                switch(type) {
-                       case L_DBG:
-                       case L_WARN:
-                       case L_DBG_WARN:
-                       case L_DBG_WARN2:
-                       case L_DBG_ERR:
-                       case L_DBG_ERR2:
-                               type = LOG_DEBUG;
-                               break;
-                       case L_AUTH:
-                       case L_PROXY:
-                       case L_ACCT:
-                               type = LOG_NOTICE;
-                               break;
-                       case L_INFO:
-                               type = LOG_INFO;
-                               break;
-                       case L_ERR:
-                               type = LOG_ERR;
-                               break;
+               case L_DBG:
+               case L_WARN:
+               case L_DBG_WARN:
+               case L_DBG_ERR:
+               case L_DBG_ERR_REQ:
+               case L_DBG_WARN_REQ:
+                       type = LOG_DEBUG;
+                       break;
+               case L_AUTH:
+               case L_PROXY:
+               case L_ACCT:
+                       type = LOG_NOTICE;
+                       break;
+               case L_INFO:
+                       type = LOG_INFO;
+                       break;
+               case L_ERR:
+                       type = LOG_ERR;
+                       break;
                }
                syslog(type, "%s", buffer);
                break;
@@ -302,7 +476,6 @@ int vradlog(log_type_t type, char const *fmt, va_list ap)
 
        return 0;
 }
-DIAG_ON(format-nonliteral)
 
 int radlog(log_type_t type, char const *msg, ...)
 {
@@ -323,14 +496,28 @@ void vp_listdebug(VALUE_PAIR *vp)
 {
        vp_cursor_t cursor;
        char tmpPair[70];
-       for (vp = paircursor(&cursor, &vp);
+       for (vp = fr_cursor_init(&cursor, &vp);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                vp_prints(tmpPair, sizeof(tmpPair), vp);
                DEBUG2("     %s", tmpPair);
        }
 }
 
+inline bool debug_enabled(log_type_t type, log_debug_t lvl)
+{
+       if ((type & L_DBG) && (debug_flag != 0) && (lvl > debug_flag)) return true;
+
+       return false;
+}
+
+inline bool rate_limit_enabled(void)
+{
+       if (rate_limit || (debug_flag < 1)) return true;
+
+       return false;
+}
+
 inline bool radlog_debug_enabled(log_type_t type, log_debug_t lvl, REQUEST *request)
 {
        /*
@@ -344,25 +531,25 @@ inline bool radlog_debug_enabled(log_type_t type, log_debug_t lvl, REQUEST *requ
         *      then don't log the message.
         */
        if ((type & L_DBG) &&
-           ((request && request->radlog && (lvl > request->options)) ||
+           ((request && request->log.func && (lvl > request->log.lvl)) ||
             ((debug_flag != 0) && (lvl > debug_flag)))) {
-               return false;
+               return false;
        }
 
        return true;
 }
 
-void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, ...)
+void vradlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, va_list ap)
 {
        size_t len = 0;
        char const *filename = default_log.file;
        FILE *fp = NULL;
-       va_list ap;
-       char buffer[8192];
+       char buffer[10240];     /* The largest config item size, then extra for prefixes and suffixes */
        char *p;
        char const *extra = "";
+       va_list aq;
 
-       va_start(ap, msg);
+       rad_assert(request);
 
        /*
         *      Debug messages get treated specially.
@@ -370,7 +557,6 @@ void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char con
        if ((type & L_DBG) != 0) {
 
                if (!radlog_debug_enabled(type, lvl, request)) {
-                       va_end(ap);
                        return;
                }
 
@@ -387,9 +573,9 @@ void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char con
        }
 
        if (request && filename) {
-               radlog_func_t rl = request->radlog;
+               radlog_func_t rl = request->log.func;
 
-               request->radlog = NULL;
+               request->log.func = NULL;
 
                /*
                 *      This is SLOW!  Doing it for every log message
@@ -398,17 +584,15 @@ void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char con
 
                 /* FIXME: escape chars! */
                if (radius_xlat(buffer, sizeof(buffer), request, filename, NULL, NULL) < 0) {
-                       va_end(ap);
                        return;
                }
-               request->radlog = rl;
+               request->log.func = rl;
 
                p = strrchr(buffer, FR_DIR_SEP);
                if (p) {
                        *p = '\0';
                        if (rad_mkdir(buffer, S_IRWXU) < 0) {
-                               ERROR("Failed creating %s: %s", buffer, strerror(errno));
-                               va_end(ap);
+                               ERROR("Failed creating %s: %s", buffer, fr_syserror(errno));
                                return;
                        }
                        *p = FR_DIR_SEP;
@@ -434,6 +618,7 @@ void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char con
                {
                        CTIME_R(&timeval, buffer, sizeof(buffer) - 1);
                }
+
                len = strlen(buffer);
                p = strrchr(buffer, '\n');
                if (p) {
@@ -441,85 +626,487 @@ void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char con
                        p[1] = '\0';
                }
 
-               len += strlcpy(buffer + len,
-                              fr_int2str(levels, type, ": "),
-                              sizeof(buffer) - len);
-
+               len += strlcpy(buffer + len, fr_int2str(levels, type, ": "), sizeof(buffer) - len);
                if (len >= sizeof(buffer)) goto finish;
        }
 
        if (request && request->module[0]) {
-               len = snprintf(buffer + len, sizeof(buffer) - len, "%s : ",
-                              request->module);
-
+               len = snprintf(buffer + len, sizeof(buffer) - len, "%s : ", request->module);
                if (len >= sizeof(buffer)) goto finish;
        }
 
-       vsnprintf(buffer + len, sizeof(buffer) - len, msg, ap);
+       /*
+        *  If we don't copy the original ap we get a segfault from vasprintf. This is apparently
+        *  due to ap sometimes being implemented with a stack offset which is invalidated if
+        *  ap is passed into another function. See here:
+        *  http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
+        *
+        *  I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
+        *  running unit tests which generate errors under CI.
+        */
+       va_copy(aq, ap);
+       vsnprintf(buffer + len, sizeof(buffer) - len, msg, aq);
+       va_end(aq);
 
        finish:
        switch (type) {
        case L_DBG_WARN:
                extra = "WARNING: ";
-               type = L_DBG_WARN2;
+               type = L_DBG_WARN_REQ;
                break;
 
        case L_DBG_ERR:
                extra = "ERROR: ";
-               type = L_DBG_ERR2;
+               type = L_DBG_ERR_REQ;
                break;
        default:
                break;
        }
 
        if (!fp) {
+
+               if (debug_flag > 2) extra = "";
+
                if (request) {
-                       radlog(type, "(%u) %s%s", request->number, extra, buffer);
+                       uint8_t indent;
+
+                       indent = request->log.indent > sizeof(spaces) ?
+                                sizeof(spaces) :
+                                request->log.indent;
+                       radlog(type, "(%u) %.*s%s%s", request->number, indent, spaces, extra, buffer);
                } else {
                        radlog(type, "%s%s", extra, buffer);
                }
        } else {
-               if (request) fprintf(fp, "(%u) ", request->number);
+               if (request) {
+                       fprintf(fp, "(%u) %s", request->number, extra);
+               }
                fputs(buffer, fp);
                fputc('\n', fp);
                fclose(fp);
        }
+}
+
+/** Martial variadic log arguments into a va_list and pass to normal logging functions
+ *
+ * @see radlog_request_error for more details.
+ *
+ * @param type the log category.
+ * @param lvl of debugging this message should be displayed at.
+ * @param request The current request.
+ * @param msg format string.
+ */
+void radlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, ...)
+{
+       va_list ap;
 
+       rad_assert(request);
+
+       if (request->log.func == NULL) return;
+
+       va_start(ap, msg);
+       request->log.func(type, lvl, request, msg, ap);
        va_end(ap);
 }
 
-void log_talloc(char const *msg)
+/** Martial variadic log arguments into a va_list and pass to error logging functions
+ *
+ * This could all be done in a macro, but it turns out some implementations of the
+ * variadic macros do not work at all well if the va_list being written to is further
+ * up the stack (which is required as you still need a function to convert the elipses
+ * into a va_list).
+ *
+ * So, we use this small wrapper function instead, which will hopefully guarantee
+ * consistent behaviour.
+ *
+ * @param type the log category.
+ * @param lvl of debugging this message should be displayed at.
+ * @param request The current request.
+ * @param msg format string.
+ */
+void radlog_request_error(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, ...)
 {
-       INFO("%s", msg);
+       va_list ap;
+
+       rad_assert(request);
+
+       va_start(ap, msg);
+       if (request->log.func) {
+               request->log.func(type, lvl, request, msg, ap);
+       }
+       vmodule_failure_msg(request, msg, ap);
+       va_end(ap);
 }
 
-void log_talloc_report(TALLOC_CTX *ctx)
+/** Parse error, write out string we were parsing, and a message indicating the error
+ *
+ * @param type the log category.
+ * @param lvl of debugging this message should be displayed at.
+ * @param request The current request.
+ * @param fmt string we were parsing.
+ * @param idx The position of the marker relative to the string.
+ * @param error What the parse error was.
+ */
+void radlog_request_marker(log_type_t type, log_debug_t lvl, REQUEST *request,
+                          char const *fmt, size_t idx, char const *error)
 {
-       FILE *fd;
-       char const *null_ctx = NULL;
-       int i = 0;
+       char const *prefix = "";
+       uint8_t indent;
+
+       rad_assert(request);
+
+       if (idx >= sizeof(spaces)) {
+               size_t offset = (idx - (sizeof(spaces) - 1)) + (sizeof(spaces) * 0.75);
+               idx -= offset;
+               fmt += offset;
 
-       if (ctx) {
-               null_ctx = talloc_get_name(NULL);
+               prefix = "... ";
        }
 
-       fd = fdopen(default_log.fd, "w");
-       if (!fd) {
-               ERROR("Couldn't write memory report, fdopen failed: %s", strerror(errno));
+       /*
+        *  Don't want format markers being indented
+        */
+       indent = request->log.indent;
+       request->log.indent = 0;
+
+       radlog_request(type, lvl, request, "%s%s", prefix, fmt);
+       radlog_request(type, lvl, request, "%s%.*s^ %s", prefix, (int) idx, spaces, error);
+
+       request->log.indent = indent;
+}
+
+typedef struct fr_logfile_entry_t {
+       int             fd;
+       int             dup;
+       uint32_t        hash;
+       time_t          last_used;
+       char            *filename;
+} fr_logfile_entry_t;
+
+
+struct fr_logfile_t {
+       uint32_t max_entries;
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_t mutex;
+#endif
+       fr_logfile_entry_t *entries;
+};
+
 
-               return;
+#ifdef HAVE_PTHREAD_H
+#define PTHREAD_MUTEX_LOCK pthread_mutex_lock
+#define PTHREAD_MUTEX_UNLOCK 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 int _logfile_free(fr_logfile_t *lf)
+{
+       uint32_t i;
+
+       PTHREAD_MUTEX_LOCK(&lf->mutex);
+
+       for (i = 0; i < lf->max_entries; i++) {
+               if (!lf->entries[i].filename) continue;
+
+               close(lf->entries[i].fd);
        }
 
-       if (!ctx) {
-               talloc_report_full(NULL, fd);
-       } else {
-               do {
-                       INFO("Context level %i", i++);
+       PTHREAD_MUTEX_UNLOCK(&lf->mutex);
+
+#ifdef HAVE_PTHREAD_H
+       pthread_mutex_destroy(&lf->mutex);
+#endif
+
+       return 0;
+}
+
 
-                       talloc_report_full(ctx, fd);
-               } while ((ctx = talloc_parent(ctx)) && (talloc_get_name(ctx) != null_ctx));  /* Stop before we hit NULL ctx */
+/** Initialize a way for multiple threads to log to one or more files.
+ *
+ * @param ctx The talloc context
+ * @return the new context, or NULL on error.
+ */
+fr_logfile_t *fr_logfile_init(TALLOC_CTX *ctx)
+{
+       fr_logfile_t *lf;
+
+       lf = talloc_zero(ctx, fr_logfile_t);
+       if (!lf) return NULL;
+
+       lf->entries = talloc_zero_array(lf, fr_logfile_entry_t, 64);
+       if (!lf->entries) {
+               talloc_free(lf);
+               return NULL;
        }
 
-       fclose(fd);
+#ifdef HAVE_PTHREAD_H
+       if (pthread_mutex_init(&lf->mutex, NULL) != 0) {
+               talloc_free(lf);
+               return NULL;
+       }
+#endif
+
+       lf->max_entries = 64;
+
+       talloc_set_destructor(lf, _logfile_free);
+
+       return lf;
 }
 
+
+/** Open a new log file, or maybe an existing one.
+ *
+ * When multithreaded, the FD is locked via a mutex.  This way we're
+ * sure that no other thread is writing to the file.
+ *
+ * @param lf The logfile context returned from fr_logfile_init().
+ * @param filename the file to open.
+ * @param permissions to use.
+ * @return an FD used to write to the file, or -1 on error.
+ */
+int fr_logfile_open(fr_logfile_t *lf, char const *filename, mode_t permissions)
+{
+       uint32_t i;
+       uint32_t hash;
+       time_t now = time(NULL);
+       struct stat st;
+
+       if (!lf || !filename) return -1;
+
+       hash = fr_hash_string(filename);
+
+       PTHREAD_MUTEX_LOCK(&lf->mutex);
+
+       /*
+        *      Clean up old entries.
+        */
+       for (i = 0; i < lf->max_entries; i++) {
+               if (!lf->entries[i].filename) continue;
+
+               /*
+                *      FIXME: make this configurable?
+                */
+               if ((lf->entries[i].last_used + 30) < now) {
+                       /*
+                        *      This will block forever if a thread is
+                        *      doing something stupid.
+                        */
+                       TALLOC_FREE(lf->entries[i].filename);
+                       close(lf->entries[i].fd);
+               }
+       }
+
+       /*
+        *      Find the matching entry.
+        */
+       for (i = 0; i < lf->max_entries; i++) {
+               if (!lf->entries[i].filename) continue;
+
+               if (lf->entries[i].hash == hash) {
+                       /*
+                        *      Same hash but different filename.  Give up.
+                        */
+                       if (strcmp(lf->entries[i].filename, filename) != 0) {
+                               PTHREAD_MUTEX_UNLOCK(&lf->mutex);
+                               return -1;
+                       }
+                       /*
+                        *      Someone else failed to create the entry.
+                        */
+                       if (!lf->entries[i].filename) {
+                               PTHREAD_MUTEX_UNLOCK(&lf->mutex);
+                               return -1;
+                       }
+                       goto do_return;
+               }
+       }
+
+       /*
+        *      Find an unused entry
+        */
+       for (i = 0; i < lf->max_entries; i++) {
+               if (!lf->entries[i].filename) break;
+       }
+
+       if (i >= lf->max_entries) {
+               fr_strerror_printf("Too many different filenames");
+               PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
+               return -1;
+       }
+
+       /*
+        *      Create a new entry.
+        */
+
+       lf->entries[i].hash = hash;
+       lf->entries[i].filename = talloc_strdup(lf->entries, filename);
+       lf->entries[i].fd = -1;
+
+       lf->entries[i].fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, permissions);
+       if (lf->entries[i].fd < 0) {
+               mode_t dirperm;
+               char *p, *dir;
+
+               /*
+                *      Maybe the directory doesn't exist.  Try to
+                *      create it.
+                */
+               dir = talloc_strdup(lf, filename);
+               if (!dir) goto error;
+               p = strrchr(dir, FR_DIR_SEP);
+               if (!p) {
+                       fr_strerror_printf("No '/' in '%s'", filename);
+                       goto error;
+               }
+               *p = '\0';
+
+               /*
+                *      Ensure that the 'x' bit is set, so that we can
+                *      read the directory.
+                */
+               dirperm = permissions;
+               if ((dirperm & 0600) != 0) dirperm |= 0100;
+               if ((dirperm & 0060) != 0) dirperm |= 0010;
+               if ((dirperm & 0006) != 0) dirperm |= 0001;
+
+               if (rad_mkdir(dir, dirperm) < 0) {
+                       fr_strerror_printf("Failed to create directory %s: %s",
+                                          dir, strerror(errno));
+                       talloc_free(dir);
+                       goto error;
+               }
+               talloc_free(dir);
+
+               lf->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
+               if (lf->entries[i].fd < 0) {
+                       fr_strerror_printf("Failed to open file %s: %s",
+                                          filename, strerror(errno));
+                       goto error;
+               } /* else fall through to creating the rest of the entry */
+       } /* else the file was already opened */
+
+do_return:
+       /*
+        *      Lock from the start of the file.
+        */
+       if (lseek(lf->entries[i].fd, 0, SEEK_SET) < 0) {
+               fr_strerror_printf("Failed to seek in file %s: %s",
+                                  filename, strerror(errno));
+
+       error:
+               lf->entries[i].hash = 0;
+               TALLOC_FREE(lf->entries[i].filename);
+               close(lf->entries[i].fd);
+               lf->entries[i].fd = -1;
+
+               PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
+               return -1;
+       }
+
+       if (rad_lockfd(lf->entries[i].fd, 0) < 0) {
+               fr_strerror_printf("Failed to lock file %s: %s",
+                                  filename, strerror(errno));
+               goto error;
+       }
+
+       /*
+        *      Maybe someone deleted the file while we were waiting
+        *      for the lock.  If so, re-open it.
+        */
+       if (fstat(lf->entries[i].fd, &st) < 0) {
+               fr_strerror_printf("Failed to stat file %s: %s",
+                                  filename, strerror(errno));
+               goto error;
+       }
+
+       if (st.st_nlink == 0) {
+               close(lf->entries[i].fd);
+               lf->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
+               if (lf->entries[i].fd < 0) {
+                       fr_strerror_printf("Failed to open file %s: %s",
+                                          filename, strerror(errno));
+                       goto error;
+               }
+       }
+
+       /*
+        *      Seek to the end of the file before returning the FD to
+        *      the caller.
+        */
+       lseek(lf->entries[i].fd, 0, SEEK_END);
+
+       /*
+        *      Return holding the mutex for the entry.
+        */
+       lf->entries[i].last_used = now;
+       lf->entries[i].dup = dup(lf->entries[i].fd);
+       if (lf->entries[i].dup < 0) {
+               fr_strerror_printf("Failed calling dup(): %s",
+                                  strerror(errno));
+               goto error;
+       }
+
+       return lf->entries[i].dup;
+}
+
+/** Close the log file.  Really just return it to the pool.
+ *
+ * When multithreaded, the FD is locked via a mutex.  This way we're
+ * sure that no other thread is writing to the file.  This function
+ * will unlock the mutex, so that other threads can write to the file.
+ *
+ * @param lf The logfile context returned from fr_logfile_init()
+ * @param fd the FD to close (i.e. return to the pool)
+ * @return 0 on success, or -1 on error
+ */
+int fr_logfile_close(fr_logfile_t *lf, int fd)
+{
+       uint32_t i;
+
+       for (i = 0; i < lf->max_entries; i++) {
+               if (!lf->entries[i].filename) continue;
+
+               /*
+                *      Unlock the bytes that we had previously locked.
+                */
+               if (lf->entries[i].dup == fd) {
+                       (void) rad_unlockfd(lf->entries[i].dup, 0);
+                       close(lf->entries[i].dup); /* releases the fcntl lock */
+                       lf->entries[i].dup = -1;
+
+                       PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
+                       return 0;
+               }
+       }
+
+       PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
+
+       fr_strerror_printf("Attempt to unlock file which does not exist");
+       return -1;
+}
+
+int fr_logfile_unlock(fr_logfile_t *lf, int fd)
+{
+       uint32_t i;
+
+       for (i = 0; i < lf->max_entries; i++) {
+               if (!lf->entries[i].filename) continue;
+
+               if (lf->entries[i].dup == fd) {
+                       lf->entries[i].dup = -1;
+                       PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
+                       return 0;
+               }
+       }
+
+       PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
+
+       fr_strerror_printf("Attempt to unlock file which does not exist");
+       return -1;
+}
index c62b5ba..a876a8b 100644 (file)
@@ -29,10 +29,6 @@ RCSID("$Id$")
 
 #include <sys/stat.h>
 
-#ifdef HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif
-
 #ifdef HAVE_PWD_H
 #include <pwd.h>
 #endif
@@ -41,10 +37,6 @@ RCSID("$Id$")
 #include <grp.h>
 #endif
 
-#ifdef HAVE_SYS_PRCTL_H
-#include <sys/prctl.h>
-#endif
-
 #ifdef HAVE_SYSLOG_H
 #      include <syslog.h>
 #endif
@@ -57,9 +49,9 @@ RCSID("$Id$")
 #include <fcntl.h>
 #endif
 
-struct main_config_t mainconfig;
+struct main_config_t main_config;
 char *debug_condition = NULL;
-extern int log_dates_utc;
+extern bool log_dates_utc;
 
 typedef struct cached_config_t {
        struct cached_config_t *next;
@@ -91,20 +83,23 @@ static char const *radlog_dest = NULL;
  */
 static char const      *localstatedir = NULL;
 static char const      *prefix = NULL;
-static char            my_name;
+static char const      *my_name = NULL;
 static char const      *sbindir = NULL;
 static char const      *run_dir = NULL;
-static char            *syslog_facility = NULL;
+static char const      *syslog_facility = NULL;
 static bool            do_colourise = false;
 
+static char const      *radius_dir = NULL;     //!< Path to raddb directory
+
 
 /*
  *  Security configuration for the server.
  */
 static const CONF_PARSER security_config[] = {
-       { "max_attributes",  PW_TYPE_INTEGER, 0, &fr_max_attributes, STRINGIFY(0) },
-       { "reject_delay",  PW_TYPE_INTEGER, 0, &mainconfig.reject_delay, STRINGIFY(0) },
-       { "status_server", PW_TYPE_BOOLEAN, 0, &mainconfig.status_server, "no"},
+       { "max_attributes",  FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
+       { "reject_delay",  FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.reject_delay), STRINGIFY(0) },
+       { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
+       { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -113,33 +108,35 @@ static const CONF_PARSER security_config[] = {
  *     Logging configuration for the server.
  */
 static const CONF_PARSER logdest_config[] = {
-       { "destination",  PW_TYPE_STRING_PTR, 0, &radlog_dest, "files" },
-       { "syslog_facility",  PW_TYPE_STRING_PTR, 0, &syslog_facility, STRINGIFY(0) },
+       { "destination",  FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), "files" },
+       { "syslog_facility",  FR_CONF_POINTER(PW_TYPE_STRING, &syslog_facility), STRINGIFY(0) },
 
-       { "file", PW_TYPE_STRING_PTR, 0, &mainconfig.log_file, "${logdir}/radius.log" },
-       { "requests", PW_TYPE_STRING_PTR, 0, &default_log.file, NULL },
+       { "file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), "${logdir}/radius.log" },
+       { "requests",  FR_CONF_POINTER(PW_TYPE_STRING, &default_log.file), NULL },
        { NULL, -1, 0, NULL, NULL }
 };
 
 
 static const CONF_PARSER serverdest_config[] = {
-       { "log", PW_TYPE_SUBSECTION, 0, NULL, (void const *) logdest_config },
-       { "log_file", PW_TYPE_STRING_PTR, 0, &mainconfig.log_file, NULL },
-       { "log_destination", PW_TYPE_STRING_PTR, 0, &radlog_dest, NULL },
-       { "use_utc", PW_TYPE_BOOLEAN, 0, &log_dates_utc, NULL },
+       { "log",  FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) logdest_config },
+       { "log_file",  FR_CONF_POINTER(PW_TYPE_STRING, &main_config.log_file), NULL },
+       { "log_destination", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dest), NULL },
+       { "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
        { NULL, -1, 0, NULL, NULL }
 };
 
 
 static const CONF_PARSER log_config_nodest[] = {
-       { "stripped_names", PW_TYPE_BOOLEAN, 0, &log_stripped_names,"no" },
-       { "auth", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth, "no" },
-       { "auth_badpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_badpass, "no" },
-       { "auth_goodpass", PW_TYPE_BOOLEAN, 0, &mainconfig.log_auth_goodpass, "no" },
-       { "msg_badpass", PW_TYPE_STRING_PTR, 0, &mainconfig.auth_badpass_msg, NULL},
-       { "msg_goodpass", PW_TYPE_STRING_PTR, 0, &mainconfig.auth_goodpass_msg, NULL},
-       { "colourise", PW_TYPE_BOOLEAN, 0, &do_colourise, NULL },
-       { "use_utc", PW_TYPE_BOOLEAN, 0, &log_dates_utc, NULL },
+       { "stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_stripped_names),"no" },
+       { "auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth), "no" },
+       { "auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_badpass), "no" },
+       { "auth_goodpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.log_auth_goodpass), "no" },
+       { "msg_badpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_badpass_msg), NULL},
+       { "msg_goodpass", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.auth_goodpass_msg), NULL},
+       { "colourise",FR_CONF_POINTER(PW_TYPE_BOOLEAN, &do_colourise), NULL },
+       { "use_utc", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &log_dates_utc), NULL },
+       { "msg_denied", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.denied_msg),
+         "You are already logged in - access denied" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -156,30 +153,28 @@ static const CONF_PARSER server_config[] = {
         *      hard-coded defines for the locations of the various
         *      files.
         */
-       { "name",              PW_TYPE_STRING_PTR, 0, &my_name,   "radiusd"},
-       { "prefix",          PW_TYPE_STRING_PTR, 0, &prefix,        "/usr/local"},
-       { "localstatedir",      PW_TYPE_STRING_PTR, 0, &localstatedir,     "${prefix}/var"},
-       { "sbindir",        PW_TYPE_STRING_PTR, 0, &sbindir,        "${prefix}/sbin"},
-       { "logdir",          PW_TYPE_STRING_PTR, 0, &radlog_dir,        "${localstatedir}/log"},
-       { "run_dir",        PW_TYPE_STRING_PTR, 0, &run_dir,       "${localstatedir}/run/${name}"},
-       { "libdir",          PW_TYPE_STRING_PTR, 0, &radlib_dir,        "${prefix}/lib"},
-       { "radacctdir",  PW_TYPE_STRING_PTR, 0, &radacct_dir,       "${logdir}/radacct" },
-       { "hostname_lookups",   PW_TYPE_BOOLEAN,    0, &fr_dns_lookups,      "no" },
-       { "max_request_time", PW_TYPE_INTEGER, 0, &mainconfig.max_request_time, STRINGIFY(MAX_REQUEST_TIME) },
-       { "cleanup_delay", PW_TYPE_INTEGER, 0, &mainconfig.cleanup_delay, STRINGIFY(CLEANUP_DELAY) },
-       { "max_requests", PW_TYPE_INTEGER, 0, &mainconfig.max_requests, STRINGIFY(MAX_REQUESTS) },
-#ifdef DELETE_BLOCKED_REQUESTS
-       { "delete_blocked_requests", PW_TYPE_INTEGER, 0, &mainconfig.kill_unresponsive_children, STRINGIFY(false) },
-#endif
-       { "pidfile", PW_TYPE_STRING_PTR, 0, &mainconfig.pid_file, "${run_dir}/radiusd.pid"},
-       { "checkrad", PW_TYPE_STRING_PTR, 0, &mainconfig.checkrad, "${sbindir}/checkrad" },
-
-       { "debug_level", PW_TYPE_INTEGER, 0, &mainconfig.debug_level, "0"},
+       { "name", FR_CONF_POINTER(PW_TYPE_STRING, &my_name), "radiusd"},
+       { "prefix", FR_CONF_POINTER(PW_TYPE_STRING, &prefix), "/usr/local"},
+       { "localstatedir", FR_CONF_POINTER(PW_TYPE_STRING, &localstatedir), "${prefix}/var"},
+       { "sbindir", FR_CONF_POINTER(PW_TYPE_STRING, &sbindir), "${prefix}/sbin"},
+       { "logdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlog_dir), "${localstatedir}/log"},
+       { "run_dir", FR_CONF_POINTER(PW_TYPE_STRING, &run_dir), "${localstatedir}/run/${name}"},
+       { "libdir", FR_CONF_POINTER(PW_TYPE_STRING, &radlib_dir), "${prefix}/lib"},
+       { "radacctdir", FR_CONF_POINTER(PW_TYPE_STRING, &radacct_dir), "${logdir}/radacct" },
+       { "panic_action", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.panic_action), NULL},
+       { "hostname_lookups", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &fr_dns_lookups), "no" },
+       { "max_request_time", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_request_time), STRINGIFY(MAX_REQUEST_TIME) },
+       { "cleanup_delay", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.cleanup_delay), STRINGIFY(CLEANUP_DELAY) },
+       { "max_requests", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_requests), STRINGIFY(MAX_REQUESTS) },
+       { "pidfile", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.pid_file), "${run_dir}/radiusd.pid"},
+       { "checkrad", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.checkrad), "${sbindir}/checkrad" },
+
+       { "debug_level", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.debug_level), "0"},
 
 #ifdef WITH_PROXY
-       { "proxy_requests", PW_TYPE_BOOLEAN, 0, &mainconfig.proxy_requests, "yes" },
+       { "proxy_requests", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.proxy_requests), "yes" },
 #endif
-       { "log", PW_TYPE_SUBSECTION, 0, NULL, (void const *) log_config_nodest },
+       { "log", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) log_config_nodest },
 
        /*
         *      People with old configs will have these.  They are listed
@@ -189,39 +184,39 @@ static const CONF_PARSER server_config[] = {
         *      DON'T exist in radiusd.conf, then the previously parsed
         *      values for "log { foo = bar}" will be used.
         */
-       { "log_auth", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth, NULL },
-       { "log_auth_badpass", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth_badpass, NULL },
-       { "log_auth_goodpass", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &mainconfig.log_auth_goodpass, NULL },
-       { "log_stripped_names", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &log_stripped_names, NULL },
+       { "log_auth", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth), NULL },
+       { "log_auth_badpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth_badpass), NULL },
+       { "log_auth_goodpass", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &main_config.log_auth_goodpass), NULL },
+       { "log_stripped_names", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &log_stripped_names), NULL },
 
-       {  "security", PW_TYPE_SUBSECTION, 0, NULL, (void const *) security_config },
+       {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) security_config },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
 static const CONF_PARSER bootstrap_security_config[] = {
 #ifdef HAVE_SETUID
-       { "user",  PW_TYPE_STRING_PTR, 0, &uid_name, NULL },
-       { "group",  PW_TYPE_STRING_PTR, 0, &gid_name, NULL },
+       { "user",  FR_CONF_POINTER(PW_TYPE_STRING, &uid_name), NULL },
+       { "group", FR_CONF_POINTER(PW_TYPE_STRING, &gid_name), NULL },
 #endif
-       { "chroot",  PW_TYPE_STRING_PTR, 0, &chroot_dir, NULL },
-       { "allow_core_dumps", PW_TYPE_BOOLEAN, 0, &allow_core_dumps, "no" },
+       { "chroot",  FR_CONF_POINTER(PW_TYPE_STRING, &chroot_dir), NULL },
+       { "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &allow_core_dumps), "no" },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
 static const CONF_PARSER bootstrap_config[] = {
-       {  "security", PW_TYPE_SUBSECTION, 0, NULL, (void const *) bootstrap_security_config },
+       {  "security", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) bootstrap_security_config },
 
        /*
         *      For backwards compatibility.
         */
 #ifdef HAVE_SETUID
-       { "user",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &uid_name, NULL },
-       { "group",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &gid_name, NULL },
+       { "user",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &uid_name), NULL },
+       { "group",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &gid_name), NULL },
 #endif
-       { "chroot",  PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, 0, &chroot_dir, NULL },
-       { "allow_core_dumps", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, 0, &allow_core_dumps, NULL },
+       { "chroot",  FR_CONF_POINTER(PW_TYPE_STRING | PW_TYPE_DEPRECATED, &chroot_dir), NULL },
+       { "allow_core_dumps", FR_CONF_POINTER(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, &allow_core_dumps), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -341,7 +336,7 @@ static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *
 
        if (!fmt || !out || (outlen < 1)) return 0;
 
-       if (!request || !request->client) {
+       if (!request->client) {
                RWDEBUG("No client associated with this request");
                *out = '\0';
                return 0;
@@ -384,7 +379,7 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        }
 
        strlcpy(buffer, p, (q + 1) - p);
-       if (ip_ptonx(buffer, &ip) <= 0) {
+       if (fr_pton(&ip, buffer, 0, false) <= 0) {
                REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer);
                goto error;
        }
@@ -417,58 +412,10 @@ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char cons
        return -1;
 }
 
-#ifdef HAVE_SYS_RESOURCE_H
-static struct rlimit core_limits;
-#endif
-
-static void fr_set_dumpable(void)
-{
-       /*
-        *      If configured, turn core dumps off.
-        */
-       if (!allow_core_dumps) {
-#ifdef HAVE_SYS_RESOURCE_H
-               struct rlimit no_core;
-
-
-               no_core.rlim_cur = 0;
-               no_core.rlim_max = 0;
-
-               if (setrlimit(RLIMIT_CORE, &no_core) < 0) {
-                       ERROR("Failed disabling core dumps: %s",
-                              strerror(errno));
-               }
-#endif
-               return;
-       }
-
-       /*
-        *      Set or re-set the dumpable flag.
-        */
-#ifdef HAVE_SYS_PRCTL_H
-#ifdef PR_SET_DUMPABLE
-       if (prctl(PR_SET_DUMPABLE, 1) < 0) {
-               ERROR("Cannot re-enable core dumps: prctl(PR_SET_DUMPABLE) failed: '%s'",
-                      strerror(errno));
-       }
-#endif
-#endif
-
-       /*
-        *      Reset the core dump limits to their original value.
-        */
-#ifdef HAVE_SYS_RESOURCE_H
-       if (setrlimit(RLIMIT_CORE, &core_limits) < 0) {
-               ERROR("Cannot update core dump limit: %s",
-                      strerror(errno));
-       }
-#endif
-}
-
 #ifdef HAVE_SETUID
-static int doing_setuid = false;
+static bool doing_setuid = false;
 
-#if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
+#  if defined(HAVE_SETRESUID) && defined (HAVE_GETRESUID)
 void fr_suid_up(void)
 {
        uid_t ruid, euid, suid;
@@ -495,7 +442,7 @@ void fr_suid_down(void)
 
        if (setresuid(-1, server_uid, geteuid()) < 0) {
                fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
-                       progname, uid_name, strerror(errno));
+                       progname, uid_name, fr_syserror(errno));
                fr_exit_now(1);
        }
 
@@ -505,7 +452,7 @@ void fr_suid_down(void)
                fr_exit_now(1);
        }
 
-       fr_set_dumpable();
+       fr_set_dumpable(allow_core_dumps);
 }
 
 void fr_suid_down_permanent(void)
@@ -514,7 +461,7 @@ void fr_suid_down_permanent(void)
 
        if (setresuid(server_uid, server_uid, server_uid) < 0) {
                ERROR("Failed in permanent switch to uid %s: %s",
-                      uid_name, strerror(errno));
+                      uid_name, fr_syserror(errno));
                fr_exit_now(1);
        }
 
@@ -523,43 +470,45 @@ void fr_suid_down_permanent(void)
                fr_exit_now(1);
        }
 
-       fr_set_dumpable();
+       fr_set_dumpable(allow_core_dumps);
 }
-#else
+#  else
 /*
  *     Much less secure...
  */
 void fr_suid_up(void)
 {
 }
+
 void fr_suid_down(void)
 {
        if (!uid_name) return;
 
        if (setuid(server_uid) < 0) {
                fprintf(stderr, "%s: Failed switching to uid %s: %s\n",
-                       progname, uid_name, strerror(errno));
+                       progname, uid_name, fr_syserror(errno));
                fr_exit(1);
        }
 
-       fr_set_dumpable();
+       fr_set_dumpable(allow_core_dumps);
 }
+
 void fr_suid_down_permanent(void)
 {
-       fr_set_dumpable();
+       fr_set_dumpable(allow_core_dumps);
 }
-#endif /* HAVE_SETRESUID && HAVE_GETRESUID */
+#  endif /* HAVE_SETRESUID && HAVE_GETRESUID */
 #else  /* HAVE_SETUID */
 void fr_suid_up(void)
 {
 }
 void fr_suid_down(void)
 {
-       fr_set_dumpable();
+       fr_set_dumpable(allow_core_dumps);
 }
 void fr_suid_down_permanent(void)
 {
-       fr_set_dumpable();
+       fr_set_dumpable(allow_core_dumps);
 }
 #endif /* HAVE_SETUID */
 
@@ -572,17 +521,15 @@ void fr_suid_down_permanent(void)
  */
 static int switch_users(CONF_SECTION *cs)
 {
-#ifdef HAVE_SYS_RESOURCE_H
        /*
         *      Get the current maximum for core files.  Do this
         *      before anything else so as to ensure it's properly
         *      initialized.
         */
-       if (getrlimit(RLIMIT_CORE, &core_limits) < 0) {
-               ERROR("Failed to get current core limit:  %s", strerror(errno));
+       if (fr_set_dumpable_init() < 0) {
+               fr_perror("radiusd");
                return 0;
        }
-#endif
 
        /*
         *      Don't do chroot/setuid/setgid if we're in debugging
@@ -604,7 +551,7 @@ static int switch_users(CONF_SECTION *cs)
                gr = getgrnam(gid_name);
                if (gr == NULL) {
                        fprintf(stderr, "%s: Cannot get ID for group %s: %s\n",
-                               progname, gid_name, strerror(errno));
+                               progname, gid_name, fr_syserror(errno));
                        return 0;
                }
                server_gid = gr->gr_gid;
@@ -621,7 +568,7 @@ static int switch_users(CONF_SECTION *cs)
                pw = getpwnam(uid_name);
                if (pw == NULL) {
                        fprintf(stderr, "%s: Cannot get passwd entry for user %s: %s\n",
-                               progname, uid_name, strerror(errno));
+                               progname, uid_name, fr_syserror(errno));
                        return 0;
                }
 
@@ -633,7 +580,7 @@ static int switch_users(CONF_SECTION *cs)
 #ifdef HAVE_INITGROUPS
                        if (initgroups(uid_name, server_gid) < 0) {
                                fprintf(stderr, "%s: Cannot initialize supplementary group list for user %s: %s\n",
-                                       progname, uid_name, strerror(errno));
+                                       progname, uid_name, fr_syserror(errno));
                                return 0;
                        }
 #endif
@@ -646,7 +593,7 @@ static int switch_users(CONF_SECTION *cs)
        if (chroot_dir) {
                if (chroot(chroot_dir) < 0) {
                        fprintf(stderr, "%s: Failed to perform chroot %s: %s",
-                               progname, chroot_dir, strerror(errno));
+                               progname, chroot_dir, fr_syserror(errno));
                        return 0;
                }
 
@@ -671,7 +618,7 @@ static int switch_users(CONF_SECTION *cs)
        /*  Set GID.  */
        if (gid_name && (setgid(server_gid) < 0)) {
                fprintf(stderr, "%s: Failed setting group to %s: %s",
-                       progname, gid_name, strerror(errno));
+                       progname, gid_name, fr_syserror(errno));
                return 0;
        }
 #endif
@@ -685,18 +632,18 @@ static int switch_users(CONF_SECTION *cs)
         *      specified on the command-line.
         */
        if (uid_name || gid_name) {
-               if ((default_log.dest == L_DST_FILES) &&
+               if ((default_log.dst == L_DST_FILES) &&
                    (default_log.fd < 0)) {
-                       default_log.fd = open(mainconfig.log_file,
+                       default_log.fd = open(main_config.log_file,
                                              O_WRONLY | O_APPEND | O_CREAT, 0640);
                        if (default_log.fd < 0) {
-                               fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno));
+                               fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
                                return 0;
                        }
 
-                       if (chown(mainconfig.log_file, server_uid, server_gid) < 0) {
+                       if (chown(main_config.log_file, server_uid, server_gid) < 0) {
                                fprintf(stderr, "%s: Cannot change ownership of log file %s: %s\n",
-                                       progname, mainconfig.log_file, strerror(errno));
+                                       progname, main_config.log_file, fr_syserror(errno));
                                return 0;
                        }
                }
@@ -713,23 +660,50 @@ static int switch_users(CONF_SECTION *cs)
         *      This also clears the dumpable flag if core dumps
         *      aren't allowed.
         */
-       fr_set_dumpable();
+       if (fr_set_dumpable(allow_core_dumps) < 0) {
+               ERROR("%s", fr_strerror());
+       }
 
        if (allow_core_dumps) {
-               INFO("Core dumps are enabled.");
+               INFO("Core dumps are enabled");
        }
 
        return 1;
 }
 #endif /* HAVE_SETUID */
 
+/** Set the global radius config directory.
+ *
+ * @param ctx Where to allocate the memory for the path string.
+ * @param path to config dir root e.g. /usr/local/etc/raddb
+ */
+void set_radius_dir(TALLOC_CTX *ctx, char const *path)
+{
+       if (radius_dir) {
+               char *p;
+
+               memcpy(&p, &radius_dir, sizeof(p));
+               talloc_free(p);
+               radius_dir = NULL;
+       }
+       if (path) radius_dir = talloc_strdup(ctx, path);
+}
+
+/** Get the global radius config directory.
+ *
+ * @return the global radius config directory.
+ */
+char const *get_radius_dir(void)
+{
+       return radius_dir;
+}
 
 /*
  *     Read config files.
  *
  *     This function can ONLY be called from the main server process.
  */
-int read_mainconfig(int reload)
+int main_config_init(void)
 {
        char const *p = NULL;
        CONF_SECTION *cs;
@@ -737,14 +711,9 @@ int read_mainconfig(int reload)
        cached_config_t *cc;
        char buffer[1024];
 
-       if (reload != 0) {
-               ERROR("Reload is not implemented");
-               return -1;
-       }
-
        if (stat(radius_dir, &statbuf) < 0) {
                ERROR("Errors reading %s: %s",
-                      radius_dir, strerror(errno));
+                      radius_dir, fr_syserror(errno));
                return -1;
        }
 
@@ -765,18 +734,61 @@ int read_mainconfig(int reload)
 #endif
        INFO("Starting - reading configuration files ...");
 
-       /* Initialize the dictionary */
-       if (!mainconfig.dictionary_dir) mainconfig.dictionary_dir = radius_dir;
-       DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
-       if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
+       /*
+        *      We need to load the dictionaries before reading the
+        *      configuration files.  This is because of the
+        *      pre-compilation in conffile.c.  That should probably
+        *      be fixed to be done as a second stage.
+        */
+       if (!main_config.dictionary_dir) {
+               main_config.dictionary_dir = DICTDIR;
+       }
+
+       /*
+        *      Read the distribution dictionaries first, then
+        *      the ones in raddb.
+        */
+       DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
+       if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
                ERROR("Errors reading dictionary: %s",
-                               fr_strerror());
+                     fr_strerror());
                return -1;
        }
 
+#define DICT_READ_OPTIONAL(_d, _n) \
+do {\
+       switch (dict_read(_d, _n)) {\
+       case -1:\
+               ERROR("Errors reading %s/%s: %s", _d, _n, fr_strerror());\
+               return -1;\
+       case 0:\
+               DEBUG2("including dictionary file %s/%s", _d,_n);\
+               break;\
+       default:\
+               break;\
+       }\
+} while (0)
+
+       /*
+        *      Try to load protocol-specific dictionaries.  It's OK
+        *      if they don't exist.
+        */
+#ifdef WITH_DHCP
+       DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.dhcp");
+#endif
+
+#ifdef WITH_VMPS
+       DICT_READ_OPTIONAL(main_config.dictionary_dir, "dictionary.vqp");
+#endif
+
+       /*
+        *      It's OK if this one doesn't exist.
+        */
+       DICT_READ_OPTIONAL(radius_dir, RADIUS_DICTIONARY);
+
        /* Read the configuration file */
        snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, mainconfig.name);
+                radius_dir, main_config.name);
        if ((cs = cf_file_read(buffer)) == NULL) {
                ERROR("Errors reading or parsing %s", buffer);
                return -1;
@@ -786,7 +798,7 @@ int read_mainconfig(int reload)
         *      If there was no log destination set on the command line,
         *      set it now.
         */
-       if (default_log.dest == L_DST_NULL) {
+       if (default_log.dst == L_DST_NULL) {
                if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
                        fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
                        cf_file_free(cs);
@@ -799,16 +811,16 @@ int read_mainconfig(int reload)
                        return -1;
                }
 
-               default_log.dest = fr_str2int(log_str2dst, radlog_dest,
+               default_log.dst = fr_str2int(log_str2dst, radlog_dest,
                                              L_DST_NUM_DEST);
-               if (default_log.dest == L_DST_NUM_DEST) {
+               if (default_log.dst == L_DST_NUM_DEST) {
                        fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n",
                                radlog_dest);
                        cf_file_free(cs);
                        return -1;
                }
 
-               if (default_log.dest == L_DST_SYSLOG) {
+               if (default_log.dst == L_DST_SYSLOG) {
                        /*
                         *      Make sure syslog_facility isn't NULL
                         *      before using it
@@ -818,8 +830,8 @@ int read_mainconfig(int reload)
                                cf_file_free(cs);
                                return -1;
                        }
-                       mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
-                       if (mainconfig.syslog_facility < 0) {
+                       main_config.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
+                       if (main_config.syslog_facility < 0) {
                                fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
                                        syslog_facility);
                                cf_file_free(cs);
@@ -831,11 +843,11 @@ int read_mainconfig(int reload)
                         *      Call openlog only once, when the
                         *      program starts.
                         */
-                       openlog(progname, LOG_PID, mainconfig.syslog_facility);
+                       openlog(progname, LOG_PID, main_config.syslog_facility);
 #endif
 
-               } else if (default_log.dest == L_DST_FILES) {
-                       if (!mainconfig.log_file) {
+               } else if (default_log.dst == L_DST_FILES) {
+                       if (!main_config.log_file) {
                                fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n");
                                cf_file_free(cs);
                                return -1;
@@ -855,12 +867,12 @@ int read_mainconfig(int reload)
         *      did switch uid/gid, then the code in switch_users()
         *      took care of setting the file permissions correctly.
         */
-       if ((default_log.dest == L_DST_FILES) &&
+       if ((default_log.dst == L_DST_FILES) &&
            (default_log.fd < 0)) {
-               default_log.fd = open(mainconfig.log_file,
+               default_log.fd = open(main_config.log_file,
                                            O_WRONLY | O_APPEND | O_CREAT, 0640);
                if (default_log.fd < 0) {
-                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno));
+                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
                        cf_file_free(cs);
                        return -1;
                }
@@ -885,9 +897,27 @@ int read_mainconfig(int reload)
                default_log.colourise = false;
        }
 
-       if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100;
-       if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5;
-       if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5;
+       /*
+        *      Starting the server, WITHOUT "-x" on the
+        *      command-line: use whatever is in the config
+        *      file.
+        */
+       if (debug_flag == 0) {
+               debug_flag = main_config.debug_level;
+       }
+       fr_debug_flag = debug_flag;
+
+       FR_INTEGER_COND_CHECK("max_request_time", main_config.max_request_time, (main_config.max_request_time != 0), 100);
+       FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, 10);
+       FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 10);
+
+       /*
+        * Set default initial request processing delay to 1/3 of a second.
+        * Will be updated by the lowest response window across all home servers,
+        * if it is less than this.
+        */
+       main_config.init_delay.tv_sec = 0;
+       main_config.init_delay.tv_usec = 1000000 / 3;
 
        /*
         *      Free the old configuration items, and replace them
@@ -896,15 +926,15 @@ int read_mainconfig(int reload)
         *      Note that where possible, we do atomic switch-overs,
         *      to ensure that the pointers are always valid.
         */
-       rad_assert(mainconfig.config == NULL);
-       root_config = mainconfig.config = cs;
+       rad_assert(main_config.config == NULL);
+       root_config = main_config.config = cs;
 
-       DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name);
+       DEBUG2("%s: #### Loading Realms and Home Servers ####", main_config.name);
        if (!realms_init(cs)) {
                return -1;
        }
 
-       DEBUG2("%s: #### Loading Clients ####", mainconfig.name);
+       DEBUG2("%s: #### Loading Clients ####", main_config.name);
        if (!clients_parse_section(cs, false)) {
                return -1;
        }
@@ -917,16 +947,6 @@ int read_mainconfig(int reload)
        xlat_register("getclient", xlat_getclient, NULL, NULL);
 
        /*
-        *      Starting the server, WITHOUT "-x" on the
-        *      command-line: use whatever is in the config
-        *      file.
-        */
-       if (debug_flag == 0) {
-               debug_flag = mainconfig.debug_level;
-       }
-       fr_debug_flag = debug_flag;
-
-       /*
         *  Go update our behaviour, based on the configuration
         *  changes.
         */
@@ -935,41 +955,34 @@ int read_mainconfig(int reload)
         *      Sanity check the configuration for internal
         *      consistency.
         */
-       if (mainconfig.reject_delay > mainconfig.cleanup_delay) {
-               mainconfig.reject_delay = mainconfig.cleanup_delay;
-       }
-       if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0;
-
-       /*  Reload the modules.  */
-       if (setup_modules(reload, mainconfig.config) < 0) {
-               return -1;
-       }
+       FR_INTEGER_BOUND_CHECK("reject_delay", main_config.reject_delay, <=, main_config.cleanup_delay);
 
        if (chroot_dir) {
                if (chdir(radlog_dir) < 0) {
                        ERROR("Failed to 'chdir %s' after chroot: %s",
-                              radlog_dir, strerror(errno));
+                              radlog_dir, fr_syserror(errno));
                        return -1;
                }
        }
 
-       cc = rad_malloc(sizeof(*cc));
-       memset(cc, 0, sizeof(*cc));
+       cc = talloc_zero(NULL, cached_config_t);
+       if (!cc) return -1;
 
-       cc->cs = cs;
+       cc->cs = talloc_steal(cc ,cs);
        rad_assert(cs_cache == NULL);
        cs_cache = cc;
 
+       /* Clear any unprocessed configuration errors */
+       (void) fr_strerror();
+
        return 0;
 }
 
 /*
  *     Free the configuration.  Called only when the server is exiting.
  */
-int free_mainconfig(void)
+int main_config_free(void)
 {
-       cached_config_t *cc, *next;
-
        virtual_servers_free(0);
 
        /*
@@ -978,17 +991,12 @@ int free_mainconfig(void)
         */
        clients_free(NULL);
        realms_free();
-       listen_free(&mainconfig.listen);
+       listen_free(&main_config.listen);
 
        /*
-        *      Free all of the cached configurations.
+        *      Frees current config and any previous configs.
         */
-       for (cc = cs_cache; cc != NULL; cc = next) {
-               next = cc->next;
-               cf_file_free(cc->cs);
-               free(cc);
-       }
-
+       TALLOC_FREE(cs_cache);
        dict_free();
 
        return 0;
@@ -998,9 +1006,9 @@ void hup_logfile(void)
 {
                int fd, old_fd;
 
-               if (default_log.dest != L_DST_FILES) return;
+               if (default_log.dst != L_DST_FILES) return;
 
-               fd = open(mainconfig.log_file,
+               fd = open(main_config.log_file,
                          O_WRONLY | O_APPEND | O_CREAT, 0640);
                if (fd >= 0) {
                        /*
@@ -1017,7 +1025,7 @@ void hup_logfile(void)
                }
 }
 
-void hup_mainconfig(void)
+void main_config_hup(void)
 {
        cached_config_t *cc;
        CONF_SECTION *cs;
@@ -1027,14 +1035,17 @@ void hup_mainconfig(void)
 
        /* Read the configuration file */
        snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
-                radius_dir, mainconfig.name);
+                radius_dir, main_config.name);
        if ((cs = cf_file_read(buffer)) == NULL) {
                ERROR("Failed to re-read or parse %s", buffer);
                return;
        }
 
-       cc = rad_malloc(sizeof(*cc));
-       memset(cc, 0, sizeof(*cc));
+       cc = talloc_zero(cs_cache, cached_config_t);
+       if (!cc) {
+               ERROR("Out of memory");
+               return;
+       }
 
        /*
         *      Save the current configuration.  Note that we do NOT
@@ -1046,7 +1057,7 @@ void hup_mainconfig(void)
         *      configurations.
         */
        cc->created = time(NULL);
-       cc->cs = cs;
+       cc->cs = talloc_steal(cc, cs);
        cc->next = cs_cache;
        cs_cache = cc;
 
@@ -1064,12 +1075,12 @@ void hup_mainconfig(void)
        /*
         *      Prefer the new module configuration.
         */
-       module_hup(cf_section_sub_find(cs, "modules"));
+       modules_hup(cf_section_sub_find(cs, "modules"));
 
        /*
         *      Load new servers BEFORE freeing old ones.
         */
        virtual_servers_load(cs);
 
-       virtual_servers_free(cc->created - mainconfig.max_request_time * 4);
+       virtual_servers_free(cc->created - main_config.max_request_time * 4);
 }
index a35cacf..d198bfb 100644 (file)
@@ -41,7 +41,7 @@ void radius_tmplfree(value_pair_tmpl_t **tmpl)
 {
        if (*tmpl == NULL) return;
 
-       dict_attr_free(&((*tmpl)->da));
+       dict_attr_free(&((*tmpl)->vpt_da));
 
        talloc_free(*tmpl);
 
@@ -57,38 +57,46 @@ void radius_tmplfree(value_pair_tmpl_t **tmpl)
  * string might be freed before you're done with the vpt use radius_attr2tmpl
  * instead.
  *
- * @param[in] name attribute name including qualifiers.
+ * The special return code of -2 is used only by radius_str2tmpl, which allow
+ * bare words which might (or might not) be an attribute reference.
+ *
  * @param[out] vpt to modify.
- * @param[in] request_def The default request to insert unqualified
- *     attributes into.
+ * @param[in] name attribute name including qualifiers.
+ * @param[in] request_def The default request to insert unqualified attributes into.
  * @param[in] list_def The default list to insert unqualified attributes into.
- * @return -1 on error or 0 on success.
+ * @return -2 on partial parse followed by error, -1 on other error, or 0 on success
  */
-int radius_parse_attr(char const *name, value_pair_tmpl_t *vpt,
-                     request_refs_t request_def,
-                     pair_lists_t list_def)
+int radius_parse_attr(value_pair_tmpl_t *vpt, char const *name, request_refs_t request_def, pair_lists_t list_def)
 {
-       DICT_ATTR const *da;
+       int error = -1;
        char const *p;
        size_t len;
+       unsigned long num;
+       char *q;
+       DICT_ATTR const *da;
 
        memset(vpt, 0, sizeof(*vpt));
        vpt->name = name;
        p = name;
 
-       vpt->request = radius_request_name(&p, request_def);
+       if (*p == '&') {
+               error = -2;
+               p++;
+       }
+
+       vpt->vpt_request = radius_request_name(&p, request_def);
        len = p - name;
-       if (vpt->request == REQUEST_UNKNOWN) {
-               ERROR("Invalid request qualifier \"%.*s\"", (int) len, name);
-               return -1;
+       if (vpt->vpt_request == REQUEST_UNKNOWN) {
+               fr_strerror_printf("Invalid request qualifier \"%.*s\"", (int) len, name);
+               return error;
        }
        name += len;
 
-       vpt->list = radius_list_name(&p, list_def);
-       if (vpt->list == PAIR_LIST_UNKNOWN) {
+       vpt->vpt_list = radius_list_name(&p, list_def);
+       if (vpt->vpt_list == PAIR_LIST_UNKNOWN) {
                len = p - name;
-               ERROR("Invalid list qualifier \"%.*s\"", (int) len, name);
-               return -1;
+               fr_strerror_printf("Invalid list qualifier \"%.*s\"", (int) len, name);
+               return error;
        }
 
        if (*p == '\0') {
@@ -96,17 +104,66 @@ int radius_parse_attr(char const *name, value_pair_tmpl_t *vpt,
                return 0;
        }
 
-       da = dict_attrbyname(p);
+       da = dict_attrbytagged_name(p);
        if (!da) {
                da = dict_attrunknownbyname(p, false);
                if (!da) {
-                       ERROR("Unknown attribute \"%s\"", p);
-                       return -1;
+                       fr_strerror_printf("Unknown attribute \"%s\"", p);
+                       return error;
                }
        }
-       vpt->da = da;
-
+       vpt->vpt_da = da;
        vpt->type = VPT_TYPE_ATTR;
+       vpt->vpt_tag = TAG_ANY;
+       vpt->vpt_num = NUM_ANY;
+
+       /*
+        *      After this point, we return -2 to indicate that parts
+        *      of the string were parsed as an attribute, but others
+        *      weren't.
+        */
+       while (*p) {
+               if (*p == ':') break;
+               if (*p == '[') break;
+               p++;
+       }
+
+       if (*p == ':') {
+               if (!da->flags.has_tag) {
+                       fr_strerror_printf("Attribute '%s' cannot have a tag", da->name);
+                       return -2;
+               }
+
+               num = strtoul(p + 1, &q, 10);
+               if (num > 0x1f) {
+                       fr_strerror_printf("Invalid tag value '%u' (should be between 0-31)", (unsigned int) num);
+                       return -2;
+               }
+
+               vpt->vpt_tag = num;
+               p = q;
+       }
+
+       if (!*p) return 0;
+
+       if (*p != '[') {
+               fr_strerror_printf("Unexpected text after tag in '%s'", name);
+               return -2;
+       }
+
+       num = strtoul(p + 1, &q, 10);
+       if (num > 1000) {
+               fr_strerror_printf("Invalid array reference '%u' (should be between 0-1000)", (unsigned int) num);
+               return -2;
+       }
+
+       if ((*q != ']') || (q[1] != '\0')) {
+               fr_strerror_printf("Unexpected text after array in '%s'", name);
+               return -2;
+       }
+
+       vpt->vpt_num = num;
+
        return 0;
 }
 
@@ -131,9 +188,10 @@ value_pair_tmpl_t *radius_attr2tmpl(TALLOC_CTX *ctx, char const *name,
        char const *copy;
 
        vpt = talloc(ctx, value_pair_tmpl_t); /* parse_attr zeroes it */
-       copy = talloc_strdup(vpt, name);
+       copy = talloc_typed_strdup(vpt, name);
 
-       if (radius_parse_attr(copy, vpt, request_def, list_def) < 0) {
+       if (radius_parse_attr(vpt, copy, request_def, list_def) < 0) {
+               ERROR("%s", fr_strerror());
                radius_tmplfree(&vpt);
                return NULL;
        }
@@ -146,73 +204,89 @@ value_pair_tmpl_t *radius_attr2tmpl(TALLOC_CTX *ctx, char const *name,
  * @param[in] ctx for talloc
  * @param[in] name string to convert.
  * @param[in] type Type of quoting around value.
+ * @param[in] request_def The default request to insert unqualified
+ *     attributes into.
+ * @param[in] list_def The default list to insert unqualified attributes into.
  * @return pointer to new VPT.
  */
-value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type)
+value_pair_tmpl_t *radius_str2tmpl(TALLOC_CTX *ctx, char const *name, FR_TOKEN type,
+                                  request_refs_t request_def, pair_lists_t list_def)
 {
+       int rcode;
+       char const *p;
        value_pair_tmpl_t *vpt;
+       char buffer[1024];
 
        vpt = talloc_zero(ctx, value_pair_tmpl_t);
-       vpt->name = talloc_strdup(vpt, name);
+       vpt->name = talloc_typed_strdup(vpt, name);
 
        switch (type) {
        case T_BARE_WORD:
-               if (*name == '&') name++;
-
-               if (!isdigit((int) *name)) {
-                       request_refs_t ref;
-                       pair_lists_t list;
-                       char const *p = name;
-
-                       ref = radius_request_name(&p, REQUEST_CURRENT);
-                       if (ref == REQUEST_UNKNOWN) goto literal;
-
-                       list = radius_list_name(&p, PAIR_LIST_REQUEST);
-                       if (list == PAIR_LIST_UNKNOWN) goto literal;
-
-                       if ((p != name) && !*p) {
-                               vpt->type = VPT_TYPE_LIST;
-
-                       } else {
-                               DICT_ATTR const *da;
-                               da = dict_attrbyname(p);
-                               if (!da) {
-                                       vpt->type = VPT_TYPE_LITERAL;
-                                       break;
-                               }
-                               vpt->da = da;
-                               vpt->type = VPT_TYPE_ATTR;
-                       }
-
-                       vpt->request = ref;
-                       vpt->list = list;
+               /*
+                *      If we can parse it as an attribute, it's an attribute.
+                *      Otherwise, treat it as a literal.
+                */
+               rcode = radius_parse_attr(vpt, vpt->name, request_def, list_def);
+               if (rcode == -2) {
+                       talloc_free(vpt);
+                       return NULL;
+               }
+               if (rcode == 0) {
                        break;
                }
                /* FALL-THROUGH */
 
        case T_SINGLE_QUOTED_STRING:
-       literal:
                vpt->type = VPT_TYPE_LITERAL;
                break;
+
        case T_DOUBLE_QUOTED_STRING:
-               vpt->type = VPT_TYPE_XLAT;
+               p = name;
+               while (*p) {
+                       if (*p == '\\') {
+                               if (!p[1]) break;
+                               p += 2;
+                               continue;
+                       }
+
+                       if (*p == '%') break;
+
+                       p++;
+               }
+
+               /*
+                *      If the double quoted string needs to be
+                *      expanded at run time, make it an xlat
+                *      expansion.  Otherwise, convert it to be a
+                *      literal.
+                */
+               if (*p) {
+                       vpt->type = VPT_TYPE_XLAT;
+               } else {
+                       vpt->type = VPT_TYPE_LITERAL;
+               }
                break;
+
        case T_BACK_QUOTED_STRING:
                vpt->type = VPT_TYPE_EXEC;
                break;
+
        case T_OP_REG_EQ: /* hack */
                vpt->type = VPT_TYPE_REGEX;
                break;
+
        default:
                rad_assert(0);
                return NULL;
        }
 
+       radius_tmpl2str(buffer, sizeof(buffer), vpt);
+
        return vpt;
 }
 
 
-/** Convert strings to value_pair_map_e
+/** Convert strings to value_pair_map_t
  *
  * Treatment of operands depends on quotation, barewords are treated
  * as attribute references, double quoted values are treated as
@@ -248,12 +322,7 @@ value_pair_map_t *radius_str2map(TALLOC_CTX *ctx, char const *lhs, FR_TOKEN lhs_
 
        map = talloc_zero(ctx, value_pair_map_t);
 
-       if ((lhs_type == T_BARE_WORD) && (*lhs == '&')) {
-               map->dst = radius_attr2tmpl(map, lhs + 1, dst_request_def, dst_list_def);
-       } else {
-               map->dst = radius_str2tmpl(map, lhs, lhs_type);
-       }
-
+       map->dst = radius_str2tmpl(map, lhs, lhs_type, dst_request_def, dst_list_def);
        if (!map->dst) {
        error:
                talloc_free(map);
@@ -262,20 +331,8 @@ value_pair_map_t *radius_str2map(TALLOC_CTX *ctx, char const *lhs, FR_TOKEN lhs_
 
        map->op = op;
 
-       /*
-        *      Ignore the RHS if it's a true / false comparison.
-        */
-       if ((map->op == T_OP_CMP_TRUE) || (map->op == T_OP_CMP_FALSE)) {
-               return map;
-       }
-
-       if ((rhs_type == T_BARE_WORD) && (*rhs == '&')) {
-               map->src = radius_attr2tmpl(map, rhs + 1, src_request_def, src_list_def);
-       } else {
-               map->src = radius_str2tmpl(map, rhs, rhs_type);
-       }
-
-       if (!map->dst) goto error;
+       map->src = radius_str2tmpl(map, rhs, rhs_type, src_request_def, src_list_def);
+       if (!map->src) goto error;
 
        return map;
 }
@@ -319,6 +376,8 @@ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
        if (!cp) return NULL;
 
        map = talloc_zero(ctx, value_pair_map_t);
+       map->op = cf_pair_operator(cp);
+       map->ci = ci;
 
        attr = cf_pair_attr(cp);
        value = cf_pair_value(cp);
@@ -327,6 +386,9 @@ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
                goto error;
        }
 
+       /*
+        *      LHS must always be an attribute reference.
+        */
        map->dst = radius_attr2tmpl(map, attr, dst_request_def, dst_list_def);
        if (!map->dst) {
                cf_log_err(ci, "Syntax error in attribute definition");
@@ -334,36 +396,46 @@ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
        }
 
        /*
-        *      Bare words always mean attribute references.
+        *      RHS might be an attribute reference.
         */
        type = cf_pair_value_type(cp);
-       if (type == T_BARE_WORD) {
-               if (*value == '&') {
-                       map->src = radius_attr2tmpl(map, value + 1, src_request_def, src_list_def);
-               } else {
-                       if (!isdigit((int) *value) &&
-                           ((strchr(value, ':') != NULL) ||
-                            (dict_attrbyname(value) != NULL))) {
-                               map->src = radius_attr2tmpl(map, value, src_request_def, src_list_def);
-                       }
-                       if (map->src) {
-                               WDEBUG("%s[%d]: Please add '&' for attribute reference '%s = &%s'",
-                                      cf_pair_filename(cp), cf_pair_lineno(cp),
-                                      attr, value);
-                       } else {
-                               map->src = radius_str2tmpl(map, value, type);
-                       }
-               }
-       } else {
-               map->src = radius_str2tmpl(map, value, type);
-       }
-
+       map->src = radius_str2tmpl(map, value, type, src_request_def, src_list_def);
        if (!map->src) {
                goto error;
        }
 
-       map->op = cf_pair_operator(cp);
-       map->ci = ci;
+       /*
+        *      Anal-retentive checks.
+        */
+       if (debug_flag > 2) {
+               if ((map->dst->type == VPT_TYPE_ATTR) && (*attr != '&')) {
+                       WARN("%s[%d]: Please change attribute reference to '&%s %s ...'",
+                              cf_pair_filename(cp), cf_pair_lineno(cp),
+                              attr, fr_int2str(fr_tokens, map->op, "<INVALID>"));
+               }
+
+               if ((map->src->type == VPT_TYPE_ATTR) && (*value != '&')) {
+                       WARN("%s[%d]: Please change attribute reference to '... %s &%s'",
+                              cf_pair_filename(cp), cf_pair_lineno(cp),
+                              fr_int2str(fr_tokens, map->op, "<INVALID>"), value);
+               }
+       }
+
+       /*
+        *      Values used by unary operators should be literal ANY
+        *
+        *      We then free the template and alloc a NULL one instead.
+        */
+       if (map->op == T_OP_CMP_FALSE) {
+               if ((map->src->type != VPT_TYPE_LITERAL) || (strcmp(map->src->name, "ANY") != 0)) {
+                       WARN("%s[%d] Wildcard deletion MUST use '!* ANY'", cf_pair_filename(cp), cf_pair_lineno(cp));
+               }
+
+               radius_tmplfree(&map->src);
+
+               map->src = talloc_zero(map, value_pair_tmpl_t);
+               map->src->type = VPT_TYPE_NULL;
+       }
 
        /*
         *      Lots of sanity checks for insane people...
@@ -373,10 +445,10 @@ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
         *      We don't support implicit type conversion,
         *      except for "octets"
         */
-       if (map->dst->da && map->src->da &&
-           (map->src->da->type != map->dst->da->type) &&
-           (map->src->da->type != PW_TYPE_OCTETS) &&
-           (map->dst->da->type != PW_TYPE_OCTETS)) {
+       if (map->dst->vpt_da && map->src->vpt_da &&
+           (map->src->vpt_da->type != map->dst->vpt_da->type) &&
+           (map->src->vpt_da->type != PW_TYPE_OCTETS) &&
+           (map->dst->vpt_da->type != PW_TYPE_OCTETS)) {
                cf_log_err(ci, "Attribute type mismatch");
                goto error;
        }
@@ -391,54 +463,69 @@ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
        }
 
        /*
-        *      Can't copy an xlat expansion or literal into a list,
-        *      we don't know what type of attribute we'd need
-        *      to create
-        */
-       if ((map->dst->type == VPT_TYPE_LIST) &&
-           ((map->src->type == VPT_TYPE_XLAT) || (map->src->type == VPT_TYPE_LITERAL))) {
-               cf_log_err(ci, "Can't copy value into list (we don't know which attribute to create)");
-               goto error;
-       }
-
-       /*
         *      Depending on the attribute type, some operators are
         *      disallowed.
         */
        if (map->dst->type == VPT_TYPE_ATTR) {
-               if ((map->op != T_OP_EQ) &&
-                   (map->op != T_OP_CMP_EQ) &&
-                   (map->op != T_OP_ADD) &&
-                   (map->op != T_OP_SUB) &&
-                   (map->op != T_OP_LE) &&
-                   (map->op != T_OP_GE) &&
-                   (map->op != T_OP_CMP_FALSE) &&
-                   (map->op != T_OP_SET)) {
+               switch (map->op) {
+               default:
                        cf_log_err(ci, "Invalid operator for attribute");
                        goto error;
+
+               case T_OP_EQ:
+               case T_OP_CMP_EQ:
+               case T_OP_ADD:
+               case T_OP_SUB:
+               case T_OP_LE:
+               case T_OP_GE:
+               case T_OP_CMP_FALSE:
+               case T_OP_SET:
+                       break;
                }
        }
 
-       switch (map->src->type) {
+       if (map->dst->type == VPT_TYPE_LIST) {
                /*
-                *      Only += and -= operators are supported for list copy.
+                *      Only += and :=, and !* operators are supported
+                *      for lists.
                 */
-               case VPT_TYPE_LIST:
-                       switch (map->op) {
-                       case T_OP_SUB:
-                       case T_OP_ADD:
+               switch (map->op) {
+               case T_OP_CMP_FALSE:
+                       break;
+
+               case T_OP_ADD:
+                       if ((map->src->type != VPT_TYPE_LIST) &&
+                           (map->src->type != VPT_TYPE_EXEC)) {
+                               cf_log_err(ci, "Invalid source for list '+='");
+                               goto error;
+                       }
+                       break;
+
+               case T_OP_SET:
+                       if (map->src->type == VPT_TYPE_EXEC) {
+                               WARN("%s[%d] Please change ':=' to '=' for list assignment",
+                                      cf_pair_filename(cp), cf_pair_lineno(cp));
                                break;
+                       }
 
-                       default:
-                               cf_log_err(ci, "Operator \"%s\" not allowed "
-                                          "for list copy",
-                                          fr_int2str(fr_tokens, map->op, "<INVALID>"));
+                       if (map->src->type != VPT_TYPE_LIST) {
+                               cf_log_err(ci, "Invalid source for ':=' operator");
                                goto error;
                        }
-               break;
+                       break;
 
-               default:
+               case T_OP_EQ:
+                       if (map->src->type != VPT_TYPE_EXEC) {
+                               cf_log_err(ci, "Invalid source for '=' operator");
+                               goto error;
+                       }
                        break;
+
+               default:
+                       cf_log_err(ci, "Operator \"%s\" not allowed for list assignment",
+                                  fr_int2str(fr_tokens, map->op, "<INVALID>"));
+                       goto error;
+               }
        }
 
        return map;
@@ -533,7 +620,7 @@ int radius_attrmap(CONF_SECTION *cs, value_pair_map_t **head,
 
        return 0;
 error:
-       talloc_free(*head);
+       TALLOC_FREE(*head);
        return -1;
 }
 
@@ -547,23 +634,32 @@ error:
  */
 size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt)
 {
+       size_t len;
        char c;
        char const *p;
        char *q = buffer;
        char *end;
 
+       if (!vpt) {
+               *buffer = '\0';
+               return 0;
+       }
+
        switch (vpt->type) {
        default:
                return 0;
 
        case VPT_TYPE_REGEX:
+       case VPT_TYPE_REGEX_STRUCT:
                c = '/';
                break;
 
        case VPT_TYPE_XLAT:
+       case VPT_TYPE_XLAT_STRUCT:
                c = '"';
                break;
 
+       case VPT_TYPE_LIST:
        case VPT_TYPE_LITERAL:  /* single-quoted or bare word */
                /*
                 *      Hack
@@ -588,38 +684,61 @@ size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vp
 
        case VPT_TYPE_ATTR:
                buffer[0] = '&';
-               if (vpt->request == REQUEST_CURRENT) {
-                       if (vpt->list == PAIR_LIST_REQUEST) {
-                               strlcpy(buffer + 1, vpt->da->name, bufsize - 1);
+               if (vpt->vpt_request == REQUEST_CURRENT) {
+                       if (vpt->vpt_list == PAIR_LIST_REQUEST) {
+                               strlcpy(buffer + 1, vpt->vpt_da->name, bufsize - 1);
                        } else {
                                snprintf(buffer + 1, bufsize - 1, "%s:%s",
-                                        fr_int2str(pair_lists, vpt->list, ""),
-                                        vpt->da->name);
+                                        fr_int2str(pair_lists, vpt->vpt_list, ""),
+                                        vpt->vpt_da->name);
                        }
 
                } else {
                        snprintf(buffer + 1, bufsize - 1, "%s.%s:%s",
-                                fr_int2str(request_refs, vpt->request, ""),
-                                fr_int2str(pair_lists, vpt->list, ""),
-                                vpt->da->name);
+                                fr_int2str(request_refs, vpt->vpt_request, ""),
+                                fr_int2str(pair_lists, vpt->vpt_list, ""),
+                                vpt->vpt_da->name);
+               }
+
+               len = strlen(buffer);
+
+               if ((vpt->vpt_tag == TAG_ANY) && (vpt->vpt_num == NUM_ANY)) {
+                       return len;
+               }
+
+               q = buffer + len;
+               bufsize -= len;
+
+               if (vpt->vpt_tag != TAG_ANY) {
+                       snprintf(q, bufsize, ":%d", vpt->vpt_tag);
+                       len = strlen(q);
+                       q += len;
+                       bufsize -= len;
+               }
+
+               if (vpt->vpt_num != NUM_ANY) {
+                       snprintf(q, bufsize, "[%u]", vpt->vpt_num);
+                       len = strlen(q);
+                       q += len;
                }
-               return strlen(buffer);
+
+               return (q - buffer);
 
        case VPT_TYPE_DATA:
-               {
+               if (vpt->vpt_value) {
                        VALUE_PAIR *vp;
                        TALLOC_CTX *ctx;
 
                        memcpy(&ctx, &vpt, sizeof(ctx)); /* hack */
 
-                       vp = pairalloc(ctx, vpt->da);
-                       memcpy(&vp->data, vpt->vpd, sizeof(vp->data));
-                       vp->length = vpt->length;
+                       MEM(vp = pairalloc(ctx, vpt->vpt_da));
+                       memcpy(&vp->data, vpt->vpt_value, sizeof(vp->data));
+                       vp->length = vpt->vpt_length;
 
-                       q = vp_aprint(vp, vp);
+                       q = vp_aprint_value(vp, vp);
 
-                       if ((vpt->da->type != PW_TYPE_STRING) &&
-                           (vpt->da->type != PW_TYPE_DATE)) {
+                       if ((vpt->vpt_da->type != PW_TYPE_STRING) &&
+                           (vpt->vpt_da->type != PW_TYPE_DATE)) {
                                strlcpy(buffer, q, bufsize);
                        } else {
                                /*
@@ -631,6 +750,10 @@ size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vp
                        talloc_free(q);
                        pairfree(&vp);
                        return strlen(buffer);
+
+               } else {
+                       *buffer = '\0';
+                       return 0;
                }
        }
 
@@ -646,7 +769,7 @@ size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vp
 
        while (*p && (q < end)) {
                if (*p == c) {
-                       if ((q - end) < 4) goto no_room; /* escape, char, quote, EOS */
+                       if ((end - q) < 4) goto no_room; /* escape, char, quote, EOS */
                        *(q++) = '\\';
                        *(q++) = *(p++);
                        continue;
@@ -654,27 +777,27 @@ size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vp
 
                switch (*p) {
                case '\\':
-                       if ((q - end) < 4) goto no_room;
+                       if ((end - q) < 4) goto no_room;
                        *(q++) = '\\';
                        *(q++) = *(p++);
                        break;
 
                case '\r':
-                       if ((q - end) < 4) goto no_room;
+                       if ((end - q) < 4) goto no_room;
                        *(q++) = '\\';
                        *(q++) = 'r';
                        p++;
                        break;
 
                case '\n':
-                       if ((q - end) < 4) goto no_room;
+                       if ((end - q) < 4) goto no_room;
                        *(q++) = '\\';
                        *(q++) = 'r';
                        p++;
                        break;
 
                case '\t':
-                       if ((q - end) < 4) goto no_room;
+                       if ((end - q) < 4) goto no_room;
                        *(q++) = '\\';
                        *(q++) = 't';
                        p++;
@@ -727,7 +850,7 @@ size_t radius_map2str(char *buffer, size_t bufsize, value_pair_map_t const *map)
        rad_assert(map->src != NULL);
 
        if ((map->dst->type == VPT_TYPE_ATTR) &&
-           (map->dst->da->type == PW_TYPE_STRING) &&
+           (map->dst->vpt_da->type == PW_TYPE_STRING) &&
            (map->src->type == VPT_TYPE_LITERAL)) {
                *(p++) = '\'';
                len = radius_tmpl2str(p, end - p, map->src);
@@ -741,3 +864,45 @@ size_t radius_map2str(char *buffer, size_t bufsize, value_pair_map_t const *map)
 
        return p - buffer;
 }
+
+/** Cast a literal vpt to a value_data_t
+ *
+ * @param[in,out] vpt the template to modify
+ * @param[in] da the dictionary attribute to case it to
+ * @return true for success, false for failure.
+ */
+bool radius_cast_tmpl(value_pair_tmpl_t *vpt, DICT_ATTR const *da)
+{
+       VALUE_PAIR *vp;
+       value_data_t *data;
+
+       rad_assert(vpt != NULL);
+       rad_assert(da != NULL);
+       rad_assert(vpt->type == VPT_TYPE_LITERAL);
+
+       vp = pairalloc(vpt, da);
+       if (!vp) return false;
+
+       if (pairparsevalue(vp, vpt->name, 0) < 0) {
+               pairfree(&vp);
+               return false;
+       }
+
+       vpt->vpt_length = vp->length;
+       vpt->vpt_value = data = talloc(vpt, value_data_t);
+       if (!vpt->vpt_value) return false;
+
+       vpt->type = VPT_TYPE_DATA;
+       vpt->vpt_da = da;
+
+       if (vp->da->flags.is_pointer) {
+               data->ptr = talloc_steal(vpt, vp->data.ptr);
+               vp->data.ptr = NULL;
+       } else {
+               memcpy(data, &vp->data, sizeof(*data));
+       }
+
+       pairfree(&vp);
+
+       return true;
+}
index f628cdd..4f73748 100644 (file)
@@ -32,7 +32,7 @@ RCSID("$Id$")
 /* mutually-recursive static functions need a prototype up front */
 static modcallable *do_compile_modgroup(modcallable *,
                                        rlm_components_t, CONF_SECTION *,
-                                       int, int);
+                                       int, int, int);
 
 /* Actions may be a positive integer (the highest one returned in the group
  * will be returned), or the keyword "return", represented here by
@@ -72,9 +72,12 @@ typedef struct {
                GROUPTYPE_COUNT
        } grouptype;                            /* after mc */
        modcallable             *children;
+       modcallable             *tail;          /* of the children list */
        CONF_SECTION            *cs;
        value_pair_map_t        *map;           /* update */
+       value_pair_tmpl_t       *vpt;           /* switch */
        fr_cond_t               *cond;          /* if/elsif */
+       bool                    done_pass2;
 } modgroup;
 
 typedef struct {
@@ -94,12 +97,14 @@ typedef struct {
        char *xlat_name;
 } modxlat;
 
+/*
 static const FR_NAME_NUMBER grouptype_table[] = {
        { "", GROUPTYPE_SIMPLE },
        { "redundant ", GROUPTYPE_REDUNDANT },
        { "append ", GROUPTYPE_APPEND },
        { NULL, -1 }
 };
+*/
 
 /* Simple conversions: modsingle and modgroup are subclasses of modcallable,
  * so we often want to go back and forth between them. */
@@ -144,22 +149,18 @@ static modcallable *mod_xlattocallable(modxlat *p)
 }
 
 /* modgroups are grown by adding a modcallable to the end */
-/* FIXME: This is O(N^2) */
 static void add_child(modgroup *g, modcallable *c)
 {
-       modcallable **head = &g->children;
-       modcallable *node = *head;
-       modcallable **last = head;
-
        if (!c) return;
 
-       while (node) {
-               last = &node->next;
-               node = node->next;
+       if (!g->children) {
+               g->children = g->tail = c;
+       } else {
+               rad_assert(g->tail->next == NULL);
+               g->tail->next = c;
+               g->tail = c;
        }
 
-       rad_assert(c->next == NULL);
-       *last = c;
        c->parent = mod_grouptocallable(g);
 }
 
@@ -279,25 +280,23 @@ static void safe_unlock(module_instance_t *instance)
 #define safe_unlock(foo)
 #endif
 
-static int call_modsingle(rlm_components_t component, modsingle *sp, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) call_modsingle(rlm_components_t component, modsingle *sp, REQUEST *request)
 {
-       int myresult;
        int blocked;
 
-       rad_assert(request != NULL);
-
        /*
         *      If the request should stop, refuse to do anything.
         */
        blocked = (request->master_state == REQUEST_STOP_PROCESSING);
        if (blocked) return RLM_MODULE_NOOP;
 
-       RDEBUG3("  modsingle[%s]: calling %s (%s) for request %d",
+       RINDENT();
+       RDEBUG3("modsingle[%s]: calling %s (%s) for request %d",
               comp2str[component], sp->modinst->name,
               sp->modinst->entry->name, request->number);
 
-       if (sp->modinst->dead) {
-               myresult = RLM_MODULE_FAIL;
+       if (sp->modinst->force) {
+               request->rcode = sp->modinst->code;
                goto fail;
        }
 
@@ -308,8 +307,7 @@ static int call_modsingle(rlm_components_t component, modsingle *sp, REQUEST *re
         */
        request->module = sp->modinst->name;
 
-       myresult = sp->modinst->entry->module->methods[component](
-                       sp->modinst->insthandle, request);
+       request->rcode = sp->modinst->entry->module->methods[component](sp->modinst->insthandle, request);
 
        request->module = "";
        safe_unlock(sp->modinst);
@@ -323,11 +321,12 @@ static int call_modsingle(rlm_components_t component, modsingle *sp, REQUEST *re
        }
 
  fail:
-       RDEBUG3("  modsingle[%s]: returned from %s (%s) for request %d",
+       REXDENT();
+       RDEBUG3("modsingle[%s]: returned from %s (%s) for request %d",
               comp2str[component], sp->modinst->name,
               sp->modinst->entry->name, request->number);
 
-       return myresult;
+       return request->rcode;
 }
 
 static int default_component_results[RLM_COMPONENT_COUNT] = {
@@ -377,7 +376,7 @@ static char const *modcall_spaces = "
  *     iteratively, and manage the call stack ourselves.
  */
 typedef struct modcall_stack_entry_t {
-       int result;
+       rlm_rcode_t result;
        int priority;
        int unwind;             /* unwind to this one if it exists */
        modcallable *c;
@@ -392,7 +391,7 @@ static bool modcall_recurse(REQUEST *request, rlm_components_t component, int de
  */
 static void modcall_child(REQUEST *request, rlm_components_t component, int depth,
                          modcall_stack_entry_t *entry, modcallable *c,
-                         int *result)
+                         rlm_rcode_t *result)
 {
        modcall_stack_entry_t *next;
 
@@ -436,7 +435,8 @@ static bool modcall_recurse(REQUEST *request, rlm_components_t component, int de
 {
        bool if_taken, was_if;
        modcallable *c;
-       int result, priority;
+       int priority;
+       rlm_rcode_t result;
 
        was_if = if_taken = false;
        result = RLM_MODULE_UNKNOWN;
@@ -474,7 +474,7 @@ redo:
                g = mod_callabletogroup(c);
                rad_assert(g->cond != NULL);
 
-               RDEBUG2("%.*s? %s %s", depth + 1, modcall_spaces,
+               RDEBUG2("%.*s %s %s", depth + 1, modcall_spaces,
                        group_name[c->type], c->name);
 
                condition = radius_evaluate_cond(request, result, 0, g->cond);
@@ -482,7 +482,7 @@ redo:
                        condition = false;
                        REDEBUG("Failed retrieving values required to evaluate condition");
                } else {
-                       RDEBUG2("%.*s? %s %s -> %s", depth + 1, modcall_spaces,
+                       RDEBUG2("%.*s %s %s -> %s", depth + 1, modcall_spaces,
                                group_name[c->type],
                                c->name, condition ? "TRUE" : "FALSE");
                }
@@ -593,7 +593,7 @@ redo:
 
                MOD_LOG_OPEN_BRACE("update");
                for (map = g->map; map != NULL; map = map->next) {
-                       rcode = radius_map2request(request, map, "update", radius_map2vp, NULL);
+                       rcode = radius_map2request(request, map, radius_map2vp, NULL);
                        if (rcode < 0) {
                                result = (rcode == -2) ? RLM_MODULE_INVALID : RLM_MODULE_FAIL;
                                MOD_LOG_CLOSE_BRACE();
@@ -611,9 +611,9 @@ redo:
         */
        if (c->type == MOD_FOREACH) {
                int i, foreach_depth = -1;
-               VALUE_PAIR *vp;
+               VALUE_PAIR *vps, *vp;
                modcall_stack_entry_t *next = NULL;
-               vp_cursor_t cursor;
+               vp_cursor_t cursor, copy;
                modgroup *g = mod_callabletogroup(c);
 
                if (depth >= MODCALL_STACK_MAX) {
@@ -639,28 +639,43 @@ redo:
                        goto calculate_result;
                }
 
-               if (radius_get_vp(&vp, request, c->name) < 0) {
-                       RDEBUG("Unknown Attribute \"%s\"", c->name);
-                       result = RLM_MODULE_FAIL;
-                       goto calculate_result;
-               }
-
-               if (!vp) {      /* nothing to loop over */
+               if (radius_tmpl_get_vp(&vp, request, g->vpt) < 0) {     /* nothing to loop over */
                        MOD_LOG_OPEN_BRACE("foreach");
                        result = RLM_MODULE_NOOP;
                        MOD_LOG_CLOSE_BRACE();
                        goto calculate_result;
                }
 
-               RDEBUG2("%.*sforeach %s ", depth + 1, modcall_spaces,
-                       c->name);
+               /*
+                *      Copy the VPs from the original request, this ensures deterministic
+                *      behaviour if someone decides to add or remove VPs in the set were
+                *      iterating over.
+                */
+               vps = NULL;
+
+               fr_cursor_init(&cursor, &vp);
 
-               paircursor(&cursor, &vp);
                /* Prime the cursor. */
                cursor.found = cursor.current;
-               while (vp) {
-                       VALUE_PAIR *copy = NULL, **copy_p;
+               for (fr_cursor_init(&copy, &vps);
+                    vp;
+                    vp = fr_cursor_next_by_da(&cursor, vp->da, g->vpt->attribute.tag)) {
+                    VALUE_PAIR *tmp;
+
+                    MEM(tmp = paircopyvp(request, vp));
+                    fr_cursor_insert(&copy, tmp);
+               }
+
+               RDEBUG2("%.*sforeach %s ", depth + 1, modcall_spaces, c->name);
+
+               rad_assert(vps != NULL);
 
+               /*
+                *      This is the actual body of the foreach loop
+                */
+               for (vp = fr_cursor_first(&copy);
+                    vp != NULL;
+                    vp = fr_cursor_next(&copy)) {
 #ifndef NDEBUG
                        if (fr_debug_flag >= 2) {
                                char buffer[1024];
@@ -672,21 +687,10 @@ redo:
 #endif
 
                        /*
-                        *      Copy only the one VP we care about.
-                        */
-                       copy = paircopyvp(request, vp);
-                       copy_p = &copy;
-
-                       /*
-                        *      @fixme: The old code freed copy on
-                        *      request_data_add or request_data_get.
-                        *      There's no way to easily do that now.
-                        *      The foreach code should be audited for
-                        *      possible memory leaks, though it's not
-                        *      a huge priority as any leaked memory
-                        *      will be freed on request free.
+                        *      Add the vp to the request, so that
+                        *      xlat.c, xlat_foreach() can find it.
                         */
-                       request_data_add(request, radius_get_vp, foreach_depth, copy_p, false);
+                       request_data_add(request, radius_get_vp, foreach_depth, &vp, false);
 
                        /*
                         *      Initialize the childs stack frame.
@@ -698,20 +702,6 @@ redo:
                        next->unwind = 0;
 
                        if (!modcall_recurse(request, component, depth + 1, next)) {
-                               request_data_get(request, radius_get_vp, foreach_depth);
-                               pairfree(&copy);
-                               break;
-                       }
-
-                       vp = pairfindnext(&cursor, vp->da->attr, vp->da->vendor, TAG_ANY);
-
-                       /*
-                        *      Delete the cached attribute, if it exists.
-                        */
-                       if (copy) {
-                               request_data_get(request, radius_get_vp, foreach_depth);
-                               pairfree(&copy);
-                       } else {
                                break;
                        }
 
@@ -725,6 +715,9 @@ redo:
                        }
                } /* loop over VPs */
 
+               pairfree(&vps);
+
+               rad_assert(next != NULL);
                result = next->result;
                priority = next->priority;
                MOD_LOG_CLOSE_BRACE();
@@ -741,9 +734,7 @@ redo:
                for (i = 8; i >= 0; i--) {
                        copy_p = request_data_get(request, radius_get_vp, i);
                        if (copy_p) {
-                               RDEBUG2("%.*s # break Foreach-Variable-%d", depth + 1,
-                                       modcall_spaces, i);
-                               pairfree(copy_p);
+                               RDEBUG2("%.*s # break Foreach-Variable-%d", depth + 1, modcall_spaces, i);
                                break;
                        }
                }
@@ -784,7 +775,11 @@ redo:
                        goto next_sibling;
                }
 
-               MOD_LOG_OPEN_BRACE(cf_section_name1(g->cs));
+               if (c->name) {
+                       MOD_LOG_OPEN_BRACE(cf_section_name1(g->cs));
+               } else {
+                       RDEBUG2("%.*s%s {", depth + 1, modcall_spaces, cf_section_name1(g->cs));
+               }
                modcall_child(request, component,
                              depth + 1, entry, g->children,
                              &result);
@@ -795,43 +790,89 @@ redo:
 #ifdef WITH_UNLANG
        if (c->type == MOD_SWITCH) {
                modcallable *this, *found, *null_case;
-               modgroup *g;
-               char buffer[1024];
+               modgroup *g, *h;
+               fr_cond_t cond;
+               value_pair_map_t map;
 
                MOD_LOG_OPEN_BRACE("switch");
 
+               g = mod_callabletogroup(c);
+
+               memset(&cond, 0, sizeof(cond));
+               memset(&map, 0, sizeof(map));
+
+               cond.type = COND_TYPE_MAP;
+               cond.data.map = &map;
+
+               map.op = T_OP_CMP_EQ;
+               map.ci = cf_sectiontoitem(g->cs);
+
+               rad_assert(g->vpt != NULL);
+
+               null_case = found = NULL;
+
                /*
-                *      If there's no %, it refers to an attribute.
-                *      Otherwise, expand it.
+                *      The attribute doesn't exist.  We can skip
+                *      directly to the default 'case' statement.
                 */
-               if (!strchr(c->name, '%')) {
-                       VALUE_PAIR *vp = NULL;
-
-                       radius_get_vp(&vp, request, c->name);
-                       if (vp) {
-                               vp_prints_value(buffer,
-                                               sizeof(buffer),
-                                               vp, 0);
-                       } else {
-                               *buffer = '\0';
+               if ((g->vpt->type == VPT_TYPE_ATTR) && (radius_tmpl_get_vp(NULL, request, g->vpt) < 0)) {
+                       for (this = g->children; this; this = this->next) {
+                               rad_assert(this->type == MOD_CASE);
+
+                               h = mod_callabletogroup(this);
+                               if (h->vpt) continue;
+
+                               found = this;
+                               break;
                        }
-               } else {
-                       radius_xlat(buffer, sizeof(buffer),
-                                   request, c->name, NULL, NULL);
+
+                       goto do_null_case;
                }
 
                /*
                 *      Find either the exact matching name, or the
                 *      "case {...}" statement.
                 */
-               g = mod_callabletogroup(c);
-               null_case = found = NULL;
                for (this = g->children; this; this = this->next) {
-                       if (!this->name) {
+                       rad_assert(this->type == MOD_CASE);
+
+                       h = mod_callabletogroup(this);
+
+                       /*
+                        *      Remember the default case
+                        */
+                       if (!h->vpt) {
                                if (!null_case) null_case = this;
                                continue;
                        }
-                       if (strcmp(buffer, this->name) == 0) {
+
+                       /*
+                        *      If we're switching over an attribute
+                        *      AND we haven't pre-parsed the data for
+                        *      the case statement, then cast the data
+                        *      to the type of the attribute.
+                        */
+                       if ((g->vpt->type == VPT_TYPE_ATTR) &&
+                           (h->vpt->type != VPT_TYPE_DATA)) {
+                               map.src = g->vpt;
+                               map.dst = h->vpt;
+                               cond.cast = g->vpt->vpt_da;
+
+                               /*
+                                *      Remove unnecessary casting.
+                                */
+                               if ((h->vpt->type == VPT_TYPE_ATTR) &&
+                                   (g->vpt->vpt_da->type == h->vpt->vpt_da->type)) {
+                                       cond.cast = NULL;
+                               }
+                       } else {
+                               map.src = h->vpt;
+                               map.dst = g->vpt;
+                               cond.cast = NULL;
+                       }
+
+                       if (radius_evaluate_map(request, RLM_MODULE_UNKNOWN, 0,
+                                               &cond) == 1) {
                                found = this;
                                break;
                        }
@@ -839,7 +880,7 @@ redo:
 
                if (!found) found = null_case;
 
-               MOD_LOG_OPEN_BRACE(group_name[c->type]);
+               do_null_case:
                modcall_child(request, component,
                              depth + 1, entry, found,
                              &result);
@@ -1034,6 +1075,9 @@ int modcall(rlm_components_t component, modcallable *c, REQUEST *request)
 {
        modcall_stack_entry_t stack[MODCALL_STACK_MAX];
 
+#ifndef NDEBUG
+       memset(stack, 0, sizeof(stack));
+#endif
        /*
         *      Set up the initial stack frame.
         */
@@ -1516,6 +1560,7 @@ static modcallable *do_compile_modupdate(modcallable *parent, UNUSED rlm_compone
        modgroup *g;
        modcallable *csingle;
        value_pair_map_t *map, *head = NULL;
+       CONF_ITEM *ci;
 
        /*
         *      This looks at cs->name2 to determine which list to update
@@ -1528,27 +1573,65 @@ static modcallable *do_compile_modupdate(modcallable *parent, UNUSED rlm_compone
                return NULL;
        }
 
-       for (map = head; map != NULL; map = map->next) {
-               if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_LITERAL)) {
+       for (map = head, ci = cf_item_find_next(cs, NULL);
+            map != NULL;
+            map = map->next, ci = cf_item_find_next(cs, ci)) {
+               /*
+                *      Can't copy an xlat expansion or literal into a list,
+                *      we don't know what type of attribute we'd need
+                *      to create.
+                *
+                *      The only exception is where were using a unary
+                *      operator like !*.
+                */
+               if ((map->dst->type == VPT_TYPE_LIST) &&
+                   (map->op != T_OP_CMP_FALSE) &&
+                   ((map->src->type == VPT_TYPE_XLAT) || (map->src->type == VPT_TYPE_LITERAL))) {
+                       cf_log_err(map->ci, "Can't copy value into list (we don't know which attribute to create)");
+                       talloc_free(head);
+                       return NULL;
+               }
+
+               /*
+                *      If LHS is an attribute, and RHS is a literal, we can
+                *      preparse the information into a VPT_TYPE_DATA.
+                *
+                *      Unless it's a unary operator in which case we
+                *      ignore map->src.
+                */
+               if ((map->dst->type == VPT_TYPE_ATTR) && (map->op != T_OP_CMP_FALSE) &&
+                   (map->src->type == VPT_TYPE_LITERAL)) {
+                       CONF_PAIR *cp;
+
+                       cp = cf_itemtopair(ci);
+                       rad_assert(cp != NULL);
+
                        /*
-                        *      If LHS is an attribute, and RHS is a literal, we can
-                        *      check that the format is correct.
+                        *      It's a literal string, just copy it.
+                        *      Don't escape anything.
                         */
-                       VALUE_PAIR *vp;
-                       bool ret;
+                       if ((map->dst->vpt_da->type == PW_TYPE_STRING) &&
+                           (cf_pair_value_type(cp) == T_SINGLE_QUOTED_STRING)) {
+                               value_data_t *vpd;
 
-                       MEM(vp = pairalloc(cs, map->dst->da));
-                       vp->op = map->op;
+                               map->src->vpt_value = vpd = talloc_zero(map->src, value_data_t);
+                               rad_assert(vpd != NULL);
 
-                       ret = pairparsevalue(vp, map->src->name);
-                       talloc_free(vp);
-                       if (!ret) {
-                               talloc_free(head);
-                               cf_log_err(map->ci, "%s", fr_strerror());
-                               return NULL;
+                               vpd->strvalue = talloc_typed_strdup(vpd, map->src->name);
+                               rad_assert(vpd->strvalue != NULL);
+
+                               map->src->type = VPT_TYPE_DATA;
+                               map->src->vpt_da = map->dst->vpt_da;
+                               map->src->vpt_length = talloc_array_length(vpd->strvalue) - 1;
+                       } else {
+                               if (!radius_cast_tmpl(map->src, map->dst->vpt_da)) {
+                                       cf_log_err(map->ci, "%s", fr_strerror());
+                                       talloc_free(head);
+                                       return NULL;
+                               }
                        }
-               }
-       }
+               } /* else we can't precompile the data */
+       } /* loop over the conf_pairs in the update section */
 
        g = rad_malloc(sizeof(*g)); /* never fails */
        memset(g, 0, sizeof(*g));
@@ -1578,24 +1661,47 @@ static modcallable *do_compile_modupdate(modcallable *parent, UNUSED rlm_compone
 }
 
 
-static modcallable *do_compile_modswitch(modcallable *parent, UNUSED rlm_components_t component, CONF_SECTION *cs)
+static modcallable *do_compile_modswitch(modcallable *parent, rlm_components_t component, CONF_SECTION *cs)
 {
-       modcallable *csingle;
        CONF_ITEM *ci;
-       int had_seen_default = false;
+       FR_TOKEN type;
+       char const *name2;
+       bool had_seen_default = false;
+       modcallable *csingle;
+       modgroup *g;
+       value_pair_tmpl_t *vpt;
 
-       if (!cf_section_name2(cs)) {
+       name2 = cf_section_name2(cs);
+       if (!name2) {
                cf_log_err_cs(cs,
-                          "You must specify a variable to switch over for 'switch'.");
+                          "You must specify a variable to switch over for 'switch'");
                return NULL;
        }
 
        if (!cf_item_find_next(cs, NULL)) {
-               cf_log_err_cs(cs, "'switch' statements cannot be empty.");
+               cf_log_err_cs(cs, "'switch' statements cannot be empty");
+               return NULL;
+       }
+
+       /*
+        *      Create the template.  If we fail, AND it's a bare word
+        *      with &Foo-Bar, it MAY be an attribute defined by a
+        *      module.  Allow it for now.  The pass2 checks below
+        *      will fix it up.
+        */
+       type = cf_section_name2_type(cs);
+       vpt = radius_str2tmpl(cs, name2, type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+       if (!vpt && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
+               cf_log_err_cs(cs, "Syntax error in '%s': %s", name2, fr_strerror());
                return NULL;
        }
 
        /*
+        *      Otherwise a NULL vpt may refer to an attribute defined
+        *      by a module.  That is checked in pass 2.
+        */
+
+       /*
         *      Walk through the children of the switch section,
         *      ensuring that they're all 'case' statements
         */
@@ -1603,12 +1709,13 @@ static modcallable *do_compile_modswitch(modcallable *parent, UNUSED rlm_compone
             ci != NULL;
             ci=cf_item_find_next(cs, ci)) {
                CONF_SECTION *subcs;
-               char const *name1, *name2;
+               char const *name1;
 
                if (!cf_item_is_section(ci)) {
                        if (!cf_item_is_pair(ci)) continue;
 
                        cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
+                       talloc_free(vpt);
                        return NULL;
                }
 
@@ -1617,6 +1724,7 @@ static modcallable *do_compile_modswitch(modcallable *parent, UNUSED rlm_compone
 
                if (strcmp(name1, "case") != 0) {
                        cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
+                       talloc_free(vpt);
                        return NULL;
                }
 
@@ -1628,45 +1736,152 @@ static modcallable *do_compile_modswitch(modcallable *parent, UNUSED rlm_compone
 
                if (!name2 || (name2[0] == '\0')) {
                        cf_log_err(ci, "\"case\" sections must have a name");
+                       talloc_free(vpt);
+                       return NULL;
+               }
+       }
+
+       csingle = do_compile_modgroup(parent, component, cs,
+                                     GROUPTYPE_SIMPLE,
+                                     GROUPTYPE_SIMPLE,
+                                     MOD_SWITCH);
+       if (!csingle) {
+               talloc_free(vpt);
+               return NULL;
+       }
+
+       g = mod_callabletogroup(csingle);
+       g->vpt = vpt;
+
+       return csingle;
+}
+
+static modcallable *do_compile_modcase(modcallable *parent, rlm_components_t component, CONF_SECTION *cs)
+{
+       int i;
+       char const *name2;
+       modcallable *csingle;
+       modgroup *g;
+       value_pair_tmpl_t *vpt;
+
+       if (!parent || (parent->type != MOD_SWITCH)) {
+               cf_log_err_cs(cs, "\"case\" statements may only appear within a \"switch\" section");
+               return NULL;
+       }
+
+       /*
+        *      case THING means "match THING"
+        *      case       means "match anything"
+        */
+       name2 = cf_section_name2(cs);
+       if (name2) {
+               FR_TOKEN type;
+
+               type = cf_section_name2_type(cs);
+
+               vpt = radius_str2tmpl(cs, name2, type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+               if (!vpt && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
+                       cf_log_err_cs(cs, "Syntax error in '%s': %s", name2, fr_strerror());
                        return NULL;
                }
+
+               /*
+                *      Otherwise a NULL vpt may refer to an attribute defined
+                *      by a module.  That is checked in pass 2.
+                */
+
+       } else {
+               vpt = NULL;
+       }
+
+       csingle= do_compile_modgroup(parent, component, cs,
+                                    GROUPTYPE_SIMPLE,
+                                    GROUPTYPE_SIMPLE,
+                                    MOD_CASE);
+       if (!csingle) {
+               talloc_free(vpt);
+               return NULL;
+       }
+
+       /*
+        *      The interpretor expects this to be NULL for the
+        *      default case.  do_compile_modgroup sets it to name2,
+        *      unless name2 is NULL, in which case it sets it to name1.
+        */
+       csingle->name = name2;
+
+       g = mod_callabletogroup(csingle);
+       g->vpt = vpt;
+
+       /*
+        *      Set all of it's codes to return, so that
+        *      when we pick a 'case' statement, we don't
+        *      fall through to processing the next one.
+        */
+       for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
+               csingle->actions[i] = MOD_ACTION_RETURN;
        }
 
-       csingle = do_compile_modgroup(parent, component, cs, GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
-       if (!csingle) return NULL;
-       csingle->type = MOD_SWITCH;
        return csingle;
 }
 
 static modcallable *do_compile_modforeach(modcallable *parent,
-                                         UNUSED rlm_components_t component, CONF_SECTION *cs,
-                                         char const *name2)
+                                         UNUSED rlm_components_t component, CONF_SECTION *cs)
 {
+       FR_TOKEN type;
+       char const *name2;
        modcallable *csingle;
+       modgroup *g;
+       value_pair_tmpl_t *vpt;
 
-       if (!cf_section_name2(cs)) {
+       name2 = cf_section_name2(cs);
+       if (!name2) {
                cf_log_err_cs(cs,
-                          "You must specify an attribute to loop over in 'foreach'.");
+                          "You must specify an attribute to loop over in 'foreach'");
                return NULL;
        }
 
        if (!cf_item_find_next(cs, NULL)) {
-               cf_log_err_cs(cs, "'foreach' blocks cannot be empty.");
+               cf_log_err_cs(cs, "'foreach' blocks cannot be empty");
                return NULL;
        }
 
-       csingle= do_compile_modgroup(parent, component, cs,
-                                    GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
-       if (!csingle) return NULL;
-       csingle->name = name2;
-       csingle->type = MOD_FOREACH;
+       /*
+        *      Create the template.  If we fail, AND it's a bare word
+        *      with &Foo-Bar, it MAY be an attribute defined by a
+        *      module.  Allow it for now.  The pass2 checks below
+        *      will fix it up.
+        */
+       type = cf_section_name2_type(cs);
+       vpt = radius_str2tmpl(cs, name2, type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+       if (!vpt && ((type != T_BARE_WORD) || (name2[0] != '&'))) {
+               cf_log_err_cs(cs, "Syntax error in '%s': %s", name2, fr_strerror());
+               return NULL;
+       }
+
+       if (vpt && (vpt->type != VPT_TYPE_ATTR)) {
+               cf_log_err_cs(cs, "MUST use attribute reference in 'foreach'");
+               return NULL;
+       }
+
+       csingle = do_compile_modgroup(parent, component, cs,
+                                     GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE,
+                                     MOD_FOREACH);
+
+       if (!csingle) {
+               talloc_free(vpt);
+               return NULL;
+       }
+
+       g = mod_callabletogroup(csingle);
+       g->vpt = vpt;
+
        return csingle;
 }
 
 static modcallable *do_compile_modbreak(modcallable *parent,
                                        rlm_components_t component, CONF_ITEM const *ci)
 {
-       modcallable *csingle;
        CONF_SECTION const *cs = NULL;
 
        for (cs = cf_item_parent(ci);
@@ -1682,12 +1897,9 @@ static modcallable *do_compile_modbreak(modcallable *parent,
                return NULL;
        }
 
-       csingle = do_compile_modgroup(parent, component, NULL,
-                                     GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
-       if (!csingle) return NULL;
-       csingle->name = "";
-       csingle->type = MOD_BREAK;
-       return csingle;
+       return do_compile_modgroup(parent, component, NULL,
+                                  GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE,
+                                  MOD_BREAK);
 }
 #endif
 
@@ -1820,6 +2032,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
        modcallable *csingle;
        module_instance_t *this;
        CONF_SECTION *cs, *subcs, *modules;
+       char const *realname;
 
        if (cf_item_is_section(ci)) {
                char const *name2;
@@ -1839,7 +2052,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                        *modname = name2;
                        return do_compile_modgroup(parent, component, cs,
                                                   GROUPTYPE_SIMPLE,
-                                                  grouptype);
+                                                  grouptype, MOD_GROUP);
 
                } else if (strcmp(modrefname, "redundant") == 0) {
                        *modname = name2;
@@ -1850,13 +2063,13 @@ static modcallable *do_compile_modsingle(modcallable *parent,
 
                        return do_compile_modgroup(parent, component, cs,
                                                   GROUPTYPE_REDUNDANT,
-                                                  grouptype);
+                                                  grouptype, MOD_GROUP);
 
                } else if (strcmp(modrefname, "append") == 0) {
                        *modname = name2;
                        return do_compile_modgroup(parent, component, cs,
                                                   GROUPTYPE_APPEND,
-                                                  grouptype);
+                                                  grouptype, MOD_GROUP);
 
                } else if (strcmp(modrefname, "load-balance") == 0) {
                        *modname = name2;
@@ -1865,12 +2078,9 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                                return NULL;
                        }
 
-                       csingle= do_compile_modgroup(parent, component, cs,
-                                                    GROUPTYPE_SIMPLE,
-                                                    grouptype);
-                       if (!csingle) return NULL;
-                       csingle->type = MOD_LOAD_BALANCE;
-                       return csingle;
+                       return do_compile_modgroup(parent, component, cs,
+                                                  GROUPTYPE_SIMPLE,
+                                                  grouptype, MOD_LOAD_BALANCE);
 
                } else if (strcmp(modrefname, "redundant-load-balance") == 0) {
                        *modname = name2;
@@ -1879,139 +2089,83 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                                return NULL;
                        }
 
-                       csingle= do_compile_modgroup(parent, component, cs,
-                                                    GROUPTYPE_REDUNDANT,
-                                                    grouptype);
-                       if (!csingle) return NULL;
-                       csingle->type = MOD_REDUNDANT_LOAD_BALANCE;
-                       return csingle;
+                       return do_compile_modgroup(parent, component, cs,
+                                                  GROUPTYPE_REDUNDANT,
+                                                  grouptype, MOD_REDUNDANT_LOAD_BALANCE);
 
 #ifdef WITH_UNLANG
                } else  if (strcmp(modrefname, "if") == 0) {
-                       modgroup *g;
-
                        if (!cf_section_name2(cs)) {
-                               cf_log_err(ci, "'if' without condition.");
+                               cf_log_err(ci, "'if' without condition");
                                return NULL;
                        }
 
                        *modname = name2;
                        csingle= do_compile_modgroup(parent, component, cs,
                                                     GROUPTYPE_SIMPLE,
-                                                    grouptype);
+                                                    grouptype, MOD_IF);
                        if (!csingle) return NULL;
-                       csingle->type = MOD_IF;
                        *modname = name2;
 
-                       g = mod_callabletogroup(csingle);
-                       g->cond = cf_data_find(g->cs, "if");
-                       rad_assert(g->cond != NULL);
-
                        return csingle;
 
                } else  if (strcmp(modrefname, "elsif") == 0) {
-                       modgroup *g;
-
                        if (parent &&
                            ((parent->type == MOD_LOAD_BALANCE) ||
                             (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
-                               cf_log_err(ci, "'elsif' cannot be used in this section.");
+                               cf_log_err(ci, "'elsif' cannot be used in this section");
                                return NULL;
                        }
 
                        if (!cf_section_name2(cs)) {
-                               cf_log_err(ci, "'elsif' without condition.");
+                               cf_log_err(ci, "'elsif' without condition");
                                return NULL;
                        }
 
                        *modname = name2;
-                       csingle= do_compile_modgroup(parent, component, cs,
-                                                    GROUPTYPE_SIMPLE,
-                                                    grouptype);
-                       if (!csingle) return NULL;
-                       csingle->type = MOD_ELSIF;
-                       *modname = name2;
-
-                       g = mod_callabletogroup(csingle);
-                       g->cond = cf_data_find(g->cs, "if");
-                       rad_assert(g->cond != NULL);
-
-                       return csingle;
+                       return do_compile_modgroup(parent, component, cs,
+                                                  GROUPTYPE_SIMPLE,
+                                                  grouptype, MOD_ELSIF);
 
                } else  if (strcmp(modrefname, "else") == 0) {
                        if (parent &&
                            ((parent->type == MOD_LOAD_BALANCE) ||
                             (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
-                               cf_log_err(ci, "'else' cannot be used in this section section.");
+                               cf_log_err(ci, "'else' cannot be used in this section section");
                                return NULL;
                        }
 
                        if (cf_section_name2(cs)) {
-                               cf_log_err(ci, "Cannot have conditions on 'else'.");
+                               cf_log_err(ci, "Cannot have conditions on 'else'");
                                return NULL;
                        }
 
                        *modname = name2;
-                       csingle= do_compile_modgroup(parent, component, cs,
-                                                    GROUPTYPE_SIMPLE,
-                                                    grouptype);
-                       if (!csingle) return NULL;
-                       csingle->type = MOD_ELSE;
-                       return csingle;
+                       return  do_compile_modgroup(parent, component, cs,
+                                                   GROUPTYPE_SIMPLE,
+                                                   grouptype, MOD_ELSE);
 
                } else  if (strcmp(modrefname, "update") == 0) {
                        *modname = name2;
 
-                       csingle = do_compile_modupdate(parent, component, cs,
-                                                      name2);
-                       if (!csingle) return NULL;
-
-                       return csingle;
+                       return do_compile_modupdate(parent, component, cs,
+                                                   name2);
 
                } else  if (strcmp(modrefname, "switch") == 0) {
                        *modname = name2;
 
-                       csingle = do_compile_modswitch(parent, component, cs);
-                       if (!csingle) return NULL;
-
-                       return csingle;
+                       return do_compile_modswitch(parent, component, cs);
 
                } else  if (strcmp(modrefname, "case") == 0) {
-                       int i;
-
                        *modname = name2;
 
-                       if (!parent) {
-                               cf_log_err(ci, "\"case\" statements may only appear within a \"switch\" section");
-                               return NULL;
-                       }
-
-                       csingle= do_compile_modgroup(parent, component, cs,
-                                                    GROUPTYPE_SIMPLE,
-                                                    grouptype);
-                       if (!csingle) return NULL;
-                       csingle->type = MOD_CASE;
-                       csingle->name = cf_section_name2(cs); /* may be NULL */
-
-                       /*
-                        *      Set all of it's codes to return, so that
-                        *      when we pick a 'case' statement, we don't
-                        *      fall through to processing the next one.
-                        */
-                       for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
-                               csingle->actions[i] = MOD_ACTION_RETURN;
-                       }
-
-                       return csingle;
+                       return do_compile_modcase(parent, component, cs);
 
                } else  if (strcmp(modrefname, "foreach") == 0) {
                        *modname = name2;
 
-                       csingle = do_compile_modforeach(parent, component, cs,
-                                                       name2);
-                       if (!csingle) return NULL;
+                       return do_compile_modforeach(parent, component, cs);
 
-                       return csingle;
 #endif
                } /* else it's something like sql { fail = 1 ...} */
 
@@ -2082,9 +2236,6 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                }
 
                if (subcs) {
-                       cf_log_module(cs, "Loading virtual module %s",
-                                     modrefname);
-
                        /*
                         *      redundant foo {} is a single.
                         */
@@ -2102,7 +2253,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                                                           component,
                                                           subcs,
                                                           GROUPTYPE_SIMPLE,
-                                                          grouptype);
+                                                          grouptype, MOD_GROUP);
                        }
                }
        }
@@ -2118,12 +2269,12 @@ static modcallable *do_compile_modsingle(modcallable *parent,
         */
        modules = cf_section_find("modules");
        this = NULL;
+       realname = modrefname;
 
        if (modules) {
                /*
                 *      Try to load the optional module.
                 */
-               char const *realname = modrefname;
                if (realname[0] == '-') realname++;
 
                /*
@@ -2224,7 +2375,7 @@ static modcallable *do_compile_modsingle(modcallable *parent,
                       sizeof csingle->actions);
        }
        rad_assert(modrefname != NULL);
-       csingle->name = modrefname;
+       csingle->name = realname;
        csingle->type = MOD_SINGLE;
        csingle->method = component;
 
@@ -2273,13 +2424,42 @@ static modcallable *do_compile_modsingle(modcallable *parent,
        return csingle;
 }
 
-modcallable *compile_modsingle(modcallable *parent,
+modcallable *compile_modsingle(modcallable **parent,
                               rlm_components_t component, CONF_ITEM *ci,
                               char const **modname)
 {
-       modcallable *ret = do_compile_modsingle(parent, component, ci,
-                                               GROUPTYPE_SIMPLE,
-                                               modname);
+       modcallable *ret;
+
+       if (!*parent) {
+               modcallable *c;
+               modgroup *g;
+               CONF_SECTION *parentcs;
+
+               g = rad_malloc(sizeof *g);
+               memset(g, 0, sizeof(*g));
+               g->grouptype = GROUPTYPE_SIMPLE;
+               c = mod_grouptocallable(g);
+               c->next = NULL;
+               memcpy(c->actions,
+                      defaultactions[component][GROUPTYPE_SIMPLE],
+                      sizeof(c->actions));
+
+               parentcs = cf_item_parent(ci);
+               c->name = cf_section_name2(parentcs);
+               if (!c->name) {
+                       c->name = cf_section_name1(parentcs);
+               }
+
+               c->type = MOD_GROUP;
+               c->method = component;
+               g->children = NULL;
+
+               *parent = mod_grouptocallable(g);
+       }
+
+       ret = do_compile_modsingle(*parent, component, ci,
+                                  GROUPTYPE_SIMPLE,
+                                  modname);
        dump_tree(component, ret);
        return ret;
 }
@@ -2290,7 +2470,7 @@ modcallable *compile_modsingle(modcallable *parent,
  */
 static modcallable *do_compile_modgroup(modcallable *parent,
                                        rlm_components_t component, CONF_SECTION *cs,
-                                       int grouptype, int parentgrouptype)
+                                       int grouptype, int parentgrouptype, int mod_type)
 {
        int i;
        modgroup *g;
@@ -2305,7 +2485,7 @@ static modcallable *do_compile_modgroup(modcallable *parent,
 
        c = mod_grouptocallable(g);
        c->parent = parent;
-       c->type = MOD_GROUP;
+       c->type = mod_type;
        c->next = NULL;
        memset(c->actions, 0, sizeof(c->actions));
 
@@ -2325,36 +2505,115 @@ static modcallable *do_compile_modgroup(modcallable *parent,
                c->name = cf_section_name1(cs);
                if (strcmp(c->name, "group") == 0) {
                        c->name = "";
-               } else {
+               } else if (c->type == MOD_GROUP) {
                        c->type = MOD_POLICY;
                }
        }
 
+#ifdef WITH_UNLANG
        /*
-        *      Loop over the children of this group.
+        *      Do load-time optimizations
         */
-       for (ci=cf_item_find_next(cs, NULL);
-            ci != NULL;
-            ci=cf_item_find_next(cs, ci)) {
+       if ((c->type == MOD_IF) || (c->type == MOD_ELSIF) || (c->type == MOD_ELSE)) {
+               modgroup *f, *p;
 
-               /*
-                *      Sections are references to other groups, or
-                *      to modules with updated return codes.
-                */
-               if (cf_item_is_section(ci)) {
-                       char const *junk = NULL;
-                       modcallable *single;
-                       CONF_SECTION *subcs = cf_itemtosection(ci);
+               rad_assert(parent != NULL);
 
-                       single = do_compile_modsingle(c, component, ci,
-                                                     grouptype, &junk);
-                       if (!single) {
-                               cf_log_err(ci, "Failed to parse \"%s\" subsection.",
-                                      cf_section_name1(subcs));
-                               modcallable_free(&c);
-                               return NULL;
+               if (c->type == MOD_IF) {
+                       g->cond = cf_data_find(g->cs, "if");
+                       rad_assert(g->cond != NULL);
+
+               check_if:
+                       if (g->cond->type == COND_TYPE_FALSE) {
+                               INFO(" # Skipping contents of '%s' as it is always 'false' -- %s:%d",
+                                    group_name[g->mc.type],
+                                    cf_section_filename(g->cs), cf_section_lineno(g->cs));
+                               goto set_codes;
                        }
-                       add_child(g, single);
+
+               } else if (c->type == MOD_ELSIF) {
+
+                       g->cond = cf_data_find(g->cs, "if");
+                       rad_assert(g->cond != NULL);
+
+                       rad_assert(parent != NULL);
+                       p = mod_callabletogroup(parent);
+
+                       rad_assert(p->tail != NULL);
+
+                       f = mod_callabletogroup(p->tail);
+                       rad_assert((f->mc.type == MOD_IF) ||
+                                  (f->mc.type == MOD_ELSIF));
+
+                       /*
+                        *      If we took the previous condition, we
+                        *      don't need to take this one.
+                        *
+                        *      We reset our condition to 'true', so
+                        *      that subsequent sections can check
+                        *      that they don't need to be executed.
+                        */
+                       if (f->cond->type == COND_TYPE_TRUE) {
+                       skip_true:
+                               INFO(" # Skipping contents of '%s' as previous '%s' is always  'true' -- %s:%d",
+                                    group_name[g->mc.type],
+                                    group_name[f->mc.type],
+                                    cf_section_filename(g->cs), cf_section_lineno(g->cs));
+                               g->cond = f->cond;
+                               goto set_codes;
+                       }
+                       goto check_if;
+
+               } else {
+                       rad_assert(c->type == MOD_ELSE);
+
+                       rad_assert(parent != NULL);
+                       p = mod_callabletogroup(parent);
+
+                       rad_assert(p->tail != NULL);
+
+                       f = mod_callabletogroup(p->tail);
+                       rad_assert((f->mc.type == MOD_IF) ||
+                                  (f->mc.type == MOD_ELSIF));
+
+                       /*
+                        *      If we took the previous condition, we
+                        *      don't need to take this one.
+                        */
+                       if (f->cond->type == COND_TYPE_TRUE) goto skip_true;
+               }
+
+               /*
+                *      Else we need to compile this section
+                */
+       }
+#endif
+
+       /*
+        *      Loop over the children of this group.
+        */
+       for (ci=cf_item_find_next(cs, NULL);
+            ci != NULL;
+            ci=cf_item_find_next(cs, ci)) {
+
+               /*
+                *      Sections are references to other groups, or
+                *      to modules with updated return codes.
+                */
+               if (cf_item_is_section(ci)) {
+                       char const *junk = NULL;
+                       modcallable *single;
+                       CONF_SECTION *subcs = cf_itemtosection(ci);
+
+                       single = do_compile_modsingle(c, component, ci,
+                                                     grouptype, &junk);
+                       if (!single) {
+                               cf_log_err(ci, "Failed to parse \"%s\" subsection.",
+                                      cf_section_name1(subcs));
+                               modcallable_free(&c);
+                               return NULL;
+                       }
+                       add_child(g, single);
 
                } else if (!cf_item_is_pair(ci)) { /* CONF_DATA */
                        continue;
@@ -2430,39 +2689,23 @@ modcallable *compile_modgroup(modcallable *parent,
 {
        modcallable *ret = do_compile_modgroup(parent, component, cs,
                                               GROUPTYPE_SIMPLE,
-                                              GROUPTYPE_SIMPLE);
-       dump_tree(component, ret);
+                                              GROUPTYPE_SIMPLE, MOD_GROUP);
+
+       if (debug_flag > 3) {
+               modcall_debug(ret, 2);
+       }
+
        return ret;
 }
 
-void add_to_modcallable(modcallable **parent, modcallable *this,
-                       rlm_components_t component, char const *name)
+void add_to_modcallable(modcallable *parent, modcallable *this)
 {
        modgroup *g;
 
        rad_assert(this != NULL);
+       rad_assert(parent != NULL);
 
-       if (*parent == NULL) {
-               modcallable *c;
-
-               g = rad_malloc(sizeof *g);
-               memset(g, 0, sizeof(*g));
-               g->grouptype = GROUPTYPE_SIMPLE;
-               c = mod_grouptocallable(g);
-               c->next = NULL;
-               memcpy(c->actions,
-                      defaultactions[component][GROUPTYPE_SIMPLE],
-                      sizeof(c->actions));
-               rad_assert(name != NULL);
-               c->name = name;
-               c->type = MOD_GROUP;
-               c->method = component;
-               g->children = NULL;
-
-               *parent = mod_grouptocallable(g);
-       } else {
-               g = mod_callabletogroup(*parent);
-       }
+       g = mod_callabletogroup(parent);
 
        add_child(g, this);
 }
@@ -2492,10 +2735,137 @@ void modcallable_free(modcallable **pc)
 
 
 #ifdef WITH_UNLANG
+static char const spaces[] = "                                                                                                                        ";
+
+static bool pass2_xlat_compile(CONF_ITEM const *ci, value_pair_tmpl_t **pvpt, bool convert)
+{
+       ssize_t slen;
+       char *fmt;
+       char const *error;
+       xlat_exp_t *head;
+       value_pair_tmpl_t *vpt;
+
+       vpt = *pvpt;
+
+       rad_assert(vpt->type == VPT_TYPE_XLAT);
+
+       fmt = talloc_typed_strdup(vpt, vpt->name);
+       slen = xlat_tokenize(vpt, fmt, &head, &error);
+
+       if (slen < 0) {
+               char const *prefix = "";
+               char const *p = vpt->name;
+               size_t indent = -slen;
+
+               if (indent >= sizeof(spaces)) {
+                       size_t offset = (indent - (sizeof(spaces) - 1)) + (sizeof(spaces) * 0.75);
+                       indent -= offset;
+                       p += offset;
+
+                       prefix = "...";
+               }
+
+               cf_log_err(ci, "Failed parsing expanded string:");
+               cf_log_err(ci, "%s%s", prefix, p);
+               cf_log_err(ci, "%s%.*s^ %s", prefix, (int) indent, spaces, error);
+
+               return false;
+       }
+
+       /*
+        *      Convert %{Attribute-Name} to &Attribute-Name
+        */
+       if (convert) {
+               value_pair_tmpl_t *attr;
+
+               attr = radius_xlat2tmpl(talloc_parent(vpt), head);
+               if (attr) {
+                       if (cf_item_is_pair(ci)) {
+                               CONF_PAIR *cp = cf_itemtopair(ci);
+
+                               WARN("%s[%d] Please change %%{%s} to &%s",
+                                      cf_pair_filename(cp), cf_pair_lineno(cp),
+                                      attr->name, attr->name);
+                       } else {
+                               CONF_SECTION *cs = cf_itemtosection(ci);
+
+                               WARN("%s[%d] Please change %%{%s} to &%s",
+                                      cf_section_filename(cs), cf_section_lineno(cs),
+                                      attr->name, attr->name);
+                       }
+                       TALLOC_FREE(*pvpt);
+                       *pvpt = attr;
+                       return true;
+               }
+       }
+
+       /*
+        *      Re-write it to be a pre-parsed XLAT structure.
+        */
+       vpt->type = VPT_TYPE_XLAT_STRUCT;
+       vpt->vpt_xlat = head;
+
+       return true;
+}
+
+
+#ifdef HAVE_REGEX_H
+static int _free_compiled_regex(regex_t *preg)
+{
+       regfree(preg);
+       return 0;
+}
+
+static bool pass2_regex_compile(CONF_ITEM const *ci, value_pair_tmpl_t *vpt)
+{
+       int rcode;
+       regex_t *preg;
+
+       rad_assert(vpt->type == VPT_TYPE_REGEX);
+
+       /*
+        *      Expanded at run-time.  We can't precompile it.
+        */
+       if (strchr(vpt->name, '%')) return true;
+
+       preg = talloc_zero(vpt, regex_t);
+       talloc_set_destructor(preg, _free_compiled_regex);
+       if (!preg) return false;
+
+       rcode = regcomp(preg, vpt->name, REG_EXTENDED | (vpt->vpt_iflag ? REG_ICASE : 0));
+       if (rcode != 0) {
+               char buffer[256];
+               regerror(rcode, preg, buffer, sizeof(buffer));
+
+               cf_log_err(ci, "Invalid regular expression %s: %s",
+                          vpt->name, buffer);
+               return false;
+       }
+
+       vpt->type = VPT_TYPE_REGEX_STRUCT;
+       vpt->vpt_preg = preg;
+
+       return true;
+}
+#endif
+
 static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
 {
        value_pair_map_t *map;
 
+       if (c->type == COND_TYPE_EXISTS) {
+               if (c->data.vpt->type == VPT_TYPE_XLAT) {
+                       return pass2_xlat_compile(c->ci, &c->data.vpt, true);
+               }
+
+               rad_assert(c->data.vpt->type != VPT_TYPE_REGEX);
+
+               /*
+                *      FIXME: fix up attribute references, too!
+                */
+               return true;
+       }
+
        /*
         *      Maps have a paircompare fixup applied to them.
         *      Others get ignored.
@@ -2505,6 +2875,7 @@ static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
                        map = c->data.map;
                        goto check_paircmp;
                }
+
                return true;
        }
 
@@ -2516,11 +2887,11 @@ static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
         *      Where "foo" is dynamically defined.
         */
        if (c->pass2_fixup == PASS2_FIXUP_TYPE) {
-               if (!dict_valbyname(map->dst->da->attr,
-                                   map->dst->da->vendor,
+               if (!dict_valbyname(map->dst->vpt_da->attr,
+                                   map->dst->vpt_da->vendor,
                                    map->src->name)) {
                        cf_log_err(map->ci, "Invalid reference to non-existent %s %s { ... }",
-                                  map->dst->da->name,
+                                  map->dst->vpt_da->name,
                                   map->src->name);
                        return false;
                }
@@ -2536,10 +2907,13 @@ static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
                value_pair_map_t *old;
                value_pair_tmpl_t vpt;
 
+               old = c->data.map;
+
                /*
                 *      It's still not an attribute.  Ignore it.
                 */
-               if (radius_parse_attr(map->dst->name, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               if (radius_parse_attr(&vpt, map->dst->name, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+                       cf_log_err(old->ci, "Failed parsing condition: %s", fr_strerror());
                        c->pass2_fixup = PASS2_FIXUP_NONE;
                        return true;
                }
@@ -2547,13 +2921,12 @@ static bool pass2_callback(UNUSED void *ctx, fr_cond_t *c)
                /*
                 *      Re-parse the LHS as an attribute.
                 */
-               old = c->data.map;
                map = radius_str2map(c, old->dst->name, T_BARE_WORD, old->op,
                                     old->src->name, T_BARE_WORD,
                                     REQUEST_CURRENT, PAIR_LIST_REQUEST,
                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
                if (!map) {
-                       cf_log_err(map->ci, "Failed parsing condition");
+                       cf_log_err(old->ci, "Failed parsing condition");
                        return false;
                }
                map->ci = old->ci;
@@ -2566,7 +2939,66 @@ check_paircmp:
        /*
         *      Just in case someone adds a new fixup later.
         */
-       rad_assert(c->pass2_fixup == PASS2_FIXUP_NONE);
+       rad_assert((c->pass2_fixup == PASS2_FIXUP_NONE) ||
+                  (c->pass2_fixup == PASS2_PAIRCOMPARE));
+
+       /*
+        *      Precompile xlat's
+        */
+       if (map->dst->type == VPT_TYPE_XLAT) {
+               /*
+                *      Don't compile the LHS to an attribute
+                *      reference for now.  When we do that, we've got
+                *      to check the RHS for type-specific data, and
+                *      parse it to a VPT_TYPE_DATA.
+                */
+               if (!pass2_xlat_compile(map->ci, &map->dst, false)) {
+                       return false;
+               }
+       }
+
+       if (map->src->type == VPT_TYPE_XLAT) {
+               /*
+                *      Convert the RHS to an attribute reference only
+                *      if the LHS is an attribute reference, too.
+                *
+                *      We can fix this when the code in evaluate.c
+                *      can handle strings on the LHS, and attributes
+                *      on the RHS.  For now, the code in parser.c
+                *      forbids this.
+                */
+               if (!pass2_xlat_compile(map->ci, &map->src, (map->dst->type == VPT_TYPE_ATTR))) {
+                       return false;
+               }
+       }
+
+       /*
+        *      Convert bare refs to %{Foreach-Variable-N}
+        */
+       if ((map->dst->type == VPT_TYPE_LITERAL) &&
+           (strncmp(map->dst->name, "Foreach-Variable-", 17) == 0)) {
+               char *fmt;
+               value_pair_tmpl_t *vpt;
+
+               fmt = talloc_asprintf(map->dst, "%%{%s}", map->dst->name);
+               vpt = radius_str2tmpl(map, fmt, T_DOUBLE_QUOTED_STRING, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+               if (!vpt) {
+                       cf_log_err(map->ci, "Failed compiling %s", map->dst->name);
+                       talloc_free(fmt);
+                       return false;
+               }
+               talloc_free(map->dst);
+               map->dst = vpt;
+       }
+
+#ifdef HAVE_REGEX_H
+       if (map->src->type == VPT_TYPE_REGEX) {
+               if (!pass2_regex_compile(map->ci, map->src)) {
+                       return false;
+               }
+       }
+       rad_assert(map->dst->type != VPT_TYPE_REGEX);
+#endif
 
        /*
         *      Only attributes can have a paircompare registered, and
@@ -2574,12 +3006,12 @@ check_paircmp:
         *      with the request pairs.
         */
        if ((map->dst->type != VPT_TYPE_ATTR) ||
-           (map->dst->request != REQUEST_CURRENT) ||
-           (map->dst->list != PAIR_LIST_REQUEST)) {
+           (map->dst->vpt_request != REQUEST_CURRENT) ||
+           (map->dst->vpt_list != PAIR_LIST_REQUEST)) {
                return true;
        }
 
-       if (!radius_find_compare(map->dst->da)) return true;
+       if (!radius_find_compare(map->dst->vpt_da)) return true;
 
        if (map->src->type == VPT_TYPE_ATTR) {
                cf_log_err(map->ci, "Cannot compare virtual attribute %s to another attribute",
@@ -2613,6 +3045,33 @@ check_paircmp:
 
        return true;
 }
+
+
+/*
+ *     Compile the RHS of update sections to xlat_exp_t
+ */
+static bool modcall_pass2_update(modgroup *g)
+{
+       value_pair_map_t *map;
+
+       for (map = g->map; map != NULL; map = map->next) {
+               if (map->src->type == VPT_TYPE_XLAT) {
+                       rad_assert(map->src->vpt_xlat == NULL);
+
+                       /*
+                        *      FIXME: compile to attribute && handle
+                        *      the conversion in radius_map2vp().
+                        */
+                       if (!pass2_xlat_compile(map->ci, &map->src, false)) {
+                               return false;
+                       }
+               }
+
+               rad_assert(map->src->type != VPT_TYPE_REGEX);
+       }
+
+       return true;
+}
 #endif
 
 /*
@@ -2629,41 +3088,343 @@ bool modcall_pass2(modcallable *mc)
                        rad_assert(0 == 1);
                        break;
 
-               case MOD_SINGLE:
 #ifdef WITH_UNLANG
-               case MOD_UPDATE: /* @todo: pre-parse xlat's */
+               case MOD_UPDATE:
+                       g = mod_callabletogroup(this);
+                       if (g->done_pass2) return true;
+
+                       if (!modcall_pass2_update(g)) {
+                               return false;
+                       }
+                       g->done_pass2 = true;
+                       break;
+
                case MOD_XLAT:   /* @todo: pre-parse xlat's */
                case MOD_BREAK:
                case MOD_REFERENCE:
 #endif
+
+               case MOD_SINGLE:
                        break;  /* do nothing */
 
 #ifdef WITH_UNLANG
                case MOD_IF:
                case MOD_ELSIF:
                        g = mod_callabletogroup(this);
+                       if (g->done_pass2) return true;
+
+                       /*
+                        *      Don't walk over these.
+                        */
+                       if ((g->cond->type == COND_TYPE_TRUE) ||
+                           (g->cond->type == COND_TYPE_FALSE)) {
+                               break;
+                       }
+
+                       /*
+                        *      The compilation code takes care of
+                        *      simplifying 'true' and 'false'
+                        *      conditions.  For others, we have to do
+                        *      a second pass to parse && compile xlats.
+                        */
                        if (!fr_condition_walk(g->cond, pass2_callback, NULL)) {
                                return false;
                        }
+
+                       if (!modcall_pass2(g->children)) return false;
+                       g->done_pass2 = true;
+                       break;
+#endif
+
+#ifdef WITH_UNLANG
+               case MOD_SWITCH:
+                       g = mod_callabletogroup(this);
+                       if (g->done_pass2) return true;
+
+                       /*
+                        *      We had &Foo-Bar, where Foo-Bar is
+                        *      defined by a module.
+                        */
+                       if (!g->vpt) {
+                               rad_assert(this->name != NULL);
+                               rad_assert(this->name[0] == '&');
+                               rad_assert(cf_section_name2_type(g->cs) == T_BARE_WORD);
+                               goto do_case;
+                       }
+
+                       /*
+                        *      Statically compile xlats
+                        */
+                       if (g->vpt->type == VPT_TYPE_XLAT) goto do_case_xlat;
+
+                       /*
+                        *      We may have: switch Foo-Bar {
+                        *
+                        *      where Foo-Bar is an attribute defined
+                        *      by a module.  Since there's no leading
+                        *      &, it's parsed as a literal.  But if
+                        *      we can parse it as an attribute,
+                        *      switch to using that.
+                        */
+                       if (g->vpt->type == VPT_TYPE_LITERAL) {
+                               value_pair_tmpl_t *vpt;
+
+                               vpt = radius_str2tmpl(g->cs, this->name,
+                                                     cf_section_name2_type(g->cs),
+                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                               if (vpt->type == VPT_TYPE_ATTR) {
+                                       talloc_free(g->vpt);
+                                       g->vpt = vpt;
+                               }
+                       }
+
+                       /*
+                        *      Warn about old-style configuration.
+                        *
+                        *      DEPRECATED: switch User-Name { ...
+                        *      ALLOWED   : switch &User-Name { ...
+                        */
+                       if ((g->vpt->type == VPT_TYPE_ATTR) &&
+                           (this->name[0] != '&')) {
+                               WARN("%s[%d]: Please change %s to &%s",
+                                      cf_section_filename(g->cs),
+                                      cf_section_lineno(g->cs),
+                                      this->name, this->name);
+                       }
+
+                       if (!modcall_pass2(g->children)) return false;
+                       g->done_pass2 = true;
+                       break;
+
+               case MOD_CASE:
+                       g = mod_callabletogroup(this);
+                       if (g->done_pass2) return true;
+
+               do_case:
+                       /*
+                        *      The statement may refer to an
+                        *      attribute which doesn't exist until
+                        *      all of the modules have been loaded.
+                        *      Check for that now.
+                        */
+                       if (!g->vpt && this->name &&
+                           (this->name[0] == '&') &&
+                           (cf_section_name2_type(g->cs) == T_BARE_WORD)) {
+                               g->vpt = radius_str2tmpl(g->cs, this->name,
+                                                        cf_section_name2_type(g->cs),
+                                                        REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                               if (!g->vpt) {
+                                       cf_log_err_cs(g->cs, "Syntax error in '%s': %s",
+                                                     this->name, fr_strerror());
+                                       return false;
+                               }
+                       }
+
+                       /*
+                        *      Do type-specific checks on the case statement
+                        */
+                       if (g->vpt && (g->vpt->type == VPT_TYPE_LITERAL)) {
+                               modgroup *f;
+
+                               rad_assert(this->parent != NULL);
+                               rad_assert(this->parent->type == MOD_SWITCH);
+
+                               f = mod_callabletogroup(mc->parent);
+                               rad_assert(f->vpt != NULL);
+
+                               /*
+                                *      We're switching over an
+                                *      attribute.  Check that the
+                                *      values match.
+                                */
+                               if (f->vpt->type == VPT_TYPE_ATTR) {
+                                       rad_assert(f->vpt->vpt_da != NULL);
+
+                                       if (!radius_cast_tmpl(g->vpt, f->vpt->vpt_da)) {
+                                               cf_log_err_cs(g->cs, "Invalid argument for case statement: %s",
+                                                             fr_strerror());
+                                               return false;
+                                       }
+                               }
+                       }
+
+               do_case_xlat:
+                       /*
+                        *      Compile and sanity check xlat
+                        *      expansions.
+                        */
+                       if (g->vpt &&
+                           (g->vpt->type == VPT_TYPE_XLAT) &&
+                           (!pass2_xlat_compile(cf_sectiontoitem(g->cs),
+                                                &g->vpt, true))) {
+                               return false;
+                       }
+
+                       if (!modcall_pass2(g->children)) return false;
+                       g->done_pass2 = true;
+                       break;
+
+               case MOD_FOREACH:
+                       g = mod_callabletogroup(this);
+                       if (g->done_pass2) return true;
+
+                       /*
+                        *      Already parsed, handle the children.
+                        */
+                       if (g->vpt) goto check_children;
+
+                       /*
+                        *      We had &Foo-Bar, where Foo-Bar is
+                        *      defined by a module.
+                        */
+                       rad_assert(this->name != NULL);
+                       rad_assert(this->name[0] == '&');
+                       rad_assert(cf_section_name2_type(g->cs) == T_BARE_WORD);
+
+                       /*
+                        *      The statement may refer to an
+                        *      attribute which doesn't exist until
+                        *      all of the modules have been loaded.
+                        *      Check for that now.
+                        */
+                       g->vpt = radius_str2tmpl(g->cs, this->name,
+                                                cf_section_name2_type(g->cs),
+                                                REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                       if (!g->vpt) {
+                               cf_log_err_cs(g->cs, "Syntax error in '%s': %s",
+                                             this->name, fr_strerror());
+                               return false;
+                       }
+
+               check_children:
+                       rad_assert(g->vpt->type == VPT_TYPE_ATTR);
+                       if (g->vpt->vpt_num != NUM_ANY) {
+                               cf_log_err_cs(g->cs, "MUST NOT use array references in 'foreach'");
+                               return false;
+                       }
+                       if (!modcall_pass2(g->children)) return false;
+                       g->done_pass2 = true;
+                       break;
+
+               case MOD_ELSE:
+               case MOD_POLICY:
                        /* FALL-THROUGH */
 #endif
 
                case MOD_GROUP:
                case MOD_LOAD_BALANCE:
                case MOD_REDUNDANT_LOAD_BALANCE:
+                       g = mod_callabletogroup(this);
+                       if (g->done_pass2) return true;
+                       if (!modcall_pass2(g->children)) return false;
+                       g->done_pass2 = true;
+                       break;
+               }
+       }
+
+       return true;
+}
+
+void modcall_debug(modcallable *mc, int depth)
+{
+       modcallable *this;
+       modgroup *g;
+       value_pair_map_t *map;
+       const char *name1;
+       char buffer[1024];
+
+       for (this = mc; this != NULL; this = this->next) {
+               switch (this->type) {
+               default:
+                       break;
+
+               case MOD_SINGLE: {
+                       modsingle *single = mod_callabletosingle(this);
+
+                       DEBUG("%.*s%s", depth, modcall_spaces,
+                               single->modinst->name);
+                       }
+                       break;
 
 #ifdef WITH_UNLANG
+               case MOD_UPDATE:
+                       g = mod_callabletogroup(this);
+                       DEBUG("%.*s%s {", depth, modcall_spaces,
+                               group_name[this->type]);
+
+                       for (map = g->map; map != NULL; map = map->next) {
+                               radius_map2str(buffer, sizeof(buffer), map);
+                               DEBUG("%.*s%s", depth + 1, modcall_spaces, buffer);
+                       }
+
+                       DEBUG("%.*s}", depth, modcall_spaces);
+                       break;
+
                case MOD_ELSE:
+                       g = mod_callabletogroup(this);
+                       DEBUG("%.*s%s {", depth, modcall_spaces,
+                               group_name[this->type]);
+                       modcall_debug(g->children, depth + 1);
+                       DEBUG("%.*s}", depth, modcall_spaces);
+                       break;
+
+               case MOD_IF:
+               case MOD_ELSIF:
+                       g = mod_callabletogroup(this);
+                       fr_cond_sprint(buffer, sizeof(buffer), g->cond);
+                       DEBUG("%.*s%s (%s) {", depth, modcall_spaces,
+                               group_name[this->type], buffer);
+                       modcall_debug(g->children, depth + 1);
+                       DEBUG("%.*s}", depth, modcall_spaces);
+                       break;
+
                case MOD_SWITCH:
                case MOD_CASE:
-               case MOD_FOREACH:
+                       g = mod_callabletogroup(this);
+                       radius_tmpl2str(buffer, sizeof(buffer), g->vpt);
+                       DEBUG("%.*s%s %s {", depth, modcall_spaces,
+                               group_name[this->type], buffer);
+                       modcall_debug(g->children, depth + 1);
+                       DEBUG("%.*s}", depth, modcall_spaces);
+                       break;
+
                case MOD_POLICY:
+               case MOD_FOREACH:
+                       g = mod_callabletogroup(this);
+                       DEBUG("%.*s%s %s {", depth, modcall_spaces,
+                               group_name[this->type], this->name);
+                       modcall_debug(g->children, depth + 1);
+                       DEBUG("%.*s}", depth, modcall_spaces);
+                       break;
+
+               case MOD_BREAK:
+                       DEBUG("%.*sbreak", depth, modcall_spaces);
+                       break;
+
 #endif
+               case MOD_GROUP:
                        g = mod_callabletogroup(this);
-                       if (!modcall_pass2(g->children)) return false;
+                       name1 = cf_section_name1(g->cs);
+                       if (strcmp(name1, "group") == 0) {
+                               DEBUG("%.*s%s {", depth, modcall_spaces,
+                                     group_name[this->type]);
+                       } else {
+                               DEBUG("%.*s%s %s {", depth, modcall_spaces,
+                                     name1, cf_section_name2(g->cs));
+                       }
+                       modcall_debug(g->children, depth + 1);
+                       DEBUG("%.*s}", depth, modcall_spaces);
+                       break;
+
+
+               case MOD_LOAD_BALANCE:
+               case MOD_REDUNDANT_LOAD_BALANCE:
+                       g = mod_callabletogroup(this);
+                       DEBUG("%.*s%s {", depth, modcall_spaces,
+                               group_name[this->type]);
+                       modcall_debug(g->children, depth + 1);
+                       DEBUG("%.*s}", depth, modcall_spaces);
                        break;
                }
        }
-
-       return true;
 }
index 8e0b03c..974655e 100644 (file)
@@ -102,6 +102,44 @@ const section_type_value_t section_type_value[RLM_COMPONENT_COUNT] = {
 #define LD_LIBRARY_PATH "LD_LIBRARY_PATH"
 #endif
 
+/** Check if the magic number in the module matches the one in the library
+ *
+ * This is used to detect potential ABI issues caused by running with modules which
+ * were built for a different version of the server.
+ *
+ * @param cs being parsed.
+ * @param module being loaded.
+ * @returns 0 on success, -1 if prefix mismatch, -2 if version mismatch, -3 if commit mismatch.
+ */
+static int check_module_magic(CONF_SECTION *cs, module_t const *module)
+{
+       if (MAGIC_PREFIX(module->magic) != MAGIC_PREFIX(RADIUSD_MAGIC_NUMBER)) {
+               cf_log_err_cs(cs, "Application and rlm_%s magic number (prefix) mismatch."
+                             "  application: %x module: %x", module->name,
+                             MAGIC_PREFIX(RADIUSD_MAGIC_NUMBER),
+                             MAGIC_PREFIX(module->magic));
+               return -1;
+       }
+
+       if (MAGIC_VERSION(module->magic) != MAGIC_VERSION(RADIUSD_MAGIC_NUMBER)) {
+               cf_log_err_cs(cs, "Application and rlm_%s magic number (version) mismatch."
+                             "  application: %lx module: %lx", module->name,
+                             (unsigned long) MAGIC_VERSION(RADIUSD_MAGIC_NUMBER),
+                             (unsigned long) MAGIC_VERSION(module->magic));
+               return -2;
+       }
+
+       if (MAGIC_COMMIT(module->magic) != MAGIC_COMMIT(RADIUSD_MAGIC_NUMBER)) {
+               cf_log_err_cs(cs, "Application and rlm_%s magic number (commit) mismatch."
+                             "  application: %lx module: %lx", module->name,
+                             (unsigned long) MAGIC_COMMIT(RADIUSD_MAGIC_NUMBER),
+                             (unsigned long) MAGIC_COMMIT(module->magic));
+               return -3;
+       }
+
+       return 0;
+}
+
 /*
  *     Because dlopen produces really shitty and inaccurate error messages
  */
@@ -109,13 +147,13 @@ static void check_lib_access(char const *name)
 {
        if (access(name, R_OK) < 0) switch (errno) {
                case EACCES:
-                       WDEBUG("Library \"%s\" exists, but we don't have permission to read", name);
+                       WARN("Library \"%s\" exists, but we don't have permission to read", name);
                        break;
                case ENOENT:
                        DEBUG4("Library not found at path \"%s\"", name);
                        break;
                default:
-                       DEBUG4("Possible issue accessing Library \"%s\": %s", name, strerror(errno));
+                       DEBUG4("Possible issue accessing Library \"%s\": %s", name, fr_syserror(errno));
                        break;
        }
 }
@@ -125,6 +163,7 @@ lt_dlhandle lt_dlopenext(char const *name)
        int flags = RTLD_NOW;
        void *handle;
        char buffer[2048];
+       char *env;
 
 #ifdef RTLD_GLOBAL
        if (strcmp(name, "rlm_perl") == 0) {
@@ -133,18 +172,54 @@ lt_dlhandle lt_dlopenext(char const *name)
 #endif
                flags |= RTLD_LOCAL;
 
+#ifndef NDEBUG
+       /*
+        *      Bind all the symbols *NOW* so we don't hit errors later
+        */
+       flags |= RTLD_NOW;
+#endif
        /*
         *      Prefer loading our libraries by absolute path.
         */
        snprintf(buffer, sizeof(buffer), "%s/%s%s", radlib_dir, name, LT_SHREXT);
 
-       check_lib_access(buffer);
+       DEBUG4("Loading library using absolute path");
 
        handle = dlopen(buffer, flags);
-       if (handle) return handle;
+       if (handle) {
+               return handle;
+       }
+       check_lib_access(buffer);
 
-       strlcpy(buffer, name, sizeof(buffer));
+       DEBUG4("Falling back to linker search path(s)");
+       if (DEBUG_ENABLED4) {
+#ifdef __APPLE__
+               env = getenv("LD_LIBRARY_PATH");
+               if (env) {
+                       DEBUG4("LD_LIBRARY_PATH            : %s", env);
+               }
+               env = getenv("DYLD_LIBRARY_PATH");
+               if (env) {
+                       DEBUG4("DYLB_LIBRARY_PATH          : %s", env);
+               }
+               env = getenv("DYLD_FALLBACK_LIBRARY_PATH");
+               if (env) {
+                       DEBUG4("DYLD_FALLBACK_LIBRARY_PATH : %s", env);
+               }
+               env = getcwd(buffer, sizeof(buffer));
+               if (env) {
+                       DEBUG4("Current directory          : %s", env);
+               }
+#else
+               env = getenv("LD_LIBRARY_PATH");
+               if (env) {
+                       DEBUG4("LD_LIBRARY_PATH  : %s", env);
+               }
+               DEBUG4("Defaults         : /lib:/usr/lib");
+#endif
+       }
 
+       strlcpy(buffer, name, sizeof(buffer));
        /*
         *      FIXME: Make this configurable...
         */
@@ -354,7 +429,7 @@ static int module_entry_free(module_entry_t *this)
         *      debugging.  This removes the symbols needed by
         *      valgrind.
         */
-       if (!mainconfig.debug_memory)
+       if (!main_config.debug_memory)
 #endif
                dlclose(this->handle);  /* ignore any errors */
        return 0;
@@ -364,7 +439,7 @@ static int module_entry_free(module_entry_t *this)
 /*
  *     Remove the module lists.
  */
-int detach_modules(void)
+int modules_free(void)
 {
        rbtree_free(instance_tree);
        rbtree_free(module_tree);
@@ -430,13 +505,9 @@ static module_entry_t *linkto_module(char const *module_name,
        /*
         *      Before doing anything else, check if it's sane.
         */
-       if (module->magic != RLM_MODULE_MAGIC_NUMBER) {
+       if (check_module_magic(cs, module) < 0) {
                dlclose(handle);
-               cf_log_err_cs(cs,
-                          "Invalid version in module '%s'",
-                          module_name);
                return NULL;
-
        }
 
        /* make room for the module type */
@@ -580,7 +651,7 @@ module_instance_t *find_module_instance(CONF_SECTION *modules,
        }
 
        if (check_config && (node->entry->module->instantiate) &&
-           (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_SAFE) == 0) {
+           (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_UNSAFE) != 0) {
                char const *value = NULL;
                CONF_PAIR *cp;
 
@@ -718,7 +789,7 @@ rlm_rcode_t indexed_modcall(rlm_components_t comp, int idx, REQUEST *request)
 
        if (idx == 0) {
                list = server->mc[comp];
-               if (!list) RWDEBUG2("Empty %s section.  Using default return values.", section_type_value[comp].section);
+               if (!list) RDEBUG3("Empty %s section.  Using default return values.", section_type_value[comp].section);
 
        } else {
                indexed_modcallable *this;
@@ -727,7 +798,7 @@ rlm_rcode_t indexed_modcall(rlm_components_t comp, int idx, REQUEST *request)
                if (this) {
                        list = this->modulelist;
                } else {
-                       RWDEBUG2("Unknown value specified for %s.  Cannot perform requested action.",
+                       RDEBUG3("%s sub-section not found.  Ignoring.",
                                section_type_value[comp].typename);
                }
        }
@@ -757,7 +828,7 @@ rlm_rcode_t indexed_modcall(rlm_components_t comp, int idx, REQUEST *request)
  */
 static int load_subcomponent_section(modcallable *parent, CONF_SECTION *cs,
                                     rbtree_t *components,
-                                    DICT_ATTR const *dattr, rlm_components_t comp)
+                                    DICT_ATTR const *da, rlm_components_t comp)
 {
        indexed_modcallable *subcomp;
        modcallable *ml;
@@ -785,7 +856,7 @@ static int load_subcomponent_section(modcallable *parent, CONF_SECTION *cs,
         *      automatically.  If it isn't found, it's a serious
         *      error.
         */
-       dval = dict_valbyname(dattr->attr, dattr->vendor, name2);
+       dval = dict_valbyname(da->attr, da->vendor, name2);
        if (!dval) {
                cf_log_err_cs(cs,
                           "%s %s Not previously configured",
@@ -804,7 +875,7 @@ static int load_subcomponent_section(modcallable *parent, CONF_SECTION *cs,
        return 1;               /* OK */
 }
 
-static int define_type(CONF_SECTION *cs, DICT_ATTR const *dattr, char const *name)
+static int define_type(CONF_SECTION *cs, DICT_ATTR const *da, char const *name)
 {
        uint32_t value;
        DICT_VALUE *dval;
@@ -813,7 +884,7 @@ static int define_type(CONF_SECTION *cs, DICT_ATTR const *dattr, char const *nam
         *      If the value already exists, don't
         *      create it again.
         */
-       dval = dict_valbyname(dattr->attr, dattr->vendor, name);
+       dval = dict_valbyname(da->attr, da->vendor, name);
        if (dval) return 1;
 
        /*
@@ -825,10 +896,10 @@ static int define_type(CONF_SECTION *cs, DICT_ATTR const *dattr, char const *nam
         */
        do {
                value = fr_rand() & 0x00ffffff;
-       } while (dict_valbyattr(dattr->attr, dattr->vendor, value));
+       } while (dict_valbyattr(da->attr, da->vendor, value));
 
-       cf_log_module(cs, "Creating %s = %s", dattr->name, name);
-       if (dict_addvalue(name, dattr->name, value) < 0) {
+       cf_log_module(cs, "Creating %s = %s", da->name, name);
+       if (dict_addvalue(name, da->name, value) < 0) {
                ERROR("%s", fr_strerror());
                return 0;
        }
@@ -836,6 +907,13 @@ static int define_type(CONF_SECTION *cs, DICT_ATTR const *dattr, char const *nam
        return 1;
 }
 
+/*
+ *     Don't complain too often.
+ */
+#define MAX_IGNORED (32)
+static int last_ignored = -1;
+static char const *ignored[MAX_IGNORED];
+
 static int load_component_section(CONF_SECTION *cs,
                                  rbtree_t *components, rlm_components_t comp)
 {
@@ -844,14 +922,13 @@ static int load_component_section(CONF_SECTION *cs,
        int idx;
        indexed_modcallable *subcomp;
        char const *modname;
-       char const *visiblename;
-       DICT_ATTR const *dattr;
+       DICT_ATTR const *da;
 
        /*
         *      Find the attribute used to store VALUEs for this section.
         */
-       dattr = dict_attrbyvalue(section_type_value[comp].attr, 0);
-       if (!dattr) {
+       da = dict_attrbyvalue(section_type_value[comp].attr, 0);
+       if (!da) {
                cf_log_err_cs(cs,
                           "No such attribute %s",
                           section_type_value[comp].typename);
@@ -878,8 +955,9 @@ static int load_component_section(CONF_SECTION *cs,
                                   section_type_value[comp].typename) == 0) {
                                if (!load_subcomponent_section(NULL, scs,
                                                               components,
-                                                              dattr,
+                                                              da,
                                                               comp)) {
+
                                        return -1; /* FIXME: memleak? */
                                }
                                continue;
@@ -887,43 +965,6 @@ static int load_component_section(CONF_SECTION *cs,
 
                        cp = NULL;
 
-                       /*
-                        *      Skip commented-out sections.
-                        *
-                        *      We skip an "if" ONLY when there's no
-                        *      "else" after it, as the run-time
-                        *      interpretor needs the results of the
-                        *      previous "if".
-                        */
-                       if (strcmp(name1, "if") == 0) {
-                               fr_cond_t const *c;
-                               CONF_ITEM *next_ci;
-
-                               next_ci = cf_item_find_next(scs, modref);
-                               if (next_ci && cf_item_is_section(next_ci)) {
-                                       char const *next_name;
-                                       CONF_SECTION *next_cs;
-
-                                       next_cs = cf_itemtosection(next_ci);
-                                       next_name = cf_section_name1(next_cs);
-                                       if ((strcmp(next_name, "else") == 0) ||
-                                           (strcmp(next_name, "elseif") == 0)) {
-                                               c = NULL;
-                                       } else {
-                                               c = cf_data_find(scs, "if");
-                                       }
-                               } else {
-                                       c = cf_data_find(scs, "if");
-                               }
-
-                               if (c && c->type == COND_TYPE_FALSE) {
-                                       DEBUG(" # Skipping contents of '%s' at %s:%d as it statically evaluates to 'false'",
-                                            name1, cf_section_filename(scs), cf_section_lineno(scs));
-                                       continue;
-                               }
-                       }
-
-
                } else if (cf_item_is_pair(modref)) {
                        cp = cf_itemtopair(modref);
 
@@ -932,26 +973,6 @@ static int load_component_section(CONF_SECTION *cs,
                }
 
                /*
-                *      Try to compile one entry.
-                */
-               this = compile_modsingle(NULL, comp, modref, &modname);
-
-               /*
-                *      It's OK for the module to not exist.
-                */
-               if (!this && modname && (modname[0] == '-')) {
-                       WDEBUG("Ignoring \"%s\" (see raddb/mods-available/README.rst)", modname + 1);
-                       continue;
-               }
-
-               if (!this) {
-                       cf_log_err_cs(cs,
-                                  "Errors parsing %s section.\n",
-                                  cf_section_name1(cs));
-                       return -1;
-               }
-
-               /*
                 *      Look for Auth-Type foo {}, which are special
                 *      cases of named sections, and allowable ONLY
                 *      at the top-level.
@@ -967,7 +988,6 @@ static int load_component_section(CONF_SECTION *cs,
                        } else {
                                modrefname = cf_section_name2(scs);
                                if (!modrefname) {
-                                       modcallable_free(&this);
                                        cf_log_err_cs(cs,
                                                   "Errors parsing %s sub-section.\n",
                                                   cf_section_name1(scs));
@@ -981,7 +1001,6 @@ static int load_component_section(CONF_SECTION *cs,
                                 *      It's a section, but nothing we
                                 *      recognize.  Die!
                                 */
-                               modcallable_free(&this);
                                cf_log_err_cs(cs,
                                           "Unknown Auth-Type \"%s\" in %s sub-section.",
                                           modrefname, section_type_value[comp].section);
@@ -995,20 +1014,54 @@ static int load_component_section(CONF_SECTION *cs,
                }
 
                subcomp = new_sublist(cs, components, comp, idx);
-               if (subcomp == NULL) {
-                       modcallable_free(&this);
+               if (!subcomp) continue;
+
+               /*
+                *      Try to compile one entry.
+                */
+               this = compile_modsingle(&subcomp->modulelist, comp, modref, &modname);
+
+               /*
+                *      It's OK for the module to not exist.
+                */
+               if (!this && modname && (modname[0] == '-')) {
+                       int i;
+
+                       if (last_ignored < 0) {
+                       save_complain:
+                               last_ignored++;
+                               ignored[last_ignored] = modname;
+
+                       complain:
+                               WARN("Ignoring \"%s\" (see raddb/mods-available/README.rst)", modname + 1);
+                               continue;
+                       }
+
+                       if (last_ignored >= MAX_IGNORED) goto complain;
+
+                       for (i = 0; i <= last_ignored; i++) {
+                               if (strcmp(ignored[i], modname) == 0) {
+                                       break;
+                               }
+                       }
+
+                       if (i > last_ignored) goto save_complain;
                        continue;
                }
 
-               /* If subcomp->modulelist is NULL, add_to_modcallable will
-                * create it */
-               visiblename = cf_section_name2(cs);
-               if (visiblename == NULL)
-                       visiblename = cf_section_name1(cs);
-               add_to_modcallable(&subcomp->modulelist, this,
-                                  comp, visiblename);
+               if (!this) {
+                       cf_log_err_cs(cs,
+                                  "Errors parsing %s section.\n",
+                                  cf_section_name1(cs));
+                       return -1;
+               }
+
+               if (debug_flag > 2) modcall_debug(this, 2);
+
+               add_to_modcallable(subcomp->modulelist, this);
        }
 
+
        return 0;
 }
 
@@ -1047,7 +1100,7 @@ static int load_byserver(CONF_SECTION *cs)
        for (comp = 0; comp < RLM_COMPONENT_COUNT; ++comp) {
                CONF_SECTION *subcs;
                CONF_ITEM *modref;
-               DICT_ATTR const *dattr;
+               DICT_ATTR const *da;
 
                subcs = cf_section_sub_find(cs,
                                            section_type_value[comp].section);
@@ -1058,8 +1111,8 @@ static int load_byserver(CONF_SECTION *cs)
                /*
                 *      Find the attribute used to store VALUEs for this section.
                 */
-               dattr = dict_attrbyvalue(section_type_value[comp].attr, 0);
-               if (!dattr) {
+               da = dict_attrbyvalue(section_type_value[comp].attr, 0);
+               if (!da) {
                        cf_log_err_cs(subcs,
                                   "No such attribute %s",
                                   section_type_value[comp].typename);
@@ -1090,7 +1143,7 @@ static int load_byserver(CONF_SECTION *cs)
                        if ((section_type_value[comp].attr == PW_AUTH_TYPE) &&
                            cf_item_is_pair(modref)) {
                                CONF_PAIR *cp = cf_itemtopair(modref);
-                               if (!define_type(cs, dattr, cf_pair_attr(cp))) {
+                               if (!define_type(cs, da, cf_pair_attr(cp))) {
                                        goto error;
                                }
 
@@ -1103,7 +1156,7 @@ static int load_byserver(CONF_SECTION *cs)
                        name1 = cf_section_name1(subsubcs);
 
                        if (strcmp(name1, section_type_value[comp].typename) == 0) {
-                         if (!define_type(cs, dattr,
+                         if (!define_type(cs, da,
                                           cf_section_name2(subsubcs))) {
                                        goto error;
                                }
@@ -1125,16 +1178,13 @@ static int load_byserver(CONF_SECTION *cs)
 
                if (cf_item_find_next(subcs, NULL) == NULL) continue;
 
-               cf_log_module(cs, "Loading %s {...}",
-                             section_type_value[comp].section);
-
                /*
                 *      Skip pre/post-proxy sections if we're not
                 *      proxying.
                 */
                if (
 #ifdef WITH_PROXY
-                   !mainconfig.proxy_requests &&
+                   !main_config.proxy_requests &&
 #endif
                    ((comp == RLM_COMPONENT_PRE_PROXY) ||
                     (comp == RLM_COMPONENT_POST_PROXY))) {
@@ -1149,10 +1199,21 @@ static int load_byserver(CONF_SECTION *cs)
                if (comp == RLM_COMPONENT_SESS) continue;
 #endif
 
+               if (debug_flag <= 3) {
+                       cf_log_module(cs, "Loading %s {...}",
+                                     section_type_value[comp].section);
+               } else {
+                       DEBUG(" %s {", section_type_value[comp].section);
+               }
+
                if (load_component_section(subcs, components, comp) < 0) {
                        goto error;
                }
 
+               if (debug_flag > 3) {
+                       DEBUG(" } # %s", section_type_value[comp].section);
+               }
+
                /*
                 *      Cache a default, if it exists.  Some people
                 *      put empty sections for some reason...
@@ -1172,14 +1233,17 @@ static int load_byserver(CONF_SECTION *cs)
         *      This is a bit of a hack...
         */
        if (!found) do {
+#if defined(WITH_VMPS) || defined(WITH_DHCP)
                CONF_SECTION *subcs;
+#endif
 #ifdef WITH_DHCP
-               DICT_ATTR const *dattr;
+               DICT_ATTR const *da;
 #endif
 
+#ifdef WITH_VMPS
                subcs = cf_section_sub_find(cs, "vmps");
                if (subcs) {
-                       cf_log_module(cs, "Checking vmps {...} for more modules to load");
+                       cf_log_module(cs, "Loading vmps {...}");
                        if (load_component_section(subcs, components,
                                                   RLM_COMPONENT_POST_AUTH) < 0) {
                                goto error;
@@ -1190,16 +1254,16 @@ static int load_byserver(CONF_SECTION *cs)
                        found = 1;
                        break;
                }
+#endif
 
 #ifdef WITH_DHCP
+               /*
+                *      It's OK to not have DHCP.
+                */
                subcs = cf_subsection_find_next(cs, NULL, "dhcp");
-               dattr = dict_attrbyname("DHCP-Message-Type");
-               if (!dattr && subcs) {
-                       cf_log_err_cs(subcs, "Found a 'dhcp' section, but no DHCP dictionaries have been loaded");
-                       goto error;
-               }
+               if (!subcs) break;
 
-               if (!dattr) break;
+               da = dict_attrbyname("DHCP-Message-Type");
 
                /*
                 *      Handle each DHCP Message type separately.
@@ -1207,10 +1271,14 @@ static int load_byserver(CONF_SECTION *cs)
                while (subcs) {
                        char const *name2 = cf_section_name2(subcs);
 
-                       DEBUG2(" Module: Checking dhcp %s {...} for more modules to load", name2);
+                       if (name2) {
+                               cf_log_module(cs, "Loading dhcp %s {...}", name2);
+                       } else {
+                               cf_log_module(cs, "Loading dhcp {...}");
+                       }
                        if (!load_subcomponent_section(NULL, subcs,
                                                       components,
-                                                      dattr,
+                                                      da,
                                                       RLM_COMPONENT_POST_AUTH)) {
                                goto error; /* FIXME: memleak? */
                        }
@@ -1231,7 +1299,7 @@ static int load_byserver(CONF_SECTION *cs)
        }
 
        if (!found && name) {
-               WDEBUG("Server %s is empty, and will do nothing!",
+               WARN("Server %s is empty, and will do nothing!",
                      name);
        }
 
@@ -1267,6 +1335,15 @@ static int load_byserver(CONF_SECTION *cs)
 }
 
 
+static int pass2_cb(UNUSED void *ctx, void *data)
+{
+       indexed_modcallable *this = data;
+
+       if (!modcall_pass2(this->modulelist)) return -1;
+
+       return 0;
+}
+
 /*
  *     Load all of the virtual servers.
  */
@@ -1274,9 +1351,9 @@ int virtual_servers_load(CONF_SECTION *config)
 {
        CONF_SECTION *cs;
        virtual_server_t *server;
-       static int first_time = true;
+       static bool first_time = true;
 
-       DEBUG2("%s: #### Loading Virtual Servers ####", mainconfig.name);
+       DEBUG2("%s: #### Loading Virtual Servers ####", main_config.name);
 
        /*
         *      If we have "server { ...}", then there SHOULD NOT be
@@ -1350,6 +1427,13 @@ int virtual_servers_load(CONF_SECTION *config)
 
                for (i = RLM_COMPONENT_AUTH; i < RLM_COMPONENT_COUNT; i++) {
                        if (!modcall_pass2(server->mc[i])) return -1;
+
+               }
+
+               if (server->components &&
+                   (rbtree_walk(server->components, RBTREE_IN_ORDER,
+                                pass2_cb, NULL) != 0)) {
+                       return -1;
                }
        }
 
@@ -1418,7 +1502,7 @@ int module_hup_module(CONF_SECTION *cs, module_instance_t *node, time_t when)
 }
 
 
-int module_hup(CONF_SECTION *modules)
+int modules_hup(CONF_SECTION *modules)
 {
        time_t when;
        CONF_ITEM *ci;
@@ -1460,37 +1544,27 @@ int module_hup(CONF_SECTION *modules)
 /*
  *     Parse the module config sections, and load
  *     and call each module's init() function.
- *
- *     Libtool makes your life a LOT easier, especially with libltdl.
- *     see: http://www.gnu.org/software/libtool/
  */
-int setup_modules(int reload, CONF_SECTION *config)
+int modules_init(CONF_SECTION *config)
 {
        CONF_ITEM       *ci, *next;
        CONF_SECTION    *cs, *modules;
        rad_listen_t    *listener;
 
-       if (reload) return 0;
-
        /*
-        *      If necessary, initialize libltdl.
+        *      Set up the internal module struct.
         */
-       if (!reload) {
-               /*
-                *      Set up the internal module struct.
-                */
-               module_tree = rbtree_create(module_entry_cmp, NULL, 0);
-               if (!module_tree) {
-                       ERROR("Failed to initialize modules\n");
-                       return -1;
-               }
+       module_tree = rbtree_create(module_entry_cmp, NULL, 0);
+       if (!module_tree) {
+               ERROR("Failed to initialize modules\n");
+               return -1;
+       }
 
-               instance_tree = rbtree_create(module_instance_cmp,
-                                             module_instance_free, 0);
-               if (!instance_tree) {
-                       ERROR("Failed to initialize modules\n");
-                       return -1;
-               }
+       instance_tree = rbtree_create(module_instance_cmp,
+                                     module_instance_free, 0);
+       if (!instance_tree) {
+               ERROR("Failed to initialize modules\n");
+               return -1;
        }
 
        memset(virtual_servers, 0, sizeof(virtual_servers));
@@ -1503,7 +1577,71 @@ int setup_modules(int reload, CONF_SECTION *config)
                WARN("Cannot find a \"modules\" section in the configuration file!");
        }
 
-       DEBUG2("%s: #### Instantiating modules ####", mainconfig.name);
+#if defined(WITH_VMPS) || defined(WITH_DHCP)
+       /*
+        *      Load dictionaries.
+        */
+       for (cs = cf_subsection_find_next(config, NULL, "server");
+            cs != NULL;
+            cs = cf_subsection_find_next(config, cs, "server")) {
+               CONF_SECTION *subcs;
+               DICT_ATTR const *da;
+
+#ifdef WITH_VMPS
+               /*
+                *      Auto-load the VMPS/VQP dictionary.
+                */
+               subcs = cf_section_sub_find(cs, "vmps");
+               if (subcs) {
+                       da = dict_attrbyname("VQP-Packet-Type");
+                       if (!da) {
+                               if (dict_read(main_config.dictionary_dir, "dictionary.vqp") < 0) {
+                                       ERROR("Failed reading dictionary.vqp: %s",
+                                             fr_strerror());
+                                       return -1;
+                               }
+                               cf_log_module(cs, "Loading dictionary.vqp");
+
+                               da = dict_attrbyname("VQP-Packet-Type");
+                               if (!da) {
+                                       ERROR("No VQP-Packet-Type in dictionary.vqp");
+                                       return -1;
+                               }
+                       }
+               }
+#endif
+
+#ifdef WITH_DHCP
+               /*
+                *      Auto-load the DHCP dictionary.
+                */
+               subcs = cf_subsection_find_next(cs, NULL, "dhcp");
+               if (subcs) {
+                       da = dict_attrbyname("DHCP-Message-Type");
+                       if (!da) {
+                               cf_log_module(cs, "Loading dictionary.dhcp");
+                               if (dict_read(main_config.dictionary_dir, "dictionary.dhcp") < 0) {
+                                       ERROR("Failed reading dictionary.dhcp: %s",
+                                             fr_strerror());
+                                       return -1;
+                               }
+
+                               da = dict_attrbyname("DHCP-Message-Type");
+                               if (!da) {
+                                       ERROR("No DHCP-Message-Type in dictionary.dhcp");
+                                       return -1;
+                               }
+                       }
+               }
+#endif
+               /*
+                *      Else it's a RADIUS virtual server, and the
+                *      dictionaries are already loaded.
+                */
+       }
+#endif
+
+       DEBUG2("%s: #### Instantiating modules ####", main_config.name);
 
        /*
         *      Loop over module definitions, looking for duplicates.
@@ -1614,7 +1752,7 @@ int setup_modules(int reload, CONF_SECTION *config)
         *      Loop over the listeners, figuring out which sections
         *      to load.
         */
-       for (listener = mainconfig.listen;
+       for (listener = main_config.listen;
             listener != NULL;
             listener = listener->next) {
                char buffer[256];
index 4dcf4c3..51cb6e1 100644 (file)
@@ -30,15 +30,29 @@ RCSID("$Id$")
 
 #define PW_CAST_BASE (1850)
 
+static const FR_NAME_NUMBER allowed_return_codes[] = {
+       { "reject",     1 },
+       { "fail",       1 },
+       { "ok",         1 },
+       { "handled",    1 },
+       { "invalid",    1 },
+       { "userlock",   1 },
+       { "notfound",   1 },
+       { "noop",       1 },
+       { "updated",    1 },
+       { NULL, 0 }
+};
+
 /*
  *     This file shouldn't use any functions from the server core.
  */
 
-size_t fr_cond_sprint(char *buffer, size_t bufsize, fr_cond_t const *c)
+size_t fr_cond_sprint(char *buffer, size_t bufsize, fr_cond_t const *in)
 {
        size_t len;
        char *p = buffer;
        char *end = buffer + bufsize - 1;
+       fr_cond_t const *c = in;
 
 next:
        if (c->negate) {
@@ -120,43 +134,6 @@ next:
 }
 
 
-/*
- *     Cast a literal vpt to a value_data_t
- */
-static int cast_vpt(value_pair_tmpl_t *vpt, DICT_ATTR const *da)
-{
-       VALUE_PAIR *vp;
-       value_data_t *data;
-
-       rad_assert(vpt->type == VPT_TYPE_LITERAL);
-
-       vp = pairalloc(vpt, da);
-       if (!vp) return false;
-
-       if (!pairparsevalue(vp, vpt->name)) {
-               pairfree(&vp);
-               return false;
-       }
-
-       vpt->length = vp->length;
-       vpt->vpd = data = talloc(vpt, value_data_t);
-       if (!vpt->vpd) return false;
-
-       vpt->type = VPT_TYPE_DATA;
-       vpt->da = da;
-
-       if (vp->da->flags.is_pointer) {
-               data->ptr = talloc_steal(vpt, vp->data.ptr);
-               vp->data.ptr = NULL;
-       } else {
-               memcpy(data, &vp->data, sizeof(*data));
-       }
-
-       pairfree(&vp);
-
-       return true;
-}
-
 static ssize_t condition_tokenize_string(TALLOC_CTX *ctx, char const *start, char **out,
                                         FR_TOKEN *op, char const **error)
 {
@@ -308,6 +285,27 @@ static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda,
                return -(p - start);
        }
 
+       /*
+        *      We can only cast to basic data types.  Complex ones
+        *      are forbidden.
+        */
+       switch (cast) {
+#ifdef WITH_ASCEND_BINARY
+       case PW_TYPE_ABINARY:
+#endif
+       case PW_TYPE_IP_ADDR:
+       case PW_TYPE_TLV:
+       case PW_TYPE_EXTENDED:
+       case PW_TYPE_LONG_EXTENDED:
+       case PW_TYPE_EVS:
+       case PW_TYPE_VSA:
+               *error = "Forbidden data type in cast";
+               return -(p - start);
+
+       default:
+               break;
+       }
+
        *pda = dict_attrbyvalue(PW_CAST_BASE + cast, 0);
        if (!*pda) {
                *error = "Cannot cast to this data type";
@@ -342,7 +340,8 @@ static ssize_t condition_tokenize_cast(char const *start, DICT_ATTR const **pda,
  *  @param[out] error the parse error (if any)
  *  @return length of the string skipped, or when negative, the offset to the offending error
  */
-static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *start, int brace, fr_cond_t **pcond, char const **error, int flags)
+static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *start, int brace,
+                                 fr_cond_t **pcond, char const **error, int flags)
 {
        ssize_t slen;
        char const *p = start;
@@ -355,6 +354,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
 
        rad_assert(c != NULL);
        lhs = rhs = NULL;
+       lhs_type = rhs_type = T_OP_INVALID;
 
        while (isspace((int) *p)) p++; /* skip spaces before condition */
 
@@ -389,6 +389,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                 *      brackets.  Go recurse to get more.
                 */
                c->type = COND_TYPE_CHILD;
+               c->ci = ci;
                slen = condition_tokenize(c, ci, p, true, &c->data.child, error, flags);
                if (slen <= 0) {
                        return_SLEN;
@@ -466,36 +467,27 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                return_0("Cannot do cast for existence check");
                        }
 
-                       if (lhs_type == T_BARE_WORD) {
-                               if ((strcmp(lhs, "true") == 0) ||
-                                   ((lhs[0] == '1') && !lhs[1])) {
-                                       c->type = COND_TYPE_TRUE;
-
-                               } else if ((strcmp(lhs, "false") == 0) ||
-                                          ((lhs[0] == '0') && !lhs[1])) {
-                                       c->type = COND_TYPE_FALSE;
-
-                               } else {
-                                       goto create_exists;
-                               }
+                       c->type = COND_TYPE_EXISTS;
+                       c->ci = ci;
 
-                       } else {
-                       create_exists:
-                               c->type = COND_TYPE_EXISTS;
-                               c->data.vpt = radius_str2tmpl(c, lhs, lhs_type);
-                               if (!c->data.vpt) {
-                                       return_P("Failed creating exists");
-                               }
+                       c->data.vpt = radius_str2tmpl(c, lhs, lhs_type, REQUEST_CURRENT, PAIR_LIST_REQUEST);
+                       if (!c->data.vpt) {
+                               return_P("Failed creating exists");
                        }
 
+                       rad_assert(c->data.vpt->type != VPT_TYPE_REGEX);
+
                } else { /* it's an operator */
-                       int regex;
+                       bool regex;
+                       bool i_flag = false;
 
                        /*
                         *      The next thing should now be a comparison operator.
                         */
                        regex = false;
                        c->type = COND_TYPE_MAP;
+                       c->ci = ci;
+
                        switch (*p) {
                        default:
                                return_P("Invalid text. Expected comparison operator");
@@ -623,7 +615,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 *      Allow /foo/i
                                 */
                                if (p[slen] == 'i') {
-                                       c->regex_i = true;
+                                       i_flag = true;
                                        slen++;
                                }
 
@@ -634,11 +626,23 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                        c->data.map = radius_str2map(c, lhs, lhs_type, op, rhs, rhs_type,
                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST,
                                                     REQUEST_CURRENT, PAIR_LIST_REQUEST);
-
                        if (!c->data.map) {
+                               /*
+                                *      FIXME: if strings are T_BARE_WORD and they start with '&',
+                                *      then they refer to attributes which have not yet been
+                                *      defined.  Create the template(s) as literals, and
+                                *      fix them up in pass2.
+                                */
+                               if (*lhs == '&') {
+                                       return_0("Unknown attribute");
+                               }
                                return_0("Syntax error");
                        }
 
+                       if (c->data.map->src->type == VPT_TYPE_REGEX) {
+                               c->data.map->src->vpt_iflag = i_flag;
+                       }
+
                        /*
                         *      Could have been a reference to an attribute which is registered later.
                         *      Mark it as being checked in pass2.
@@ -654,30 +658,6 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                        c->data.map->ci = ci;
 
                        /*
-                        *      foo =* bar is just (foo)
-                        *      foo !* bar is just (!foo)
-                        */
-                       if ((op == T_OP_CMP_TRUE) || (op == T_OP_CMP_FALSE)) {
-                               value_pair_tmpl_t *vpt;
-
-                               vpt = talloc_steal(c, c->data.map->dst);
-                               c->data.map->dst = NULL;
-
-                               talloc_free(c->data.map);
-                               c->type = COND_TYPE_EXISTS;
-                               c->data.vpt = vpt;
-
-                               /*
-                                *      Invert the negation bit.
-                                */
-                               if (op == T_OP_CMP_FALSE) {
-                                       c->negate = !c->negate;
-                               }
-
-                               goto done_cond;
-                       }
-
-                       /*
                         *      @todo: check LHS and RHS separately, to
                         *      get better errors
                         */
@@ -694,7 +674,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                         */
                        if (c->cast) {
                                if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
-                                   (c->cast->type != c->data.map->src->da->type)) {
+                                   (c->cast->type != c->data.map->src->vpt_da->type)) {
                                        goto same_type;
                                }
 
@@ -707,7 +687,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 *      Cast it to the appropriate data type.
                                 */
                                if ((c->data.map->dst->type == VPT_TYPE_LITERAL) &&
-                                   !cast_vpt(c->data.map->dst, c->cast)) {
+                                   !radius_cast_tmpl(c->data.map->dst, c->cast)) {
                                        *error = "Failed to parse field";
                                        if (lhs) talloc_free(lhs);
                                        if (rhs) talloc_free(rhs);
@@ -721,18 +701,77 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 */
                                if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
                                    (c->data.map->src->type == VPT_TYPE_LITERAL) &&
-                                   !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
+                                   !radius_cast_tmpl(c->data.map->src, c->data.map->dst->vpt_da)) {
                                        return_rhs("Failed to parse field");
                                }
 
                                /*
+                                *      We may be casting incompatible
+                                *      types.  We check this based on
+                                *      their size.
+                                */
+                               if (c->data.map->dst->type == VPT_TYPE_ATTR) {
+                                       /*
+                                        *      dst.min == src.min
+                                        *      dst.max == src.max
+                                        */
+                                       if ((dict_attr_sizes[c->cast->type][0] == dict_attr_sizes[c->data.map->dst->vpt_da->type][0]) &&
+                                           (dict_attr_sizes[c->cast->type][1] == dict_attr_sizes[c->data.map->dst->vpt_da->type][1])) {
+                                               goto cast_ok;
+                                       }
+
+                                       /*
+                                        *      Run-time parsing of strings.
+                                        *      Run-time copying of octets.
+                                        */
+                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_STRING) ||
+                                           (c->data.map->dst->vpt_da->type == PW_TYPE_OCTETS)) {
+                                               goto cast_ok;
+                                       }
+
+                                       /*
+                                        *      ipaddr to ipv4prefix is OK
+                                        */
+                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_IPV4_ADDR) &&
+                                           (c->cast->type == PW_TYPE_IPV4_PREFIX)) {
+                                               goto cast_ok;
+                                       }
+
+                                       /*
+                                        *      ipv6addr to ipv6prefix is OK
+                                        */
+                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_IPV6_ADDR) &&
+                                           (c->cast->type == PW_TYPE_IPV6_PREFIX)) {
+                                               goto cast_ok;
+                                       }
+
+                                       /*
+                                        *      integer64 to ethernet is OK.
+                                        */
+                                       if ((c->data.map->dst->vpt_da->type == PW_TYPE_INTEGER64) &&
+                                           (c->cast->type == PW_TYPE_ETHERNET)) {
+                                               goto cast_ok;
+                                       }
+
+                                       /*
+                                        *      dst.max < src.min
+                                        *      dst.min > src.max
+                                        */
+                                       if ((dict_attr_sizes[c->cast->type][1] < dict_attr_sizes[c->data.map->dst->vpt_da->type][0]) ||
+                                           (dict_attr_sizes[c->cast->type][0] > dict_attr_sizes[c->data.map->dst->vpt_da->type][1])) {
+                                               return_0("Cannot cast to attribute of incompatible size");
+                                       }
+                               }
+
+                       cast_ok:
+                               /*
                                 *      Casting to a redundant type means we don't need the cast.
                                 *
                                 *      Do this LAST, as the rest of the code above assumes c->cast
                                 *      is not NULL.
                                 */
                                if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
-                                   (c->cast->type == c->data.map->dst->da->type)) {
+                                   (c->cast->type == c->data.map->dst->vpt_da->type)) {
                                        c->cast = NULL;
                                }
 
@@ -742,9 +781,9 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 */
                                if ((c->data.map->src->type == VPT_TYPE_ATTR) &&
                                    (c->data.map->dst->type == VPT_TYPE_ATTR) &&
-                                   (c->data.map->dst->da->type != c->data.map->src->da->type)) {
+                                   (c->data.map->dst->vpt_da->type != c->data.map->src->vpt_da->type)) {
                                same_type:
-                                       return_0("Attribute comparisons must be of the same attribute type");
+                                       return_0("Attribute comparisons must be of the same data type");
                                }
 
                                /*
@@ -764,10 +803,15 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                /*
                                 *      Invalid: User-Name == bob
                                 *      Valid:   User-Name == "bob"
+                                *
+                                *      There's no real reason for
+                                *      this, other than consistency.
                                 */
                                if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
                                    (c->data.map->src->type != VPT_TYPE_ATTR) &&
-                                   (c->data.map->dst->da->type == PW_TYPE_STRING) &&
+                                   (c->data.map->dst->vpt_da->type == PW_TYPE_STRING) &&
+                                   (c->data.map->op != T_OP_CMP_TRUE) &&
+                                   (c->data.map->op != T_OP_CMP_FALSE) &&
                                    (rhs_type == T_BARE_WORD)) {
                                        return_rhs("Must have string as value for attribute");
                                }
@@ -779,9 +823,9 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 */
                                if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
                                    (c->data.map->src->type != VPT_TYPE_ATTR) &&
-                                   (c->data.map->dst->da->type != PW_TYPE_STRING) &&
-                                   (c->data.map->dst->da->type != PW_TYPE_OCTETS) &&
-                                   (c->data.map->dst->da->type != PW_TYPE_DATE) &&
+                                   (c->data.map->dst->vpt_da->type != PW_TYPE_STRING) &&
+                                   (c->data.map->dst->vpt_da->type != PW_TYPE_OCTETS) &&
+                                   (c->data.map->dst->vpt_da->type != PW_TYPE_DATE) &&
                                    (rhs_type == T_SINGLE_QUOTED_STRING)) {
                                        *error = "Value must be an unquoted string";
                                return_rhs:
@@ -796,7 +840,7 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 *      literal.  Cast the RHS to the type of the cast.
                                 */
                                if (c->cast && (c->data.map->src->type == VPT_TYPE_LITERAL) &&
-                                   !cast_vpt(c->data.map->src, c->cast)) {
+                                   !radius_cast_tmpl(c->data.map->src, c->cast)) {
                                        return_rhs("Failed to parse field");
                                }
 
@@ -806,8 +850,8 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                 */
                                if ((c->data.map->dst->type == VPT_TYPE_ATTR) &&
                                    (c->data.map->src->type == VPT_TYPE_LITERAL) &&
-                                   !cast_vpt(c->data.map->src, c->data.map->dst->da)) {
-                                       DICT_ATTR const *da = c->data.map->dst->da;
+                                   !radius_cast_tmpl(c->data.map->src, c->data.map->dst->vpt_da)) {
+                                       DICT_ATTR const *da = c->data.map->dst->vpt_da;
 
                                        if ((da->vendor == 0) &&
                                            ((da->attr == PW_AUTH_TYPE) ||
@@ -832,7 +876,6 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
                                }
                        }
 
-               done_cond:
                        p += slen;
 
                        while (isspace((int) *p)) p++; /* skip spaces after RHS */
@@ -849,7 +892,6 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
 
                p++;
                while (isspace((int) *p)) p++; /* skip spaces after closing brace */
-               brace = false;
                goto done;
        }
 
@@ -895,7 +937,16 @@ static ssize_t condition_tokenize(TALLOC_CTX *ctx, CONF_ITEM *ci, char const *st
 
 done:
        /*
-        *      Normalize it before returning it.
+        *      Normalize the condition before returning.
+        *
+        *      We collapse multiple levels of braces to one.  Then
+        *      convert maps to literals.  Then literals to true/false
+        *      statements.  Then true/false ||/&& followed by other
+        *      conditions to just conditions.
+        *
+        *      Order is important.  The more complex cases are
+        *      converted to simpler ones, from the most complex cases
+        *      to the simplest ones.
         */
 
        /*
@@ -947,11 +998,12 @@ done:
        }
 
        /*
-        *      Normalize negation.  This doesn't really make any
-        *      difference, but it simplifies the run-time code in
-        *      evaluate.c
+        *      Convert maps to literals.  Convert one form of map to
+        *      a standardized form.  This doesn't make any
+        *      theoretical difference, but it does mean that the
+        *      run-time evaluation has fewer cases to check.
         */
-       if (c->type == COND_TYPE_MAP) {
+       if (c->type == COND_TYPE_MAP) do {
                /*
                 *      !FOO !~ BAR --> FOO =~ BAR
                 */
@@ -978,7 +1030,8 @@ done:
 
                /*
                 *      This next one catches "LDAP-Group != foo",
-                *      which doesn't really work, but this hack fixes it.
+                *      which doesn't work as-is, but this hack fixes
+                *      it.
                 *
                 *      FOO != BAR --> !FOO == BAR
                 */
@@ -987,6 +1040,37 @@ done:
                        c->data.map->op = T_OP_CMP_EQ;
                }
 
+               /*
+                *      FOO =* BAR --> FOO
+                *      FOO !* BAR --> !FOO
+                */
+               if ((c->data.map->op == T_OP_CMP_TRUE) ||
+                   (c->data.map->op == T_OP_CMP_FALSE)) {
+                       value_pair_tmpl_t *vpt;
+
+                       vpt = talloc_steal(c, c->data.map->dst);
+                       c->data.map->dst = NULL;
+
+                       /*
+                        *      Invert the negation bit.
+                        */
+                       if (c->data.map->op == T_OP_CMP_FALSE) {
+                               c->negate = !c->negate;
+                       }
+
+                       TALLOC_FREE(c->data.map);
+
+                       c->type = COND_TYPE_EXISTS;
+                       c->data.vpt = vpt;
+                       break;  /* it's no longer a map */
+               }
+
+               /*
+                *      Both are data (IP address, integer, etc.)
+                *
+                *      We can do the evaluation here, so that it
+                *      doesn't need to be done at run time
+                */
                if ((c->data.map->dst->type == VPT_TYPE_DATA) &&
                    (c->data.map->src->type == VPT_TYPE_DATA)) {
                        int rcode;
@@ -994,18 +1078,193 @@ done:
                        rad_assert(c->cast != NULL);
 
                        rcode = radius_evaluate_map(NULL, 0, 0, c);
-                       talloc_free(c->data.map);
-                       c->data.map = NULL;
+                       TALLOC_FREE(c->data.map);
                        c->cast = NULL;
-                       c->regex_i = false;
                        if (rcode) {
                                c->type = COND_TYPE_TRUE;
                        } else {
                                c->type = COND_TYPE_FALSE;
                        }
+
+                       break;  /* it's no longer a map */
+               }
+
+               /*
+                *      Both are literal strings.  They're not parsed
+                *      as VPT_TYPE_DATA because there's no cast to an
+                *      attribute.
+                *
+                *      We can do the evaluation here, so that it
+                *      doesn't need to be done at run time
+                */
+               if ((c->data.map->src->type == VPT_TYPE_LITERAL) &&
+                   (c->data.map->dst->type == VPT_TYPE_LITERAL)) {
+                       int rcode;
+
+                       rad_assert(c->cast == NULL);
+
+                       rcode = radius_evaluate_map(NULL, 0, 0, c);
+                       if (rcode) {
+                               c->type = COND_TYPE_TRUE;
+                       } else {
+                               c->type = COND_TYPE_FALSE;
+                       }
+
+                       /*
+                        *      Free map after using it above.
+                        */
+                       TALLOC_FREE(c->data.map);
+                       break;
+               }
+
+               /*
+                *      <ipaddr>"foo" CMP &Attribute-Name The cast is
+                *      unnecessary, and we can re-write it so that
+                *      the attribute reference is on the LHS.
+                */
+               if (c->cast &&
+                   (c->data.map->src->type == VPT_TYPE_ATTR) &&
+                   (c->data.map->dst->type != VPT_TYPE_ATTR)) {
+                       value_pair_tmpl_t *tmp;
+
+                       tmp = c->data.map->src;
+                       c->data.map->src = c->data.map->dst;
+                       c->data.map->dst = tmp;
+
+                       c->cast = NULL;
+
+                       switch (c->data.map->op) {
+                       case T_OP_CMP_EQ:
+                               /* do nothing */
+                               break;
+
+                       case T_OP_LE:
+                               c->data.map->op = T_OP_GE;
+                               break;
+
+                       case T_OP_LT:
+                               c->data.map->op = T_OP_GT;
+                               break;
+
+                       case T_OP_GE:
+                               c->data.map->op = T_OP_LE;
+                               break;
+
+                       case T_OP_GT:
+                               c->data.map->op = T_OP_LT;
+                               break;
+
+                       default:
+                               return_0("Internal sanity check failed");
+                       }
+
+                       /*
+                        *      This must have been parsed into VPT_TYPE_DATA.
+                        */
+                       rad_assert(c->data.map->src->type != VPT_TYPE_LITERAL);
+               }
+
+       } while (0);
+
+       /*
+        *      Existence checks.  We short-circuit static strings,
+        *      too.
+        */
+       if (c->type == COND_TYPE_EXISTS) {
+               switch (c->data.vpt->type) {
+               case VPT_TYPE_XLAT:
+               case VPT_TYPE_ATTR:
+               case VPT_TYPE_LIST:
+               case VPT_TYPE_EXEC:
+                       break;
+
+                       /*
+                        *      'true' and 'false' are special strings
+                        *      which mean themselves.
+                        *
+                        *      For integers, 0 is false, all other
+                        *      integers are true.
+                        *
+                        *      For strings, '' and "" are false.
+                        *      'foo' and "foo" are true.
+                        *
+                        *      The str2tmpl function takes care of
+                        *      marking "%{foo}" as VPT_TYPE_XLAT, so
+                        *      the strings here are fixed at compile
+                        *      time.
+                        *
+                        *      `exec` and "%{...}" are left alone.
+                        *
+                        *      Bare words must be module return
+                        *      codes.
+                        */
+               case VPT_TYPE_LITERAL:
+                       if ((strcmp(c->data.vpt->name, "true") == 0) ||
+                           (strcmp(c->data.vpt->name, "1") == 0)) {
+                               c->type = COND_TYPE_TRUE;
+                               TALLOC_FREE(c->data.vpt);
+
+                       } else if ((strcmp(c->data.vpt->name, "false") == 0) ||
+                                  (strcmp(c->data.vpt->name, "0") == 0)) {
+                               c->type = COND_TYPE_FALSE;
+                               TALLOC_FREE(c->data.vpt);
+
+                       } else if (!*c->data.vpt->name) {
+                               c->type = COND_TYPE_FALSE;
+                               TALLOC_FREE(c->data.vpt);
+
+                       } else if ((lhs_type == T_SINGLE_QUOTED_STRING) ||
+                                  (lhs_type == T_DOUBLE_QUOTED_STRING)) {
+                               c->type = COND_TYPE_TRUE;
+                               TALLOC_FREE(c->data.vpt);
+
+                       } else if (lhs_type == T_BARE_WORD) {
+                               int rcode;
+                               char const *q;
+
+                               for (q = c->data.vpt->name;
+                                    *q != '\0';
+                                    q++) {
+                                       if (!isdigit((int) *q)) {
+                                               break;
+                                       }
+                               }
+
+                               /*
+                                *      It's all digits, and therefore
+                                *      'true'.
+                                */
+                               if (!*q) {
+                                       c->type = COND_TYPE_TRUE;
+                                       TALLOC_FREE(c->data.vpt);
+                                       break;
+                               }
+
+                               rcode = fr_str2int(allowed_return_codes,
+                                                  c->data.vpt->name, 0);
+                               if (!rcode) {
+                                       return_0("Expected a module return code");
+                               }
+                       }
+
+                       /*
+                        *      Else lhs_type==T_OP_INVALID, and this
+                        *      node was made by promoting a child
+                        *      which had already been normalized.
+                        */
+                       break;
+
+               case VPT_TYPE_DATA:
+                       return_0("Cannot use data here");
+
+               default:
+                       return_0("Internal sanity check failed");
                }
        }
 
+       /*
+        *      !TRUE -> FALSE
+        */
        if (c->type == COND_TYPE_TRUE) {
                if (c->negate) {
                        c->negate = false;
@@ -1013,6 +1272,9 @@ done:
                }
        }
 
+       /*
+        *      !FALSE -> TRUE
+        */
        if (c->type == COND_TYPE_FALSE) {
                if (c->negate) {
                        c->negate = false;
index ba23f07..671dd50 100644 (file)
@@ -47,12 +47,17 @@ extern pid_t radius_pid;
 extern bool check_config;
 extern fr_cond_t *debug_condition;
 
-static int spawn_flag = 0;
-static int just_started = true;
-time_t                         fr_start_time;
+static bool spawn_flag = false;
+static bool just_started = true;
+time_t fr_start_time = (time_t)-1;
 static fr_packet_list_t *pl = NULL;
 static fr_event_list_t *el = NULL;
 
+fr_event_list_t *radius_event_list_corral(UNUSED event_corral_t hint) {
+       /* Currently we do not run a second event loop for modules. */
+       return el;
+}
+
 static char const *action_codes[] = {
        "INVALID",
        "run",
@@ -66,7 +71,25 @@ static char const *action_codes[] = {
 };
 
 #ifdef DEBUG_STATE_MACHINE
-#define TRACE_STATE_MACHINE if (debug_flag) printf("(%u) ********\tSTATE %s action %s live M%u C%u\t********\n", request->number, __FUNCTION__, action_codes[action], request->master_state, request->child_state)
+#define TRACE_STATE_MACHINE if (debug_flag) printf("(%u) ********\tSTATE %s action %s live M-%s C-%s\t********\n", request->number, __FUNCTION__, action_codes[action], master_state_names[request->master_state], child_state_names[request->child_state])
+
+static char const *master_state_names[REQUEST_MASTER_NUM_STATES] = {
+       "?",
+       "active",
+       "stop-processing",
+       "counted"
+};
+
+static char const *child_state_names[REQUEST_CHILD_NUM_STATES] = {
+       "?",
+       "queued",
+       "running",
+       "proxied",
+       "reject-delay",
+       "cleanup-delay",
+       "done"
+};
+
 #else
 #define TRACE_STATE_MACHINE {}
 #endif
@@ -75,7 +98,7 @@ static char const *action_codes[] = {
  *     Declare a state in the state machine.
  *
  */
-#define STATE_MACHINE_DECL(_x) static void _x(REQUEST *request, int action)
+#define STATE_MACHINE_DECL(_x) static void CC_HINT(nonnull) _x(REQUEST *request, int action)
 
 #define STATE_MACHINE_TIMER(_x) request->timer_action = _x; \
                fr_event_insert(el, request_timer, request, \
@@ -105,7 +128,7 @@ static char const *action_codes[] = {
  * -   Y: Reply is ready.  Rejects MAY be delayed here.  All other
  *        replies are sent immediately.
  *
- * -   J: Reject is sent "reject_delay" after the reply is ready.
+ * -   J: Reject is sent "response_delay" after the reply is ready.
  *
  * -   C: For Access-Requests, After "cleanup_delay", the request is
  *        deleted.  Accounting-Request packets go directly from Y to C.
@@ -167,32 +190,91 @@ static fr_packet_list_t *proxy_list = NULL;
 
 #ifdef HAVE_PTHREAD_H
 #ifdef WITH_PROXY
-static pthread_mutex_t proxy_mutex;
-static rad_listen_t *proxy_listener_list = NULL;
-static int proxy_no_new_sockets = false;
+static pthread_mutex_t proxy_mutex;
+static bool proxy_no_new_sockets = false;
 #endif
 
 #define PTHREAD_MUTEX_LOCK if (spawn_flag) pthread_mutex_lock
 #define PTHREAD_MUTEX_UNLOCK if (spawn_flag) pthread_mutex_unlock
 
 static pthread_t NO_SUCH_CHILD_PID;
+#define NO_CHILD_THREAD request->child_pid = NO_SUCH_CHILD_PID
+
 #else
 /*
  *     This is easier than ifdef's throughout the code.
  */
 #define PTHREAD_MUTEX_LOCK(_x)
 #define PTHREAD_MUTEX_UNLOCK(_x)
+#define NO_CHILD_THREAD
+#endif
+
+#if  defined(HAVE_PTHREAD_H) && !defined (NDEBUG)
+static bool we_are_master(void)
+{
+       if (spawn_flag &&
+           (pthread_equal(pthread_self(), NO_SUCH_CHILD_PID) == 0)) {
+               return false;
+       }
+
+       return true;
+}
+#define ASSERT_MASTER  if (!we_are_master()) rad_panic("We are not master")
+
+#else
+#define we_are_master(_x) (1)
+#define ASSERT_MASTER
 #endif
 
+static int event_new_fd(rad_listen_t *this);
+
 /*
  *     We need mutexes around the event FD list *only* in certain
  *     cases.
  */
 #if defined (HAVE_PTHREAD_H) && (defined(WITH_PROXY) || defined(WITH_TCP))
+static rad_listen_t *new_listeners = NULL;
+
 static pthread_mutex_t fd_mutex;
 #define FD_MUTEX_LOCK if (spawn_flag) pthread_mutex_lock
 #define FD_MUTEX_UNLOCK if (spawn_flag) pthread_mutex_unlock
+
+void radius_update_listener(rad_listen_t *this)
+{
+       /*
+        *      Just do it ourselves.
+        */
+       if (we_are_master()) {
+               event_new_fd(this);
+               return;
+       }
+
+       FD_MUTEX_LOCK(&fd_mutex);
+
+       /*
+        *      If it's already in the list, don't add it again.
+        */
+       if (this->next) {
+               FD_MUTEX_UNLOCK(&fd_mutex);
+               return;
+       }
+
+       /*
+        *      Otherwise, add it to the list
+        */
+       this->next = new_listeners;
+       new_listeners = this;
+       FD_MUTEX_UNLOCK(&fd_mutex);
+       radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD);
+}
 #else
+void radius_update_listener(rad_listen_t *this)
+{
+       /*
+        *      No threads.  Just insert it.
+        */
+       event_new_fd(this);
+}
 /*
  *     This is easier than ifdef's throughout the code.
  */
@@ -200,13 +282,14 @@ static pthread_mutex_t    fd_mutex;
 #define FD_MUTEX_UNLOCK(_x)
 #endif
 
-static int request_num_counter = 0;
+static int request_num_counter = 1;
 #ifdef WITH_PROXY
 static int request_will_proxy(REQUEST *request);
 static int request_proxy(REQUEST *request, int retransmit);
 STATE_MACHINE_DECL(proxy_wait_for_reply);
+STATE_MACHINE_DECL(proxy_no_reply);
 STATE_MACHINE_DECL(proxy_running);
-static int process_proxy_reply(REQUEST *request);
+static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply);
 static void remove_from_proxy_hash(REQUEST *request);
 static void remove_from_proxy_hash_nl(REQUEST *request, bool yank);
 static int insert_into_proxy_hash(REQUEST *request);
@@ -216,31 +299,14 @@ static REQUEST *request_setup(rad_listen_t *listener, RADIUS_PACKET *packet,
                              RADCLIENT *client, RAD_REQUEST_FUNP fun);
 
 STATE_MACHINE_DECL(request_common);
-
-#if  defined(HAVE_PTHREAD_H) && !defined (NDEBUG)
-static int we_are_master(void)
-{
-       if (spawn_flag &&
-           (pthread_equal(pthread_self(), NO_SUCH_CHILD_PID) == 0)) {
-               return 0;
-       }
-
-       return 1;
-}
-#define ASSERT_MASTER  if (!we_are_master()) rad_panic("We are not master")
-
-#else
-#define we_are_master(_x) (1)
-#define ASSERT_MASTER
-#endif
-
-STATE_MACHINE_DECL(request_reject_delay);
+STATE_MACHINE_DECL(request_response_delay);
 STATE_MACHINE_DECL(request_cleanup_delay);
 STATE_MACHINE_DECL(request_running);
 #ifdef WITH_COA
-static void request_coa_timer(REQUEST *request);
 static void request_coa_originate(REQUEST *request);
-STATE_MACHINE_DECL(request_coa_process);
+STATE_MACHINE_DECL(coa_running);
+STATE_MACHINE_DECL(coa_wait_for_reply);
+STATE_MACHINE_DECL(coa_no_reply);
 static void request_coa_separate(REQUEST *coa);
 #endif
 
@@ -277,7 +343,7 @@ static void tv_add(struct timeval *tv, int usec_delay)
 /*
  *     In daemon mode, AND this request has debug flags set.
  */
-#define DEBUG_PACKET if (!debug_flag && request->options && request->radlog) debug_packet
+#define DEBUG_PACKET if (!debug_flag && request->log.lvl && request->log.func) debug_packet
 
 static void debug_packet(REQUEST *request, RADIUS_PACKET *packet, int direction)
 {
@@ -286,11 +352,11 @@ static void debug_packet(REQUEST *request, RADIUS_PACKET *packet, int direction)
        char buffer[1024];
        char const *received, *from;
        fr_ipaddr_t const *ip;
-       int port;
+       uint16_t port;
 
        if (!packet) return;
 
-       rad_assert(request->radlog != NULL);
+       rad_assert(request->log.func != NULL);
 
        if (direction == 0) {
                received = "Received";
@@ -311,22 +377,22 @@ static void debug_packet(REQUEST *request, RADIUS_PACKET *packet, int direction)
         *
         *      This really belongs in a utility library
         */
-       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
-               RDEBUG("%s %s packet %s host %s port %d, id=%d, length=%d",
+       if (is_radius_code(packet->code)) {
+               RDEBUG("%s %s packet %s host %s port %i, id=%i, length=%zu",
                       received, fr_packet_codes[packet->code], from,
                       inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
                       port, packet->id, packet->data_len);
        } else {
-               RDEBUG("%s packet %s host %s port %d code=%d, id=%d, length=%d",
+               RDEBUG("%s packet %s host %s port %d code=%d, id=%d, length=%zu",
                       received, from,
                       inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
                       port,
                       packet->code, packet->id, packet->data_len);
        }
 
-       for (vp = paircursor(&cursor, &packet->vps);
+       for (vp = fr_cursor_init(&cursor, &packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                vp_prints(buffer, sizeof(buffer), vp);
                RDEBUG("\t%s", buffer);
        }
@@ -339,6 +405,24 @@ static void debug_packet(REQUEST *request, RADIUS_PACKET *packet, int direction)
  *
  ***********************************************************************/
 
+static struct timeval *request_response_window(REQUEST *request)
+{
+       /*
+        *      The client hasn't set the response window.  Return
+        *      either the home server one, if set, or the global one.
+        */
+       if (!timerisset(&request->client->response_window)) {
+               return &request->home_server->response_window;
+       }
+
+       if (timercmp(&request->client->response_window,
+                    &request->home_server->response_window, <)) {
+               return &request->client->response_window;
+       }
+
+       return &request->home_server->response_window;
+}
+
 /*
  *     Callback for ALL timer events related to the request.
  */
@@ -352,14 +436,15 @@ static void request_timer(void *ctx)
        request->process(request, action);
 }
 
-#define USEC (1000000)
-
 /*
  *     Only ever called from the master thread.
  */
 STATE_MACHINE_DECL(request_done)
 {
        struct timeval now, when;
+#ifdef WITH_PROXY
+       char buffer[128];
+#endif
 
        TRACE_STATE_MACHINE;
 
@@ -383,7 +468,7 @@ STATE_MACHINE_DECL(request_done)
         */
        if (!we_are_master()) {
                request->child_state = REQUEST_DONE;
-               request->child_pid = NO_SUCH_CHILD_PID;;
+               NO_CHILD_THREAD;
                return;
        }
 #endif
@@ -392,14 +477,10 @@ STATE_MACHINE_DECL(request_done)
        /*
         *      Move the CoA request to its own handler.
         */
-       if (request->coa) request_coa_separate(request->coa);
-
-       /*
-        *      If we're the CoA request, make the parent forget about
-        *      us.
-        */
-       if (request->parent && (request->parent->coa == request)) {
-               request->parent->coa = NULL;
+       if (request->coa) {
+               request_coa_separate(request->coa);
+       } else if (request->parent && (request->parent->coa == request)) {
+               request_coa_separate(request);
        }
 
 #endif
@@ -414,6 +495,8 @@ STATE_MACHINE_DECL(request_done)
                if (request->reply->code != 0) {
                        request->listener->send(request->listener, request);
                        return;
+               } else {
+                       RDEBUG("No reply.  Ignoring retransmit");
                }
                break;
 
@@ -464,7 +547,10 @@ STATE_MACHINE_DECL(request_done)
                }
 #endif
 #ifdef DEBUG_STATE_MACHINE
-               if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_DONE);
+               if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
+                                      request->number, __FUNCTION__,
+                                      child_state_names[request->child_state],
+                                      child_state_names[REQUEST_DONE]);
 #endif
                request->child_state = REQUEST_DONE;
                break;
@@ -483,8 +569,12 @@ STATE_MACHINE_DECL(request_done)
                 *      packets from the home server.
                 */
        case FR_ACTION_PROXY_REPLY:
-               request_common(request, action);
-               break;
+               RDEBUG2("Reply from home server %s port %d  - ID: %d arrived too late.  Try increasing 'retry_delay' or 'max_request_time'",
+                      inet_ntop(request->proxy->src_ipaddr.af,
+                                &request->proxy->src_ipaddr.ipaddr,
+                                buffer, sizeof(buffer)),
+                       request->proxy->dst_port, request->proxy->id);
+               return;
 #endif
 
        default:
@@ -501,15 +591,6 @@ STATE_MACHINE_DECL(request_done)
                        rad_assert(0 == 1);
                }
                request->in_request_hash = false;
-
-               /*
-                *      @todo: do final states for TCP sockets, too?
-                */
-               request_stats_final(request);
-
-#ifdef WITH_TCP
-               request->listener->count--;
-#endif
        }
 
 #ifdef WITH_PROXY
@@ -524,13 +605,13 @@ STATE_MACHINE_DECL(request_done)
                when = request->proxy->timestamp;
 
 #ifdef WITH_COA
-               if (((request->proxy->code == PW_COA_REQUEST) ||
-                    (request->proxy->code == PW_DISCONNECT_REQUEST)) &&
+               if (((request->proxy->code == PW_CODE_COA_REQUEST) ||
+                    (request->proxy->code == PW_CODE_DISCONNECT_REQUEST)) &&
                    (request->packet->code != request->proxy->code)) {
                        when.tv_sec += request->home_server->coa_mrd;
                } else
 #endif
-               when.tv_sec += request->home_server->response_window;
+                       timeradd(&when, request_response_window(request), &when);
 
                /*
                 *      We haven't received all responses, AND there's still
@@ -588,6 +669,14 @@ STATE_MACHINE_DECL(request_done)
        rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
 #endif
 
+       /*
+        *      @todo: do final states for TCP sockets, too?
+        */
+       request_stats_final(request);
+#ifdef WITH_TCP
+       if (request->listener) request->listener->count--;
+#endif
+
        if (request->packet) {
                RDEBUG2("Cleaning up request packet ID %u with timestamp +%d",
                        request->packet->id,
@@ -604,7 +693,8 @@ static void request_cleanup_delay_init(REQUEST *request, struct timeval const *p
 {
        struct timeval now, when;
 
-       if (request->packet->code == PW_ACCOUNTING_REQUEST) goto done;
+       if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) goto done;
+
        if (!request->root->cleanup_delay) goto done;
 
        if (pnow) {
@@ -663,7 +753,8 @@ static void request_process_timer(REQUEST *request)
        if (request->coa) request_coa_separate(request->coa);
 
        /*
-        *      Check request stuff ONLY if we're running the request.
+        *      If we're the request, OR it isn't originating a CoA
+        *      request, check more things.
         */
        if (!request->proxy || (request->packet->code == request->proxy->code))
 #endif
@@ -675,27 +766,65 @@ static void request_process_timer(REQUEST *request)
                 *      there is no point in continuing.
                 */
                if (request->listener->status != RAD_LISTEN_STATUS_KNOWN) {
-                       WDEBUG("Socket was closed while processing request %u: Stopping it.", request->number);
-#ifdef WITH_ACCOUNTING
-                       goto done;
-#else
+                       if ((request->master_state == REQUEST_ACTIVE) &&
+                           (request->child_state < REQUEST_RESPONSE_DELAY)) {
+                               WARN("Socket was closed while processing request %u: Stopping it.", request->number);
+                               request->master_state = REQUEST_STOP_PROCESSING;
+                       }
+               }
+       }
+
+       gettimeofday(&now, NULL);
+
+       /*
+        *      The request was forcibly stopped.
+        */
+       if (request->master_state == REQUEST_STOP_PROCESSING) {
+               switch (request->child_state) {
+               case REQUEST_QUEUED:
+               case REQUEST_RUNNING:
+#ifdef HAVE_PTHREAD_H
+                       rad_assert(spawn_flag == true);
+#endif
+
+               delay:
+                       /*
+                        *      Sleep for some more.  We HOPE that the
+                        *      child will become responsive at some
+                        *      point in the future.
+                        */
+                       when = now;
+                       tv_add(&when, request->delay);
+                       request->delay += request->delay >> 1;
+                       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+                       return;
+
+                       /*
+                        *      These should all be managed by the master thread
+                        */
+#ifdef WITH_PROXY
+               case REQUEST_PROXIED:
+#endif
+               case REQUEST_RESPONSE_DELAY:
+               case REQUEST_CLEANUP_DELAY:
+               case REQUEST_DONE:
                done:
                        request_done(request, FR_ACTION_DONE);
                        return;
-#endif
-
                }
        }
 
-       gettimeofday(&now, NULL);
+       rad_assert(request->master_state == REQUEST_ACTIVE);
 
        /*
-        *      A child thread is still working on the request,
-        *      OR it was proxied, and there was no response,
-        *      OR it was sitting in the queue for too long.
+        *      It's still supposed to be running.
         */
-       if ((request->child_state != REQUEST_DONE) &&
-           (request->master_state != REQUEST_STOP_PROCESSING)) {
+       switch (request->child_state) {
+       case REQUEST_QUEUED:
+       case REQUEST_RUNNING:
+#ifdef WITH_PROXY
+       case REQUEST_PROXIED:
+#endif
                when = request->packet->timestamp;
                when.tv_sec += request->root->max_request_time;
 
@@ -711,118 +840,111 @@ static void request_process_timer(REQUEST *request)
                        if (spawn_flag &&
                            (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0)) {
                                ERROR("Unresponsive child for request %u, in component %s module %s",
-                                      request->number,
-                                      request->component ? request->component : "<core>",
-                              request->module ? request->module : "<core>");
+                                     request->number,
+                                     request->component ? request->component : "<core>",
+                                     request->module ? request->module : "<core>");
                                exec_trigger(request, NULL, "server.thread.unresponsive", true);
                        }
 #endif
+                       request->master_state = REQUEST_STOP_PROCESSING;
+               }
+
+#ifdef WITH_PROXY
+               /*
+                *      We should wait for the proxy reply.
+                */
+               if (request->child_state == REQUEST_PROXIED) {
+                       rad_assert(request->proxy != NULL);
 
+#ifdef WITH_COA
                        /*
-                        *      Tell the request to stop it.
+                        *      Ugh.
                         */
-                       goto done;
-               } /* else we're not at max_request_time */
-
-#ifdef WITH_PROXY
-               if ((request->master_state != REQUEST_STOP_PROCESSING) &&
-                   request->proxy &&
-                   (request->process == request_running)) {
-#ifdef DEBUG_STATE_MACHINE
-                       if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_proxied");
+                       if (request->packet->code != request->proxy->code) {
+                               if (request->proxy_reply) {
+                                       request->process = coa_running;
+                               } else {
+                                       request->process = coa_wait_for_reply;
+                               }
+                               goto delay;
+                       }
 #endif
-                       request->process = proxy_wait_for_reply;
+                       if (request->proxy_reply) {
+                               request->process = proxy_running;
+                       } else {
+                               request->process = proxy_wait_for_reply;
+                       }
                }
 #endif
 
                /*
-                *      Wake up again in the future, to check for
-                *      more things to do.
+                *      If the request has been told to die, we wait.
+                *      Otherwise, we wait for the child thread to
+                *      finish it's work.
                 */
-               when = now;
-               tv_add(&when, request->delay);
-               request->delay += request->delay >> 1;
+               goto delay;
 
-               STATE_MACHINE_TIMER(FR_ACTION_TIMER);
-               return;
-       }
+       case REQUEST_RESPONSE_DELAY:
+               rad_assert(request->response_delay > 0);
+#ifdef WITH_COA
+               rad_assert(!request->proxy || (request->packet->code == request->proxy->code));
+#endif
 
-#ifdef WITH_ACCOUNTING
-       if (request->reply->code == PW_ACCOUNTING_RESPONSE) {
-       done:
-               request_done(request, FR_ACTION_DONE);
-               return;
-       }
+               request->process = request_response_delay;
+
+               when = request->reply->timestamp;
+
+               tv_add(&when, request->response_delay * USEC);
+
+               if (timercmp(&when, &now, >)) {
+#ifdef DEBUG_STATE_MACHINE
+                       if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_response_delay");
 #endif
+                       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+                       return;
+               } /* else it's time to send the reject */
+
+               RDEBUG2("Sending delayed response");
+               DEBUG_PACKET(request, request->reply, 1);
+               request->listener->send(request->listener, request);
+               request->child_state = REQUEST_CLEANUP_DELAY;
+               /* FALL-THROUGH */
+
+       case REQUEST_CLEANUP_DELAY:
+               rad_assert(request->root->cleanup_delay > 0);
 
 #ifdef WITH_COA
-       if (!request->proxy || (request->packet->code == request->proxy->code))
+               rad_assert(!request->proxy || (request->packet->code == request->proxy->code));
 #endif
 
-       if ((request->reply->code == PW_AUTHENTICATION_REJECT) &&
-           (request->root->reject_delay)) {
-               rad_assert(request->reply->timestamp.tv_sec != 0);
+               request->process = request_cleanup_delay;
 
                when = request->reply->timestamp;
-               when.tv_sec += request->root->reject_delay;
+               when.tv_sec += request->root->cleanup_delay;
 
-               /*
-                *      Set timer for when we need to send it.
-                */
                if (timercmp(&when, &now, >)) {
 #ifdef DEBUG_STATE_MACHINE
-                       if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_reject_delay");
+                       if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
 #endif
-                       request->process = request_reject_delay;
-
                        STATE_MACHINE_TIMER(FR_ACTION_TIMER);
                        return;
-               }
-
-               if (request->process == request_reject_delay) {
-                       /*
-                        *      Assume we're at (or near) the reject
-                        *      delay time.
-                        */
-                       request->reply->timestamp = now;
+               } /* else it's time to clean up */
+               /* FALL-THROUGH */
 
-                       RDEBUG2("Sending delayed reject");
-                       DEBUG_PACKET(request, request->reply, 1);
-                       request->process = request_cleanup_delay;
-                       request->listener->send(request->listener, request);
-               }
+       case REQUEST_DONE:
+               goto done;
        }
 
-       /*
-        *      The cleanup_delay is zero for accounting packets, and
-        *      enforced for all other packets.  We do the
-        *      cleanup_delay even if we don't respond to the NAS, so
-        *      that any retransmit is *not* processed as a new packet.
-        */
-       request_cleanup_delay_init(request, &now);
-       return;
 }
 
 static void request_queue_or_run(UNUSED REQUEST *request,
                                 fr_request_process_t process)
 {
-       struct timeval when;
 #ifdef DEBUG_STATE_MACHINE
        int action = FR_ACTION_TIMER;
 #endif
 
        TRACE_STATE_MACHINE;
-       ASSERT_MASTER;
-
-       /*
-        *      (re) set the initial delay.
-        */
-       request->delay = USEC / 3;
-       gettimeofday(&when, NULL);
-       tv_add(&when, request->delay);
-       request->delay += request->delay >> 1;
-
-       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
 
        /*
         *      Do this here so that fewer other functions need to do
@@ -830,7 +952,11 @@ static void request_queue_or_run(UNUSED REQUEST *request,
         */
        if (request->master_state == REQUEST_STOP_PROCESSING) {
 #ifdef DEBUG_STATE_MACHINE
-               if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_DONE);
+               if (debug_flag) printf("(%u) ********\tSTATE %s M-%s causes C-%s-> C-%s\t********\n",
+                                      request->number, __FUNCTION__,
+                                      master_state_names[request->master_state],
+                                      child_state_names[request->child_state],
+                                      child_state_names[REQUEST_DONE]);
 #endif
                request_done(request, FR_ACTION_DONE);
                return;
@@ -838,34 +964,48 @@ static void request_queue_or_run(UNUSED REQUEST *request,
 
        request->process = process;
 
-#ifdef HAVE_PTHREAD_H
-       if (spawn_flag) {
-               /*
-                *      A child thread will eventually pick it up.
-                */
-               if (request_enqueue(request)) return;
+       if (we_are_master()) {
+               struct timeval when;
 
                /*
-                *      Otherwise we're not going to do anything with
-                *      it...
+                *      (re) set the initial delay.
                 */
-               request_done(request, FR_ACTION_DONE);
-               return;
+               request->delay =  (main_config.init_delay.tv_sec * USEC) + main_config.init_delay.tv_usec;
+               if (request->delay > USEC) request->delay = USEC;
+               gettimeofday(&when, NULL);
+               tv_add(&when, request->delay);
+               request->delay += request->delay >> 1;
 
-       } else
+               STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+
+#ifdef HAVE_PTHREAD_H
+               if (spawn_flag) {
+                       /*
+                        *      A child thread will eventually pick it up.
+                        */
+                       if (request_enqueue(request)) return;
+
+                       /*
+                        *      Otherwise we're not going to do anything with
+                        *      it...
+                        */
+                       request_done(request, FR_ACTION_DONE);
+                       return;
+               }
 #endif
-       {
-               request->process(request, FR_ACTION_RUN);
+       }
+
+       request->child_state = REQUEST_RUNNING;
+       request->process(request, FR_ACTION_RUN);
 
 #ifdef WNOHANG
-               /*
-                *      Requests that care about child process exit
-                *      codes have already either called
-                *      rad_waitpid(), or they've given up.
-                */
-               while (waitpid(-1, NULL, WNOHANG) > 0);
+       /*
+        *      Requests that care about child process exit
+        *      codes have already either called
+        *      rad_waitpid(), or they've given up.
+        */
+       while (waitpid(-1, NULL, WNOHANG) > 0);
 #endif
-       }
 }
 
 STATE_MACHINE_DECL(request_common)
@@ -875,23 +1015,35 @@ STATE_MACHINE_DECL(request_common)
 #endif
 
        TRACE_STATE_MACHINE;
+       ASSERT_MASTER;
+
+       /*
+        *      Bail out as early as possible.
+        */
+       if (request->master_state == REQUEST_STOP_PROCESSING) {
+               request_done(request, FR_ACTION_DONE);
+               return;
+       }
 
        switch (action) {
        case FR_ACTION_DUP:
 #ifdef WITH_PROXY
-               if ((request->master_state != REQUEST_STOP_PROCESSING) &&
-                    request->proxy && !request->proxy_reply) {
-                       /*
-                        *      TODO: deal with this in a better way?
-                        */
+               /*
+                *      We're still waiting for a proxy reply.
+                */
+               if (request->child_state == REQUEST_PROXIED) {
+                       request->process = proxy_wait_for_reply;
                        proxy_wait_for_reply(request, action);
                        return;
                }
 #endif
-               ERROR("(%u) Discarding duplicate request from "
-                      "client %s port %d - ID: %u due to unfinished request",
-                      request->number, request->client->shortname,
-                      request->packet->src_port,request->packet->id);
+
+               ERROR("(%u) Ignoring duplicate packet from "
+                     "client %s port %d - ID: %u due to unfinished request "
+                     "in component %s module %s",
+                     request->number, request->client->shortname,
+                     request->packet->src_port,request->packet->id,
+                     request->component, request->module);
                break;
 
        case FR_ACTION_CONFLICTING:
@@ -908,12 +1060,11 @@ STATE_MACHINE_DECL(request_common)
 
 #ifdef WITH_PROXY
        case FR_ACTION_PROXY_REPLY:
-               DEBUG2("Reply from home server %s port %d  - ID: %d arrived too late for request %u. Try increasing 'retry_delay' or 'max_request_time'",
-                      inet_ntop(request->proxy->src_ipaddr.af,
-                                &request->proxy->src_ipaddr.ipaddr,
+               RDEBUG2("Reply from home server %s port %d  - ID: %d arrived too late.  Try increasing 'retry_delay' or 'max_request_time'",
+                      inet_ntop(request->proxy->dst_ipaddr.af,
+                                &request->proxy->dst_ipaddr.ipaddr,
                                 buffer, sizeof(buffer)),
-                      request->proxy->dst_port, request->proxy->id,
-                      request->number);
+                       request->proxy->dst_port, request->proxy->id);
                return;
 #endif
 
@@ -935,7 +1086,7 @@ STATE_MACHINE_DECL(request_cleanup_delay)
                if (request->reply->code != 0) {
                        request->listener->send(request->listener, request);
                } else {
-                       RDEBUG("No reply.  Ignoring retransmit.");
+                       RDEBUG("No reply.  Ignoring retransmit");
                }
 
                /*
@@ -948,10 +1099,13 @@ STATE_MACHINE_DECL(request_cleanup_delay)
                STATE_MACHINE_TIMER(FR_ACTION_TIMER);
                return;
 
+       case FR_ACTION_CONFLICTING:
+               request_done(request, FR_ACTION_DONE);
+               break;
+
 #ifdef WITH_PROXY
        case FR_ACTION_PROXY_REPLY:
 #endif
-       case FR_ACTION_CONFLICTING:
        case FR_ACTION_TIMER:
                request_common(request, action);
                return;
@@ -962,7 +1116,7 @@ STATE_MACHINE_DECL(request_cleanup_delay)
        }
 }
 
-STATE_MACHINE_DECL(request_reject_delay)
+STATE_MACHINE_DECL(request_response_delay)
 {
        TRACE_STATE_MACHINE;
        ASSERT_MASTER;
@@ -970,7 +1124,7 @@ STATE_MACHINE_DECL(request_reject_delay)
        switch (action) {
        case FR_ACTION_DUP:
                ERROR("(%u) Discarding duplicate request from "
-                      "client %s port %d - ID: %u due to delayed reject",
+                      "client %s port %d - ID: %u due to delayed response",
                       request->number, request->client->shortname,
                       request->packet->src_port,request->packet->id);
                return;
@@ -990,7 +1144,7 @@ STATE_MACHINE_DECL(request_reject_delay)
 }
 
 
-static int request_pre_handler(REQUEST *request, UNUSED int action)
+static int CC_HINT(nonnull) request_pre_handler(REQUEST *request, UNUSED int action)
 {
        TRACE_STATE_MACHINE;
 
@@ -1009,34 +1163,8 @@ static int request_pre_handler(REQUEST *request, UNUSED int action)
                return 1;
        }
 
-#ifdef WITH_PROXY
-       /*
-        *      Put the decoded packet into it's proper place.
-        */
-       if (request->proxy_reply != NULL) {
-               /*
-                *      There may be a proxy reply, but it may be too late.
-                */
-               if (!request->proxy_listener) return 0;
-
-               rcode = request->proxy_listener->decode(request->proxy_listener, request);
-               DEBUG_PACKET(request, request->proxy_reply, 0);
-
-               /*
-                *      Pro-actively remove it from the proxy hash.
-                *      This is later than in 2.1.x, but it means that
-                *      the replies are authenticated before being
-                *      removed from the hash.
-                */
-               if ((rcode == 0) &&
-                   (request->num_proxied_requests <= request->num_proxied_responses)) {
-                       remove_from_proxy_hash(request);
-               }
-
-       } else
-#endif
-       if (request->packet->vps == NULL) {
-               rcode = request->listener->decode(request->listener, request);
+       if (!request->packet->vps) { /* FIXME: check for correct state */
+               rcode = request->listener->decode(request->listener, request);
 
 #ifdef WITH_UNLANG
                if (debug_condition) {
@@ -1044,8 +1172,8 @@ static int request_pre_handler(REQUEST *request, UNUSED int action)
                         *      Ignore parse errors.
                         */
                        if (radius_evaluate_cond(request, RLM_MODULE_OK, 0, debug_condition)) {
-                               request->options = 2;
-                               request->radlog = radlog_request;
+                               request->log.lvl = L_DBG_LVL_2;
+                               request->log.func = vradlog_request;
                        }
                }
 #endif
@@ -1065,12 +1193,6 @@ static int request_pre_handler(REQUEST *request, UNUSED int action)
                request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
        }
 
-#ifdef WITH_PROXY
-       if (action == FR_ACTION_PROXY_REPLY) {
-               return process_proxy_reply(request);
-       }
-#endif
-
        return 1;
 }
 
@@ -1082,7 +1204,10 @@ STATE_MACHINE_DECL(request_finish)
 
        (void) action;  /* -Wunused */
 
-       if (request->master_state == REQUEST_STOP_PROCESSING) return;
+       if (request->master_state == REQUEST_STOP_PROCESSING) {
+               NO_CHILD_THREAD;
+               return;
+       }
 
        /*
         *      Don't send replies if there are none to send.
@@ -1104,6 +1229,7 @@ STATE_MACHINE_DECL(request_finish)
                         */
                }
 #else
+               NO_CHILD_THREAD;
                return;
 #endif
        }
@@ -1123,16 +1249,16 @@ STATE_MACHINE_DECL(request_finish)
        /*
         *      Catch Auth-Type := Reject BEFORE proxying the packet.
         */
-       else if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+       else if (request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) {
                if (request->reply->code == 0) {
                        vp = pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY);
 
-                       if (!vp || (vp->vp_integer != PW_AUTHENTICATION_REJECT)) {
+                       if (!vp || (vp->vp_integer != PW_CODE_AUTHENTICATION_REJECT)) {
                                RDEBUG2("There was no response configured: "
                                        "rejecting request");
                        }
 
-                       request->reply->code = PW_AUTHENTICATION_REJECT;
+                       request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                }
        }
 
@@ -1144,10 +1270,10 @@ STATE_MACHINE_DECL(request_finish)
        if (vp) pairadd(&request->reply->vps, vp);
 
        switch (request->reply->code) {
-       case PW_AUTHENTICATION_ACK:
+       case PW_CODE_AUTHENTICATION_ACK:
                rad_postauth(request);
                break;
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_CHALLENGE:
                pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0,
                           TAG_ANY);
                vp = pairmake_config("Post-Auth-Type", "Challenge", T_OP_SET);
@@ -1165,24 +1291,13 @@ STATE_MACHINE_DECL(request_finish)
         *      We do this separately so ACK and challenge can change the code
         *      to reject if a module returns reject.
         */
-       if (request->reply->code == PW_AUTHENTICATION_REJECT) {
+       if (request->reply->code == PW_CODE_AUTHENTICATION_REJECT) {
                pairdelete(&request->config_items, PW_POST_AUTH_TYPE, 0, TAG_ANY);
                vp = pairmake_config("Post-Auth-Type", "Reject", T_OP_SET);
                if (vp) rad_postauth(request);
        }
 
        /*
-        *      Send the reply here.
-        */
-       if ((request->reply->code != PW_AUTHENTICATION_REJECT) ||
-           (request->root->reject_delay == 0)) {
-               DEBUG_PACKET(request, request->reply, 1);
-               request->listener->send(request->listener,
-                                       request);
-               pairfree(&request->reply->vps);
-       }
-
-       /*
         *      Clean up.  These are no longer needed.
         */
        pairfree(&request->config_items);
@@ -1200,7 +1315,67 @@ STATE_MACHINE_DECL(request_finish)
        }
 #endif
 
-       RDEBUG2("Finished request %u.", request->number);
+       gettimeofday(&request->reply->timestamp, NULL);
+
+       /*
+        *      Ignore all "do not respond" packets.
+        */
+       if (!request->reply->code) {
+               RDEBUG("Not sending reply");
+               goto done;
+       }
+
+       /*
+        *      See if we need to delay an Access-Reject packet.
+        */
+       if ((request->reply->code == PW_CODE_AUTHENTICATION_REJECT) &&
+           (request->root->reject_delay > 0)) {
+               request->response_delay = request->root->reject_delay;
+
+#ifdef WITH_PROXY
+               /*
+                *      If we timed out a proxy packet, don't delay
+                *      the reject any more.
+                */
+               if (request->proxy && !request->proxy_reply) {
+                       request->response_delay = 0;
+               }
+#endif
+
+       }
+
+       /*
+        *      Send the reply.
+        */
+       if (!request->response_delay) {
+               DEBUG_PACKET(request, request->reply, 1);
+               request->listener->send(request->listener,
+                                       request);
+
+       done:
+               pairfree(&request->reply->vps);
+
+               RDEBUG2("Finished request");
+#ifdef WITH_ACCOUNTING
+               if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
+                       NO_CHILD_THREAD;
+                       request->child_state = REQUEST_DONE;
+               } else
+#endif
+
+               if (request->root->cleanup_delay == 0) {
+                       NO_CHILD_THREAD;
+                       request->child_state = REQUEST_DONE;
+               } else {
+                       NO_CHILD_THREAD;
+                       request->child_state = REQUEST_CLEANUP_DELAY;
+               }
+       } else {
+               RDEBUG2("Delaying response for %d seconds",
+                       request->response_delay);
+               NO_CHILD_THREAD;
+               request->child_state = REQUEST_RESPONSE_DELAY;
+       }
 }
 
 STATE_MACHINE_DECL(request_running)
@@ -1208,30 +1383,42 @@ STATE_MACHINE_DECL(request_running)
        TRACE_STATE_MACHINE;
 
        switch (action) {
+       case FR_ACTION_TIMER:
+               request_process_timer(request);
+               break;
+
        case FR_ACTION_CONFLICTING:
        case FR_ACTION_DUP:
-       case FR_ACTION_TIMER:
                request_common(request, action);
                return;
 
 #ifdef WITH_PROXY
-       case FR_ACTION_PROXY_REPLY:
-#ifdef HAVE_PTHREAD_H
                /*
-                *      Catch the case of a proxy reply when called
-                *      from the main worker thread.
+                *      This can happen due to a race condition where
+                *      we send a proxied request, and immediately get
+                *      another reply, before the timer has a chance
+                *      to update the various states.
                 */
-               if (we_are_master() &&
-                   (request->process != proxy_running)) {
-                       request_queue_or_run(request, proxy_running);
-                       return;
-               }
-               /* FALL-THROUGH */
-#endif
+       case FR_ACTION_PROXY_REPLY:
+               request->child_state = REQUEST_RUNNING;
+               request->process = proxy_running;
+               request->process(request, FR_ACTION_RUN);
+               break;
 #endif
 
        case FR_ACTION_RUN:
-               if (!request_pre_handler(request, action)) goto done;
+               if (!request_pre_handler(request, action)) {
+#ifdef DEBUG_STATE_MACHINE
+                       if (debug_flag) printf("(%u) ********\tSTATE %s failed in pre-handler C-%s -> C-%s\t********\n",
+                                              request->number, __FUNCTION__,
+                                              child_state_names[request->child_state],
+                                              child_state_names[REQUEST_DONE]);
+#endif
+
+                       NO_CHILD_THREAD;
+                       request->child_state = REQUEST_DONE;
+                       break;
+               }
 
                rad_assert(request->handle != NULL);
                request->handle(request);
@@ -1272,22 +1459,6 @@ STATE_MACHINE_DECL(request_running)
                finished:
 #endif
                        request_finish(request, action);
-
-               done:
-                       /*
-                        *      Get the time of the reply, which is
-                        *      when we're done.
-                        */
-                       gettimeofday(&request->reply->timestamp, NULL);
-
-#ifdef DEBUG_STATE_MACHINE
-                       if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_DONE);
-#endif
-
-#ifdef HAVE_PTHREAD_H
-                       request->child_pid = NO_SUCH_CHILD_PID;
-#endif
-                       request->child_state = REQUEST_DONE;
                }
                break;
 
@@ -1300,7 +1471,7 @@ STATE_MACHINE_DECL(request_running)
 int request_receive(rad_listen_t *listener, RADIUS_PACKET *packet,
                    RADCLIENT *client, RAD_REQUEST_FUNP fun)
 {
-       int count;
+       uint32_t count;
        RADIUS_PACKET **packet_p;
        REQUEST *request = NULL;
        struct timeval now;
@@ -1338,54 +1509,74 @@ int request_receive(rad_listen_t *listener, RADIUS_PACKET *packet,
                    (memcmp(request->packet->vector, packet->vector,
                            sizeof(packet->vector)) == 0)) {
 
+                       /*
+                        *      If the request is running, it'
+                        */
+                       if (request->child_state != REQUEST_DONE) {
+                               request->process(request, FR_ACTION_DUP);
+
 #ifdef WITH_STATS
-                       switch (packet->code) {
-                       case PW_AUTHENTICATION_REQUEST:
-                               FR_STATS_INC(auth, total_dup_requests);
-                               break;
+                               switch (packet->code) {
+                               case PW_CODE_AUTHENTICATION_REQUEST:
+                                       FR_STATS_INC(auth, total_dup_requests);
+                                       break;
 
 #ifdef WITH_ACCOUNTING
-                       case PW_ACCOUNTING_REQUEST:
-                               FR_STATS_INC(acct, total_dup_requests);
-                               break;
+                               case PW_CODE_ACCOUNTING_REQUEST:
+                                       FR_STATS_INC(acct, total_dup_requests);
+                                       break;
 #endif
 #ifdef WITH_COA
-                       case PW_COA_REQUEST:
-                               FR_STATS_INC(coa, total_dup_requests);
-                               break;
+                               case PW_CODE_COA_REQUEST:
+                                       FR_STATS_INC(coa, total_dup_requests);
+                                       break;
 
-                       case PW_DISCONNECT_REQUEST:
-                               FR_STATS_INC(dsc, total_dup_requests);
-                               break;
+                               case PW_CODE_DISCONNECT_REQUEST:
+                                       FR_STATS_INC(dsc, total_dup_requests);
+                                       break;
 #endif
 
-                       default:
-                         break;
-                       }
+                               default:
+                                       break;
+                               }
 #endif /* WITH_STATS */
+                               return 0; /* duplicate of live request */
+                       }
+#ifdef HAVE_PTHREAD_H
+                       /*
+                        *      There should no longer be a child
+                        *      thread associated with this request.
+                        */
+                       rad_assert(pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) != 0);
+#endif
 
-                       request->process(request, FR_ACTION_DUP);
-                       return 0;
-               }
+                       /*
+                        *      Clean up the old request, and allow
+                        *      the new one to continue.
+                        */
+                       request_done(request, FR_ACTION_DONE);
+                       request = NULL;
 
-               /*
-                *      Say we're ignoring the old one, and continue
-                *      to process the new one.
-                */
-               request->process(request, FR_ACTION_CONFLICTING);
-               request = NULL;
+               } else {
+                       /*
+                        *      Say we're ignoring the old one, and continue
+                        *      to process the new one.
+                        */
+                       request->process(request, FR_ACTION_CONFLICTING);
+                       request = NULL;
+               }
        }
 
        /*
         *      Quench maximum number of outstanding requests.
         */
-       if (mainconfig.max_requests &&
-           ((count = fr_packet_list_num_elements(pl)) > mainconfig.max_requests)) {
-               ERROR("Dropping request (%d is too many): from client %s port %d - ID: %d", count,
-                      client->shortname,
-                      packet->src_port, packet->id);
-               WARN("Please check the configuration file.\n"
-                    "\tThe value for 'max_requests' is probably set too low.\n");
+       if (main_config.max_requests &&
+           ((count = fr_packet_list_num_elements(pl)) > main_config.max_requests)) {
+               RATE_LIMIT(ERROR("Dropping request (%d is too many): from client %s port %d - ID: %d", count,
+                                client->shortname,
+                                packet->src_port, packet->id);
+                          WARN("Please check the configuration file.\n"
+                               "\tThe value for 'max_requests' is probably set too low.\n"));
 
                exec_trigger(NULL, NULL, "server.max_requests", true);
                return 0;
@@ -1396,11 +1587,9 @@ skip_dup:
         *      Rate-limit the incoming packets
         */
        if (sock && sock->max_rate) {
-               int pps;
-
-               pps = rad_pps(&sock->rate_pps_old, &sock->rate_pps_now,
-                             &sock->rate_time, &now);
+               uint32_t pps;
 
+               pps = rad_pps(&sock->rate_pps_old, &sock->rate_pps_now, &sock->rate_time, &now);
                if (pps > sock->max_rate) {
                        DEBUG("Dropping request due to rate limiting");
                        return 0;
@@ -1433,7 +1622,12 @@ skip_dup:
                request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
 
                fun(request);
-               request->listener->send(request->listener, request);
+
+               if (request->reply->code != 0) {
+                       request->listener->send(request->listener, request);
+               } else {
+                       RDEBUG("Not sending reply");
+               }
                request_free(&request);
                return 1;
        }
@@ -1456,7 +1650,7 @@ static REQUEST *request_setup(rad_listen_t *listener, RADIUS_PACKET *packet,
        /*
         *      Create and initialize the new request.
         */
-       request = request_alloc(listener);
+       request = request_alloc(NULL);
        request->reply = rad_alloc(request, 0);
        if (!request->reply) {
                ERROR("No memory");
@@ -1471,21 +1665,22 @@ static REQUEST *request_setup(rad_listen_t *listener, RADIUS_PACKET *packet,
        request->priority = listener->type;
        request->master_state = REQUEST_ACTIVE;
 #ifdef DEBUG_STATE_MACHINE
-       if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_ACTIVE);
+       if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n",
+                              request->number, __FUNCTION__,
+                              child_state_names[request->child_state],
+                              child_state_names[REQUEST_RUNNING]);
 #endif
-       request->child_state = REQUEST_ACTIVE;
+       request->child_state = REQUEST_RUNNING;
        request->handle = fun;
-#ifdef HAVE_PTHREAD_H
-       request->child_pid = NO_SUCH_CHILD_PID;
-#endif
+       NO_CHILD_THREAD;
 
 #ifdef WITH_STATS
        request->listener->stats.last_packet = request->packet->timestamp.tv_sec;
-       if (packet->code == PW_AUTHENTICATION_REQUEST) {
+       if (packet->code == PW_CODE_AUTHENTICATION_REQUEST) {
                request->client->auth.last_packet = request->packet->timestamp.tv_sec;
                radius_auth_stats.last_packet = request->packet->timestamp.tv_sec;
 #ifdef WITH_ACCOUNTING
-       } else if (packet->code == PW_ACCOUNTING_REQUEST) {
+       } else if (packet->code == PW_CODE_ACCOUNTING_REQUEST) {
                request->client->acct.last_packet = request->packet->timestamp.tv_sec;
                radius_acct_stats.last_packet = request->packet->timestamp.tv_sec;
 #endif
@@ -1495,7 +1690,7 @@ static REQUEST *request_setup(rad_listen_t *listener, RADIUS_PACKET *packet,
        /*
         *      Status-Server packets go to the head of the queue.
         */
-       if (request->packet->code == PW_STATUS_SERVER) request->priority = 0;
+       if (request->packet->code == PW_CODE_STATUS_SERVER) request->priority = 0;
 
        /*
         *      Set virtual server identity
@@ -1508,7 +1703,7 @@ static REQUEST *request_setup(rad_listen_t *listener, RADIUS_PACKET *packet,
                request->server = NULL;
        }
 
-       request->root = &mainconfig;
+       request->root = &main_config;
 #ifdef WITH_TCP
        request->listener->count++;
 #endif
@@ -1558,6 +1753,8 @@ static void tcp_socket_timer(void *ctx)
        char buffer[256];
        fr_socket_limit_t *limit;
 
+       ASSERT_MASTER;
+
        fr_event_now(el, &now);
 
        if (listener->status != RAD_LISTEN_STATUS_KNOWN) return;
@@ -1713,6 +1910,9 @@ static int eol_listener(void *ctx, void *data)
  *
  ***********************************************************************/
 
+/*
+ *     Called with the proxy mutex held
+ */
 static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
 {
        if (!request->in_proxy_hash) return;
@@ -1743,6 +1943,7 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
        }
 
 #ifdef WITH_TCP
+       rad_assert(request->proxy_listener != NULL);
        request->proxy_listener->count--;
 #endif
        request->proxy_listener = NULL;
@@ -1777,7 +1978,7 @@ static void remove_from_proxy_hash(REQUEST *request)
 
        remove_from_proxy_hash_nl(request, true);
 
-       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
 }
 
 static int insert_into_proxy_hash(REQUEST *request)
@@ -1798,6 +1999,7 @@ static int insert_into_proxy_hash(REQUEST *request)
 
        for (tries = 0; tries < 2; tries++) {
                rad_listen_t *this;
+               listen_socket_t *sock;
 
                RDEBUG3("proxy: Trying to allocate ID (%d/2)", tries);
                rcode = fr_packet_list_id_alloc(proxy_list,
@@ -1817,23 +2019,40 @@ static int insert_into_proxy_hash(REQUEST *request)
                this = proxy_new_listener(request->home_server, 0);
                if (!this) {
                        PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
-                       ERROR("proxy: Failed to create a new outbound socket");
                        goto fail;
                }
 
                request->proxy->src_port = 0; /* Use any new socket */
                proxy_listener = this;
 
+               sock = this->data;
+               if (!fr_packet_list_socket_add(proxy_list, this->fd,
+                                              sock->proto,
+                                              &sock->other_ipaddr, sock->other_port,
+                                              this)) {
+
+#ifdef HAVE_PTHREAD_H
+                       proxy_no_new_sockets = true;
+#endif
+                       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+
+                       /*
+                        *      This is bad.  However, the
+                        *      packet list now supports 256
+                        *      open sockets, which should
+                        *      minimize this problem.
+                        */
+                       ERROR("Failed adding proxy socket: %s",
+                             fr_strerror());
+                       goto fail;
+               }
+
                /*
-                *      Add it to the event loop (and to the packet list)
-                *      before we try to grab another Id.
+                *      Add it to the event loop.  Ensure that we have
+                *      only one mutex locked at a time.
                 */
                PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
-               if (!event_new_fd(this)) {
-                       RDEBUG3("proxy: Failed inserting new socket into event loop");
-                       listen_free(&this);
-                       goto fail;
-               }
+               radius_update_listener(this);
                PTHREAD_MUTEX_LOCK(&proxy_mutex);
        }
 
@@ -1865,7 +2084,7 @@ static int insert_into_proxy_hash(REQUEST *request)
 
        PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
 
-       RDEBUG3(" proxy: allocating destination %s port %d - Id %d",
+       RDEBUG3("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,
@@ -1874,13 +2093,18 @@ static int insert_into_proxy_hash(REQUEST *request)
        return 1;
 }
 
-static int process_proxy_reply(REQUEST *request)
+static int process_proxy_reply(REQUEST *request, RADIUS_PACKET *reply)
 {
        int rcode;
        int post_proxy_type = 0;
        VALUE_PAIR *vp;
 
        /*
+        *      There may be a proxy reply, but it may be too late.
+        */
+       if (!request->proxy_listener) return 0;
+
+       /*
         *      Delete any reply we had accumulated until now.
         */
        pairfree(&request->reply->vps);
@@ -1895,9 +2119,9 @@ static int process_proxy_reply(REQUEST *request)
         *      If we have a proxy_reply, and it was a reject, setup
         *      post-proxy-type Reject
         */
-       if (!vp && request->proxy_reply &&
-           request->proxy_reply->code == PW_AUTHENTICATION_REJECT) {
-               DICT_VALUE      *dval;
+       if (!vp && reply &&
+           reply->code == PW_CODE_AUTHENTICATION_REJECT) {
+               DICT_VALUE      *dval;
 
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Reject");
                if (dval) {
@@ -1911,18 +2135,39 @@ static int process_proxy_reply(REQUEST *request)
        if (vp) {
                post_proxy_type = vp->vp_integer;
 
-               RDEBUG2("  Found Post-Proxy-Type %s",
-                       dict_valnamebyattr(PW_POST_PROXY_TYPE, 0,
-                                          post_proxy_type));
+               RDEBUG2("Found Post-Proxy-Type %s", dict_valnamebyattr(PW_POST_PROXY_TYPE, 0, post_proxy_type));
+       }
+
+       if (reply) {
+               /*
+                *      Decode the packet.
+                */
+               rcode = request->proxy_listener->decode(request->proxy_listener, request);
+               DEBUG_PACKET(request, reply, 0);
+
+               /*
+                *      Pro-actively remove it from the proxy hash.
+                *      This is later than in 2.1.x, but it means that
+                *      the replies are authenticated before being
+                *      removed from the hash.
+                */
+               if ((rcode == 0) &&
+                   (request->num_proxied_requests <= request->num_proxied_responses)) {
+                       remove_from_proxy_hash(request);
+               }
+       } else {
+               remove_from_proxy_hash(request);
        }
 
        if (request->home_pool && request->home_pool->virtual_server) {
                char const *old_server = request->server;
 
                request->server = request->home_pool->virtual_server;
-               RDEBUG2(" server %s {", request->server);
+               RDEBUG2("server %s {", request->server);
+               RINDENT();
                rcode = process_post_proxy(post_proxy_type, request);
-               RDEBUG2(" }");
+               REXDENT();
+               RDEBUG2("}");
                request->server = old_server;
        } else {
                rcode = process_post_proxy(post_proxy_type, request);
@@ -1941,25 +2186,15 @@ static int process_proxy_reply(REQUEST *request)
         *      There may NOT be a proxy reply, as we may be
         *      running Post-Proxy-Type = Fail.
         */
-       if (request->proxy_reply) {
+       if (reply) {
+               request->reply->vps = paircopy(request->reply, reply->vps);
+
                /*
                 *      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, 0, TAG_ANY);
-
-               /*
-                *      Add the attributes left in the proxy
-                *      reply to the reply list.
-                */
-               pairfilter(request->reply, &request->reply->vps,
-                         &request->proxy_reply->vps, 0, 0, TAG_ANY);
-
-               /*
-                *      Free proxy request pairs.
-                */
-               pairfree(&request->proxy->vps);
+               pairdelete(&request->reply->vps, PW_PROXY_STATE, 0, TAG_ANY);
        }
 
        switch (rcode) {
@@ -2030,7 +2265,7 @@ int request_proxy_reply(RADIUS_PACKET *packet)
        /*
         *      Status-Server packets don't count as real packets.
         */
-       if (request->proxy->code != PW_STATUS_SERVER) {
+       if (request->proxy->code != PW_CODE_STATUS_SERVER) {
                listen_socket_t *sock = request->proxy_listener->data;
 
                request->home_server->last_packet_recv = now.tv_sec;
@@ -2054,7 +2289,7 @@ int request_proxy_reply(RADIUS_PACKET *packet)
         *      Call the state machine to do something useful with the
         *      request.
         */
-       request->proxy_reply = packet;
+       request->proxy_reply = talloc_steal(request, packet);
        packet->timestamp = now;
        request->priority = RAD_LISTEN_PROXY;
 
@@ -2070,10 +2305,10 @@ int request_proxy_reply(RADIUS_PACKET *packet)
        request->home_server->stats.last_packet = packet->timestamp.tv_sec;
        request->proxy_listener->stats.last_packet = packet->timestamp.tv_sec;
 
-       if (request->proxy->code == PW_AUTHENTICATION_REQUEST) {
+       if (request->proxy->code == PW_CODE_AUTHENTICATION_REQUEST) {
                proxy_auth_stats.last_packet = packet->timestamp.tv_sec;
 #ifdef WITH_ACCOUNTING
-       } else if (request->proxy->code == PW_ACCOUNTING_REQUEST) {
+       } else if (request->proxy->code == PW_CODE_ACCOUNTING_REQUEST) {
                proxy_acct_stats.last_packet = packet->timestamp.tv_sec;
 #endif
        }
@@ -2087,8 +2322,8 @@ int request_proxy_reply(RADIUS_PACKET *packet)
         */
        if (request->parent) {
                rad_assert(request->parent->coa == request);
-               rad_assert((request->proxy->code == PW_COA_REQUEST) ||
-                          (request->proxy->code == PW_DISCONNECT_REQUEST));
+               rad_assert((request->proxy->code == PW_CODE_COA_REQUEST) ||
+                          (request->proxy->code == PW_CODE_DISCONNECT_REQUEST));
                rad_assert(request->process != NULL);
                request_coa_separate(request);
        }
@@ -2105,29 +2340,28 @@ static int setup_post_proxy_fail(REQUEST *request)
        DICT_VALUE const *dval = NULL;
        VALUE_PAIR *vp;
 
-       if (request->proxy->code == PW_AUTHENTICATION_REQUEST) {
+       if (request->proxy->code == PW_CODE_AUTHENTICATION_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0,
                                      "Fail-Authentication");
 
-       } else if (request->proxy->code == PW_ACCOUNTING_REQUEST) {
+       } else if (request->proxy->code == PW_CODE_ACCOUNTING_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0,
                                      "Fail-Accounting");
 #ifdef WITH_COA
-       } else if (request->proxy->code == PW_COA_REQUEST) {
+       } else if (request->proxy->code == PW_CODE_COA_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-CoA");
 
-       } else if (request->proxy->code == PW_DISCONNECT_REQUEST) {
+       } else if (request->proxy->code == PW_CODE_DISCONNECT_REQUEST) {
                dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail-Disconnect");
 #endif
        } else {
-               WDEBUG("Unknown packet type in Post-Proxy-Type Fail: ignoring");
+               WARN("Unknown packet type in Post-Proxy-Type Fail: ignoring");
                return 0;
        }
 
        if (!dval) dval = dict_valbyname(PW_POST_PROXY_TYPE, 0, "Fail");
 
        if (!dval) {
-               DEBUG("No Post-Proxy-Type Fail: ignoring");
                pairdelete(&request->config_items, PW_POST_PROXY_TYPE, 0, TAG_ANY);
                return 0;
        }
@@ -2140,7 +2374,7 @@ static int setup_post_proxy_fail(REQUEST *request)
        return 1;
 }
 
-STATE_MACHINE_DECL(proxy_running)
+STATE_MACHINE_DECL(proxy_no_reply)
 {
        TRACE_STATE_MACHINE;
 
@@ -2153,7 +2387,10 @@ STATE_MACHINE_DECL(proxy_running)
                break;
 
        case FR_ACTION_RUN:
-               request_running(request, FR_ACTION_PROXY_REPLY);
+               if (process_proxy_reply(request, NULL)) {
+                       request_finish(request, action);
+               }
+               request_done(request, FR_ACTION_DONE);
                break;
 
        default:
@@ -2162,10 +2399,8 @@ STATE_MACHINE_DECL(proxy_running)
        }
 }
 
-STATE_MACHINE_DECL(request_virtual_server)
+STATE_MACHINE_DECL(proxy_running)
 {
-       char const *old;
-
        TRACE_STATE_MACHINE;
 
        switch (action) {
@@ -2177,10 +2412,12 @@ STATE_MACHINE_DECL(request_virtual_server)
                break;
 
        case FR_ACTION_RUN:
-               old = request->server;
-               request->server = request->home_server->server;
-               request_running(request, action);
-               request->server = old;
+               if (process_proxy_reply(request, request->proxy_reply)) {
+                       request->handle(request);
+                       request_finish(request, action);
+               } else {
+                       request_done(request, FR_ACTION_DONE);
+               }
                break;
 
        default:
@@ -2189,19 +2426,18 @@ STATE_MACHINE_DECL(request_virtual_server)
        }
 }
 
-
 static int request_will_proxy(REQUEST *request)
 {
        int rcode, pre_proxy_type = 0;
        char const *realmname = NULL;
        VALUE_PAIR *vp, *strippedname;
-       home_server *home;
+       home_server_t *home;
        REALM *realm = NULL;
        home_pool_t *pool = NULL;
 
        if (!request->root->proxy_requests) return 0;
        if (request->packet->dst_port == 0) return 0;
-       if (request->packet->code == PW_STATUS_SERVER) return 0;
+       if (request->packet->code == PW_CODE_STATUS_SERVER) return 0;
        if (request->in_proxy_hash) return 0;
 
        /*
@@ -2223,17 +2459,17 @@ static int request_will_proxy(REQUEST *request)
                /*
                 *      Figure out which pool to use.
                 */
-               if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+               if (request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) {
                        pool = realm->auth_pool;
 
 #ifdef WITH_ACCOUNTING
-               } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+               } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
                        pool = realm->acct_pool;
 #endif
 
 #ifdef WITH_COA
-               } else if ((request->packet->code == PW_COA_REQUEST) ||
-                          (request->packet->code == PW_DISCONNECT_REQUEST)) {
+               } else if ((request->packet->code == PW_CODE_COA_REQUEST) ||
+                          (request->packet->code == PW_CODE_DISCONNECT_REQUEST)) {
                        pool = realm->coa_pool;
 #endif
 
@@ -2248,19 +2484,19 @@ static int request_will_proxy(REQUEST *request)
                if (!vp) return 0;
 
                switch (request->packet->code) {
-               case PW_AUTHENTICATION_REQUEST:
+               case PW_CODE_AUTHENTICATION_REQUEST:
                        pool_type = HOME_TYPE_AUTH;
                        break;
 
 #ifdef WITH_ACCOUNTING
-               case PW_ACCOUNTING_REQUEST:
+               case PW_CODE_ACCOUNTING_REQUEST:
                        pool_type = HOME_TYPE_ACCT;
                        break;
 #endif
 
 #ifdef WITH_COA
-               case PW_COA_REQUEST:
-               case PW_DISCONNECT_REQUEST:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
                        pool_type = HOME_TYPE_COA;
                        break;
 #endif
@@ -2328,13 +2564,13 @@ static int request_will_proxy(REQUEST *request)
                vp = pairfind(request->proxy->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!vp) {
                        vp_cursor_t cursor;
-                       vp = radius_paircreate(request, NULL,
+                       vp = radius_paircreate(NULL, NULL,
                                               PW_USER_NAME, 0);
                        rad_assert(vp != NULL); /* handled by above function */
                        /* Insert at the START of the list */
                        /* FIXME: Can't make assumptions about ordering */
-                       paircursor(&cursor, &vp);
-                       pairinsert(&cursor, request->proxy->vps);
+                       fr_cursor_init(&cursor, &vp);
+                       fr_cursor_insert(&cursor, request->proxy->vps);
                        request->proxy->vps = vp;
                }
                pairstrcpy(vp, strippedname->vp_strvalue);
@@ -2350,25 +2586,18 @@ static int request_will_proxy(REQUEST *request)
         *      since we can't use the request authenticator
         *      anymore - we changed it.
         */
-       if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
+       if ((request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) &&
            pairfind(request->proxy->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
            pairfind(request->proxy->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
-               uint8_t *p;
-               vp = radius_paircreate(request, &request->proxy->vps,
-                                      PW_CHAP_CHALLENGE, 0);
-               vp->length = sizeof(request->packet->vector);
-               vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
-
-               memcpy(p, request->packet->vector,
-                      sizeof(request->packet->vector));
+               vp = radius_paircreate(request->proxy, &request->proxy->vps, PW_CHAP_CHALLENGE, 0);
+               pairmemcpy(vp, request->packet->vector, sizeof(request->packet->vector));
        }
 
        /*
         *      The RFC's say we have to do this, but FreeRADIUS
         *      doesn't need it.
         */
-       vp = radius_paircreate(request, &request->proxy->vps,
-                              PW_PROXY_STATE, 0);
+       vp = radius_paircreate(request->proxy, &request->proxy->vps, PW_PROXY_STATE, 0);
        pairsprintf(vp, "%u", request->packet->id);
 
        /*
@@ -2382,7 +2611,10 @@ static int request_will_proxy(REQUEST *request)
         */
        vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
        if (vp) {
-               RDEBUG2("  Found Pre-Proxy-Type %s", vp->vp_strvalue);
+               DICT_VALUE const *dval = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
+               /* Must be a validation issue */
+               rad_assert(dval);
+               RDEBUG2("Found Pre-Proxy-Type %s", dval->name);
                pre_proxy_type = vp->vp_integer;
        }
 
@@ -2392,10 +2624,14 @@ static int request_will_proxy(REQUEST *request)
                char const *old_server = request->server;
 
                request->server = request->home_pool->virtual_server;
-               RDEBUG2(" server %s {", request->server);
+
+               RDEBUG2("server %s {", request->server);
+               RINDENT();
                rcode = process_pre_proxy(pre_proxy_type, request);
-               RDEBUG2(" }");
-                       request->server = old_server;
+               REXDENT();
+               RDEBUG2("}");
+
+               request->server = old_server;
        } else {
                rcode = process_pre_proxy(pre_proxy_type, request);
        }
@@ -2441,37 +2677,87 @@ static int request_proxy(REQUEST *request, int retransmit)
 #endif
 
        /*
-        *      The request may be sent to a virtual server.  If we're
-        *      in a child thread, just process it here. If we're the
-        *      master, push it back onto the queue for later
-        *      processing.
+        *      The request may need sending to a virtual server.
+        *      This code is more than a little screwed up.  The rest
+        *      of the state machine doesn't handle parent / child
+        *      relationships well.  i.e. if the child request takes
+        *      too long, the core will mark the *parent* as "stop
+        *      processing".  And the child will continue without
+        *      knowing anything...
+        *
+        *      So, we have some horrible hacks to get around that.
         */
        if (request->home_server->server) {
+               REQUEST *fake;
+
+               if (request->packet->dst_port == 0) {
+                       WARN("Cannot proxy an internal request");
+                       return 0;
+               }
+
                DEBUG("Proxying to virtual server %s",
                      request->home_server->server);
 
-               if (!we_are_master()) {
-                       request_virtual_server(request, FR_ACTION_RUN);
-#ifdef HAVE_PTHREAD_H
-                       request->child_pid = NO_SUCH_CHILD_PID;
-#endif
-                       return 1;
-               }
+               /*
+                *      Packets to virtual serrers don't get
+                *      retransmissions sent to them.  And the virtual
+                *      server is run ONLY if we have no child
+                *      threads, or we're running in a child thread.
+                */
+               rad_assert(retransmit == 0);
+               rad_assert(!spawn_flag || !we_are_master());
 
-               request_queue_or_run(request, request_virtual_server);
-               return 1;
+               fake = request_alloc_fake(request);
+
+               fake->packet->vps = paircopy(fake->packet, request->packet->vps);
+               talloc_free(request->proxy);
+
+               fake->server = request->home_server->server;
+               fake->handle = request->handle;
+               fake->process = NULL; /* should never be run for anything */
+
+               /*
+                *      Run the virtual server.
+                */
+               request_running(fake, FR_ACTION_RUN);
+
+               request->proxy = talloc_steal(request, fake->packet);
+               fake->packet = NULL;
+               request->proxy_reply = talloc_steal(request, fake->reply);
+               fake->reply = NULL;
+
+               request_free(&fake);
+
+               /*
+                *      Just do the work here, rather than trying to
+                *      run the "decode proxy reply" stuff...
+                */
+               process_proxy_reply(request, request->proxy_reply);
+
+               request->handle(request); /* to do more post-proxy stuff */
+
+               return -1;      /* so we call request_finish */
        }
 
        /*
         *      We're actually sending a proxied packet.  Do that now.
         */
        if (!request->in_proxy_hash && !insert_into_proxy_hash(request)) {
-               RPROXY("Failed to insert initial packet into the proxy list.");
+               ERROR("Failed to insert request into the proxy list");
                return -1;
        }
 
        rad_assert(request->proxy->id >= 0);
 
+#ifdef WITH_TLS
+       if (request->home_server->tls) {
+               RDEBUG2("Proxying request to home server %s port %d (TLS)",
+                       inet_ntop(request->proxy->dst_ipaddr.af,
+                                 &request->proxy->dst_ipaddr.ipaddr,
+                                 buffer, sizeof(buffer)),
+                       request->proxy->dst_port);
+       } else
+#endif
        RDEBUG2("Proxying request to home server %s port %d",
               inet_ntop(request->proxy->dst_ipaddr.af,
                         &request->proxy->dst_ipaddr.ipaddr,
@@ -2486,10 +2772,9 @@ static int request_proxy(REQUEST *request, int retransmit)
                request->home_server->last_packet_sent = request->proxy_retransmit.tv_sec;
        }
 
-#ifdef HAVE_PTHREAD_H
-       request->child_pid = NO_SUCH_CHILD_PID;
-#endif
        FR_STATS_TYPE_INC(request->home_server->stats.total_requests);
+       NO_CHILD_THREAD;
+       request->child_state = REQUEST_PROXIED;
        request->proxy_listener->send(request->proxy_listener,
                                      request);
        return 1;
@@ -2500,7 +2785,7 @@ static int request_proxy(REQUEST *request, int retransmit)
  */
 static int request_proxy_anew(REQUEST *request)
 {
-       home_server *home;
+       home_server_t *home;
 
        /*
         *      Delete the request from the proxy list.
@@ -2531,7 +2816,7 @@ static int request_proxy_anew(REQUEST *request)
        home_server_update_request(home, request);
 
        if (!insert_into_proxy_hash(request)) {
-               RPROXY("Failed to insert retransmission into the proxy list.");
+               RPROXY("Failed to insert retransmission into the proxy list");
                goto post_proxy_fail;
        }
 
@@ -2546,11 +2831,11 @@ static int request_proxy_anew(REQUEST *request)
        /*
         *      Update the Acct-Delay-Time attribute.
         */
-       if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+       if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
                VALUE_PAIR *vp;
 
                vp = pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
-               if (!vp) vp = radius_paircreate(request,
+               if (!vp) vp = radius_paircreate(request->proxy,
                                                &request->proxy->vps,
                                                PW_ACCT_DELAY_TIME, 0);
                if (vp) {
@@ -2569,7 +2854,7 @@ static int request_proxy_anew(REQUEST *request)
 
 STATE_MACHINE_DECL(request_ping)
 {
-       home_server *home = request->home_server;
+       home_server_t *home = request->home_server;
        char buffer[128];
 
        TRACE_STATE_MACHINE;
@@ -2607,11 +2892,14 @@ STATE_MACHINE_DECL(request_ping)
                if (home->state == HOME_STATE_ALIVE) break;
 
                /*
-                *      We haven't received enough ping responses to mark it
-                *      "alive".  Wait a bit.
+                *      It's dead, and we haven't received enough ping
+                *      responses to mark it "alive".  Wait a bit.
+                *
+                *      If it's zombie, we mark it alive immediately.
                 */
-               if (home->num_received_pings < home->num_pings_to_alive) {
-                       break;
+               if ((home->state == HOME_STATE_IS_DEAD) &&
+                   (home->num_received_pings < home->num_pings_to_alive)) {
+                       return;
                }
 
                /*
@@ -2619,7 +2907,7 @@ STATE_MACHINE_DECL(request_ping)
                 *      pings.
                 */
                home->state = HOME_STATE_ALIVE;
-               exec_trigger(request, request->home_server->cs, "home_server.alive", false);
+               exec_trigger(request, home->cs, "home_server.alive", false);
                home->currently_outstanding = 0;
                home->num_sent_pings = 0;
                home->num_received_pings = 0;
@@ -2650,7 +2938,7 @@ STATE_MACHINE_DECL(request_ping)
  */
 static void ping_home_server(void *ctx)
 {
-       home_server *home = ctx;
+       home_server_t *home = ctx;
        REQUEST *request;
        VALUE_PAIR *vp;
        struct timeval when, now;
@@ -2679,21 +2967,19 @@ static void ping_home_server(void *ctx)
 
        request = request_alloc(NULL);
        request->number = request_num_counter++;
-#ifdef HAVE_PTHREAD_H
-       request->child_pid = NO_SUCH_CHILD_PID;
-#endif
+       NO_CHILD_THREAD;
 
        request->proxy = rad_alloc(request, 1);
        rad_assert(request->proxy != NULL);
 
        if (home->ping_check == HOME_PING_CHECK_STATUS_SERVER) {
-               request->proxy->code = PW_STATUS_SERVER;
+               request->proxy->code = PW_CODE_STATUS_SERVER;
 
                pairmake(request->proxy, &request->proxy->vps,
                         "Message-Authenticator", "0x00", T_OP_SET);
 
        } else if (home->type == HOME_TYPE_AUTH) {
-               request->proxy->code = PW_AUTHENTICATION_REQUEST;
+               request->proxy->code = PW_CODE_AUTHENTICATION_REQUEST;
 
                pairmake(request->proxy, &request->proxy->vps,
                         "User-Name", home->ping_user_name, T_OP_SET);
@@ -2706,7 +2992,7 @@ static void ping_home_server(void *ctx)
 
        } else {
 #ifdef WITH_ACCOUNTING
-               request->proxy->code = PW_ACCOUNTING_REQUEST;
+               request->proxy->code = PW_CODE_ACCOUNTING_REQUEST;
 
                pairmake(request->proxy, &request->proxy->vps,
                         "User-Name", home->ping_user_name, T_OP_SET);
@@ -2734,7 +3020,9 @@ static void ping_home_server(void *ctx)
        request->proxy->dst_port = home->port;
        request->home_server = home;
 #ifdef DEBUG_STATE_MACHINE
-       if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_DONE);
+       if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
+                              child_state_names[request->child_state],
+                              child_state_names[REQUEST_DONE]);
        if (debug_flag) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_ping");
 #endif
 #ifdef HAVE_PTHREAD_H
@@ -2785,7 +3073,7 @@ static void ping_home_server(void *ctx)
        INSERT_EVENT(ping_home_server, home);
 }
 
-static void home_trigger(home_server *home, char const *trigger)
+static void home_trigger(home_server_t *home, char const *trigger)
 {
        REQUEST my_request;
        RADIUS_PACKET my_packet;
@@ -2799,8 +3087,9 @@ static void home_trigger(home_server *home, char const *trigger)
        exec_trigger(&my_request, home->cs, trigger, false);
 }
 
-static void mark_home_server_zombie(home_server *home)
+static void mark_home_server_zombie(home_server_t *home, struct timeval *now, struct timeval *response_window)
 {
+       time_t start;
        char buffer[128];
 
        ASSERT_MASTER;
@@ -2810,33 +3099,45 @@ static void mark_home_server_zombie(home_server *home)
 
 #ifdef WITH_TCP
        if (home->proto == IPPROTO_TCP) {
-               WDEBUG("Not marking TCP server %s zombie", home->name);
+               WARN("Not marking TCP server %s zombie", home->name);
                return;
        }
 #endif
 
+       /*
+        *      We've received a real packet recently.  Don't mark the
+        *      server as zombie until we've received NO packets for a
+        *      while.  The "1/4" of zombie period was chosen rather
+        *      arbitrarily.  It's a balance between too short, which
+        *      gives quick fail-over and fail-back, or too long,
+        *      where the proxy still sends packets to an unresponsive
+        *      home server.
+        */
+       start = now->tv_sec - ((home->zombie_period + 3) / 4);
+       if (home->last_packet_recv >= start) {
+               DEBUG("Recieved reply from home server %d seconds ago.  Might not be zombie.",
+                     (int) (now->tv_sec - home->last_packet_recv));
+               return;
+       }
+
        home->state = HOME_STATE_ZOMBIE;
        home_trigger(home, "home_server.zombie");
 
        /*
-        *      Back-date the zombie period to when we last expected
-        *      to see a response.  i.e. when we last sent a request.
+        *      Set the home server to "zombie", as of the time
+        *      calculated above.
         */
-       if (home->last_packet_sent == 0) {
-               gettimeofday(&home->zombie_period_start, NULL);
-       } else {
-               home->zombie_period_start.tv_sec = home->last_packet_sent;
-               home->zombie_period_start.tv_usec = 0;
-       }
+       home->zombie_period_start.tv_sec = start;
+       home->zombie_period_start.tv_usec = USEC / 2;
 
        fr_event_delete(el, &home->ev);
        home->num_sent_pings = 0;
        home->num_received_pings = 0;
 
-       PROXY( "Marking home server %s port %d as zombie (it has not responded in %d seconds).",
+       PROXY( "Marking home server %s port %d as zombie (it has not responded in %d.%06d seconds).",
               inet_ntop(home->ipaddr.af, &home->ipaddr.ipaddr,
                         buffer, sizeof(buffer)),
-              home->port, home->response_window);
+              home->port, (int) response_window->tv_sec, (int) response_window->tv_usec);
 
        ping_home_server(home);
 }
@@ -2844,7 +3145,7 @@ static void mark_home_server_zombie(home_server *home)
 
 void revive_home_server(void *ctx)
 {
-       home_server *home = ctx;
+       home_server_t *home = ctx;
        char buffer[128];
 
 #ifdef WITH_TCP
@@ -2867,14 +3168,14 @@ void revive_home_server(void *ctx)
               home->port);
 }
 
-void mark_home_server_dead(home_server *home, struct timeval *when)
+void mark_home_server_dead(home_server_t *home, struct timeval *when)
 {
        int previous_state = home->state;
        char buffer[128];
 
 #ifdef WITH_TCP
        if (home->proto == IPPROTO_TCP) {
-               WDEBUG("Not marking TCP server dead");
+               WARN("Not marking TCP server dead");
                return;
        }
 #endif
@@ -2917,12 +3218,13 @@ void mark_home_server_dead(home_server *home, struct timeval *when)
 STATE_MACHINE_DECL(proxy_wait_for_reply)
 {
        struct timeval now, when;
-       home_server *home = request->home_server;
+       struct timeval *response_window = NULL;
+       home_server_t *home = request->home_server;
        char buffer[128];
 
        TRACE_STATE_MACHINE;
 
-       rad_assert(request->packet->code != PW_STATUS_SERVER);
+       rad_assert(request->packet->code != PW_CODE_STATUS_SERVER);
        rad_assert(request->home_server != NULL);
 
        if (request->master_state == REQUEST_STOP_PROCESSING) {
@@ -2934,8 +3236,17 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 
        switch (action) {
        case FR_ACTION_DUP:
+               /*
+                *      We have a reply, ignore the retransmit.
+                */
                if (request->proxy_reply) return;
 
+               /*
+                *      The request was proxied to a virtual server.
+                *      Ignore the retransmit.
+                */
+               if (request->home_server->server) return;
+
                if ((home->state == HOME_STATE_IS_DEAD) ||
                    !request->proxy_listener ||
                    (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
@@ -2945,7 +3256,7 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
 
 #ifdef WITH_TCP
                if (home->proto == IPPROTO_TCP) {
-                       DEBUG2("Suppressing duplicate proxied request to home server %s port %d proto TCP - ID: %d",
+                       DEBUG2("Suppressing duplicate proxied request (tcp) to home server %s port %d proto TCP - ID: %d",
                               inet_ntop(request->proxy->dst_ipaddr.af,
                                         &request->proxy->dst_ipaddr.ipaddr,
                                         buffer, sizeof(buffer)),
@@ -2955,12 +3266,29 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                }
 #endif
 
+               /*
+                *      More than one retransmit a second is stupid,
+                *      and should be suppressed by the proxy.
+                */
+               when = request->proxy_retransmit;
+               when.tv_sec++;
+
+               if (timercmp(&now, &when, <)) {
+                       DEBUG2("Suppressing duplicate proxied request (too fast) to home server %s port %d proto TCP - ID: %d",
+                              inet_ntop(request->proxy->dst_ipaddr.af,
+                                        &request->proxy->dst_ipaddr.ipaddr,
+                                        buffer, sizeof(buffer)),
+                              request->proxy->dst_port,
+                              request->proxy->id);
+                       return;
+               }
+
 #ifdef WITH_ACCOUNTING
                /*
                 *      If we update the Acct-Delay-Time, we need to
                 *      get a new ID.
                 */
-               if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+               if ((request->packet->code == PW_CODE_ACCOUNTING_REQUEST) &&
                    pairfind(request->proxy->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY)) {
                        request_proxy_anew(request);
                        return;
@@ -2979,30 +3307,51 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                DEBUG_PACKET(request, request->proxy, 1);
                FR_STATS_TYPE_INC(home->stats.total_requests);
                home->last_packet_sent = now.tv_sec;
+               request->proxy_retransmit = now;
                request->proxy_listener->send(request->proxy_listener,
                                              request);
                break;
 
        case FR_ACTION_TIMER:
-               /*
-                *      Wake up "response_window" time in the future.
-                *      i.e. when MY packet hasn't received a response.
-                *
-                *      Note that we DO NOT mark the home server as
-                *      zombie if it doesn't respond to us.  It may be
-                *      responding to other (better looking) packets.
-                */
-               when = request->proxy->timestamp;
-               when.tv_sec += home->response_window;
+               response_window = request_response_window(request);
 
-               /*
-                *      Not at the response window.  Set the timer for
-                *      that.
-                */
-               if (timercmp(&when, &now, >)) {
-                       RDEBUG("Expecting proxy response no later than %d seconds from now", home->response_window);
-                       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
-                       return;
+#ifdef WITH_TCP
+               if (!request->proxy_listener ||
+                   (request->proxy_listener->status != RAD_LISTEN_STATUS_KNOWN)) {
+                       remove_from_proxy_hash(request);
+
+                       when = request->packet->timestamp;
+                       when.tv_sec += request->root->max_request_time;
+
+                       if (timercmp(&when, &now, >)) {
+                               RDEBUG("Waiting for client retransmission in order to do a proxy retransmit");
+                               STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+                               return;
+                       }
+               } else
+#endif
+               {
+                       /*
+                        *      Wake up "response_window" time in the future.
+                        *      i.e. when MY packet hasn't received a response.
+                        *
+                        *      Note that we DO NOT mark the home server as
+                        *      zombie if it doesn't respond to us.  It may be
+                        *      responding to other (better looking) packets.
+                        */
+                       when = request->proxy->timestamp;
+                       timeradd(&when, response_window, &when);
+
+                       /*
+                        *      Not at the response window.  Set the timer for
+                        *      that.
+                        */
+                       if (timercmp(&when, &now, >)) {
+                               RDEBUG("Expecting proxy response no later than %d.%06d seconds from now",
+                                      (int) response_window->tv_sec, (int) response_window->tv_usec);
+                               STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+                               return;
+                       }
                }
 
                RDEBUG("No proxy response, giving up on request and marking it done");
@@ -3020,12 +3369,12 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                 *      server state machine.
                 */
                if (((home->state == HOME_STATE_ALIVE) ||
-                    (home->state == HOME_STATE_UNKNOWN)) &&
+                    (home->state == HOME_STATE_UNKNOWN))
 #ifdef WITH_TCP
-                   (home->proto != IPPROTO_TCP) &&
+                   && (home->proto != IPPROTO_TCP)
 #endif
-                   ((home->last_packet_recv + home->response_window) <= now.tv_sec)) {
-                       mark_home_server_zombie(home);
+                       ) {
+                       mark_home_server_zombie(home, &now, response_window);
                }
 
                FR_STATS_TYPE_INC(home->stats.total_timeouts);
@@ -3046,18 +3395,19 @@ STATE_MACHINE_DECL(proxy_wait_for_reply)
                 *      may have failed over to another home server.
                 *      But that one may be dead, too.
                 */
-               RERROR("Failing request due to lack of any response from home server %s port %d",
+               RERROR("Failing proxied request, due to lack of any response from home server %s port %d",
                               inet_ntop(request->proxy->dst_ipaddr.af,
                                         &request->proxy->dst_ipaddr.ipaddr,
                                         buffer, sizeof(buffer)),
                               request->proxy->dst_port);
 
-               if (!setup_post_proxy_fail(request)) {
+               if (setup_post_proxy_fail(request)) {
+                       request_queue_or_run(request, proxy_no_reply);
+               } else {
                        gettimeofday(&request->reply->timestamp, NULL);
                        request_cleanup_delay_init(request, NULL);
-                       return;
                }
-               /* FALL-THROUGH */
+               break;
 
                /*
                 *      Duplicate proxy replies have been quenched by
@@ -3118,7 +3468,7 @@ static void request_coa_originate(REQUEST *request)
        if (vp) {
                if (vp->vp_integer == 0) {
                fail:
-                       request_done(request->coa, FR_ACTION_DONE);
+                       request_free(&request->coa);
                        return;
                }
        }
@@ -3176,7 +3526,7 @@ static void request_coa_originate(REQUEST *request)
                home_server_update_request(coa->home_server, coa);
 
        } else if (!coa->home_server) {
-               int port = PW_COA_UDP_PORT;
+               uint16_t port = PW_COA_UDP_PORT;
 
                vp = pairfind(coa->proxy->vps, PW_PACKET_DST_PORT, 0, TAG_ANY);
                if (vp) port = vp->vp_integer;
@@ -3193,8 +3543,8 @@ static void request_coa_originate(REQUEST *request)
        vp = pairfind(coa->proxy->vps, PW_PACKET_TYPE, 0, TAG_ANY);
        if (vp) {
                switch (vp->vp_integer) {
-               case PW_COA_REQUEST:
-               case PW_DISCONNECT_REQUEST:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
                        coa->proxy->code = vp->vp_integer;
                        break;
 
@@ -3205,7 +3555,7 @@ static void request_coa_originate(REQUEST *request)
                }
        }
 
-       if (!coa->proxy->code) coa->proxy->code = PW_COA_REQUEST;
+       if (!coa->proxy->code) coa->proxy->code = PW_CODE_COA_REQUEST;
 
        /*
         *      The rest of the server code assumes that
@@ -3214,25 +3564,24 @@ static void request_coa_originate(REQUEST *request)
         */
        rad_assert(coa->packet != NULL);
        rad_assert(coa->packet->vps == NULL);
-       memcpy(coa->packet, request->packet, sizeof(*request->packet));
-       coa->packet->vps = paircopy(coa->packet, request->packet->vps);
-       coa->packet->data = NULL;
-       rad_assert(coa->reply != NULL);
-       rad_assert(coa->reply->vps == NULL);
-       memcpy(coa->reply, request->reply, sizeof(*request->reply));
-       coa->reply->vps = paircopy(coa->reply, request->reply->vps);
-       coa->reply->data = NULL;
+
+       coa->packet = rad_copy_packet(coa, request->packet);
+       coa->reply = rad_copy_packet(coa, request->reply);
+
        coa->config_items = paircopy(coa, request->config_items);
        coa->num_coa_requests = 0;
        coa->handle = null_handler;
-       coa->number = request->number ^ (1 << 24);
+       coa->number = request->number; /* it's associated with the same request */
 
        /*
         *      Call the pre-proxy routines.
         */
        vp = pairfind(request->config_items, PW_PRE_PROXY_TYPE, 0, TAG_ANY);
        if (vp) {
-               RDEBUG2("  Found Pre-Proxy-Type %s", vp->vp_strvalue);
+               DICT_VALUE const *dval = dict_valbyattr(vp->da->attr, vp->da->vendor, vp->vp_integer);
+               /* Must be a validation issue */
+               rad_assert(dval);
+               RDEBUG2("Found Pre-Proxy-Type %s", dval->name);
                pre_proxy_type = vp->vp_integer;
        }
 
@@ -3240,9 +3589,11 @@ static void request_coa_originate(REQUEST *request)
                char const *old_server = coa->server;
 
                coa->server = coa->home_pool->virtual_server;
-               RDEBUG2(" server %s {", coa->server);
+               RDEBUG2("server %s {", coa->server);
+               RINDENT();
                rcode = process_pre_proxy(pre_proxy_type, coa);
-               RDEBUG2(" }");
+               REXDENT();
+               RDEBUG2("}");
                coa->server = old_server;
        } else {
                rcode = process_pre_proxy(pre_proxy_type, coa);
@@ -3268,7 +3619,7 @@ static void request_coa_originate(REQUEST *request)
        coa->proxy->dst_port = coa->home_server->port;
 
        if (!insert_into_proxy_hash(coa)) {
-               radlog_request(L_PROXY, 0, coa, "Failed to insert CoA request into proxy list.");
+               radlog_request(L_PROXY, 0, coa, "Failed to insert CoA request into proxy list");
                goto fail;
        }
 
@@ -3287,11 +3638,16 @@ static void request_coa_originate(REQUEST *request)
 
        DEBUG_PACKET(coa, coa->proxy, 1);
 
-       coa->process = request_coa_process;
+       coa->process = coa_wait_for_reply;
 #ifdef DEBUG_STATE_MACHINE
-       if (debug_flag) printf("(%u) ********\tSTATE %s C%u -> C%u\t********\n", request->number, __FUNCTION__, request->child_state, REQUEST_ACTIVE);
+       if (debug_flag) printf("(%u) ********\tSTATE %s C-%s -> C-%s\t********\n", request->number, __FUNCTION__,
+                              child_state_names[request->child_state],
+                              child_state_names[REQUEST_RUNNING]);
+#endif
+#ifdef HAVE_PTHREAD_H
+       coa->child_pid = NO_SUCH_CHILD_PID;
 #endif
-       coa->child_state = REQUEST_ACTIVE;
+       coa->child_state = REQUEST_PROXIED;
        rad_assert(coa->proxy_reply == NULL);
        FR_STATS_TYPE_INC(coa->home_server->stats.total_requests);
        coa->home_server->last_packet_sent = coa->proxy->timestamp.tv_sec;
@@ -3299,33 +3655,9 @@ static void request_coa_originate(REQUEST *request)
 }
 
 
-static void request_coa_separate(REQUEST *request)
+static void coa_timer(REQUEST *request)
 {
-#ifdef DEBUG_STATE_MACHINE
-       int action = FR_ACTION_TIMER;
-#endif
-       TRACE_STATE_MACHINE;
-
-       rad_assert(request->parent != NULL);
-       rad_assert(request->parent->coa == request);
-       rad_assert(request->ev == NULL);
-       rad_assert(!request->in_request_hash);
-
-       rad_assert(request->proxy_listener != NULL);
-       request = talloc_steal(request->proxy_listener, request);
-       request->parent->coa = NULL;
-       request->parent = NULL;
-
-       /*
-        *      Set up timers for the CoA request.  These do all kinds
-        *      of different things....
-        */
-       request_coa_timer(request);
-}
-
-static void request_coa_timer(REQUEST *request)
-{
-       int delay, frac;
+       uint32_t delay, frac;
        struct timeval now, when, mrd;
 
        rad_assert(request->parent == NULL);
@@ -3370,11 +3702,20 @@ static void request_coa_timer(REQUEST *request)
         */
        if (request->home_server->coa_mrc &&
            (request->num_coa_requests >= request->home_server->coa_mrc)) {
-               if (!setup_post_proxy_fail(request)) {
-                       return;
-               }
+               char buffer[128];
 
-               request_queue_or_run(request, proxy_running);
+               RERROR("Failing request - originate-coa ID %u, due to lack of any response from coa server %s port %d",
+                      request->proxy->id,
+                              inet_ntop(request->proxy->dst_ipaddr.af,
+                                        &request->proxy->dst_ipaddr.ipaddr,
+                                        buffer, sizeof(buffer)),
+                              request->proxy->dst_port);
+
+               if (setup_post_proxy_fail(request)) {
+                       request_queue_or_run(request, coa_no_reply);
+               } else {
+                       request_done(request, FR_ACTION_DONE);
+               }
                return;
        }
 
@@ -3436,23 +3777,81 @@ static void request_coa_timer(REQUEST *request)
                                      request);
 }
 
-
-#ifdef HAVE_PTHREAD_H
-STATE_MACHINE_DECL(coa_running)
+STATE_MACHINE_DECL(coa_wait_for_reply)
 {
+       rad_assert(request->parent == NULL);
+
        TRACE_STATE_MACHINE;
 
        switch (action) {
        case FR_ACTION_TIMER:
-               request_coa_timer(request);
+               /*
+                *      This is big enough to be in it's own function.
+                */
+               coa_timer(request);
                break;
 
        case FR_ACTION_PROXY_REPLY:
+               rad_assert(request->parent == NULL);
+               request_queue_or_run(request, coa_running);
+               break;
+
+       default:
+               RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
+               break;
+       }
+}
+
+static void request_coa_separate(REQUEST *request)
+{
+#ifdef DEBUG_STATE_MACHINE
+       int action = FR_ACTION_TIMER;
+#endif
+       TRACE_STATE_MACHINE;
+
+       rad_assert(request->parent != NULL);
+       rad_assert(request->parent->coa == request);
+       rad_assert(request->ev == NULL);
+       rad_assert(!request->in_request_hash);
+       rad_assert(request->coa == NULL);
+
+       rad_assert(request->proxy_listener != NULL);
+
+       (void) talloc_steal(NULL, request);
+       request->parent->coa = NULL;
+       request->parent = NULL;
+
+       /*
+        *      Should be coa_wait_for_reply()
+        */
+       request->process(request, FR_ACTION_TIMER);
+}
+
+STATE_MACHINE_DECL(coa_no_reply)
+{
+       char buffer[128];
+
+       TRACE_STATE_MACHINE;
+
+       switch (action) {
+       case FR_ACTION_TIMER:
                request_common(request, action);
                break;
 
+       case FR_ACTION_PROXY_REPLY: /* too late! */
+               RDEBUG2("Reply from CoA server %s port %d  - ID: %d arrived too late.",
+                       inet_ntop(request->proxy->src_ipaddr.af,
+                                 &request->proxy->src_ipaddr.ipaddr,
+                                 buffer, sizeof(buffer)),
+                       request->proxy->dst_port, request->proxy->id);
+               break;
+
        case FR_ACTION_RUN:
-               request_running(request, FR_ACTION_PROXY_REPLY);
+               /*
+                *      FIXME: do recv_coa Fail
+                */
+               (void) process_proxy_reply(request, NULL);
+               request_done(request, FR_ACTION_DONE);
                break;
 
        default:
@@ -3460,37 +3859,27 @@ STATE_MACHINE_DECL(coa_running)
                break;
        }
 }
-#endif /* HAVE_PTHREAD_H */
 
-
-/*
- *     Process CoA requests that we originated.
- */
-STATE_MACHINE_DECL(request_coa_process)
+STATE_MACHINE_DECL(coa_running)
 {
        TRACE_STATE_MACHINE;
 
        switch (action) {
        case FR_ACTION_TIMER:
-               request_coa_timer(request);
+               request_process_timer(request);
                break;
 
        case FR_ACTION_PROXY_REPLY:
-               rad_assert(request->parent == NULL);
-#ifdef HAVE_PTHREAD_H
-               /*
-                *      Catch the case of a proxy reply when called
-                *      from the main worker thread.
-                */
-               if (we_are_master() &&
-                   (request->process != coa_running)) {
-                       request_queue_or_run(request, coa_running);
-                       return;
-               }
-               /* FALL-THROUGH */
-#endif
+               request_common(request, action);
+               break;
+
        case FR_ACTION_RUN:
-               request_running(request, action);
+               if (process_proxy_reply(request, request->proxy_reply)) {
+                       request->handle(request);
+                       request_finish(request, action);
+               } else {
+                       request_done(request, FR_ACTION_DONE);
+               }
                break;
 
        default:
@@ -3498,7 +3887,6 @@ STATE_MACHINE_DECL(request_coa_process)
                break;
        }
 }
-
 #endif /* WITH_COA */
 
 /***********************************************************************
@@ -3537,6 +3925,8 @@ static void event_socket_handler(UNUSED fr_event_list_t *xel, UNUSED int fd, voi
 }
 
 #ifdef WITH_DETAIL
+#ifdef WITH_DETAIL_THREAD
+#else
 /*
  *     This function is called periodically to see if this detail
  *     file is available for reading.
@@ -3571,7 +3961,8 @@ static void event_poll_detail(void *ctx)
                fr_exit(1);
        }
 }
-#endif
+#endif /* WITH_DETAIL_THREAD */
+#endif /* WITH_DETAIL */
 
 static void event_status(struct timeval *wake)
 {
@@ -3581,14 +3972,14 @@ static void event_status(struct timeval *wake)
 
        if (debug_flag == 0) {
                if (just_started) {
-                       INFO("Ready to process requests.");
+                       INFO("Ready to process requests");
                        just_started = false;
                }
                return;
        }
 
        if (!wake) {
-               INFO("Ready to process requests.");
+               INFO("Ready to process requests");
 
        } else if ((wake->tv_sec != 0) ||
                   (wake->tv_usec >= 100000)) {
@@ -3616,11 +4007,83 @@ static void event_status(struct timeval *wake)
 
 }
 
+#ifdef WITH_TCP
+static void listener_free_cb(void *ctx)
+{
+       rad_listen_t *this = ctx;
+       char buffer[1024];
+
+       if (this->count > 0) {
+               struct timeval when;
+               listen_socket_t *sock = this->data;
+
+               fr_event_now(el, &when);
+               when.tv_sec += 3;
+
+               if (!fr_event_insert(el, listener_free_cb, this, &when,
+                                    &(sock->ev))) {
+                       rad_panic("Failed to insert event");
+               }
 
-int event_new_fd(rad_listen_t *this)
+               return;
+       }
+
+       /*
+        *      It's all free, close the socket.
+        */
+
+       this->print(this, buffer, sizeof(buffer));
+       DEBUG("... cleaning up socket %s", buffer);
+       listen_free(&this);
+}
+#endif
+
+#ifdef WITH_PROXY
+static int proxy_eol_cb(void *ctx, void *data)
+{
+       struct timeval when;
+       REQUEST *request = fr_packet2myptr(REQUEST, proxy, data);
+
+       if (request->proxy_listener != ctx) return 0;
+
+       /*
+        *      We don't care if it's being processed in a child thread.
+        */
+
+#ifdef WITH_ACCOUNTING
+       /*
+        *      Accounting packets should be deleted immediately.
+        *      They will never be retransmitted by the client.
+        */
+       if (request->proxy->code == PW_CODE_ACCOUNTING_REQUEST) {
+               RDEBUG("Stopping request due to failed connection to home server");
+               request->master_state = REQUEST_STOP_PROCESSING;
+       }
+#endif
+
+       /*
+        *      Reset the timer to be now, so that the request is
+        *      quickly updated.  But spread the requests randomly
+        *      over the next second, so that we don't overload the
+        *      server.
+        */
+       fr_event_now(el, &when);
+       tv_add(&when, fr_rand() % USEC);
+       STATE_MACHINE_TIMER(FR_ACTION_TIMER);
+
+       /*
+        *      Don't delete it from the list.
+        */
+       return 0;
+}
+#endif
+
+static int event_new_fd(rad_listen_t *this)
 {
        char buffer[1024];
 
+       ASSERT_MASTER;
+
        if (this->status == RAD_LISTEN_STATUS_KNOWN) return 1;
 
        this->print(this, buffer, sizeof(buffer));
@@ -3634,109 +4097,86 @@ int event_new_fd(rad_listen_t *this)
                        INFO(" ... adding new socket %s", buffer);
                }
 
-#ifdef WITH_PROXY
-               /*
-                *      Add it to the list of sockets we can use.
-                *      Server sockets (i.e. auth/acct) are never
-                *      added to the packet list.
-                */
-               if (this->type == RAD_LISTEN_PROXY) {
-                       PTHREAD_MUTEX_LOCK(&proxy_mutex);
-                       if (!fr_packet_list_socket_add(proxy_list, this->fd,
-                                                      sock->proto,
-                                                      &sock->other_ipaddr, sock->other_port,
-                                                      this)) {
-
-#ifdef HAVE_PTHREAD_H
-                               proxy_no_new_sockets = true;
-#endif
-                               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
-
-                               /*
-                                *      This is bad.  However, the
-                                *      packet list now supports 256
-                                *      open sockets, which should
-                                *      minimize this problem.
-                                */
-                               ERROR("Failed adding proxy socket: %s",
-                                      fr_strerror());
-                               return 0;
-                       }
-
-                       if (sock->home) {
-                               sock->home->limit.num_connections++;
-
-#ifdef HAVE_PTHREAD_H
-                               /*
-                                *      If necessary, add it to the list of
-                                *      new proxy listeners.
-                                */
-                               if (sock->home->limit.lifetime || sock->home->limit.idle_timeout) {
-                                       this->next = proxy_listener_list;
-                                       proxy_listener_list = this;
-                               }
-#endif
-                       }
-                       PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
-
-                       /*
-                        *      Tell the main thread that we've added
-                        *      a proxy listener, but only if we need
-                        *      to update the event list.  Do this
-                        *      with the mutex unlocked, to reduce
-                        *      contention.
-                        */
-                       if (sock->home) {
-                               if (sock->home->limit.lifetime || sock->home->limit.idle_timeout) {
-                                       radius_signal_self(RADIUS_SIGNAL_SELF_NEW_FD);
-                               }
-                       }
-               }
-#endif
-
+               switch (this->type) {
 #ifdef WITH_DETAIL
                /*
                 *      Detail files are always known, and aren't
                 *      put into the socket event loop.
                 */
-               if (this->type == RAD_LISTEN_DETAIL) {
+               case RAD_LISTEN_DETAIL:
                        this->status = RAD_LISTEN_STATUS_KNOWN;
 
+#ifndef WITH_DETAIL_THREAD
                        /*
                         *      Set up the first poll interval.
                         */
                        event_poll_detail(this);
                        return 1;
-               }
+#else
+                       break;  /* add the FD to the list */
 #endif
+#endif /* WITH_DETAIL */
 
-#ifdef WITH_TCP
+#ifdef WITH_PROXY
                /*
-                *      Add timers to child sockets, if necessary.
+                *      Add it to the list of sockets we can use.
+                *      Server sockets (i.e. auth/acct) are never
+                *      added to the packet list.
                 */
-               if (sock->proto == IPPROTO_TCP && sock->opened &&
-                   (sock->limit.lifetime || sock->limit.idle_timeout)) {
-                       struct timeval when;
+               case RAD_LISTEN_PROXY:
+#ifdef WITH_TCP
+                       /*
+                        *      Add timers to outgoing child sockets, if necessary.
+                        */
+                       if (sock->proto == IPPROTO_TCP && sock->opened &&
+                           (sock->home->limit.lifetime || sock->home->limit.idle_timeout)) {
+                               struct timeval when;
+
+                               when.tv_sec = sock->opened + 1;
+                               when.tv_usec = 0;
+
+                               if (!fr_event_insert(el, tcp_socket_timer, this, &when,
+                                                    &(sock->ev))) {
+                                       rad_panic("Failed to insert event");
+                               }
+                       }
+#endif
+                       break;
+#endif /* WITH_PROXY */
 
-                       ASSERT_MASTER;
+                       /*
+                        *      FIXME: put idle timers on command sockets.
+                        */
 
-                       when.tv_sec = sock->opened + 1;
-                       when.tv_usec = 0;
+               default:
+#ifdef WITH_TCP
+                       /*
+                        *      Add timers to incoming child sockets, if necessary.
+                        */
+                       if (sock->proto == IPPROTO_TCP && sock->opened &&
+                           (sock->limit.lifetime || sock->limit.idle_timeout)) {
+                               struct timeval when;
 
-                       if (!fr_event_insert(el, tcp_socket_timer, this, &when,
-                                            &(sock->ev))) {
-                               rad_panic("Failed to insert event");
+                               when.tv_sec = sock->opened + 1;
+                               when.tv_usec = 0;
+
+                               if (!fr_event_insert(el, tcp_socket_timer, this, &when,
+                                                    &(sock->ev))) {
+                                       rad_panic("Failed to insert event");
+                               }
                        }
-               }
 #endif
+                       break;
+               } /* switch over listener types */
 
-               FD_MUTEX_LOCK(&fd_mutex);
+               /*
+                *      All sockets: add the FD to the event handler.
+                */
                if (!fr_event_fd_insert(el, 0, this->fd,
                                        event_socket_handler, this)) {
                        ERROR("Failed adding event handler for socket!");
                        fr_exit(1);
                }
-               FD_MUTEX_UNLOCK(&fd_mutex);
 
                this->status = RAD_LISTEN_STATUS_KNOWN;
                return 1;
@@ -3747,6 +4187,11 @@ int event_new_fd(rad_listen_t *this)
         *      Stop using this socket, if at all possible.
         */
        if (this->status == RAD_LISTEN_STATUS_EOL) {
+               /*
+                *      Remove it from the list of live FD's.
+                */
+               fr_event_fd_delete(el, 0, this->fd);
+
 #ifdef WITH_PROXY
                /*
                 *      Proxy sockets get frozen, so that we don't use
@@ -3758,10 +4203,11 @@ int event_new_fd(rad_listen_t *this)
                        PTHREAD_MUTEX_LOCK(&proxy_mutex);
                        if (!fr_packet_list_socket_freeze(proxy_list,
                                                          this->fd)) {
-                               radlog(L_ERR, "Fatal error freezing socket: %s",
-                                      fr_strerror());
+                               ERROR("Fatal error freezing socket: %s", fr_strerror());
                                fr_exit(1);
                        }
+
+                       fr_packet_list_walk(proxy_list, this, proxy_eol_cb);
                        PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
                }
 #endif
@@ -3805,13 +4251,7 @@ int event_new_fd(rad_listen_t *this)
 #ifdef WITH_TCP
                listen_socket_t *sock = this->data;
 #endif
-
-               /*
-                *      Remove it from the list of live FD's.
-                */
-               FD_MUTEX_LOCK(&fd_mutex);
-               fr_event_fd_delete(el, 0, this->fd);
-               FD_MUTEX_UNLOCK(&fd_mutex);
+               struct timeval when;
 
                /*
                 *      Re-open the socket, pointing it to /dev/null.
@@ -3830,12 +4270,12 @@ int event_new_fd(rad_listen_t *this)
                devnull = open("/dev/null", O_RDWR);
                if (devnull < 0) {
                        ERROR("FATAL failure opening /dev/null: %s",
-                              strerror(errno));
+                              fr_syserror(errno));
                        fr_exit(1);
                }
                if (dup2(devnull, this->fd) < 0) {
                        ERROR("FATAL failure closing socket: %s",
-                              strerror(errno));
+                              fr_syserror(errno));
                        fr_exit(1);
                }
                close(devnull);
@@ -3845,7 +4285,7 @@ int event_new_fd(rad_listen_t *this)
 #endif
 
 #ifdef WITH_TCP
-               INFO(" ... closing socket %s", buffer);
+               INFO(" ... shutting down socket %s", buffer);
 
 #ifdef WITH_PROXY
                /*
@@ -3855,17 +4295,13 @@ int event_new_fd(rad_listen_t *this)
                 */
                if (this->type == RAD_LISTEN_PROXY) {
                        PTHREAD_MUTEX_LOCK(&proxy_mutex);
-                       fr_packet_list_walk(proxy_list, this,
-                                           eol_proxy_listener);
+                       fr_packet_list_walk(proxy_list, this, eol_proxy_listener);
 
                        if (!fr_packet_list_socket_del(proxy_list, this->fd)) {
                                ERROR("Fatal error removing socket %s: %s",
                                      buffer, fr_strerror());
                                fr_exit(1);
                        }
-                       if (sock->home &&  sock->home->limit.num_connections) {
-                               sock->home->limit.num_connections--;
-                       }
                        PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
                } else
 #endif
@@ -3873,19 +4309,28 @@ int event_new_fd(rad_listen_t *this)
                        /*
                         *      EOL all requests using this socket.
                         */
-                       fr_packet_list_walk(pl, this,
-                                           eol_listener);
+                       fr_packet_list_walk(pl, this, eol_listener);
                }
 
                /*
-                *      Remove any pending cleanups.
+                *      No child threads, clean it up now.
                 */
-               if (sock->ev) fr_event_delete(el, &sock->ev);
+               if (!spawn_flag) {
+                       if (sock->ev) fr_event_delete(el, &sock->ev);
+                       listen_free(&this);
+                       return 1;
+               }
 
                /*
-                *      And finally, close the socket.
+                *      Wait until all requests using this socket are done.
                 */
-               listen_free(&this);
+               gettimeofday(&when, NULL);
+               when.tv_sec += 3;
+
+               if (!fr_event_insert(el, listener_free_cb, this, &when,
+                                    &(sock->ev))) {
+                       rad_panic("Failed to insert event");
+               }
        }
 #endif /* WITH_TCP */
 
@@ -3900,6 +4345,8 @@ int event_new_fd(rad_listen_t *this)
 
 static void handle_signal_self(int flag)
 {
+       ASSERT_MASTER;
+
        if ((flag & (RADIUS_SIGNAL_SELF_EXIT | RADIUS_SIGNAL_SELF_TERM)) != 0) {
                if ((flag & RADIUS_SIGNAL_SELF_EXIT) != 0) {
                        INFO("Signalled to exit");
@@ -3926,7 +4373,7 @@ static void handle_signal_self(int flag)
                        return;
                }
 
-               INFO("Received HUP signal.");
+               INFO("Received HUP signal");
 
                last_hup = when;
 
@@ -3935,13 +4382,14 @@ static void handle_signal_self(int flag)
        }
 
 #ifdef WITH_DETAIL
+#ifndef WITH_DETAIL_THREAD
        if ((flag & RADIUS_SIGNAL_SELF_DETAIL) != 0) {
                rad_listen_t *this;
 
                /*
                 *      FIXME: O(N) loops suck.
                 */
-               for (this = mainconfig.listen;
+               for (this = main_config.listen;
                     this != NULL;
                     this = this->next) {
                        if (this->type != RAD_LISTEN_DETAIL) continue;
@@ -3959,58 +4407,48 @@ static void handle_signal_self(int flag)
                }
        }
 #endif
+#endif
 
 #ifdef WITH_TCP
 #ifdef WITH_PROXY
 #ifdef HAVE_PTHREAD_H
        /*
-        *      Add event handlers for idle timeouts && maximum lifetime.
+        *      There are new listeners in the list.  Run
+        *      event_new_fd() on them.
         */
        if ((flag & RADIUS_SIGNAL_SELF_NEW_FD) != 0) {
-               struct timeval when, now;
-
-               fr_event_now(el, &now);
+               rad_listen_t *this, *next;
 
-               PTHREAD_MUTEX_LOCK(&proxy_mutex);
-
-               while (proxy_listener_list) {
-                       rad_listen_t *this = proxy_listener_list;
-                       listen_socket_t *sock = this->data;
+               FD_MUTEX_LOCK(&fd_mutex);
 
-                       rad_assert(sock->proto == IPPROTO_TCP);
-                       proxy_listener_list = this->next;
+               /*
+                *      FIXME: unlock the mutex before calling
+                *      event_new_fd()?
+                */
+               for (this = new_listeners; this != NULL; this = next) {
+                       next = this->next;
                        this->next = NULL;
 
-                       if (!sock->home) continue; /* skip UDP sockets */
-
-                       when = now;
-
-                       /*
-                        *      Sockets should only be added to the
-                        *      proxy_listener_list if they have limits.
-                        *
-                        */
-                       rad_assert(sock->home->limit.lifetime || sock->home->limit.idle_timeout);
-
-                       if (!fr_event_insert(el, tcp_socket_timer, this, &when,
-                                            &(sock->ev))) {
-                               rad_panic("Failed to insert event");
-                       }
+                       event_new_fd(this);
                }
 
-               PTHREAD_MUTEX_UNLOCK(&proxy_mutex);
+               new_listeners = NULL;
+               FD_MUTEX_UNLOCK(&fd_mutex);
        }
 #endif /* HAVE_PTHREAD_H */
 #endif /* WITH_PROXY */
 #endif /* WITH_TCP */
 }
 
-#ifndef WITH_SELF_PIPE
+#ifndef HAVE_PTHREAD_H
 void radius_signal_self(int flag)
 {
-       handle_signal_self(flag);
+       return handle_signal_self(flag);
 }
+
 #else
+static int self_pipe[2] = { -1, -1 };
+
 /*
  *     Inform ourselves that we received a signal.
  */
@@ -4035,7 +4473,7 @@ void radius_signal_self(int flag)
 
        buffer[0] |= flag;
 
-       write(self_pipe[1], buffer, 1);
+       if (write(self_pipe[1], buffer, 1) < 0) fr_exit(0);
 }
 
 
@@ -4057,7 +4495,7 @@ static void event_signal_handler(UNUSED fr_event_list_t *xel,
 
        handle_signal_self(buffer[0]);
 }
-#endif
+#endif /* HAVE_PTHREAD_H */
 
 /***********************************************************************
  *
@@ -4068,16 +4506,26 @@ static void event_signal_handler(UNUSED fr_event_list_t *xel,
 /*
  *     Externally-visibly functions.
  */
-int radius_event_init(CONF_SECTION *cs, int have_children)
+int radius_event_init(TALLOC_CTX *ctx) {
+       el = fr_event_list_create(ctx, event_status);
+       if (!el) return 0;
+
+       return 1;
+}
+
+int radius_event_start(CONF_SECTION *cs, bool have_children)
 {
        rad_listen_t *head = NULL;
 
-       if (el) return 0;
+       if (fr_start_time != (time_t)-1) return 0;
 
        time(&fr_start_time);
 
-       el = fr_event_list_create(event_status);
-       if (!el) return 0;
+       /*
+        *  radius_event_init() must be called first
+        */
+       rad_assert(el);
+       if (fr_start_time == (time_t)-1) return 0;
 
        pl = fr_packet_list_create(0);
        if (!pl) return 0;      /* leak el */
@@ -4085,7 +4533,7 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
        request_num_counter = 0;
 
 #ifdef WITH_PROXY
-       if (mainconfig.proxy_requests) {
+       if (main_config.proxy_requests) {
                /*
                 *      Create the tree for managing proxied requests and
                 *      responses.
@@ -4096,13 +4544,20 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
 #ifdef HAVE_PTHREAD_H
                if (pthread_mutex_init(&proxy_mutex, NULL) != 0) {
                        ERROR("FATAL: Failed to initialize proxy mutex: %s",
-                              strerror(errno));
+                              fr_syserror(errno));
                        fr_exit(1);
                }
 #endif
        }
 #endif
 
+       /*
+        *      Move all of the thread calls to this file?
+        *
+        *      It may be best for the mutexes to be in this file...
+        */
+       spawn_flag = have_children;
+
 #ifdef HAVE_PTHREAD_H
        NO_SUCH_CHILD_PID = pthread_self(); /* not a child thread */
 
@@ -4111,21 +4566,14 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
         *      we're running normally.
         */
        if (have_children && !check_config &&
-           (thread_pool_init(cs, &have_children) < 0)) {
+           (thread_pool_init(cs, &spawn_flag) < 0)) {
                fr_exit(1);
        }
 #endif
 
-       /*
-        *      Move all of the thread calls to this file?
-        *
-        *      It may be best for the mutexes to be in this file...
-        */
-       spawn_flag = have_children;
-
        if (check_config) {
                DEBUG("%s: #### Skipping IP addresses and Ports ####",
-                      mainconfig.name);
+                      main_config.name);
                if (listen_init(cs, &head, spawn_flag) < 0) {
                        fflush(NULL);
                        fr_exit(1);
@@ -4133,26 +4581,26 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
                return 1;
        }
 
-#ifdef WITH_SELF_PIPE
+#ifdef HAVE_PTHREAD_H
        /*
         *      Child threads need a pipe to signal us, as do the
         *      signal handlers.
         */
        if (pipe(self_pipe) < 0) {
                ERROR("radiusd: Error opening internal pipe: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                fr_exit(1);
        }
        if ((fcntl(self_pipe[0], F_SETFL, O_NONBLOCK) < 0) ||
            (fcntl(self_pipe[0], F_SETFD, FD_CLOEXEC) < 0)) {
                ERROR("radiusd: Error setting internal flags: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                fr_exit(1);
        }
        if ((fcntl(self_pipe[1], F_SETFL, O_NONBLOCK) < 0) ||
            (fcntl(self_pipe[1], F_SETFD, FD_CLOEXEC) < 0)) {
                ERROR("radiusd: Error setting internal flags: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                fr_exit(1);
        }
 
@@ -4161,10 +4609,10 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
                ERROR("Failed creating handler for signals");
                fr_exit(1);
        }
-#endif /* WITH_SELF_PIPE */
+#endif
 
        DEBUG("%s: #### Opening IP addresses and Ports ####",
-              mainconfig.name);
+              main_config.name);
 
        /*
        *       The server temporarily switches to an unprivileged
@@ -4179,7 +4627,7 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
                fr_exit_now(1);
        }
 
-       mainconfig.listen = head;
+       main_config.listen = head;
 
        /*
         *      At this point, no one has any business *ever* going
@@ -4191,40 +4639,85 @@ int radius_event_init(CONF_SECTION *cs, int have_children)
 }
 
 
-static int request_hash_cb(UNUSED void *ctx, void *data)
+#ifdef WITH_PROXY
+static int proxy_delete_cb(UNUSED void *ctx, void *data)
 {
-       REQUEST *request = fr_packet2myptr(REQUEST, packet, data);
+       REQUEST *request = fr_packet2myptr(REQUEST, proxy, data);
 
-#ifdef WITH_PROXY
-       rad_assert(request->in_proxy_hash == false);
+       request->master_state = REQUEST_STOP_PROCESSING;
+
+#ifdef HAVE_PTHREAD_H
+       if (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0) return 0;
 #endif
 
-       request_done(request, FR_ACTION_DONE);
+       /*
+        *      If it's queued we can't delete it from the queue.
+        *
+        *      Otherwise, it's OK to delete it.  Even RUNNING, because
+        *      that will get caught by the check above.
+        */
+       if (request->child_state == REQUEST_QUEUED) return 0;
 
-       return 0;
+       request->in_proxy_hash = false;
+
+       if (!request->in_request_hash) {
+               request_done(request, FR_ACTION_DONE);
+       }
+
+       /*
+        *      Delete it from the list.
+        */
+       return 2;
 }
+#endif
 
 
-#ifdef WITH_PROXY
-static int proxy_hash_cb(UNUSED void *ctx, void *data)
+static int request_delete_cb(UNUSED void *ctx, void *data)
 {
-       REQUEST *request = fr_packet2myptr(REQUEST, proxy, data);
+       REQUEST *request = fr_packet2myptr(REQUEST, packet, data);
 
-       request_done(request, FR_ACTION_DONE);
+       request->master_state = REQUEST_STOP_PROCESSING;
 
-       return 0;
-}
+       /*
+        *      Not done, or the child thread is still processing it.
+        */
+       if (request->child_state < REQUEST_RESPONSE_DELAY) return 0; /* continue */
+
+#ifdef HAVE_PTHREAD_H
+       if (pthread_equal(request->child_pid, NO_SUCH_CHILD_PID) == 0) return 0;
 #endif
 
-void radius_event_free(void)
-{
+#ifdef WITH_PROXY
+       rad_assert(request->in_proxy_hash == false);
+#endif
+
+       request->in_request_hash = false;
+       if (request->ev) fr_event_delete(el, &request->ev);
+
+       if (main_config.memory_report) {
+               RDEBUG2("Cleaning up request packet ID %u with timestamp +%d",
+                       request->packet->id,
+                       (unsigned int) (request->timestamp - fr_start_time));
+       }
+
+#ifdef WITH_COA
+       if (request->coa) {
+               rad_assert(!request->coa->in_proxy_hash);
+       }
+#endif
+
+       request_free(&request);
+
        /*
-        *      Stop and join all threads.
+        *      Delete it from the list, and continue;
         */
-#ifdef HAVE_PTHREAD_H
-       thread_pool_stop();
+       return 2;
+}
+
+
+void radius_event_free(void)
+{
        ASSERT_MASTER;
-#endif
 
 #ifdef WITH_PROXY
        /*
@@ -4232,18 +4725,57 @@ void radius_event_free(void)
         *      referenced from anywhere else.  Remove them first.
         */
        if (proxy_list) {
-               fr_packet_list_walk(proxy_list, NULL, proxy_hash_cb);
-               fr_packet_list_free(proxy_list);
-               proxy_list = NULL;
+               fr_packet_list_walk(proxy_list, NULL, proxy_delete_cb);
        }
 #endif
 
-       fr_packet_list_walk(pl, NULL, request_hash_cb);
+       fr_packet_list_walk(pl, NULL, request_delete_cb);
+
+       if (spawn_flag) {
+               /*
+                *      Now that all requests have been marked "please stop",
+                *      ensure that all of the threads have exited.
+                */
+#ifdef HAVE_PTHREAD_H
+               thread_pool_stop();
+#endif
+
+               /*
+                *      Walk the lists again, ensuring that all
+                *      requests are done.
+                */
+               if (main_config.memory_report) {
+                       int num;
+
+#ifdef WITH_PROXY
+                       if (proxy_list) {
+                               fr_packet_list_walk(proxy_list, NULL, proxy_delete_cb);
+                               num = fr_packet_list_num_elements(proxy_list);
+                               if (num > 0) {
+                                       ERROR("Proxy list has %d requests still in it.", num);
+                               }
+                       }
+#endif
+
+                       fr_packet_list_walk(pl, NULL, request_delete_cb);
+                       num = fr_packet_list_num_elements(pl);
+                       if (num > 0) {
+                               ERROR("Request list has %d requests still in it.", num);
+                       }
+               }
+       }
 
        fr_packet_list_free(pl);
        pl = NULL;
 
-       fr_event_list_free(el);
+#ifdef WITH_PROXY
+       fr_packet_list_free(proxy_list);
+       proxy_list = NULL;
+#endif
+
+       TALLOC_FREE(el);
+
+       if (debug_condition) talloc_free(debug_condition);
 }
 
 int radius_event_process(void)
index d01f4b4..792379d 100644 (file)
@@ -538,7 +538,7 @@ static void parse_xlat(char const *input, char *output, size_t outlen)
 {
        ssize_t slen;
        char const *error = NULL;
-       char *fmt = talloc_strdup(NULL, input);
+       char *fmt = talloc_typed_strdup(NULL, input);
        xlat_exp_t *head;
 
        slen = xlat_tokenize(fmt, fmt, &head, &error);
@@ -569,7 +569,6 @@ static void process_file(const char *root_dir, char const *filename)
 
        if (strcmp(filename, "-") == 0) {
                fp = stdin;
-               filename = "<stdin>";
                directory[0] = '\0';
 
        } else {
@@ -582,9 +581,11 @@ static void process_file(const char *root_dir, char const *filename)
                fp = fopen(directory, "r");
                if (!fp) {
                        fprintf(stderr, "Error opening %s: %s\n",
-                               directory, strerror(errno));
+                               directory, fr_syserror(errno));
                        exit(1);
                }
+
+               filename = directory;
        }
 
        lineno = 0;
@@ -619,6 +620,8 @@ static void process_file(const char *root_dir, char const *filename)
                while (isspace((int) *p)) p++;
                if (!*p) continue;
 
+               DEBUG2("%s[%d]: %s\n", filename, lineno, buffer);
+
                strlcpy(input, p, sizeof(input));
 
                if (strncmp(p, "raw ", 4) == 0) {
@@ -635,9 +638,17 @@ static void process_file(const char *root_dir, char const *filename)
                                continue;
                        }
 
+                       if (outlen > sizeof(data)) outlen = sizeof(data);
+
+                       if (outlen >= (sizeof(output) / 2)) {
+                               outlen = (sizeof(output) / 2) - 1;
+                       }
+
                        data_len = outlen;
                        for (i = 0; i < outlen; i++) {
-                               snprintf(output + 3*i, sizeof(output),
+                               if (sizeof(output) < (3*i)) break;
+
+                               snprintf(output + 3*i, sizeof(output) - (3*i) - 1,
                                         "%02x ", data[i]);
                        }
                        outlen = strlen(output);
@@ -647,8 +658,8 @@ static void process_file(const char *root_dir, char const *filename)
 
                if (strncmp(p, "data ", 5) == 0) {
                        if (strcmp(p + 5, output) != 0) {
-                               fprintf(stderr, "Mismatch in line %d of %s, expected: %s\n",
-                                       lineno, directory, output);
+                               fprintf(stderr, "Mismatch in line %d of %s, got: %s expected: %s\n",
+                                       lineno, directory, output, p + 5);
                                exit(1);
                        }
                        continue;
@@ -734,9 +745,9 @@ static void process_file(const char *root_dir, char const *filename)
                        if (head) {
                                vp_cursor_t cursor;
                                p = output;
-                               for (vp = paircursor(&cursor, &head);
+                               for (vp = fr_cursor_init(&cursor, &head);
                                     vp;
-                                    vp = pairnext(&cursor)) {
+                                    vp = fr_cursor_next(&cursor)) {
                                        vp_prints(p, sizeof(output) - (p - output), vp);
                                        p += strlen(p);
 
@@ -755,6 +766,18 @@ static void process_file(const char *root_dir, char const *filename)
                        continue;
                }
 
+               if (strncmp(p, "attribute ", 10) == 0) {
+                       p += 10;
+
+                       if (userparse(NULL, p, &head) != T_EOL) {
+                               strlcpy(output, fr_strerror(), sizeof(output));
+                               continue;
+                       }
+
+                       vp_prints(output, sizeof(output), head);
+                       continue;
+               }
+
                if (strncmp(p, "$INCLUDE ", 9) == 0) {
                        char *q;
 
@@ -795,14 +818,25 @@ static void process_file(const char *root_dir, char const *filename)
 int main(int argc, char *argv[])
 {
        int c;
-       int report = false;
+       bool report = false;
        char const *radius_dir = RADDBDIR;
+       char const *dict_dir = DICTDIR;
 
-       while ((c = getopt(argc, argv, "d:xM")) != EOF) switch(c) {
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radattr");
+               exit(EXIT_FAILURE);
+       }
+#endif
+
+       while ((c = getopt(argc, argv, "d:D:xM")) != EOF) switch(c) {
                case 'd':
                        radius_dir = optarg;
                        break;
-               case 'x':
+               case 'D':
+                       dict_dir = optarg;
+                       break;
+               case 'x':
                        fr_debug_flag++;
                        debug_flag = fr_debug_flag;
                        break;
@@ -816,12 +850,20 @@ int main(int argc, char *argv[])
        argc -= (optind - 1);
        argv += (optind - 1);
 
-       if (report) {
-               talloc_enable_null_tracking();
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radattr");
+               return 1;
+       }
+
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radattr");
+               return 1;
        }
-       talloc_set_log_fn(log_talloc);
 
-       if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
                fr_perror("radattr");
                return 1;
        }
@@ -840,7 +882,7 @@ int main(int argc, char *argv[])
 
        if (report) {
                dict_free();
-               log_talloc_report(NULL);
+               fr_log_talloc_report(NULL);
        }
 
        return 0;
index 5db05c0..87f91f9 100644 (file)
  *   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,2006,2014  The FreeRADIUS server project
  * Copyright 2000  Miquel van Smoorenburg <miquels@cistron.nl>
  * Copyright 2000  Alan DeKok <aland@ox.org>
  */
 
 RCSID("$Id$")
 
-#include <freeradius-devel/libradius.h>
-#include <freeradius-devel/conf.h>
+#include <freeradius-devel/radclient.h>
 #include <freeradius-devel/radpaths.h>
-
+#include <freeradius-devel/conf.h>
 #include <ctype.h>
 
 #ifdef HAVE_GETOPT_H
-#      include <getopt.h>
+#  include <getopt.h>
 #endif
 
 #include <assert.h>
@@ -41,24 +40,22 @@ typedef struct REQUEST REQUEST;     /* to shut up warnings about mschap.h */
 #include "smbdes.h"
 #include "mschap.h"
 
-static int success = 0;
 static int retries = 3;
 static float timeout = 5;
 static char const *secret = NULL;
-static int do_output = 1;
-static int totalapp = 0;
-static int totaldeny = 0;
-static int totallost = 0;
+static bool do_output = true;
+
+static rc_stats_t stats;
 
-static int server_port = 0;
+static uint16_t server_port = 0;
 static int packet_code = 0;
 static fr_ipaddr_t server_ipaddr;
 static int resend_count = 1;
-static int done = 1;
-static int print_filename = 0;
+static bool done = true;
+static bool print_filename = false;
 
 static fr_ipaddr_t client_ipaddr;
-static int client_port = 0;
+static uint16_t client_port = 0;
 
 static int sockfd;
 static int last_used_id = -1;
@@ -73,27 +70,12 @@ static fr_packet_list_t *pl = NULL;
 
 static int sleep_time = -1;
 
-typedef struct radclient_t {
-       struct          radclient_t *prev;
-       struct          radclient_t *next;
-
-       char const      *filename;
-       int             packet_number; /* in the file */
-       char            password[256];
-       time_t          timestamp;
-       RADIUS_PACKET   *request;
-       RADIUS_PACKET   *reply;
-       int             resend;
-       int             tries;
-       int             done;
-} radclient_t;
-
-static radclient_t *radclient_head = NULL;
-static radclient_t *radclient_tail = NULL;
+static rc_request_t *request_head = NULL;
+static rc_request_t *rc_request_tail = NULL;
 
 char const *radclient_version = "radclient version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
-" (git #" RADIUSD_VERSION_COMMIT ")"
+" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
 #endif
 ", built on " __DATE__ " at " __TIME__;
 
@@ -101,62 +83,74 @@ static void NEVER_RETURNS usage(void)
 {
        fprintf(stderr, "Usage: radclient [options] server[:port] <command> [<secret>]\n");
 
-       fprintf(stderr, "  <command>     One of auth, acct, status, coa, or disconnect.\n");
-       fprintf(stderr, "  -c <count>    Send each packet 'count' times.\n");
-       fprintf(stderr, "  -d <raddb>    Set dictionary directory.\n");
-       fprintf(stderr, "  -f <file>     Read packets from file, not stdin.\n");
-       fprintf(stderr, "  -F            Print the file name, packet number and reply code.\n");
-       fprintf(stderr, "  -h            Print usage help information.\n");
-       fprintf(stderr, "  -i <id>       Set request id to 'id'.  Values may be 0..255\n");
-       fprintf(stderr, "  -n <num>      Send N requests/s\n");
-       fprintf(stderr, "  -p <num>      Send 'num' packets from a file in parallel.\n");
-       fprintf(stderr, "  -q            Do not print anything out.\n");
-       fprintf(stderr, "  -r <retries>  If timeout, retry sending the packet 'retries' times.\n");
-       fprintf(stderr, "  -s            Print out summary information of auth results.\n");
-       fprintf(stderr, "  -S <file>     read secret from file, not command line.\n");
-       fprintf(stderr, "  -t <timeout>  Wait 'timeout' seconds before retrying (may be a floating point number).\n");
-       fprintf(stderr, "  -v            Show program version information.\n");
-       fprintf(stderr, "  -x            Debugging mode.\n");
-       fprintf(stderr, "  -4            Use IPv4 address of server\n");
-       fprintf(stderr, "  -6            Use IPv6 address of server.\n");
+       fprintf(stderr, "  <command>              One of auth, acct, status, coa, or disconnect.\n");
+       fprintf(stderr, "  -4                     Use IPv4 address of server\n");
+       fprintf(stderr, "  -6                     Use IPv6 address of server.\n");
+       fprintf(stderr, "  -c <count>             Send each packet 'count' times.\n");
+       fprintf(stderr, "  -d <raddb>             Set user dictionary directory (defaults to " RADDBDIR ").\n");
+       fprintf(stderr, "  -D <dictdir>           Set main dictionary directory (defaults to " DICTDIR ").\n");
+       fprintf(stderr, "  -f <file>[:<file>]     Read packets from file, not stdin.\n");
+       fprintf(stderr, "                         If a second file is provided, it will be used to verify responses\n");
+       fprintf(stderr, "  -F                     Print the file name, packet number and reply code.\n");
+       fprintf(stderr, "  -h                     Print usage help information.\n");
+       fprintf(stderr, "  -i <id>                Set request id to 'id'.  Values may be 0..255\n");
+       fprintf(stderr, "  -n <num>               Send N requests/s\n");
+       fprintf(stderr, "  -p <num>               Send 'num' packets from a file in parallel.\n");
+       fprintf(stderr, "  -q                     Do not print anything out.\n");
+       fprintf(stderr, "  -r <retries>           If timeout, retry sending the packet 'retries' times.\n");
+       fprintf(stderr, "  -s                     Print out summary information of auth results.\n");
+       fprintf(stderr, "  -S <file>              read secret from file, not command line.\n");
+       fprintf(stderr, "  -t <timeout>           Wait 'timeout' seconds before retrying (may be a floating point number).\n");
+       fprintf(stderr, "  -v                     Show program version information.\n");
+       fprintf(stderr, "  -x                     Debugging mode.\n");
+
 #ifdef WITH_TCP
-       fprintf(stderr, "  -P <proto>    Use proto (tcp or udp) for transport.\n");
+       fprintf(stderr, "  -P <proto>             Use proto (tcp or udp) for transport.\n");
 #endif
 
        exit(1);
 }
 
+static const FR_NAME_NUMBER request_types[] = {
+       { "auth",       PW_CODE_AUTHENTICATION_REQUEST },
+       { "challenge",  PW_CODE_ACCESS_CHALLENGE },
+       { "acct",       PW_CODE_ACCOUNTING_REQUEST },
+       { "status",     PW_CODE_STATUS_SERVER },
+       { "disconnect", PW_CODE_DISCONNECT_REQUEST },
+       { "coa",        PW_CODE_COA_REQUEST },
+       { "auto",       PW_CODE_UNDEFINED },
+
+       { NULL, 0}
+};
+
 /*
  *     Free a radclient struct, which may (or may not)
  *     already be in the list.
  */
-static void radclient_free(radclient_t *radclient)
+static int _rc_request_free(rc_request_t *request)
 {
-       radclient_t *prev, *next;
-
-       if (radclient->request) rad_free(&radclient->request);
-       if (radclient->reply) rad_free(&radclient->reply);
+       rc_request_t *prev, *next;
 
-       prev = radclient->prev;
-       next = radclient->next;
+       prev = request->prev;
+       next = request->next;
 
        if (prev) {
-               assert(radclient_head != radclient);
+               assert(request_head != request);
                prev->next = next;
-       } else if (radclient_head) {
-               assert(radclient_head == radclient);
-               radclient_head = next;
+       } else if (request_head) {
+               assert(request_head == request);
+               request_head = next;
        }
 
        if (next) {
-               assert(radclient_tail != radclient);
+               assert(rc_request_tail != request);
                next->prev = prev;
-       } else if (radclient_tail) {
-               assert(radclient_tail == radclient);
-               radclient_tail = prev;
+       } else if (rc_request_tail) {
+               assert(rc_request_tail == request);
+               rc_request_tail = prev;
        }
 
-       free(radclient);
+       return 0;
 }
 
 static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
@@ -164,7 +158,7 @@ static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
 {
        unsigned int i;
        uint8_t *p;
-       VALUE_PAIR *challenge, *response;
+       VALUE_PAIR *challenge, *reply;
        uint8_t nthash[16];
 
        challenge = paircreate(packet, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT);
@@ -179,15 +173,15 @@ static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
                p[i] = fr_rand();
        }
 
-       response = paircreate(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
-       if (!response) {
+       reply = paircreate(packet, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT);
+       if (!reply) {
                return 0;
        }
 
-       pairadd(request, response);
-       response->length = 50;
-       response->vp_octets = p = talloc_array(response, uint8_t, response->length);
-       memset(p, 0, response->length);
+       pairadd(request, reply);
+       reply->length = 50;
+       reply->vp_octets = p = talloc_array(reply, uint8_t, reply->length);
+       memset(p, 0, reply->length);
 
        p[1] = 0x01; /* NT hash */
 
@@ -200,35 +194,86 @@ static int mschapv1_encode(RADIUS_PACKET *packet, VALUE_PAIR **request,
 }
 
 
+static int getport(char const *name)
+{
+       struct  servent *svp;
+
+       svp = getservbyname(name, "udp");
+       if (!svp) {
+               return 0;
+       }
+
+       return ntohs(svp->s_port);
+}
+
+static void radclient_get_port(PW_CODE type, uint16_t *port)
+{
+       switch (type) {
+       default:
+       case PW_CODE_AUTHENTICATION_REQUEST:
+       case PW_CODE_ACCESS_CHALLENGE:
+       case PW_CODE_STATUS_SERVER:
+               if (*port == 0) *port = getport("radius");
+               if (*port == 0) *port = PW_AUTH_UDP_PORT;
+               return;
+
+       case PW_CODE_ACCOUNTING_REQUEST:
+               if (*port == 0) *port = getport("radacct");
+               if (*port == 0) *port = PW_ACCT_UDP_PORT;
+               return;
+
+       case PW_CODE_DISCONNECT_REQUEST:
+       case PW_CODE_COA_REQUEST:
+               if (*port == 0) *port = PW_COA_UDP_PORT;
+               return;
+
+       case PW_CODE_UNDEFINED:
+               if (*port == 0) *port = 0;
+       }
+}
+
 /*
  *     Initialize a radclient data structure and add it to
  *     the global linked list.
  */
-static int radclient_init(char const *filename)
+static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
 {
-       FILE *fp;
+       FILE *packets, *filters = NULL;
+
        vp_cursor_t cursor;
        VALUE_PAIR *vp;
-       radclient_t *radclient;
-       int filedone = 0;
-       int packet_number = 1;
+       rc_request_t *request;
+       bool packets_done = false;
+       uint64_t num = 0;
 
-       assert(filename != NULL);
+       assert(files->packets != NULL);
 
        /*
         *      Determine where to read the VP's from.
         */
-       if (strcmp(filename, "-") != 0) {
-               fp = fopen(filename, "r");
-               if (!fp) {
-                       fprintf(stderr, "radclient: Error opening %s: %s\n",
-                               filename, strerror(errno));
+       if (strcmp(files->packets, "-") != 0) {
+               packets = fopen(files->packets, "r");
+               if (!packets) {
+                       ERROR("Error opening %s: %s", files->packets, strerror(errno));
                        return 0;
                }
+
+               /*
+                *      Read in the pairs representing the expected response.
+                */
+               if (files->filters) {
+                       filters = fopen(files->filters, "r");
+                       if (!filters) {
+                               ERROR("Error opening %s: %s", files->filters, strerror(errno));
+                               fclose(packets);
+                               return 0;
+                       }
+               }
        } else {
-               fp = stdin;
+               packets = stdin;
        }
 
+
        /*
         *      Loop until the file is done.
         */
@@ -236,69 +281,158 @@ static int radclient_init(char const *filename)
                /*
                 *      Allocate it.
                 */
-               radclient = malloc(sizeof(*radclient));
-               if (!radclient) {
-                       goto oom;
+               request = talloc_zero(ctx, rc_request_t);
+               if (!request) {
+                       ERROR("Out of memory");
+                       goto error;
                }
-               memset(radclient, 0, sizeof(*radclient));
+               talloc_set_destructor(request, _rc_request_free);
 
-               radclient->request = rad_alloc(NULL, 1);
-               if (!radclient->request) {
-                       goto oom;
+               request->packet = rad_alloc(request, 1);
+               if (!request->packet) {
+                       ERROR("Out of memory");
+                       goto error;
                }
 
 #ifdef WITH_TCP
-               radclient->request->src_ipaddr = client_ipaddr;
-               radclient->request->src_port = client_port;
-               radclient->request->dst_ipaddr = server_ipaddr;
-               radclient->request->dst_port = server_port;
+               request->packet->src_ipaddr = client_ipaddr;
+               request->packet->src_port = client_port;
+               request->packet->dst_ipaddr = server_ipaddr;
+               request->packet->dst_port = server_port;
+               request->packet->proto = ipproto;
 #endif
 
-               radclient->filename = filename;
-               radclient->request->id = -1; /* allocate when sending */
-               radclient->packet_number = packet_number++;
+               request->files = files;
+               request->packet->id = -1; /* allocate when sending */
+               request->num = num++;
 
                /*
-                *      Read the VP's.
+                *      Read the request VP's.
                 */
-               radclient->request->vps = readvp2(NULL, fp, &filedone, "radclient:");
-               if (!radclient->request->vps) {
-                       rad_free(&radclient->request);
-                       free(radclient);
-                       if (fp != stdin) fclose(fp);
-                       return 1;
+               if (readvp2(&request->packet->vps, request->packet, packets, &packets_done) < 0) {
+                       ERROR("Error parsing \"%s\"", files->packets);
+                       goto error;
+               }
+
+               fr_cursor_init(&cursor, &request->filter);
+               vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
+               if (vp) {
+                       fr_cursor_remove(&cursor);
+                       request->packet_code = vp->vp_integer;
+                       talloc_free(vp);
+               } else {
+                       request->packet_code = packet_code; /* Use the default set on the command line */
                }
 
                /*
-                *      Keep a copy of the the User-Password attribute.
+                *      Read in filter VP's.
                 */
-               if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
-                       strlcpy(radclient->password, vp->vp_strvalue,
-                               sizeof(radclient->password));
+               if (filters) {
+                       bool filters_done;
+
+                       if (readvp2(&request->filter, request, filters, &filters_done) < 0) {
+                               ERROR("Error parsing \"%s\"", files->filters);
+                               goto error;
+                       }
+
+                       if (!request->filter) {
+                               goto error;
+                       }
+
+                       if (filters_done && !packets_done) {
+                               ERROR("Differing number of packets/filters in %s:%s "
+                                     "(too many requests))", files->packets, files->filters);
+                               goto error;
+                       }
+
+                       if (!filters_done && packets_done) {
+                               ERROR("Differing number of packets/filters in %s:%s "
+                                     "(too many filters))", files->packets, files->filters);
+                               goto error;
+                       }
+
+                       fr_cursor_init(&cursor, &request->filter);
+                       vp = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
+                       if (vp) {
+                               fr_cursor_remove(&cursor);
+                               request->filter_code = vp->vp_integer;
+                               talloc_free(vp);
+                       }
+
                        /*
-                        *      Otherwise keep a copy of the CHAP-Password attribute.
+                        *      xlat expansions aren't supported here
                         */
-               } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
-                       strlcpy(radclient->password, vp->vp_strvalue,
-                               sizeof(radclient->password));
+                       for (vp = fr_cursor_init(&cursor, &request->filter);
+                            vp;
+                            vp = fr_cursor_next(&cursor)) {
+                               if (vp->type == VT_XLAT) {
+                                       vp->type = VT_DATA;
+                                       vp->vp_strvalue = vp->value.xlat;
+                               }
+                       }
+
+                       /*
+                        *      This allows efficient list comparisons later
+                        */
+                       pairsort(&request->filter, attrtagcmp);
+               }
+
+               /*
+                *      Determine the response code from the request (if not already set)
+                */
+               if (!request->filter_code) {
+                       switch (request->packet_code) {
+                       case PW_CODE_AUTHENTICATION_REQUEST:
+                               request->filter_code = PW_CODE_AUTHENTICATION_ACK;
+                               break;
+
+                       case PW_CODE_ACCOUNTING_REQUEST:
+                               request->filter_code = PW_CODE_ACCOUNTING_RESPONSE;
+                               break;
+
+                       case PW_CODE_COA_REQUEST:
+                               request->filter_code = PW_CODE_COA_ACK;
+                               break;
+
+                       case PW_CODE_DISCONNECT_REQUEST:
+                               request->filter_code = PW_CODE_DISCONNECT_ACK;
+                               break;
+
+                       default:
+                               break;
+                       }
+               }
+
+               /*
+                *      Keep a copy of the the User-Password attribute.
+                */
+               if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
+                       strlcpy(request->password, vp->vp_strvalue,
+                               sizeof(request->password));
+               /*
+                *      Otherwise keep a copy of the CHAP-Password attribute.
+                */
+               } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
+                       strlcpy(request->password, vp->vp_strvalue,
+                               sizeof(request->password));
 
-               } else if ((vp = pairfind(radclient->request->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
-                       strlcpy(radclient->password, vp->vp_strvalue,
-                               sizeof(radclient->password));
+               } else if ((vp = pairfind(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
+                       strlcpy(request->password, vp->vp_strvalue,
+                               sizeof(request->password));
                } else {
-                       radclient->password[0] = '\0';
+                       request->password[0] = '\0';
                }
 
                /*
                 *      Fix up Digest-Attributes issues
                 */
-               for (vp = paircursor(&cursor, &radclient->request->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
-                       /*
-                        *      Double quoted strings get marked up as xlat expansions,
-                        *      but we don't support that in radclient.
-                        */
+                    vp = fr_cursor_next(&cursor)) {
+                       /*
+                        *      Double quoted strings get marked up as xlat expansions,
+                        *      but we don't support that in request.
+                        */
                        if (vp->type == VT_XLAT) {
                                vp->vp_strvalue = vp->value.xlat;
                                vp->value.xlat = NULL;
@@ -314,35 +448,35 @@ static int radclient_init(char const *filename)
                                 *      the attributes read from the file.
                                 */
                        case PW_PACKET_TYPE:
-                               radclient->request->code = vp->vp_integer;
+                               request->packet->code = vp->vp_integer;
                                break;
 
                        case PW_PACKET_DST_PORT:
-                               radclient->request->dst_port = (vp->vp_integer & 0xffff);
+                               request->packet->dst_port = (vp->vp_integer & 0xffff);
                                break;
 
                        case PW_PACKET_DST_IP_ADDRESS:
-                               radclient->request->dst_ipaddr.af = AF_INET;
-                               radclient->request->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+                               request->packet->dst_ipaddr.af = AF_INET;
+                               request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                                break;
 
                        case PW_PACKET_DST_IPV6_ADDRESS:
-                               radclient->request->dst_ipaddr.af = AF_INET6;
-                               radclient->request->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
+                               request->packet->dst_ipaddr.af = AF_INET6;
+                               request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
                                break;
 
                        case PW_PACKET_SRC_PORT:
-                               radclient->request->src_port = (vp->vp_integer & 0xffff);
+                               request->packet->src_port = (vp->vp_integer & 0xffff);
                                break;
 
                        case PW_PACKET_SRC_IP_ADDRESS:
-                               radclient->request->src_ipaddr.af = AF_INET;
-                               radclient->request->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+                               request->packet->src_ipaddr.af = AF_INET;
+                               request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                                break;
 
                        case PW_PACKET_SRC_IPV6_ADDRESS:
-                               radclient->request->src_ipaddr.af = AF_INET6;
-                               radclient->request->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
+                               request->packet->src_ipaddr.af = AF_INET6;
+                               request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
                                break;
 
                        case PW_DIGEST_REALM:
@@ -358,7 +492,7 @@ static int radclient_init(char const *filename)
                                /* overlapping! */
                                {
                                        DICT_ATTR const *da;
-                                       uint8_t *p;
+                                       uint8_t *p, *q;
 
                                        p = talloc_array(vp, uint8_t, vp->length + 2);
 
@@ -367,14 +501,29 @@ static int radclient_init(char const *filename)
                                        vp->length += 2;
                                        p[1] = vp->length;
 
-                                       pairmemsteal(vp, p);
-
                                        da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
                                        if (!da) {
-                                               goto oom;
+                                               ERROR("Out of memory");
+                                               goto error;
                                        }
-
                                        vp->da = da;
+
+                                       /*
+                                        *      Re-do pairmemsteal ourselves,
+                                        *      because we play games with
+                                        *      vp->da, and pairmemsteal goes
+                                        *      to GREAT lengths to sanitize
+                                        *      and fix and change and
+                                        *      double-check the various
+                                        *      fields.
+                                        */
+                                       memcpy(&q, &vp->vp_octets, sizeof(q));
+                                       talloc_free(q);
+
+                                       vp->vp_octets = talloc_steal(vp, p);
+                                       vp->type = VT_DATA;
+
+                                       VERIFY_VP(vp);
                                }
 
                                break;
@@ -382,33 +531,44 @@ static int radclient_init(char const *filename)
                } /* loop over the VP's we read in */
 
                /*
+                *      Automatically set the port if we don't have a global
+                *      or packet specific one.
+                */
+               if ((server_port == 0) && (request->packet->dst_port == 0)) {
+                       radclient_get_port(request->packet->code, &request->packet->dst_port);
+               }
+
+               /*
                 *      Add it to the tail of the list.
                 */
-               if (!radclient_head) {
-                       assert(radclient_tail == NULL);
-                       radclient_head = radclient;
-                       radclient->prev = NULL;
+               if (!request_head) {
+                       assert(rc_request_tail == NULL);
+                       request_head = request;
+                       request->prev = NULL;
                } else {
-                       assert(radclient_tail->next == NULL);
-                       radclient_tail->next = radclient;
-                       radclient->prev = radclient_tail;
+                       assert(rc_request_tail->next == NULL);
+                       rc_request_tail->next = request;
+                       request->prev = rc_request_tail;
                }
-               radclient_tail = radclient;
-               radclient->next = NULL;
+               rc_request_tail = request;
+               request->next = NULL;
 
-       } while (!filedone); /* loop until the file is done. */
+       } while (!packets_done); /* loop until the file is done. */
 
-       if (fp != stdin) fclose(fp);
+       if (packets != stdin) fclose(packets);
+       if (filters) fclose(filters);
 
        /*
         *      And we're done.
         */
        return 1;
 
-       oom:
-       fprintf(stderr, "radclient: Out of memory\n");
-       free(radclient);
-       if (fp != stdin) fclose(fp);
+error:
+       talloc_free(request);
+
+       if (packets != stdin) fclose(packets);
+       if (filters) fclose(filters);
+
        return 0;
 }
 
@@ -416,49 +576,57 @@ static int radclient_init(char const *filename)
 /*
  *     Sanity check each argument.
  */
-static int radclient_sane(radclient_t *radclient)
+static int radclient_sane(rc_request_t *request)
 {
-       if (radclient->request->dst_port == 0) {
-               radclient->request->dst_port = server_port;
+       if (request->packet->dst_port == 0) {
+               request->packet->dst_port = server_port;
        }
-       if (radclient->request->dst_ipaddr.af == AF_UNSPEC) {
+       if (request->packet->dst_ipaddr.af == AF_UNSPEC) {
                if (server_ipaddr.af == AF_UNSPEC) {
-                       fprintf(stderr, "radclient: No server was given, but request %d in file %s did not contain Packet-Dst-IP-Address\n",
-                               radclient->packet_number, radclient->filename);
+                       ERROR("No server was given, and request %" PRIu64 " in file %s did not contain "
+                             "Packet-Dst-IP-Address", request->num, request->files->packets);
                        return -1;
                }
-               radclient->request->dst_ipaddr = server_ipaddr;
+               request->packet->dst_ipaddr = server_ipaddr;
        }
-       if (radclient->request->code == 0) {
+       if (request->packet->code == 0) {
                if (packet_code == -1) {
-                       fprintf(stderr, "radclient: Request was \"auto\", but request %d in file %s did not contain Packet-Type\n",
-                               radclient->packet_number, radclient->filename);
+                       ERROR("Request was \"auto\", and request %" PRIu64 " in file %s did not contain Packet-Type",
+                             request->num, request->files->packets);
                        return -1;
                }
-               radclient->request->code = packet_code;
+               request->packet->code = packet_code;
        }
-       radclient->request->sockfd = -1;
+       request->packet->sockfd = -1;
 
        return 0;
 }
 
 
 /*
- *     For request handline.
+ *     For request handling.
  */
 static int filename_cmp(void const *one, void const *two)
 {
-       return strcmp((char const *) one, (char const *) two);
+       int cmp;
+
+       rc_file_pair_t const *a = one;
+       rc_file_pair_t const *b = two;
+
+       cmp = strcmp(a->packets, b->packets);
+       if (cmp != 0) return cmp;
+
+       return strcmp(a->filters, b->filters);
 }
 
 static int filename_walk(UNUSED void *context, void *data)
 {
-       char const      *filename = data;
+       rc_file_pair_t *files = data;
 
        /*
         *      Read request(s) from the file.
         */
-       if (!radclient_init(filename)) {
+       if (!radclient_init(files, files)) {
                return -1;      /* stop walking */
        }
 
@@ -469,117 +637,53 @@ static int filename_walk(UNUSED void *context, void *data)
 /*
  *     Deallocate packet ID, etc.
  */
-static void deallocate_id(radclient_t *radclient)
+static void deallocate_id(rc_request_t *request)
 {
-       if (!radclient || !radclient->request ||
-           (radclient->request->id < 0)) {
+       if (!request || !request->packet ||
+           (request->packet->id < 0)) {
                return;
        }
 
        /*
         *      One more unused RADIUS ID.
         */
-       fr_packet_list_id_free(pl, radclient->request, true);
+       fr_packet_list_id_free(pl, request->packet, true);
 
        /*
         *      If we've already sent a packet, free up the old one,
         *      and ensure that the next packet has a unique
         *      authentication vector.
         */
-       if (radclient->request->data) {
-               talloc_free(radclient->request->data);
-               radclient->request->data = NULL;
+       if (request->packet->data) {
+               TALLOC_FREE(request->packet->data);
        }
 
-       if (radclient->reply) rad_free(&radclient->reply);
-}
-
-
-static void print_hex(RADIUS_PACKET *packet)
-{
-       int i;
-
-       if (!packet->data) return;
-
-       printf("  Code:\t\t%u\n", packet->data[0]);
-       printf("  Id:\t\t%u\n", packet->data[1]);
-       printf("  Length:\t%u\n", ((packet->data[2] << 8) |
-                                  (packet->data[3])));
-       printf("  Vector:\t");
-       for (i = 4; i < 20; i++) {
-               printf("%02x", packet->data[i]);
-       }
-       printf("\n");
-
-       if (packet->data_len > 20) {
-               int total;
-               uint8_t const *ptr;
-               printf("  Data:");
-
-               total = packet->data_len - 20;
-               ptr = packet->data + 20;
-
-               while (total > 0) {
-                       int attrlen;
-
-                       printf("\t\t");
-                       if (total < 2) { /* too short */
-                               printf("%02x\n", *ptr);
-                               break;
-                       }
-
-                       if (ptr[1] > total) { /* too long */
-                               for (i = 0; i < total; i++) {
-                                       printf("%02x ", ptr[i]);
-                               }
-                               break;
-                       }
-
-                       printf("%02x  %02x  ", ptr[0], ptr[1]);
-                       attrlen = ptr[1] - 2;
-                       ptr += 2;
-                       total -= 2;
-
-                       for (i = 0; i < attrlen; i++) {
-                               if ((i > 0) && ((i & 0x0f) == 0x00))
-                                       printf("\t\t\t");
-                               printf("%02x ", ptr[i]);
-                               if ((i & 0x0f) == 0x0f) printf("\n");
-                       }
-
-                       if ((attrlen & 0x0f) != 0x00) printf("\n");
-
-                       ptr += attrlen;
-                       total -= attrlen;
-               }
-       }
-       fflush(stdout);
+       if (request->reply) rad_free(&request->reply);
 }
 
 /*
  *     Send one packet.
  */
-static int send_one_packet(radclient_t *radclient)
+static int send_one_packet(rc_request_t *request)
 {
-       assert(radclient->done == 0);
+       assert(request->done == false);
 
        /*
         *      Remember when we have to wake up, to re-send the
-        *      request, of we didn't receive a response.
+        *      request, of we didn't receive a reply.
         */
-       if ((sleep_time == -1) ||
-           (sleep_time > (int) timeout)) {
+       if ((sleep_time == -1) || (sleep_time > (int) timeout)) {
                sleep_time = (int) timeout;
        }
 
        /*
         *      Haven't sent the packet yet.  Initialize it.
         */
-       if (radclient->request->id == -1) {
+       if (request->packet->id == -1) {
                int i;
                bool rcode;
 
-               assert(radclient->reply == NULL);
+               assert(request->reply == NULL);
 
                /*
                 *      Didn't find a free packet ID, we're not done,
@@ -587,9 +691,9 @@ static int send_one_packet(radclient_t *radclient)
                 *      this packet.
                 */
        retry:
-               radclient->request->src_ipaddr.af = server_ipaddr.af;
+               request->packet->src_ipaddr.af = server_ipaddr.af;
                rcode = fr_packet_list_id_alloc(pl, ipproto,
-                                               &radclient->request, NULL);
+                                               &request->packet, NULL);
                if (!rcode) {
                        int mysockfd;
 
@@ -602,38 +706,37 @@ static int send_one_packet(radclient_t *radclient)
 #endif
                        mysockfd = fr_socket(&client_ipaddr, 0);
                        if (mysockfd < 0) {
-                               fprintf(stderr, "radclient: Can't open new socket: %s\n",
-                                       strerror(errno));
+                               ERROR("Can't open new socket: %s", strerror(errno));
                                exit(1);
                        }
                        if (!fr_packet_list_socket_add(pl, mysockfd, ipproto,
                                                       &server_ipaddr,
                                                       server_port, NULL)) {
-                               fprintf(stderr, "radclient: Can't add new socket\n");
+                               ERROR("Can't add new socket");
                                exit(1);
                        }
                        goto retry;
                }
 
-               assert(radclient->request->id != -1);
-               assert(radclient->request->data == NULL);
+               assert(request->packet->id != -1);
+               assert(request->packet->data == NULL);
 
                for (i = 0; i < 4; i++) {
-                       ((uint32_t *) radclient->request->vector)[i] = fr_rand();
+                       ((uint32_t *) request->packet->vector)[i] = fr_rand();
                }
 
                /*
                 *      Update the password, so it can be encrypted with the
                 *      new authentication vector.
                 */
-               if (radclient->password[0] != '\0') {
+               if (request->password[0] != '\0') {
                        VALUE_PAIR *vp;
 
-                       if ((vp = pairfind(radclient->request->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
-                               pairstrcpy(vp, radclient->password);
+                       if ((vp = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY)) != NULL) {
+                               pairstrcpy(vp, request->password);
 
-                       } else if ((vp = pairfind(radclient->request->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
-                               int already_hex = 0;
+                       } else if ((vp = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
+                               bool already_hex = false;
 
                                /*
                                 *      If it's 17 octets, it *might* be already encoded.
@@ -645,7 +748,7 @@ static int send_one_packet(radclient_t *radclient)
                                if (vp->length == 17) {
                                        for (i = 0; i < 17; i++) {
                                                if (vp->vp_octets[i] < 32) {
-                                                       already_hex = 1;
+                                                       already_hex = true;
                                                        break;
                                                }
                                        }
@@ -658,43 +761,43 @@ static int send_one_packet(radclient_t *radclient)
                                        uint8_t *p;
                                        size_t len, len2;
 
-                                       len = len2 = strlen(radclient->password);
+                                       len = len2 = strlen(request->password);
                                        if (len2 < 17) len2 = 17;
 
                                        p = talloc_zero_array(vp, uint8_t, len2);
 
-                                       memcpy(p, radclient->password, len);
+                                       memcpy(p, request->password, len);
 
-                                       rad_chap_encode(radclient->request,
+                                       rad_chap_encode(request->packet,
                                                        p,
                                                        fr_rand() & 0xff, vp);
                                        vp->vp_octets = p;
                                        vp->length = 17;
                                }
-                       } else if (pairfind(radclient->request->vps, PW_MSCHAP_PASSWORD, 0, TAG_ANY) != NULL) {
-                               mschapv1_encode(radclient->request,
-                                               &radclient->request->vps,
-                                               radclient->password);
-                       } else if (fr_debug_flag) {
-                               printf("WARNING: No password in the request\n");
+                       } else if (pairfind(request->packet->vps, PW_MS_CHAP_PASSWORD, 0, TAG_ANY) != NULL) {
+                               mschapv1_encode(request->packet,
+                                               &request->packet->vps,
+                                               request->password);
+                       } else {
+                               DEBUG("WARNING: No password in the request");
                        }
                }
 
-               radclient->timestamp = time(NULL);
-               radclient->tries = 1;
-               radclient->resend++;
+               request->timestamp = time(NULL);
+               request->tries = 1;
+               request->resend++;
 
 #ifdef WITH_TCP
                /*
                 *      WTF?
                 */
                if (client_port == 0) {
-                       client_ipaddr = radclient->request->src_ipaddr;
-                       client_port = radclient->request->src_port;
+                       client_ipaddr = request->packet->src_ipaddr;
+                       client_port = request->packet->src_port;
                }
 #endif
 
-       } else {                /* radclient->request->id >= 0 */
+       } else {                /* request->packet->id >= 0 */
                time_t now = time(NULL);
 
                /*
@@ -707,15 +810,15 @@ static int send_one_packet(radclient_t *radclient)
                /*
                 *      Not time for a retry, do so.
                 */
-               if ((now - radclient->timestamp) < timeout) {
+               if ((now - request->timestamp) < timeout) {
                        /*
                         *      When we walk over the tree sending
                         *      packets, we update the minimum time
                         *      required to sleep.
                         */
                        if ((sleep_time == -1) ||
-                           (sleep_time > (now - radclient->timestamp))) {
-                               sleep_time = now - radclient->timestamp;
+                           (sleep_time > (now - request->timestamp))) {
+                               sleep_time = now - request->timestamp;
                        }
                        return 0;
                }
@@ -723,47 +826,44 @@ static int send_one_packet(radclient_t *radclient)
                /*
                 *      We're not trying later, maybe the packet is done.
                 */
-               if (radclient->tries == retries) {
-                       assert(radclient->request->id >= 0);
+               if (request->tries == retries) {
+                       assert(request->packet->id >= 0);
 
                        /*
                         *      Delete the request from the tree of
                         *      outstanding requests.
                         */
-                       fr_packet_list_yank(pl, radclient->request);
+                       fr_packet_list_yank(pl, request->packet);
 
-                       fprintf(stderr, "radclient: no response from server for ID %d socket %d\n", radclient->request->id, radclient->request->sockfd);
-                       deallocate_id(radclient);
+                       REDEBUG("No reply from server for ID %d socket %d",
+                               request->packet->id, request->packet->sockfd);
+                       deallocate_id(request);
 
                        /*
                         *      Normally we mark it "done" when we've received
-                        *      the response, but this is a special case.
+                        *      the reply, but this is a special case.
                         */
-                       if (radclient->resend == resend_count) {
-                               radclient->done = 1;
+                       if (request->resend == resend_count) {
+                               request->done = true;
                        }
-                       totallost++;
+                       stats.lost++;
                        return -1;
                }
 
                /*
                 *      We are trying later.
                 */
-               radclient->timestamp = now;
-               radclient->tries++;
+               request->timestamp = now;
+               request->tries++;
        }
 
-
        /*
         *      Send the packet.
         */
-       if (rad_send(radclient->request, NULL, secret) < 0) {
-               fprintf(stderr, "radclient: Failed to send packet for ID %d: %s\n",
-                       radclient->request->id, fr_strerror());
+       if (rad_send(request->packet, NULL, secret) < 0) {
+               REDEBUG("Failed to send packet for ID %d", request->packet->id);
        }
 
-       if (fr_debug_flag > 2) print_hex(radclient->request);
-
        return 0;
 }
 
@@ -774,8 +874,8 @@ static int recv_one_packet(int wait_time)
 {
        fd_set          set;
        struct timeval  tv;
-       radclient_t     *radclient;
-       RADIUS_PACKET   *reply, **request_p;
+       rc_request_t    *request;
+       RADIUS_PACKET   *reply, **packet_p;
        volatile int max_fd;
 
        /* And wait for reply, timing out as necessary */
@@ -801,11 +901,9 @@ static int recv_one_packet(int wait_time)
        /*
         *      Look for the packet.
         */
-
        reply = fr_packet_list_recv(pl, &set);
        if (!reply) {
-               fprintf(stderr, "radclient: received bad packet: %s\n",
-                       fr_strerror());
+               ERROR("Received bad packet");
 #ifdef WITH_TCP
                /*
                 *      If the packet is bad, we close the socket.
@@ -829,109 +927,136 @@ static int recv_one_packet(int wait_time)
        reply->src_port = server_port;
 #endif
 
-       if (fr_debug_flag > 2) print_hex(reply);
-
-       request_p = fr_packet_list_find_byreply(pl, reply);
-       if (!request_p) {
-               fprintf(stderr, "radclient: received response to request we did not send. (id=%d socket %d)\n", reply->id, reply->sockfd);
+       packet_p = fr_packet_list_find_byreply(pl, reply);
+       if (!packet_p) {
+               ERROR("Received reply to request we did not send. (id=%d socket %d)",
+                     reply->id, reply->sockfd);
                rad_free(&reply);
                return -1;      /* got reply to packet we didn't send */
        }
-       radclient = fr_packet2myptr(radclient_t, request, request_p);
+       request = fr_packet2myptr(rc_request_t, packet, packet_p);
 
        /*
         *      Fails the signature validation: not a real reply.
         *      FIXME: Silently drop it and listen for another packet.
         */
-       if (rad_verify(reply, radclient->request, secret) < 0) {
-               fr_perror("rad_verify");
-               totallost++;
+       if (rad_verify(reply, request->packet, secret) < 0) {
+               REDEBUG("Reply verification failed");
+               stats.lost++;
                goto packet_done; /* shared secret is incorrect */
        }
 
-       if (print_filename) printf("%s:%d %d\n",
-                                  radclient->filename,
-                                  radclient->packet_number,
-                                  reply->code);
-       deallocate_id(radclient);
-       radclient->reply = reply;
+       if (print_filename) {
+               RDEBUG("%s response code %d", request->files->packets, reply->code);
+       }
+
+       deallocate_id(request);
+       request->reply = reply;
        reply = NULL;
 
        /*
         *      If this fails, we're out of memory.
         */
-       if (rad_decode(radclient->reply, radclient->request, secret) != 0) {
-               fr_perror("rad_decode");
-               totallost++;
+       if (rad_decode(request->reply, request->packet, secret) != 0) {
+               REDEBUG("Reply decode failed");
+               stats.lost++;
                goto packet_done;
        }
 
-       /* libradius debug already prints out the value pairs for us */
-       if (!fr_debug_flag && do_output) {
-               printf("Received response ID %d, code %d, length = %zd\n",
-                      radclient->reply->id, radclient->reply->code,
-                      radclient->reply->data_len);
-               vp_printlist(stdout, radclient->reply->vps);
+       /*
+        *      Increment counters...
+        */
+       switch (request->reply->code) {
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_ACCOUNTING_RESPONSE:
+       case PW_CODE_COA_ACK:
+       case PW_CODE_DISCONNECT_ACK:
+               stats.accepted++;
+               break;
+
+       case PW_CODE_ACCESS_CHALLENGE:
+               break;
+
+       default:
+               stats.rejected++;
        }
 
-       if ((radclient->reply->code == PW_AUTHENTICATION_ACK) ||
-           (radclient->reply->code == PW_ACCOUNTING_RESPONSE) ||
-           (radclient->reply->code == PW_COA_ACK) ||
-           (radclient->reply->code == PW_DISCONNECT_ACK)) {
-               success = 1;            /* have a good response */
-               totalapp++;
+       /*
+        *      If we had an expected response code, check to see if the
+        *      packet matched that.
+        */
+       if (request->reply->code != request->filter_code) {
+               if (is_radius_code(request->packet_code)) {
+                       REDEBUG("Expected %s got %s", fr_packet_codes[request->filter_code],
+                               fr_packet_codes[request->reply->code]);
+               } else {
+                       REDEBUG("Expected %u got %i", request->filter_code,
+                               request->reply->code);
+               }
+               stats.failed++;
+       /*
+        *      Check if the contents of the packet matched the filter
+        */
+       } else if (!request->filter) {
+               stats.passed++;
        } else {
-               totaldeny++;
+               VALUE_PAIR const *failed[2];
+
+               pairsort(&request->reply->vps, attrtagcmp);
+               if (pairvalidate(failed, request->filter, request->reply->vps)) {
+                       RDEBUG("Response passed filter");
+                       stats.passed++;
+               } else {
+                       pairvalidate_debug(request, failed);
+                       REDEBUG("Response failed filter");
+                       stats.failed++;
+               }
        }
 
-       if (radclient->resend == resend_count) {
-               radclient->done = 1;
+       if (request->resend == resend_count) {
+               request->done = true;
        }
 
- packet_done:
-       rad_free(&radclient->reply);
+packet_done:
+       rad_free(&request->reply);
        rad_free(&reply);       /* may be NULL */
 
        return 0;
 }
 
-
-static int getport(char const *name)
-{
-       struct  servent         *svp;
-
-       svp = getservbyname (name, "udp");
-       if (!svp) {
-               return 0;
-       }
-
-       return ntohs(svp->s_port);
-}
-
 int main(int argc, char **argv)
 {
-       char *p;
        int c;
        char const *radius_dir = RADDBDIR;
+       char const *dict_dir = DICTDIR;
        char filesecret[256];
        FILE *fp;
-       int do_summary = 0;
+       int do_summary = false;
        int persec = 0;
        int parallel = 1;
-       radclient_t     *this;
+       rc_request_t    *this;
        int force_af = AF_UNSPEC;
 
-       fr_debug_flag = 0;
+       fr_debug_flag = 2;
+       fr_log_fp = stdout;
+
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radclient");
+               exit(EXIT_FAILURE);
+       }
+#endif
 
        talloc_set_log_stderr();
 
        filename_tree = rbtree_create(filename_cmp, NULL, 0);
        if (!filename_tree) {
-               fprintf(stderr, "radclient: Out of memory\n");
+       oom:
+               ERROR("Out of memory");
                exit(1);
        }
 
-       while ((c = getopt(argc, argv, "46c:d:f:Fhi:n:p:qr:sS:t:vx"
+       while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx"
 #ifdef WITH_TCP
                "P:"
 #endif
@@ -947,14 +1072,34 @@ int main(int argc, char **argv)
                                usage();
                        resend_count = atoi(optarg);
                        break;
+               case 'D':
+                       dict_dir = optarg;
+                       break;
                case 'd':
                        radius_dir = optarg;
                        break;
                case 'f':
-                       rbtree_insert(filename_tree, optarg);
+               {
+                       char const *p;
+                       rc_file_pair_t *files;
+
+                       files = talloc(talloc_autofree_context(), rc_file_pair_t);
+                       if (!files) goto oom;
+
+                       p = strchr(optarg, ':');
+                       if (p) {
+                               files->packets = talloc_strndup(files, optarg, p - optarg);
+                               if (!files->packets) goto oom;
+                               files->filters = p + 1;
+                       } else {
+                               files->packets = optarg;
+                               files->filters = NULL;
+                       }
+                       rbtree_insert(filename_tree, (void *) files);
+               }
                        break;
                case 'F':
-                       print_filename = 1;
+                       print_filename = true;
                        break;
                case 'i':       /* currently broken */
                        if (!isdigit((int) *optarg))
@@ -975,7 +1120,7 @@ int main(int argc, char **argv)
                         *      parallel can over-run the kernel
                         *      queues, and Linux will happily discard
                         *      packets.  So even if the server responds,
-                        *      the client may not see the response.
+                        *      the client may not see the reply.
                         */
                case 'p':
                        parallel = atoi(optarg);
@@ -999,7 +1144,7 @@ int main(int argc, char **argv)
 #endif
 
                case 'q':
-                       do_output = 0;
+                       do_output = false;
                        fr_log_fp = NULL; /* no output from you, either! */
                        break;
                case 'r':
@@ -1009,35 +1154,36 @@ int main(int argc, char **argv)
                        if ((retries == 0) || (retries > 1000)) usage();
                        break;
                case 's':
-                       do_summary = 1;
+                       do_summary = true;
                        break;
                case 'S':
-                      fp = fopen(optarg, "r");
-                      if (!fp) {
-                              fprintf(stderr, "radclient: Error opening %s: %s\n",
-                                      optarg, strerror(errno));
+               {
+                       char *p;
+                       fp = fopen(optarg, "r");
+                       if (!fp) {
+                              ERROR("Error opening %s: %s", optarg, fr_syserror(errno));
                               exit(1);
-                      }
-                      if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
-                              fprintf(stderr, "radclient: Error reading %s: %s\n",
-                                      optarg, strerror(errno));
+                       }
+                       if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
+                              ERROR("Error reading %s: %s", optarg, fr_syserror(errno));
                               exit(1);
-                      }
-                      fclose(fp);
+                       }
+                       fclose(fp);
 
-                      /* truncate newline */
-                      p = filesecret + strlen(filesecret) - 1;
-                      while ((p >= filesecret) &&
+                       /* truncate newline */
+                       p = filesecret + strlen(filesecret) - 1;
+                       while ((p >= filesecret) &&
                              (*p < ' ')) {
                               *p = '\0';
                               --p;
-                      }
+                       }
 
-                      if (strlen(filesecret) < 2) {
-                              fprintf(stderr, "radclient: Secret in %s is too short\n", optarg);
+                       if (strlen(filesecret) < 2) {
+                              ERROR("Secret in %s is too short", optarg);
                               exit(1);
-                      }
-                      secret = filesecret;
+                       }
+                       secret = filesecret;
+               }
                       break;
                case 't':
                        if (!isdigit((int) *optarg))
@@ -1045,12 +1191,11 @@ int main(int argc, char **argv)
                        timeout = atof(optarg);
                        break;
                case 'v':
-                       printf("%s\n", radclient_version);
+                       DEBUG("%s", radclient_version);
                        exit(0);
                        break;
                case 'x':
                        fr_debug_flag++;
-                       fr_log_fp = stdout;
                        break;
                case 'h':
                default:
@@ -1060,15 +1205,42 @@ int main(int argc, char **argv)
        argc -= (optind - 1);
        argv += (optind - 1);
 
-       if ((argc < 3)  ||
-           ((secret == NULL) && (argc < 4))) {
+       if ((argc < 3)  || ((secret == NULL) && (argc < 4))) {
+               ERROR("Insufficient arguments");
                usage();
        }
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radclient");
+               return 1;
+       }
+
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radclient");
+               return 1;
+       }
 
-       if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
                fr_perror("radclient");
                return 1;
        }
+       fr_strerror();  /* Clear the error buffer */
+
+
+       /*
+        *      Get the request type
+        */
+       if (!isdigit((int) argv[2][0])) {
+               packet_code = fr_str2int(request_types, argv[2], -2);
+               if (packet_code == -2) {
+                       ERROR("Unrecognised request type \"%s\"", argv[2]);
+                       usage();
+               }
+       } else {
+               packet_code = atoi(argv[2]);
+       }
 
        /*
         *      Resolve hostname.
@@ -1076,6 +1248,7 @@ int main(int argc, char **argv)
        if (force_af == AF_UNSPEC) force_af = AF_INET;
        server_ipaddr.af = force_af;
        if (strcmp(argv[1], "-") != 0) {
+               char *p;
                char const *hostname = argv[1];
                char const *portname = argv[1];
                char buffer[256];
@@ -1101,8 +1274,8 @@ int main(int argc, char **argv)
                        portname = NULL;
                }
 
-               if (ip_hton(hostname, force_af, &server_ipaddr) < 0) {
-                       fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
+               if (ip_hton(&server_ipaddr, force_af, hostname, false) < 0) {
+                       ERROR("Failed to find IP address for host %s: %s", hostname, strerror(errno));
                        exit(1);
                }
 
@@ -1112,48 +1285,7 @@ int main(int argc, char **argv)
                if (portname) server_port = atoi(portname);
        }
 
-       /*
-        *      See what kind of request we want to send.
-        */
-       if (strcmp(argv[2], "auth") == 0) {
-               if (server_port == 0) server_port = getport("radius");
-               if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
-               packet_code = PW_AUTHENTICATION_REQUEST;
-
-       } else if (strcmp(argv[2], "challenge") == 0) {
-               if (server_port == 0) server_port = getport("radius");
-               if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
-               packet_code = PW_ACCESS_CHALLENGE;
-
-       } else if (strcmp(argv[2], "acct") == 0) {
-               if (server_port == 0) server_port = getport("radacct");
-               if (server_port == 0) server_port = PW_ACCT_UDP_PORT;
-               packet_code = PW_ACCOUNTING_REQUEST;
-               do_summary = 0;
-
-       } else if (strcmp(argv[2], "status") == 0) {
-               if (server_port == 0) server_port = getport("radius");
-               if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
-               packet_code = PW_STATUS_SERVER;
-
-       } else if (strcmp(argv[2], "disconnect") == 0) {
-               if (server_port == 0) server_port = PW_COA_UDP_PORT;
-               packet_code = PW_DISCONNECT_REQUEST;
-
-       } else if (strcmp(argv[2], "coa") == 0) {
-               if (server_port == 0) server_port = PW_COA_UDP_PORT;
-               packet_code = PW_COA_REQUEST;
-
-       } else if (strcmp(argv[2], "auto") == 0) {
-               packet_code = -1;
-
-       } else if (isdigit((int) argv[2][0])) {
-               if (server_port == 0) server_port = getport("radius");
-               if (server_port == 0) server_port = PW_AUTH_UDP_PORT;
-               packet_code = atoi(argv[2]);
-       } else {
-               usage();
-       }
+       radclient_get_port(packet_code, &server_port);
 
        /*
         *      Add the secret.
@@ -1164,22 +1296,28 @@ int main(int argc, char **argv)
         *      If no '-f' is specified, we're reading from stdin.
         */
        if (rbtree_num_elements(filename_tree) == 0) {
-               if (!radclient_init("-")) exit(1);
+               rc_file_pair_t *files;
+
+               files = talloc_zero(talloc_autofree_context(), rc_file_pair_t);
+               files->packets = "-";
+               if (!radclient_init(files, files)) {
+                       exit(1);
+               }
        }
 
        /*
         *      Walk over the list of filenames, creating the requests.
         */
-       if (rbtree_walk(filename_tree, InOrder, filename_walk, NULL) != 0) {
-               fprintf(stderr, "Failed walking over filenames\n");
+       if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) {
+               ERROR("Failed parsing input files");
                exit(1);
        }
 
        /*
         *      No packets read.  Die.
         */
-       if (!radclient_head) {
-               fprintf(stderr, "radclient: Nothing to send.\n");
+       if (!request_head) {
+               ERROR("Nothing to send");
                exit(1);
        }
 
@@ -1187,13 +1325,13 @@ int main(int argc, char **argv)
         *      Bind to the first specified IP address and port.
         *      This means we ignore later ones.
         */
-       if (radclient_head->request->src_ipaddr.af == AF_UNSPEC) {
+       if (request_head->packet->src_ipaddr.af == AF_UNSPEC) {
                memset(&client_ipaddr, 0, sizeof(client_ipaddr));
                client_ipaddr.af = server_ipaddr.af;
                client_port = 0;
        } else {
-               client_ipaddr = radclient_head->request->src_ipaddr;
-               client_port = radclient_head->request->src_port;
+               client_ipaddr = request_head->packet->src_ipaddr;
+               client_port = request_head->packet->src_port;
        }
 #ifdef WITH_TCP
        if (proto) {
@@ -1202,19 +1340,19 @@ int main(int argc, char **argv)
 #endif
        sockfd = fr_socket(&client_ipaddr, client_port);
        if (sockfd < 0) {
-               fprintf(stderr, "radclient: socket: %s\n", fr_strerror());
+               ERROR("Error opening socket");
                exit(1);
        }
 
        pl = fr_packet_list_create(1);
        if (!pl) {
-               fprintf(stderr, "radclient: Out of memory\n");
+               ERROR("Out of memory");
                exit(1);
        }
 
        if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr,
                                       server_port, NULL)) {
-               fprintf(stderr, "radclient: Out of memory\n");
+               ERROR("Out of memory");
                exit(1);
        }
 
@@ -1222,9 +1360,9 @@ int main(int argc, char **argv)
         *      Walk over the list of packets, sanity checking
         *      everything.
         */
-       for (this = radclient_head; this != NULL; this = this->next) {
-               this->request->src_ipaddr = client_ipaddr;
-               this->request->src_port = client_port;
+       for (this = request_head; this != NULL; this = this->next) {
+               this->packet->src_ipaddr = client_ipaddr;
+               this->packet->src_port = client_port;
                if (radclient_sane(this) != 0) {
                        exit(1);
                }
@@ -1241,17 +1379,17 @@ int main(int argc, char **argv)
         */
        do {
                int n = parallel;
-               radclient_t *next;
+               rc_request_t *next;
                char const *filename = NULL;
 
-               done = 1;
+               done = true;
                sleep_time = -1;
 
                /*
                 *      Walk over the packets, sending them.
                 */
 
-               for (this = radclient_head; this != NULL; this = next) {
+               for (this = request_head; this != NULL; this = next) {
                        next = this->next;
 
                        /*
@@ -1265,7 +1403,7 @@ int main(int argc, char **argv)
                         *      This packet is done.  Delete it.
                         */
                        if (this->done) {
-                               radclient_free(this);
+                               talloc_free(this);
                                continue;
                        }
 
@@ -1278,8 +1416,8 @@ int main(int argc, char **argv)
                         *      which case N packets from each file
                         *      are sent in parallel.
                         */
-                       if (this->filename != filename) {
-                               filename = this->filename;
+                       if (this->files->packets != filename) {
+                               filename = this->files->packets;
                                n = parallel;
                        }
 
@@ -1328,13 +1466,13 @@ int main(int argc, char **argv)
                                 *      and we shouldn't sleep.
                                 */
                                if (this->resend < resend_count) {
-                                       done = 0;
+                                       done = false;
                                        sleep_time = 0;
                                }
                        } else { /* haven't sent this packet, we're not done */
-                               assert(this->done == 0);
+                               assert(this->done == false);
                                assert(this->reply == NULL);
-                               done = 0;
+                               done = false;
                        }
                }
 
@@ -1342,7 +1480,7 @@ int main(int argc, char **argv)
                 *      Still have outstanding requests.
                 */
                if (fr_packet_list_num_elements(pl) > 0) {
-                       done = 0;
+                       done = false;
                } else {
                        sleep_time = 0;
                }
@@ -1361,16 +1499,26 @@ int main(int argc, char **argv)
 
        rbtree_free(filename_tree);
        fr_packet_list_free(pl);
-       while (radclient_head) radclient_free(radclient_head);
+       while (request_head) TALLOC_FREE(request_head);
        dict_free();
 
        if (do_summary) {
-               printf("\n\t   Total approved auths:  %d\n", totalapp);
-               printf("\t     Total denied auths:  %d\n", totaldeny);
-               printf("\t       Total lost auths:  %d\n", totallost);
+               DEBUG("Packet summary:\n"
+                     "\tAccess-Accepts  : %" PRIu64 "\n"
+                     "\tAccess-Rejects  : %" PRIu64 "\n"
+                     "\tLost            : %" PRIu64 "\n"
+                     "\tPassed filter   : %" PRIu64 "\n"
+                     "\tFailed filter   : %" PRIu64,
+                     stats.accepted,
+                     stats.rejected,
+                     stats.lost,
+                     stats.passed,
+                     stats.failed
+               );
        }
 
-       if (success) return 0;
-
-       return 1;
+       if ((stats.lost > 0) || (stats.failed > 0)) {
+               exit(1);
+       }
+       exit(0);
 }
diff --git a/src/main/radconf2xml.c b/src/main/radconf2xml.c
deleted file mode 100644 (file)
index 42fdd57..0000000
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * radconf2xml.c       Converts radiusd.conf to XML.
- *
- * 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 2008   The FreeRADIUS server project
- * Copyright 2008   Alan DeKok <aland@deployingradius.com>
- */
-
-RCSID("$Id$")
-
-#include <freeradius-devel/radiusd.h>
-
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
-/*
- *     For configuration file stuff.
- */
-char const *raddb_dir = RADDBDIR;
-char const *progname = "radconf2xml";
-
-/*
- *     The rest of this is because the conffile.c, etc. assume
- *     they're running inside of the server.  And we don't (yet)
- *     have a "libfreeradius-server", or "libfreeradius-util".
- */
-log_debug_t debug_flag = 0;
-struct main_config_t mainconfig;
-char *request_log_file = NULL;
-char *debug_log_file = NULL;
-
-#include <sys/wait.h>
-pid_t rad_fork(void)
-{
-       return fork();
-}
-
-#ifdef HAVE_PTHREAD_H
-pid_t rad_waitpid(pid_t pid, int *status)
-{
-       return waitpid(pid, status, 0);
-}
-#endif
-
-bool check_config = false;
-
-static int usage(void)
-{
-       printf("Usage: %s [ -d raddb_dir ] [ -o output_file ] [ -n name ]\n", progname);
-       printf("  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
-       printf("  -n name        Read raddb/name.conf instead of raddb/radiusd.conf\n");
-       printf("  -o output_file  File where XML output will be written.\n");
-
-       exit(1);
-}
-
-int main(int argc, char **argv)
-{
-       int argval;
-       CONF_SECTION *cs;
-       char const *file = NULL;
-       char const *name = "radiusd";
-       FILE *fp;
-       char buffer[2048];
-
-       if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
-               progname = argv[0];
-       else
-               progname++;
-
-       while ((argval = getopt(argc, argv, "d:ho:n:")) != EOF) {
-               switch(argval) {
-               case 'd':
-                       if (file) {
-                               fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname);
-                               exit(1);
-                       }
-                       raddb_dir = optarg;
-                       break;
-
-               default:
-               case 'h':
-                       usage();
-                       break;
-
-               case 'n':
-                       name = optarg;
-                       break;
-
-               case 'o':
-                       file = optarg;
-                       break;
-               }
-       }
-
-       snprintf(buffer, sizeof(buffer), "%s/%s.conf", raddb_dir, name);
-       cs = cf_file_read(buffer);
-       if (!cs) {
-               fprintf(stderr, "%s: Errors reading or parsing %s\n",
-                       progname, buffer);
-               exit(1);
-       }
-
-       if (!file || (strcmp(file, "-") == 0)) {
-               fp = stdout;
-               file = NULL;
-       } else {
-               fp = fopen(file, "w");
-               if (!fp) {
-                       fprintf(stderr, "%s: Failed openng %s: %s\n",
-                               progname, file, strerror(errno));
-                       exit(1);
-               }
-       }
-
-       if (!cf_section2xml(fp, cs)) {
-               if (file) unlink(file);
-               return 1;
-       }
-
-       return 0;
-}
diff --git a/src/main/radconf2xml.mk b/src/main/radconf2xml.mk
deleted file mode 100644 (file)
index 2c15cb6..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-TARGET         := radconf2xml
-SOURCES                := radconf2xml.c
-
-TGT_PREREQS    := libfreeradius-server.a libfreeradius-radius.a
-TGT_LDLIBS     := $(LIBS)
index 9eb1d6f..bffb2e5 100644 (file)
@@ -36,8 +36,6 @@ RCSID("$Id$")
 #include <fcntl.h>
 #include <ctype.h>
 
-#include <signal.h>
-
 #ifdef HAVE_GETOPT_H
 #      include <getopt.h>
 #endif
@@ -63,11 +61,10 @@ char const *radlib_dir = NULL;
 bool log_stripped_names;
 log_debug_t debug_flag = 0;
 bool check_config = false;
-bool memory_report = false;
 
 char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
-" (git #" RADIUSD_VERSION_COMMIT ")"
+" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
 #endif
 ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
 
@@ -87,14 +84,6 @@ static void sig_fatal (int);
 static void sig_hup (int);
 #endif
 
-#ifdef WITH_VERIFY_PTR
-static void die_horribly(char const *reason)
-{
-       ERROR("talloc abort: %s\n", reason);
-       abort();
-}
-#endif
-
 /*
  *     The main guy.
  */
@@ -103,14 +92,28 @@ int main(int argc, char *argv[])
        int rcode = EXIT_SUCCESS;
        int status;
        int argval;
-       int spawn_flag = true;
-       int dont_fork = false;
-       int write_pid = false;
+       bool spawn_flag = true;
+       bool write_pid = false;
+       bool display_version = false;
        int flag = 0;
        int from_child[2] = {-1, -1};
 
-#ifdef HAVE_SIGACTION
-       struct sigaction act;
+       /*
+        *      We probably don't want to free the talloc autofree context
+        *      directly, so we'll allocate a new context beneath it, and
+        *      free that before any leak reports.
+        */
+       TALLOC_CTX *autofree = talloc_init("main");
+
+       /*
+        *      If the server was built with debugging enabled always install
+        *      the basic fatal signal handlers.
+        */
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radiusd");
+               exit(EXIT_FAILURE);
+       }
 #endif
 
 #ifdef OSFC2
@@ -126,37 +129,31 @@ int main(int argc, char *argv[])
        {
                WSADATA wsaData;
                if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
-                 fprintf(stderr, "%s: Unable to initialize socket library.\n", progname);
-                       return 1;
+                       fprintf(stderr, "%s: Unable to initialize socket library.\n", progname);
+                       exit(EXIT_FAILURE);
                }
        }
 #endif
 
        debug_flag = 0;
-       spawn_flag = true;
-       radius_dir = talloc_strdup(NULL, RADIUS_DIR);
+       set_radius_dir(autofree, RADIUS_DIR);
 
        /*
         *      Ensure that the configuration is initialized.
         */
-       memset(&mainconfig, 0, sizeof(mainconfig));
-       mainconfig.myip.af = AF_UNSPEC;
-       mainconfig.port = -1;
-       mainconfig.name = "radiusd";
-
-#ifdef HAVE_SIGACTION
-       memset(&act, 0, sizeof(act));
-       act.sa_flags = 0 ;
-       sigemptyset( &act.sa_mask ) ;
-#endif
+       memset(&main_config, 0, sizeof(main_config));
+       main_config.myip.af = AF_UNSPEC;
+       main_config.port = 0;
+       main_config.name = "radiusd";
+       main_config.daemonize = true;
 
        /*
         *      Don't put output anywhere until we get told a little
         *      more.
         */
-       default_log.dest = L_DST_NULL;
+       default_log.dst = L_DST_NULL;
        default_log.fd = -1;
-       mainconfig.log_file = NULL;
+       main_config.log_file = NULL;
 
        /*  Process the options.  */
        while ((argval = getopt(argc, argv, "Cd:D:fhi:l:mMn:p:PstvxX")) != EOF) {
@@ -165,22 +162,19 @@ int main(int argc, char *argv[])
                        case 'C':
                                check_config = true;
                                spawn_flag = false;
-                               dont_fork = true;
+                               main_config.daemonize = false;
                                break;
 
                        case 'd':
-                               if (radius_dir) {
-                                       rad_const_free(radius_dir);
-                               }
-                               radius_dir = talloc_strdup(NULL, optarg);
+                               set_radius_dir(autofree, optarg);
                                break;
 
                        case 'D':
-                               mainconfig.dictionary_dir = talloc_strdup(NULL, optarg);
+                               main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
                                break;
 
                        case 'f':
-                               dont_fork = true;
+                               main_config.daemonize = false;
                                break;
 
                        case 'h':
@@ -191,19 +185,19 @@ int main(int argc, char *argv[])
                                if (strcmp(optarg, "stdout") == 0) {
                                        goto do_stdout;
                                }
-                               mainconfig.log_file = strdup(optarg);
-                               default_log.dest = L_DST_FILES;
-                               default_log.fd = open(mainconfig.log_file,
+                               main_config.log_file = strdup(optarg);
+                               default_log.dst = L_DST_FILES;
+                               default_log.fd = open(main_config.log_file,
                                                            O_WRONLY | O_APPEND | O_CREAT, 0640);
                                if (default_log.fd < 0) {
-                                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno));
+                                       fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", main_config.log_file, fr_syserror(errno));
                                        exit(EXIT_FAILURE);
                                }
                                fr_log_fp = fdopen(default_log.fd, "a");
                                break;
 
                        case 'i':
-                               if (ip_hton(optarg, AF_UNSPEC, &mainconfig.myip) < 0) {
+                               if (ip_hton(&main_config.myip, AF_UNSPEC, optarg, false) < 0) {
                                        fprintf(stderr, "radiusd: Invalid IP Address or hostname \"%s\"\n", optarg);
                                        exit(EXIT_FAILURE);
                                }
@@ -211,26 +205,31 @@ int main(int argc, char *argv[])
                                break;
 
                        case 'n':
-                               mainconfig.name = optarg;
+                               main_config.name = optarg;
                                break;
 
                        case 'm':
-                               mainconfig.debug_memory = 1;
+                               main_config.debug_memory = true;
                                break;
 
                        case 'M':
-                               memory_report = 1;
-                               mainconfig.debug_memory = 1;
+                               main_config.memory_report = true;
+                               main_config.debug_memory = true;
                                break;
 
                        case 'p':
-                               mainconfig.port = atoi(optarg);
-                               if ((mainconfig.port <= 0) ||
-                                   (mainconfig.port >= 65536)) {
-                                       fprintf(stderr, "radiusd: Invalid port number %s\n", optarg);
+                       {
+                               unsigned long port;
+
+                               port = strtoul(optarg, 0, 10);
+                               if ((port == 0) || (port > UINT16_MAX)) {
+                                       fprintf(stderr, "radiusd: Invalid port number \"%s\"\n", optarg);
                                        exit(EXIT_FAILURE);
                                }
+
+                               main_config.port = (uint16_t) port;
                                flag |= 2;
+                       }
                                break;
 
                        case 'P':
@@ -240,7 +239,7 @@ int main(int argc, char *argv[])
 
                        case 's':       /* Single process mode */
                                spawn_flag = false;
-                               dont_fork = true;
+                               main_config.daemonize = false;
                                break;
 
                        case 't':       /* no child threads */
@@ -248,24 +247,19 @@ int main(int argc, char *argv[])
                                break;
 
                        case 'v':
-                               /* Don't print timestamps */
-                               debug_flag += 2;
-                               fr_log_fp = stdout;
-                               default_log.dest = L_DST_STDOUT;
-                               default_log.fd = STDOUT_FILENO;
+                               display_version = true;
+                               break;
 
-                               version();
-                               exit(EXIT_SUCCESS);
                        case 'X':
                                spawn_flag = false;
-                               dont_fork = true;
+                               main_config.daemonize = false;
                                debug_flag += 2;
-                               mainconfig.log_auth = true;
-                               mainconfig.log_auth_badpass = true;
-                               mainconfig.log_auth_goodpass = true;
+                               main_config.log_auth = true;
+                               main_config.log_auth_badpass = true;
+                               main_config.log_auth_goodpass = true;
                do_stdout:
                                fr_log_fp = stdout;
-                               default_log.dest = L_DST_STDOUT;
+                               default_log.dst = L_DST_STDOUT;
                                default_log.fd = STDOUT_FILENO;
                                break;
 
@@ -279,28 +273,26 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (memory_report) {
-               talloc_enable_null_tracking();
-#ifdef WITH_VERIFY_PTR
-               talloc_set_abort_fn(die_horribly);
-#endif
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radiusd");
+               exit(EXIT_FAILURE);
+       }
+
+       if (rad_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               exit(EXIT_FAILURE);
        }
-       talloc_set_log_fn(log_talloc);
 
        /*
         *      Mismatch between build time OpenSSL and linked SSL,
         *      better to die here than segfault later.
         */
 #ifdef HAVE_OPENSSL_CRYPTO_H
-       if (ssl_check_version() < 0) {
+       if (ssl_check_consistency() < 0) {
                exit(EXIT_FAILURE);
        }
-
-       /*
-        *      Initialising OpenSSL once, here, is safer than having individual
-        *      modules do it.
-        */
-       tls_global_init();
 #endif
 
        if (flag && (flag != 0x03)) {
@@ -308,30 +300,101 @@ int main(int argc, char *argv[])
                exit(EXIT_FAILURE);
        }
 
+       /*
+        *      Better here, so it doesn't matter whether we get passed
+        *      -xv or -vx.
+        */
+       if (display_version) {
+               /* Don't print timestamps */
+               debug_flag += 2;
+               fr_log_fp = stdout;
+               default_log.dst = L_DST_STDOUT;
+               default_log.fd = STDOUT_FILENO;
+
+               version();
+               exit(EXIT_SUCCESS);
+       }
+
        if (debug_flag) {
                version();
        }
 
+       /*
+        *  Initialising OpenSSL once, here, is safer than having individual
+        *  modules do it.
+        */
+#ifdef HAVE_OPENSSL_CRYPTO_H
+       tls_global_init();
+#endif
+
+       /*
+        *  Initialize any event loops just enough so module instantiations
+        *  can add fd/event to them, but do not start them yet.
+        */
+       if (!radius_event_init(autofree)) {
+               exit(EXIT_FAILURE);
+       }
+
        /*  Read the configuration files, BEFORE doing anything else.  */
-       if (read_mainconfig(0) < 0) {
+       if (main_config_init() < 0) {
+               exit(EXIT_FAILURE);
+       }
+
+       /*  Check for vulnerabilities in the version of libssl were linked against */
+#ifdef HAVE_OPENSSL_CRYPTO_H
+       if (tls_global_version_check(main_config.allow_vulnerable_openssl) < 0) {
+               exit(EXIT_FAILURE);
+       }
+#endif
+
+       /*
+        *  Load the modules
+        */
+       if (modules_init(main_config.config) < 0) {
+               exit(EXIT_FAILURE);
+       }
+
+       /* Set the panic action (if required) */
+       if (main_config.panic_action &&
+#ifndef NDEBUG
+           !getenv("PANIC_ACTION") &&
+#endif
+           (fr_fault_setup(main_config.panic_action, argv[0]) < 0)) {
+               fr_perror("radiusd");
                exit(EXIT_FAILURE);
        }
 
 #ifndef __MINGW32__
+
+
        /*
         *  Disconnect from session
         */
-       if (dont_fork == false) {
+       if (main_config.daemonize) {
                pid_t pid;
+               int devnull;
+
+               /*
+                *  Really weird things happen if we leave stdin open and call things like
+                *  system() later.
+                */
+               devnull = open("/dev/null", O_RDWR);
+               if (devnull < 0) {
+                       ERROR("Failed opening /dev/null: %s", fr_syserror(errno));
+                       exit(EXIT_FAILURE);
+               }
+               dup2(devnull, STDIN_FILENO);
+
+               close(devnull);
 
                if (pipe(from_child) != 0) {
-                       ERROR("Couldn't open pipe for child status: %s", strerror(errno));
+                       ERROR("Couldn't open pipe for child status: %s", fr_syserror(errno));
                        exit(EXIT_FAILURE);
                }
 
                pid = fork();
                if (pid < 0) {
-                       ERROR("Couldn't fork: %s", strerror(errno));
+                       ERROR("Couldn't fork: %s", fr_syserror(errno));
                        exit(EXIT_FAILURE);
                }
 
@@ -373,58 +436,30 @@ int main(int argc, char *argv[])
 
                /* so the pipe is correctly widowed if the parent exits?! */
                close(from_child[0]);
-#ifdef HAVE_SETSID
+#  ifdef HAVE_SETSID
                setsid();
-#endif
+#  endif
        }
 #endif
 
        /*
-        *  Ensure that we're using the CORRECT pid after forking,
-        *  NOT the one we started with.
+        *      Ensure that we're using the CORRECT pid after forking,
+        *      NOT the one we started with.
         */
        radius_pid = getpid();
 
        /*
-        *      If we're running as a daemon, close the default file
-        *      descriptors, AFTER forking.
+        *      Redirect stderr/stdout as appropriate.
         */
-       if (!debug_flag) {
-               int devnull;
-
-               devnull = open("/dev/null", O_RDWR);
-               if (devnull < 0) {
-                       ERROR("Failed opening /dev/null: %s\n",
-                              strerror(errno));
-                       exit(EXIT_FAILURE);
-               }
-               dup2(devnull, STDIN_FILENO);
-               if (default_log.dest == L_DST_STDOUT) {
-                       setlinebuf(stdout);
-                       default_log.fd = STDOUT_FILENO;
-               } else {
-                       dup2(devnull, STDOUT_FILENO);
-               }
-               if (default_log.dest == L_DST_STDERR) {
-                       setlinebuf(stderr);
-                       default_log.fd = STDERR_FILENO;
-               } else {
-                       dup2(devnull, STDERR_FILENO);
-               }
-               close(devnull);
-
-       } else {
-               setlinebuf(stdout); /* unbuffered output */
+       if (radlog_init(&default_log, main_config.daemonize) < 0) {
+               ERROR("%s", fr_strerror());
+               exit(EXIT_FAILURE);
        }
 
        /*
-        *      Now we have logging check that the OpenSSL
-        */
-
-       /*
-        *      Initialize the event pool, including threads.
+        *      Start the event loop(s) and threads.
         */
-       radius_event_init(mainconfig.config, spawn_flag);
+       radius_event_start(main_config.config, spawn_flag);
 
        /*
         *      Now that we've set everything up, we can install the signal
@@ -434,43 +469,37 @@ int main(int argc, char *argv[])
 #ifdef SIGPIPE
        signal(SIGPIPE, SIG_IGN);
 #endif
-#ifdef HAVE_SIGACTION
-       act.sa_handler = sig_hup;
-       sigaction(SIGHUP, &act, NULL);
-       act.sa_handler = sig_fatal;
-       sigaction(SIGTERM, &act, NULL);
-#else
-#ifdef SIGHUP
-       signal(SIGHUP, sig_hup);
-#endif
-       signal(SIGTERM, sig_fatal);
-#endif
+
+       if ((fr_set_signal(SIGHUP, sig_hup) < 0) ||
+           (fr_set_signal(SIGTERM, sig_fatal) < 0)) {
+               ERROR("%s", fr_strerror());
+               exit(EXIT_FAILURE);
+       }
+
        /*
         *      If we're debugging, then a CTRL-C will cause the
         *      server to die immediately.  Use SIGTERM to shut down
         *      the server cleanly in that case.
         */
-       if ((mainconfig.debug_memory == 1) || (debug_flag == 0)) {
-#ifdef HAVE_SIGACTION
-               act.sa_handler = sig_fatal;
-               sigaction(SIGINT, &act, NULL);
-               sigaction(SIGQUIT, &act, NULL);
-#else
-               signal(SIGINT, sig_fatal);
+       if (main_config.debug_memory || (debug_flag == 0)) {
+               if ((fr_set_signal(SIGINT, sig_fatal) < 0)
 #ifdef SIGQUIT
-               signal(SIGQUIT, sig_fatal);
-#endif
+               || (fr_set_signal(SIGQUIT, sig_fatal) < 0)
 #endif
+               ) {
+                       ERROR("%s", fr_strerror());
+                       exit(EXIT_FAILURE);
+               }
        }
 
        /*
         *      Everything seems to have loaded OK, exit gracefully.
         */
        if (check_config) {
-               DEBUG("Configuration appears to be OK.");
+               DEBUG("Configuration appears to be OK");
 
                /* for -C -m|-M */
-               if (mainconfig.debug_memory) {
+               if (main_config.debug_memory) {
                        goto cleanup;
                }
 
@@ -482,20 +511,18 @@ int main(int argc, char *argv[])
 #endif
 
        /*
-        *      Write out the PID anyway if were in foreground mode.
+        *      Write the PID always if we're running as a daemon.
         */
-       if (!dont_fork) write_pid = true;
+       if (main_config.daemonize) write_pid = true;
 
        /*
-        *  Only write the PID file if we're running as a daemon.
-        *
-        *  And write it AFTER we've forked, so that we write the
-        *  correct PID.
+        *      Write the PID after we've forked, so that we write the
+        *      correct one.
         */
        if (write_pid) {
                FILE *fp;
 
-               fp = fopen(mainconfig.pid_file, "w");
+               fp = fopen(main_config.pid_file, "w");
                if (fp != NULL) {
                        /*
                         *      FIXME: What about following symlinks,
@@ -505,7 +532,7 @@ int main(int argc, char *argv[])
                        fclose(fp);
                } else {
                        ERROR("Failed creating PID file %s: %s\n",
-                              mainconfig.pid_file, strerror(errno));
+                              main_config.pid_file, fr_syserror(errno));
                        exit(EXIT_FAILURE);
                }
        }
@@ -519,28 +546,33 @@ int main(int argc, char *argv[])
         *      we just close the pipe on exit, and the parent gets a
         *      read failure.
         */
-       if (!dont_fork) {
+       if (main_config.daemonize) {
                if (write(from_child[1], "\001", 1) < 0) {
                        WARN("Failed informing parent of successful start: %s",
-                            strerror(errno));
+                            fr_syserror(errno));
                }
                close(from_child[1]);
        }
 
        /*
+        *      Clear the libfreeradius error buffer
+        */
+       fr_strerror();
+
+       /*
         *      Process requests until HUP or exit.
         */
        while ((status = radius_event_process()) == 0x80) {
 #ifdef WITH_STATS
                radius_stats_init(1);
 #endif
-               hup_mainconfig();
+               main_config_hup();
        }
        if (status < 0) {
                ERROR("Exiting due to internal error: %s", fr_strerror());
                rcode = EXIT_FAILURE;
        } else {
-               INFO("Exiting normally.");
+               INFO("Exiting normally");
        }
 
        exec_trigger(NULL, NULL, "server.stop", false);
@@ -566,8 +598,8 @@ int main(int argc, char *argv[])
         *      file.  (If it doesn't exist, we can ignore
         *      the error returned by unlink)
         */
-       if (dont_fork == false) {
-               unlink(mainconfig.pid_file);
+       if (main_config.daemonize) {
+               unlink(main_config.pid_file);
        }
 
        radius_event_free();
@@ -576,24 +608,31 @@ cleanup:
        /*
         *      Detach any modules.
         */
-       detach_modules();
+       modules_free();
 
        xlat_free();            /* modules may have xlat's */
 
        /*
         *      Free the configuration items.
         */
-       free_mainconfig();
-
-       rad_const_free(radius_dir);
+       main_config_free();
 
 #ifdef WIN32
        WSACleanup();
 #endif
 
-       if (memory_report) {
+#ifdef HAVE_OPENSSL_CRYPTO_H
+       tls_global_cleanup();
+#endif
+
+       /*
+        *      So we don't see autofreed memory in the talloc report
+        */
+       talloc_free(autofree);
+
+       if (main_config.memory_report) {
                INFO("Allocated memory at time of report:");
-               log_talloc_report(NULL);
+               fr_log_talloc_report(NULL);
        }
 
        return rcode;
@@ -607,17 +646,18 @@ static void NEVER_RETURNS usage(int status)
 {
        FILE *output = status?stderr:stdout;
 
-       fprintf(output, "Usage: %s [-d db_dir] [-l log_dir] [-i address] [-n name] [-fsvXx]\n", progname);
+       fprintf(output, "Usage: %s [options]\n", progname);
        fprintf(output, "Options:\n");
        fprintf(output, "  -C            Check configuration and exit.\n");
-       fprintf(output, "  -d raddb_dir  Configuration files are in \"raddbdir/*\".\n");
+       fprintf(stderr, "  -d <raddb>    Set configuration directory (defaults to " RADDBDIR ").\n");
+       fprintf(stderr, "  -D <dictdir>  Set main dictionary directory (defaults to " DICTDIR ").\n");
        fprintf(output, "  -f            Run as a foreground process, not a daemon.\n");
        fprintf(output, "  -h            Print this help message.\n");
-       fprintf(output, "  -i ipaddr     Listen on ipaddr ONLY.\n");
-       fprintf(output, "  -l log_file   Logging output will be written to this file.\n");
+       fprintf(output, "  -i <ipaddr>   Listen on ipaddr ONLY.\n");
+       fprintf(output, "  -l <log_file> Logging output will be written to this file.\n");
        fprintf(output, "  -m            On SIGINT or SIGQUIT exit cleanly instead of immediately.\n");
-       fprintf(output, "  -n name       Read raddb/name.conf instead of raddb/radiusd.conf.\n");
-       fprintf(output, "  -p port       Listen on port ONLY.\n");
+       fprintf(output, "  -n <name>     Read raddb/name.conf instead of raddb/radiusd.conf.\n");
+       fprintf(output, "  -p <port>     Listen on port ONLY.\n");
        fprintf(output, "  -P            Always write out PID, even with -f.\n");
        fprintf(output, "  -s            Do not spawn child processes to handle requests.\n");
        fprintf(output, "  -t            Disable threads.\n");
@@ -636,22 +676,22 @@ static void sig_fatal(int sig)
        if (getpid() != radius_pid) _exit(sig);
 
        switch(sig) {
-               case SIGTERM:
-                       radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
-                       break;
+       case SIGTERM:
+               radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
+               break;
 
-               case SIGINT:
+       case SIGINT:
 #ifdef SIGQUIT
-               case SIGQUIT:
+       case SIGQUIT:
 #endif
-                       if (mainconfig.debug_memory || memory_report) {
-                               radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
-                               break;
-                       }
-                       /* FALL-THROUGH */
+               if (main_config.debug_memory || main_config.memory_report) {
+                       radius_signal_self(RADIUS_SIGNAL_SELF_TERM);
+                       break;
+               }
+               /* FALL-THROUGH */
 
-               default:
-                       _exit(sig);
+       default:
+               _exit(sig);
        }
 }
 
index 85ae65a..e428f55 100644 (file)
@@ -66,11 +66,10 @@ RCSID("$Id$")
 /*
  *     For configuration file stuff.
  */
-char const *radius_dir = RADDBDIR;
 char const *progname = "radmin";
 char const *radmin_version = "radmin version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
-" (git #" RADIUSD_VERSION_COMMIT ")"
+" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
 #endif
 ", built on " __DATE__ " at " __TIME__;
 
@@ -81,12 +80,12 @@ char const *radmin_version = "radmin version " RADIUSD_VERSION_STRING
  *     have a "libfreeradius-server", or "libfreeradius-util".
  */
 log_debug_t debug_flag = 0;
-struct main_config_t mainconfig;
+struct main_config_t main_config;
 
 bool check_config = false;
 
 static FILE *outputfp = NULL;
-static int echo = false;
+static bool echo = false;
 static char const *secret = "testing123";
 
 #include <sys/wait.h>
@@ -107,6 +106,7 @@ static void NEVER_RETURNS usage(int status)
        FILE *output = status ? stderr : stdout;
        fprintf(output, "Usage: %s [ args ]\n", progname);
        fprintf(output, "  -d raddb_dir    Configuration files are in \"raddbdir/*\".\n");
+       fprintf(stderr, "  -D <dictdir>    Set main dictionary directory (defaults to " DICTDIR ").\n");
        fprintf(output, "  -e command      Execute 'command' and then exit.\n");
        fprintf(output, "  -E              Echo commands as they are being executed.\n");
        fprintf(output, "  -f socket_file  Open socket_file directly, without reading radius.conf\n");
@@ -135,7 +135,7 @@ static int fr_domain_socket(char const *path)
 
        if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
                fprintf(stderr, "%s: Failed creating socket: %s\n",
-                       progname, strerror(errno));
+                       progname, fr_syserror(errno));
                return -1;
        }
 
@@ -149,7 +149,7 @@ static int fr_domain_socket(char const *path)
 
                close(sockfd);
                fprintf(stderr, "%s: Failed connecting to %s: %s\n",
-                       progname, path, strerror(errno));
+                       progname, path, fr_syserror(errno));
 
                /*
                 *      The file doesn't exist.  Tell the user how to
@@ -169,7 +169,7 @@ static int fr_domain_socket(char const *path)
 
                if ((flags = fcntl(sockfd, F_GETFL, NULL)) < 0)  {
                        fprintf(stderr, "%s: Failure getting socket flags: %s",
-                               progname, strerror(errno));
+                               progname, fr_syserror(errno));
                        close(sockfd);
                        return -1;
                }
@@ -177,7 +177,7 @@ static int fr_domain_socket(char const *path)
                flags |= O_NONBLOCK;
                if( fcntl(sockfd, F_SETFL, flags) < 0) {
                        fprintf(stderr, "%s: Failure setting socket flags: %s",
-                               progname, strerror(errno));
+                               progname, fr_syserror(errno));
                        close(sockfd);
                        return -1;
                }
@@ -189,7 +189,8 @@ static int fr_domain_socket(char const *path)
 
 static int client_socket(char const *server)
 {
-       int sockfd, port;
+       int sockfd;
+       uint16_t port;
        fr_ipaddr_t ipaddr;
        char *p, buffer[1024];
 
@@ -203,16 +204,16 @@ static int client_socket(char const *server)
                *p = '\0';
        }
 
-       if (ip_hton(buffer, AF_INET, &ipaddr) < 0) {
+       if (ip_hton(&ipaddr, AF_INET, buffer, false) < 0) {
                fprintf(stderr, "%s: Failed looking up host %s: %s\n",
-                       progname, buffer, strerror(errno));
+                       progname, buffer, fr_syserror(errno));
                exit(1);
        }
 
        sockfd = fr_tcp_client_socket(NULL, &ipaddr, port);
        if (sockfd < 0) {
                fprintf(stderr, "%s: Failed opening socket %s: %s\n",
-                       progname, server, strerror(errno));
+                       progname, server, fr_syserror(errno));
                exit(1);
        }
 
@@ -240,7 +241,7 @@ static void do_challenge(int sockfd)
                        if (errno == EINTR) continue;
 
                        fprintf(stderr, "%s: Failed reading data: %s\n",
-                               progname, strerror(errno));
+                               progname, fr_syserror(errno));
                        exit(1);
                }
                total += r;
@@ -252,7 +253,7 @@ static void do_challenge(int sockfd)
 
        if (write(sockfd, challenge, sizeof(challenge)) < 0) {
                fprintf(stderr, "%s: Failed writing challenge data: %s\n",
-                       progname, strerror(errno));
+                       progname, fr_syserror(errno));
        }
 }
 
@@ -292,7 +293,7 @@ static ssize_t run_command(int sockfd, char const *command,
                        if (errno == EINTR) continue;
 
                        fprintf(stderr, "%s: Failed selecting: %s\n",
-                               progname, strerror(errno));
+                               progname, fr_syserror(errno));
                        exit(1);
                }
 
@@ -320,7 +321,7 @@ static ssize_t run_command(int sockfd, char const *command,
                        }
 
                        fprintf(stderr, "%s: Error reading socket: %s\n",
-                               progname, strerror(errno));
+                               progname, fr_syserror(errno));
                        exit(1);
                }
                if (len == 0) return 0; /* clean exit */
@@ -357,33 +358,45 @@ static ssize_t run_command(int sockfd, char const *command,
 
 int main(int argc, char **argv)
 {
-       int argval, quiet = 0;
-       int done_license = 0;
-       int sockfd;
-       uint32_t magic, needed;
-       char *line = NULL;
-       ssize_t len, size;
-       char const *file = NULL;
-       char const *name = "radiusd";
-       char *p, buffer[65536];
-       char const *input_file = NULL;
-       FILE *inputfp = stdin;
-       char const *output_file = NULL;
-       char const *server = NULL;
+       int             argval;
+       bool            quiet = false;
+       bool            done_license = false;
+       int             sockfd;
+       uint32_t        magic, needed;
+       char            *line = NULL;
+       ssize_t         len, size;
+       char const      *file = NULL;
+       char const      *name = "radiusd";
+       char            *p, buffer[65536];
+       char const      *input_file = NULL;
+       FILE            *inputfp = stdin;
+       char const      *output_file = NULL;
+       char const      *server = NULL;
+
+       char const      *radius_dir = RADIUS_DIR;
+       char const      *dict_dir = DICTDIR;
 
        char *commands[MAX_COMMANDS];
        int num_commands = -1;
 
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radmin");
+               exit(EXIT_FAILURE);
+       }
+#endif
+
        talloc_set_log_stderr();
 
        outputfp = stdout;      /* stdout is not a constant value... */
 
-       if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
+       if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) {
                progname = argv[0];
-       else
+       } else {
                progname++;
+       }
 
-       while ((argval = getopt(argc, argv, "d:hi:e:Ef:n:o:qs:S")) != EOF) {
+       while ((argval = getopt(argc, argv, "d:D:hi:e:Ef:n:o:qs:S")) != EOF) {
                switch(argval) {
                case 'd':
                        if (file) {
@@ -397,6 +410,10 @@ int main(int argc, char **argv)
                        radius_dir = optarg;
                        break;
 
+               case 'D':
+                       dict_dir = optarg;
+                       break;
+
                case 'e':
                        num_commands++; /* starts at -1 */
                        if (num_commands >= MAX_COMMANDS) {
@@ -425,7 +442,7 @@ int main(int argc, char **argv)
                        if (strcmp(optarg, "-") != 0) {
                                input_file = optarg;
                        }
-                       quiet = 1;
+                       quiet = true;
                        break;
 
                case 'n':
@@ -436,11 +453,11 @@ int main(int argc, char **argv)
                        if (strcmp(optarg, "-") != 0) {
                                output_file = optarg;
                        }
-                       quiet = 1;
+                       quiet = true;
                        break;
 
                case 'q':
-                       quiet = 1;
+                       quiet = true;
                        break;
 
                case 's':
@@ -458,14 +475,35 @@ int main(int argc, char **argv)
                }
        }
 
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radmin");
+               exit(1);
+       }
+
        if (radius_dir) {
                int rcode;
                CONF_SECTION *cs, *subcs;
 
                file = NULL;    /* MUST read it from the conffile now */
 
-               snprintf(buffer, sizeof(buffer), "%s/%s.conf",
-                        radius_dir, name);
+               snprintf(buffer, sizeof(buffer), "%s/%s.conf", radius_dir, name);
+
+               /*
+                *      Need to read in the dictionaries, else we may get
+                *      validation errors when we try and parse the config.
+                */
+               if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+                       fr_perror("radmin");
+                       exit(64);
+               }
+
+               if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+                       fr_perror("radmin");
+                       exit(64);
+               }
 
                cs = cf_file_read(buffer);
                if (!cs) {
@@ -488,9 +526,7 @@ int main(int argc, char **argv)
                        /*
                         *      Now find the socket name (sigh)
                         */
-                       rcode = cf_item_parse(subcs, "socket",
-                                             PW_TYPE_STRING_PTR,
-                                             &file, NULL);
+                       rcode = cf_item_parse(subcs, "socket", FR_ITEM_POINTER(PW_TYPE_STRING, &file), NULL);
                        if (rcode < 0) {
                                fprintf(stderr, "%s: Failed parsing listen section\n", progname);
                                exit(1);
@@ -512,7 +548,7 @@ int main(int argc, char **argv)
        if (input_file) {
                inputfp = fopen(input_file, "r");
                if (!inputfp) {
-                       fprintf(stderr, "%s: Failed opening %s: %s\n", progname, input_file, strerror(errno));
+                       fprintf(stderr, "%s: Failed opening %s: %s\n", progname, input_file, fr_syserror(errno));
                        exit(1);
                }
        }
@@ -520,15 +556,21 @@ int main(int argc, char **argv)
        if (output_file) {
                outputfp = fopen(output_file, "w");
                if (!outputfp) {
-                       fprintf(stderr, "%s: Failed creating %s: %s\n", progname, output_file, strerror(errno));
+                       fprintf(stderr, "%s: Failed creating %s: %s\n", progname, output_file, fr_syserror(errno));
                        exit(1);
                }
        }
 
+       if (!file && !server) {
+               fprintf(stderr, "%s: Must use one of '-d' or '-f' or '-s'\n",
+                       progname);
+               exit(1);
+       }
+
        /*
         *      Check if stdin is a TTY only if input is from stdin
         */
-       if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = 1;
+       if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = true;
 
 #ifdef USE_READLINE
        if (!quiet) {
@@ -559,7 +601,7 @@ int main(int argc, char **argv)
                len = read(sockfd, buffer + size, 8 - size);
                if (len < 0) {
                        fprintf(stderr, "%s: Error reading initial data from socket: %s\n",
-                               progname, strerror(errno));
+                               progname, fr_syserror(errno));
                        exit(1);
                }
        }
@@ -616,7 +658,7 @@ int main(int argc, char **argv)
                printf("You may redistribute copies of FreeRADIUS under the terms of the\n");
                printf("GNU General Public License v2.\n");
 
-               done_license = 1;
+               done_license = true;
        }
 
        /*
index cd735f7..90641be 100644 (file)
 /*
- *  radsniff.c Display the RADIUS traffic on the network.
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
  *
- *  Version:    $Id$
+ *   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.
  *
- *  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
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file radsniff.c
+ * @brief Capture, filter, and generate statistics for RADIUS traffic
  *
- *  Copyright 2006  The FreeRADIUS server project
- *  Copyright 2006  Nicolas Baradakis <nicolas.baradakis@cegetel.net>
+ * @copyright 2013 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ * @copyright 2006 The FreeRADIUS server project
+ * @copyright 2006 Nicolas Baradakis <nicolas.baradakis@cegetel.net>
  */
 
 RCSID("$Id$")
 
 #define _LIBRADIUS 1
+#include <assert.h>
+#include <time.h>
+#include <math.h>
 #include <freeradius-devel/libradius.h>
-
-#include <pcap.h>
+#include <freeradius-devel/event.h>
 
 #include <freeradius-devel/radpaths.h>
 #include <freeradius-devel/conf.h>
+#include <freeradius-devel/pcap.h>
 #include <freeradius-devel/radsniff.h>
 
-static char const *radius_secret = "testing123";
-static VALUE_PAIR *filter_vps = NULL;
-
-static int do_sort = 0;
-static int to_stdout = 0;
-static FILE *log_dst;
-
-#ifndef PCAP_NETMASK_UNKNOWN
-#  define PCAP_NETMASK_UNKNOWN 0
+#ifdef HAVE_COLLECTDC_H
+#  include <collectd/client.h>
 #endif
 
-#undef DEBUG1
-#define DEBUG1 if (fr_debug_flag > 2) fprintf
-#undef DEBUG
-#define DEBUG if (fr_debug_flag > 1) fprintf
-#undef INFO
-#define INFO if (fr_debug_flag > 0) fprintf
-
+static rs_t *conf;
 struct timeval start_pcap = {0, 0};
-static rbtree_t *filter_tree = NULL;
+static char timestr[50];
+
 static rbtree_t *request_tree = NULL;
-static pcap_dumper_t *out = NULL;
-static RADIUS_PACKET *nullpacket = NULL;
+static rbtree_t *link_tree = NULL;
+static fr_event_list_t *events;
+static bool cleanup;
+
+static int self_pipe[2] = {-1, -1};            //!< Signals from sig handlers
 
 typedef int (*rbcmp)(void const *, void const *);
 
 static char const *radsniff_version = "radsniff version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
-" (git #" RADIUSD_VERSION_COMMIT ")"
+" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
 #endif
 ", built on " __DATE__ " at " __TIME__;
 
-static int filter_packet(RADIUS_PACKET *packet)
+static int rs_useful_codes[] = {
+       PW_CODE_AUTHENTICATION_REQUEST,         //!< RFC2865 - Authentication request
+       PW_CODE_AUTHENTICATION_ACK,             //!< RFC2865 - Access-Accept
+       PW_CODE_AUTHENTICATION_REJECT,          //!< RFC2865 - Access-Reject
+       PW_CODE_ACCOUNTING_REQUEST,             //!< RFC2866 - Accounting-Request
+       PW_CODE_ACCOUNTING_RESPONSE,            //!< RFC2866 - Accounting-Response
+       PW_CODE_ACCESS_CHALLENGE,               //!< RFC2865 - Access-Challenge
+       PW_CODE_STATUS_SERVER,                  //!< RFC2865/RFC5997 - Status Server (request)
+       PW_CODE_STATUS_CLIENT,                  //!< RFC2865/RFC5997 - Status Server (response)
+       PW_CODE_DISCONNECT_REQUEST,             //!< RFC3575/RFC5176 - Disconnect-Request
+       PW_CODE_DISCONNECT_ACK,                 //!< RFC3575/RFC5176 - Disconnect-Ack (positive)
+       PW_CODE_DISCONNECT_NAK,                 //!< RFC3575/RFC5176 - Disconnect-Nak (not willing to perform)
+       PW_CODE_COA_REQUEST,                    //!< RFC3575/RFC5176 - CoA-Request
+       PW_CODE_COA_ACK,                        //!< RFC3575/RFC5176 - CoA-Ack (positive)
+       PW_CODE_COA_NAK,                        //!< RFC3575/RFC5176 - CoA-Nak (not willing to perform)
+};
+
+const FR_NAME_NUMBER rs_events[] = {
+       { "received",   RS_NORMAL       },
+       { "norsp",      RS_LOST         },
+       { "rtx",        RS_RTX          },
+       { "noreq",      RS_UNLINKED     },
+       { "reused",     RS_REUSED       },
+       { "error",      RS_ERROR        },
+       {  NULL , -1 }
+};
+
+static void NEVER_RETURNS usage(int status);
+
+/** Fork and kill the parent process, writing out our PID
+ *
+ * @param pidfile the PID file to write our PID to
+ */
+static void rs_daemonize(char const *pidfile)
 {
-       vp_cursor_t cursor, check_cursor;
-       VALUE_PAIR *check_item;
-       VALUE_PAIR *vp;
-       unsigned int pass, fail;
-       int compare;
+       FILE *fp;
+       pid_t pid, sid;
 
-       pass = fail = 0;
-       for (vp = paircursor(&cursor, &packet->vps);
-            vp;
-            vp = pairnext(&cursor)) {
-               for (check_item = paircursor(&check_cursor, &filter_vps);
-                    check_item;
-                    check_item = pairnext(&check_cursor))
-                       if ((check_item->da == vp->da)
-                        && (check_item->op != T_OP_SET)) {
-                               compare = paircmp(check_item, vp);
-                               if (compare == 1)
-                                       pass++;
-                               else
-                                       fail++;
-                       }
+       pid = fork();
+       if (pid < 0) {
+               exit(EXIT_FAILURE);
        }
 
-       if (fail == 0 && pass != 0) {
-               /*
-                *      Cache authentication requests, as the replies
-                *      may not match the RADIUS filter.
-                */
-               if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
-                   (packet->code == PW_ACCOUNTING_REQUEST)) {
-                       rbtree_deletebydata(filter_tree, packet);
-
-                       if (!rbtree_insert(filter_tree, packet)) {
-                       oom:
-                               fprintf(stderr, "radsniff: Out of memory\n");
-                               exit(1);
-                       }
-               }
-               return 0;       /* matched */
+       /*
+        *      Kill the parent...
+        */
+       if (pid > 0) {
+               close(self_pipe[0]);
+               close(self_pipe[1]);
+               exit(EXIT_SUCCESS);
        }
 
        /*
-        *      Don't create erroneous matches.
+        *      Continue as the child.
         */
-       if ((packet->code == PW_AUTHENTICATION_REQUEST) ||
-           (packet->code == PW_ACCOUNTING_REQUEST)) {
-               rbtree_deletebydata(filter_tree, packet);
-               return 1;
+
+       /* Create a new SID for the child process */
+       sid = setsid();
+       if (sid < 0) {
+               exit(EXIT_FAILURE);
        }
 
        /*
-        *      Else see if a previous Access-Request
-        *      matched.  If so, also print out the
-        *      matching accept, reject, or challenge.
+        *      Change the current working directory. This prevents the current
+        *      directory from being locked; hence not being able to remove it.
         */
-       if ((packet->code == PW_AUTHENTICATION_ACK) ||
-           (packet->code == PW_AUTHENTICATION_REJECT) ||
-           (packet->code == PW_ACCESS_CHALLENGE) ||
-           (packet->code == PW_ACCOUNTING_RESPONSE)) {
-               RADIUS_PACKET *reply;
+       if ((chdir("/")) < 0) {
+               exit(EXIT_FAILURE);
+       }
 
-               /*
-                *      This swaps the various fields.
-                */
-               reply = rad_alloc_reply(NULL, packet);
-               if (!reply) goto oom;
+       /*
+        *      And write it AFTER we've forked, so that we write the
+        *      correct PID.
+        */
+       fp = fopen(pidfile, "w");
+       if (fp != NULL) {
+               fprintf(fp, "%d\n", (int) sid);
+               fclose(fp);
+       } else {
+               ERROR("Failed creating PID file %s: %s", pidfile, fr_syserror(errno));
+               exit(EXIT_FAILURE);
+       }
 
-               compare = 1;
-               if (rbtree_finddata(filter_tree, reply)) {
-                       compare = 0;
+       /*
+        *      Close stdout and stderr if they've not been redirected.
+        */
+       if (isatty(fileno(stdout))) {
+               if (!freopen("/dev/null", "w", stdout)) {
+                       exit(EXIT_FAILURE);
                }
-
-               rad_free(&reply);
-               return compare;
        }
 
-       return 1;
+       if (isatty(fileno(stderr))) {
+               if (!freopen("/dev/null", "w", stderr)) {
+                       exit(EXIT_FAILURE);
+               }
+       }
 }
 
 #define USEC 1000000
-static void tv_sub(struct timeval const *end, struct timeval const *start,
-                  struct timeval *elapsed)
+static void rs_tv_sub(struct timeval const *end, struct timeval const *start, struct timeval *elapsed)
 {
        elapsed->tv_sec = end->tv_sec - start->tv_sec;
        if (elapsed->tv_sec > 0) {
@@ -165,483 +178,2354 @@ static void tv_sub(struct timeval const *end, struct timeval const *start,
        }
 }
 
-static void got_packet(UNUSED uint8_t *args, struct pcap_pkthdr const*header, uint8_t const *data)
+static void rs_tv_add_ms(struct timeval const *start, unsigned long interval, struct timeval *result) {
+    result->tv_sec = start->tv_sec + (interval / 1000);
+    result->tv_usec = start->tv_usec + ((interval % 1000) * 1000);
+
+    if (result->tv_usec > USEC) {
+       result->tv_usec -= USEC;
+       result->tv_sec++;
+    }
+}
+
+static void rs_time_print(char *out, size_t len, struct timeval const *t)
+{
+       size_t ret;
+       struct timeval now;
+       uint32_t usec;
+
+       if (!t) {
+               gettimeofday(&now, NULL);
+               t = &now;
+       }
+
+       ret = strftime(out, len, "%Y-%m-%d %H:%M:%S", localtime(&t->tv_sec));
+       if (ret >= len) {
+               return;
+       }
+
+       usec = t->tv_usec;
+
+       if (usec) {
+               while (usec < 100000) usec *= 10;
+               snprintf(out + ret, len - ret, ".%i", usec);
+       } else {
+               snprintf(out + ret, len - ret, ".000000");
+       }
+}
+
+static void rs_packet_print_null(UNUSED uint64_t count, UNUSED rs_status_t status, UNUSED fr_pcap_t *handle,
+                                UNUSED RADIUS_PACKET *packet, UNUSED struct timeval *elapsed,
+                                UNUSED struct timeval *latency, UNUSED bool response, UNUSED bool body)
+{
+       return;
+}
+
+static size_t rs_prints_csv(char *out, size_t outlen, char const *in, size_t inlen)
+{
+       char const      *start = out;
+       uint8_t const   *str = (uint8_t const *) in;
+
+       if (!in) {
+               if (outlen) {
+                       *out = '\0';
+               }
+
+               return 0;
+       }
+
+       if (inlen == 0) {
+               inlen = strlen(in);
+       }
+
+       while ((inlen > 0) && (outlen > 2)) {
+               /*
+                *      Escape double quotes with... MORE DOUBLE QUOTES!
+                */
+               if (*str == '"') {
+                       *out++ = '"';
+                       outlen--;
+               }
+
+               /*
+                *      Safe chars which require no escaping
+                */
+               if ((*str == '\r') || (*str == '\n') || ((*str >= '\x20') && (*str <= '\x7E'))) {
+                       *out++ = *str++;
+                       outlen--;
+                       inlen--;
+
+                       continue;
+               }
+
+               /*
+                *      Everything else is dropped
+                */
+               str++;
+               inlen--;
+       }
+       *out = '\0';
+
+       return out - start;
+}
+
+static void rs_packet_print_csv_header(void)
+{
+       char buffer[2048];
+       char *p = buffer;
+       int i;
+
+       ssize_t len, s = sizeof(buffer);
+
+       len = strlcpy(p, "\"Status\",\"Count\",\"Time\",\"Latency\",\"Type\",\"Interface\","
+                     "\"Src IP\",\"Src Port\",\"Dst IP\",\"Dst Port\",\"ID\",", s);
+       p += len;
+       s -= len;
+
+       if (s <= 0) return;
+
+       for (i = 0; i < conf->list_da_num; i++) {
+               char const *in;
+
+               *p++ = '"';
+               s -= 1;
+               if (s <= 0) return;
+
+               for (in = conf->list_da[i]->name; *in; in++) {
+                       *p++ = *in;
+                       s -= len;
+                       if (s <= 0) return;
+               }
+
+               *p++ = '"';
+               s -= 1;
+               if (s <= 0) return;
+               *p++ = ',';
+               s -= 1;
+               if (s <= 0) return;
+       }
+
+       *--p = '\0';
+
+       fprintf(stdout , "%s\n", buffer);
+}
+
+static void rs_packet_print_csv(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
+                               UNUSED struct timeval *elapsed, struct timeval *latency, UNUSED bool response,
+                               bool body)
+{
+       char const *status_str;
+       char buffer[2048];
+       char *p = buffer;
+
+       char src[INET6_ADDRSTRLEN];
+       char dst[INET6_ADDRSTRLEN];
+
+       ssize_t len, s = sizeof(buffer);
+
+       inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, src, sizeof(src));
+       inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst, sizeof(dst));
+
+       status_str = fr_int2str(rs_events, status, NULL);
+       assert(status_str);
+
+       len = snprintf(p, s, "%s,%" PRIu64 ",%s,", status_str, count, timestr);
+       p += len;
+       s -= len;
+
+       if (s <= 0) return;
+
+       if (latency) {
+               len = snprintf(p, s, "%u.%03u,",
+                              (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
+               p += len;
+               s -= len;
+       } else {
+               *p = ',';
+               p += 1;
+               s -= 1;
+       }
+
+       if (s <= 0) return;
+
+       /* Status, Type, Interface, Src, Src port, Dst, Dst port, ID */
+       if (is_radius_code(packet->code)) {
+               len = snprintf(p, s, "%s,%s,%s,%i,%s,%i,%i,", fr_packet_codes[packet->code], handle->name,
+                              src, packet->src_port, dst, packet->dst_port, packet->id);
+       } else {
+               len = snprintf(p, s, "%i,%s,%s,%i,%s,%i,%i,", packet->code, handle->name,
+                              src, packet->src_port, dst, packet->dst_port, packet->id);
+       }
+       p += len;
+       s -= len;
+
+       if (s <= 0) return;
+
+       if (body) {
+               int i;
+               VALUE_PAIR *vp;
+
+               for (i = 0; i < conf->list_da_num; i++) {
+                       vp = pairfind_da(packet->vps, conf->list_da[i], TAG_ANY);
+                       if (vp && (vp->length > 0)) {
+                               if (conf->list_da[i]->type == PW_TYPE_STRING) {
+                                       *p++ = '"';
+                                       s--;
+                                       if (s <= 0) return;
+
+                                       len = rs_prints_csv(p, s, vp->vp_strvalue, vp->length);
+                                       p += len;
+                                       s -= len;
+                                       if (s <= 0) return;
+
+                                       *p++ = '"';
+                                       s--;
+                                       if (s <= 0) return;
+                               } else {
+                                       len = vp_prints_value(p, s, vp, 0);
+                                       p += len;
+                                       s -= len;
+                                       if (s <= 0) return;
+                               }
+                       }
+
+                       *p++ = ',';
+                       s -= 1;
+                       if (s <= 0) return;
+               }
+       } else {
+               s -= conf->list_da_num;
+               if (s <= 0) return;
+
+               memset(p, ',', conf->list_da_num);
+               p += conf->list_da_num;
+       }
+
+       *--p = '\0';
+       fprintf(stdout , "%s\n", buffer);
+}
+
+static void rs_packet_print_fancy(uint64_t count, rs_status_t status, fr_pcap_t *handle, RADIUS_PACKET *packet,
+                                 struct timeval *elapsed, struct timeval *latency, bool response, bool body)
+{
+       char buffer[2048];
+       char *p = buffer;
+
+       char src[INET6_ADDRSTRLEN];
+       char dst[INET6_ADDRSTRLEN];
+
+       ssize_t len, s = sizeof(buffer);
+
+       inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, src, sizeof(src));
+       inet_ntop(packet->dst_ipaddr.af, &packet->dst_ipaddr.ipaddr, dst, sizeof(dst));
+
+       /* Only print out status str if something's not right */
+       if (status != RS_NORMAL) {
+               char const *status_str;
+
+               status_str = fr_int2str(rs_events, status, NULL);
+               assert(status_str);
+
+               len = snprintf(p, s, "** %s ** ", status_str);
+               p += len;
+               s -= len;
+               if (s <= 0) return;
+       }
+
+       if (is_radius_code(packet->code)) {
+               len = snprintf(p, s, "%s Id %i %s:%s:%d %s %s:%i ",
+                              fr_packet_codes[packet->code],
+                              packet->id,
+                              handle->name,
+                              response ? dst : src,
+                              response ? packet->dst_port : packet->src_port,
+                              response ? "<-" : "->",
+                              response ? src : dst ,
+                              response ? packet->src_port : packet->dst_port);
+       } else {
+               len = snprintf(p, s, "%i Id %i %s:%s:%i %s %s:%i ",
+                              packet->code,
+                              packet->id,
+                              handle->name,
+                              response ? dst : src,
+                              response ? packet->dst_port : packet->src_port,
+                              response ? "<-" : "->",
+                              response ? src : dst ,
+                              response ? packet->src_port : packet->dst_port);
+       }
+       p += len;
+       s -= len;
+       if (s <= 0) return;
+
+       if (elapsed) {
+               len = snprintf(p, s, "+%u.%03u ",
+                              (unsigned int) elapsed->tv_sec, ((unsigned int) elapsed->tv_usec / 1000));
+               p += len;
+               s -= len;
+               if (s <= 0) return;
+       }
+
+       if (latency) {
+               len = snprintf(p, s, "+%u.%03u ",
+                              (unsigned int) latency->tv_sec, ((unsigned int) latency->tv_usec / 1000));
+               p += len;
+               s -= len;
+               if (s <= 0) return;
+       }
+
+       *--p = '\0';
+
+       RIDEBUG("%s", buffer);
+
+       if (body) {
+               /*
+                *      Print out verbose HEX output
+                */
+               if (conf->print_packet && (fr_debug_flag > 3)) {
+                       rad_print_hex(packet);
+               }
+
+               if (conf->print_packet && (fr_debug_flag > 1)) {
+                       char vector[(AUTH_VECTOR_LEN * 2) + 1];
+
+                       if (packet->vps) {
+                               pairsort(&packet->vps, attrtagcmp);
+                               vp_printlist(fr_log_fp, packet->vps);
+                       }
+
+                       fr_bin2hex(vector, packet->vector, AUTH_VECTOR_LEN);
+                       INFO("\tAuthenticator-Field = 0x%s", vector);
+               }
+       }
+}
+
+static void rs_stats_print(rs_latency_t *stats, PW_CODE code)
+{
+       int i;
+       bool have_rt = false;
+
+       for (i = 0; i <= RS_RETRANSMIT_MAX; i++) {
+               if (stats->interval.rt[i]) {
+                       have_rt = true;
+               }
+       }
+
+       if (!stats->interval.received && !have_rt && !stats->interval.reused) {
+               return;
+       }
+
+       if (stats->interval.received || stats->interval.linked) {
+               INFO("%s counters:", fr_packet_codes[code]);
+               if (stats->interval.received > 0) {
+                       INFO("\tTotal     : %.3lf/s" , stats->interval.received);
+               }
+       }
+
+       if (stats->interval.linked > 0) {
+               INFO("\tLinked    : %.3lf/s", stats->interval.linked);
+               INFO("\tUnlinked  : %.3lf/s", stats->interval.unlinked);
+               INFO("%s latency:", fr_packet_codes[code]);
+               INFO("\tHigh      : %.3lfms", stats->interval.latency_high);
+               INFO("\tLow       : %.3lfms", stats->interval.latency_low);
+               INFO("\tAverage   : %.3lfms", stats->interval.latency_average);
+               INFO("\tMA        : %.3lfms", stats->latency_smoothed);
+       }
+
+       if (have_rt || stats->interval.lost || stats->interval.reused) {
+               INFO("%s retransmits & loss:",  fr_packet_codes[code]);
+
+               if (stats->interval.lost) {
+                       INFO("\tLost      : %.3lf/s", stats->interval.lost);
+               }
+
+               if (stats->interval.reused) {
+                       INFO("\tID Reused : %.3lf/s", stats->interval.reused);
+               }
+
+               for (i = 0; i <= RS_RETRANSMIT_MAX; i++) {
+                       if (!stats->interval.rt[i]) {
+                               continue;
+                       }
+
+                       if (i != RS_RETRANSMIT_MAX) {
+                               INFO("\tRT (%i)    : %.3lf/s", i, stats->interval.rt[i]);
+                       } else {
+                               INFO("\tRT (%i+)   : %.3lf/s", i, stats->interval.rt[i]);
+                       }
+               }
+       }
+}
+
+/** Query libpcap to see if it dropped any packets
+ *
+ * We need to check to see if libpcap dropped any packets and if it did, we need to stop stats output for long
+ * enough for inaccurate statistics to be cleared out.
+ *
+ * @param in pcap handle to check.
+ * @param interval time between checks (used for debug output)
+ * @return 0, no drops, -1 we couldn't check, -2 dropped because of buffer exhaustion, -3 dropped because of NIC.
+ */
+static int rs_check_pcap_drop(fr_pcap_t *in, int interval) {
+       int ret = 0;
+       struct pcap_stat pstats;
+
+       if (pcap_stats(in->handle, &pstats) != 0) {
+               ERROR("%s failed retrieving pcap stats: %s", in->name, pcap_geterr(in->handle));
+               return -1;
+       }
+
+       INFO("\t%s%*s: %.3lf/s", in->name, (int) (10 - strlen(in->name)), "",
+            ((double) (pstats.ps_recv - in->pstats.ps_recv)) / interval);
+
+       if (pstats.ps_drop - in->pstats.ps_drop > 0) {
+               ERROR("%s dropped %i packets: Buffer exhaustion", in->name, pstats.ps_drop - in->pstats.ps_drop);
+               ret = -2;
+       }
+
+       if (pstats.ps_ifdrop - in->pstats.ps_ifdrop > 0) {
+               ERROR("%s dropped %i packets: Interface", in->name, pstats.ps_ifdrop - in->pstats.ps_ifdrop);
+               ret = -3;
+       }
+
+       in->pstats = pstats;
+
+       return ret;
+}
+
+/** Update smoothed average
+ *
+ */
+static void rs_stats_process_latency(rs_latency_t *stats)
+{
+       /*
+        *      If we didn't link any packets during this interval, we don't have a value to return.
+        *      returning 0 is misleading as it would be like saying the latency had dropped to 0.
+        *      We instead set NaN which libcollectd converts to a 'U' or unknown value.
+        *
+        *      This will cause gaps in graphs, but is completely legitimate as we are missing data.
+        *      This is unfortunately an effect of being just a passive observer.
+        */
+       if (stats->interval.linked_total == 0) {
+               double unk = strtod("NAN()", (char **) NULL);
+
+               stats->interval.latency_average = unk;
+               stats->interval.latency_high = unk;
+               stats->interval.latency_low = unk;
+
+               /*
+                *      We've not yet been able to determine latency, so latency_smoothed is also NaN
+                */
+               if (stats->latency_smoothed_count == 0) {
+                       stats->latency_smoothed = unk;
+               }
+               return;
+       }
+
+       if (stats->interval.linked_total && stats->interval.latency_total) {
+               stats->interval.latency_average = (stats->interval.latency_total / stats->interval.linked_total);
+       }
+
+       if (isnan(stats->latency_smoothed)) {
+               stats->latency_smoothed = 0;
+       }
+       if (stats->interval.latency_average > 0) {
+               stats->latency_smoothed_count++;
+               stats->latency_smoothed += ((stats->interval.latency_average - stats->latency_smoothed) /
+                                      ((stats->latency_smoothed_count < 100) ? stats->latency_smoothed_count : 100));
+       }
+}
+
+static void rs_stats_process_counters(rs_latency_t *stats)
+{
+       int i;
+
+       stats->interval.received = ((long double) stats->interval.received_total) / conf->stats.interval;
+       stats->interval.linked = ((long double) stats->interval.linked_total) / conf->stats.interval;
+       stats->interval.unlinked = ((long double) stats->interval.unlinked_total) / conf->stats.interval;
+       stats->interval.reused = ((long double) stats->interval.reused_total) / conf->stats.interval;
+       stats->interval.lost = ((long double) stats->interval.lost_total) / conf->stats.interval;
+
+       for (i = 0; i < RS_RETRANSMIT_MAX; i++) {
+               stats->interval.rt[i] = ((long double) stats->interval.rt_total[i]) / conf->stats.interval;
+       }
+}
+
+/** Process stats for a single interval
+ *
+ */
+static void rs_stats_process(void *ctx)
 {
+       size_t i;
+       size_t rs_codes_len = (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes));
+       fr_pcap_t               *in_p;
+       rs_update_t             *this = ctx;
+       rs_stats_t              *stats = this->stats;
+       struct timeval          now;
 
-       static int count = 1;                   /* Packets seen */
+       gettimeofday(&now, NULL);
+
+       stats->intervals++;
+
+       INFO("######### Stats Iteration %i #########", stats->intervals);
 
        /*
-        *  Define pointers for packet's attributes
+        *      Verify that none of the pcap handles have dropped packets.
         */
-       const struct ip_header *ip;             /* The IP header */
-       const struct udp_header *udp;           /* The UDP header */
-       const uint8_t *payload;                 /* Packet payload */
+       INFO("Interface capture rate:");
+       for (in_p = this->in;
+            in_p;
+            in_p = in_p->next) {
+               if (rs_check_pcap_drop(in_p, conf->stats.interval) < 0) {
+                       ERROR("Muting stats for the next %i milliseconds", conf->stats.timeout);
+
+                       rs_tv_add_ms(&now, conf->stats.timeout, &stats->quiet);
+                       goto clear;
+               }
+       }
+
+       if ((stats->quiet.tv_sec + (stats->quiet.tv_usec / 1000000.0)) -
+           (now.tv_sec + (now.tv_usec / 1000000.0)) > 0) {
+               INFO("Stats muted because of warmup, or previous error");
+               goto clear;
+       }
 
        /*
-        *  And define the size of the structures we're using
+        *      Latency stats need a bit more work to calculate the SMA.
+        *
+        *      No further work is required for codes.
         */
-       int size_ethernet = sizeof(struct ethernet_header);
-       int size_ip = sizeof(struct ip_header);
-       int size_udp = sizeof(struct udp_header);
+       for (i = 0; i < rs_codes_len; i++) {
+               rs_stats_process_latency(&stats->exchange[rs_useful_codes[i]]);
+               rs_stats_process_counters(&stats->exchange[rs_useful_codes[i]]);
+               if (fr_debug_flag > 0) {
+                       rs_stats_print(&stats->exchange[rs_useful_codes[i]], rs_useful_codes[i]);
+               }
+       }
 
+#ifdef HAVE_COLLECTDC_H
        /*
-        *  For FreeRADIUS
+        *      Update stats in collectd using the complex structures we
+        *      initialised earlier.
         */
-       RADIUS_PACKET *packet, *original;
-       struct timeval elapsed;
+       if ((conf->stats.out == RS_STATS_OUT_COLLECTD) && conf->stats.handle) {
+               rs_stats_collectd_do_stats(conf, conf->stats.tmpl, &now);
+       }
+#endif
 
+       clear:
        /*
-        * Define our packet's attributes
+        *      Rinse and repeat...
         */
+       for (i = 0; i < rs_codes_len; i++) {
+               memset(&stats->exchange[rs_useful_codes[i]].interval, 0,
+                      sizeof(stats->exchange[rs_useful_codes[i]].interval));
+       }
 
-       if ((data[0] == 2) && (data[1] == 0) &&
-           (data[2] == 0) && (data[3] == 0)) {
-               ip = (struct ip_header const *) (data + 4);
+       {
+               static fr_event_t *event;
 
-       } else {
-               ip = (struct ip_header const *)(data + size_ethernet);
+               now.tv_sec += conf->stats.interval;
+               now.tv_usec = 0;
+
+               if (!fr_event_insert(this->list, rs_stats_process, ctx, &now, &event)) {
+                       ERROR("Failed inserting stats interval event");
+               }
        }
+}
 
-       udp = (struct udp_header const *)(((uint8_t const *) ip) + size_ip);
-       payload = (uint8_t const *)(((uint8_t const *) udp) + size_udp);
 
-       packet = rad_alloc(NULL, 0);
-       if (!packet) {
-               fprintf(stderr, "Out of memory\n");
-               return;
-       }
+/** Update latency statistics for request/response and forwarded packets
+ *
+ */
+static void rs_stats_update_latency(rs_latency_t *stats, struct timeval *latency)
+{
+       double lint;
 
-       packet->src_ipaddr.af = AF_INET;
-       packet->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
-       packet->src_port = ntohs(udp->udp_sport);
-       packet->dst_ipaddr.af = AF_INET;
-       packet->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
-       packet->dst_port = ntohs(udp->udp_dport);
+       stats->interval.linked_total++;
+       /* More useful is this in milliseconds */
+       lint = (latency->tv_sec + (latency->tv_usec / 1000000.0)) * 1000;
+       if (lint > stats->interval.latency_high) {
+               stats->interval.latency_high = lint;
+       }
+       if (!stats->interval.latency_low || (lint < stats->interval.latency_low)) {
+               stats->interval.latency_low = lint;
+       }
+       stats->interval.latency_total += lint;
 
-       memcpy(&packet->data, &payload, sizeof(packet->data));
-       packet->data_len = header->len - (payload - data);
+}
 
-       if (!rad_packet_ok(packet, 0)) {
-               DEBUG(log_dst, "Packet: %s\n", fr_strerror());
+/** Copy a subset of attributes from one list into the other
+ *
+ * Should be O(n) if all the attributes exist.  List must be pre-sorted.
+ */
+static int rs_get_pairs(TALLOC_CTX *ctx, VALUE_PAIR **out, VALUE_PAIR *vps, DICT_ATTR const *da[], int num)
+{
+       vp_cursor_t list_cursor, out_cursor;
+       VALUE_PAIR *match, *last_match, *copy;
+       uint64_t count = 0;
+       int i;
+
+       last_match = vps;
+
+       fr_cursor_init(&list_cursor, &last_match);
+       fr_cursor_init(&out_cursor, out);
+       for (i = 0; i < num; i++) {
+               match = fr_cursor_next_by_da(&list_cursor, da[i], TAG_ANY);
+               if (!match) {
+                       fr_cursor_init(&list_cursor, &last_match);
+                       continue;
+               }
 
-               DEBUG(log_dst, "  From     %s:%d\n", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
-               DEBUG(log_dst, "  To:      %s:%d\n", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
-               DEBUG(log_dst, "  Type:    %s\n", fr_packet_codes[packet->code]);
+               do {
+                       copy = paircopyvp(ctx, match);
+                       if (!copy) {
+                               pairfree(out);
+                               return -1;
+                       }
+                       fr_cursor_insert(&out_cursor, copy);
+                       last_match = match;
 
-               rad_free(&packet);
-               return;
+                       count++;
+               } while ((match = fr_cursor_next_by_da(&list_cursor, da[i], TAG_ANY)));
        }
 
-       switch (packet->code) {
-       case PW_COA_REQUEST:
-               /* we need a 16 x 0 byte vector for decrypting encrypted VSAs */
-               original = nullpacket;
-               break;
-       case PW_AUTHENTICATION_ACK:
-               /* look for a matching request and use it for decoding */
-               original = rbtree_finddata(request_tree, packet);
-               break;
-       case PW_AUTHENTICATION_REQUEST:
-               /* save the request for later matching */
-               original = rad_alloc_reply(NULL, packet);
-               if (original) { /* just ignore allocation failures */
-                       rbtree_deletebydata(request_tree, original);
-                       rbtree_insert(request_tree, original);
-               }
-               /* fallthrough */
-       default:
-               /* don't attempt to decode any encrypted attributes */
-               original = NULL;
-       }
+       return count;
+}
 
+static int _request_free(rs_request_t *request)
+{
        /*
-        *  Decode the data without bothering to check the signatures.
+        *      If were attempting to cleanup the request, and it's no longer in the request_tree
+        *      something has gone very badly wrong.
         */
-       if (rad_decode(packet, original, radius_secret) != 0) {
-               rad_free(&packet);
-               fr_perror("decode");
-               return;
+       if (request->in_request_tree) {
+               assert(rbtree_deletebydata(request_tree, request));
        }
 
-       /*
-        *  We've seen a successful reply to this, so delete it now
-        */
-       if (original)
-               rbtree_deletebydata(request_tree, original);
+       if (request->in_link_tree) {
+               assert(rbtree_deletebydata(link_tree, request));
+       }
 
-       if (filter_vps && filter_packet(packet)) {
-               rad_free(&packet);
-               DEBUG(log_dst, "Packet number %d doesn't match\n", count++);
-               return;
+       if (request->event) {
+               assert(fr_event_delete(events, &request->event));
        }
 
-       if (out) {
-               pcap_dump((void *) out, header, data);
-               goto check_filter;
+       rad_free(&request->packet);
+       rad_free(&request->expect);
+       rad_free(&request->linked);
+
+       return 0;
+}
+
+static void rs_packet_cleanup(rs_request_t *request)
+{
+
+       RADIUS_PACKET *packet = request->packet;
+       uint64_t count = request->id;
+
+       assert(request->stats_req);
+       assert(!request->rt_rsp || request->stats_rsp);
+       assert(packet);
+
+       /*
+        *      Don't pollute stats or print spurious messages as radsniff closes.
+        */
+       if (cleanup) {
+               talloc_free(request);
+               return;
+       }
+
+       if (RIDEBUG_ENABLED()) {
+               rs_time_print(timestr, sizeof(timestr), &request->when);
+       }
+
+       /*
+        *      Were at packet cleanup time which is when the packet was received + timeout
+        *      and it's not been linked with a forwarded packet or a response.
+        *
+        *      We now count it as lost.
+        */
+       if (!request->silent_cleanup) {
+               if (!request->linked) {
+                       request->stats_req->interval.lost_total++;
+
+                       if (conf->event_flags & RS_LOST) {
+                               /* @fixme We should use flags in the request to indicate whether it's been dumped
+                                * to a PCAP file or logged yet, this simplifies the body logging logic */
+                               conf->logger(request->id, RS_LOST, request->in, packet, NULL, NULL, false,
+                                            conf->filter_response_vps || !(conf->event_flags & RS_NORMAL));
+                       }
+               }
+
+               if (request->in->type == PCAP_INTERFACE_IN) {
+                       RDEBUG("Cleaning up request packet ID %i", request->expect->id);
+               }
+       }
+
+       /*
+        *      Now the request is done, we can update the retransmission stats
+        */
+       if (request->rt_req > RS_RETRANSMIT_MAX) {
+               request->stats_req->interval.rt_total[RS_RETRANSMIT_MAX]++;
+       } else {
+               request->stats_req->interval.rt_total[request->rt_req]++;
+       }
+
+       if (request->rt_rsp) {
+               if (request->rt_rsp > RS_RETRANSMIT_MAX) {
+                       request->stats_rsp->interval.rt_total[RS_RETRANSMIT_MAX]++;
+               } else {
+                       request->stats_rsp->interval.rt_total[request->rt_rsp]++;
+               }
+       }
+
+       talloc_free(request);
+}
+
+static void _rs_event(void *ctx)
+{
+       rs_request_t *request = talloc_get_type_abort(ctx, rs_request_t);
+       request->event = NULL;
+       rs_packet_cleanup(request);
+}
+
+/** Wrapper around fr_packet_cmp to strip off the outer request struct
+ *
+ */
+static int rs_packet_cmp(rs_request_t const *a, rs_request_t const *b)
+{
+       return fr_packet_cmp(a->expect, b->expect);
+}
+
+/* This is the same as immediately scheduling the cleanup event */
+#define RS_CLEANUP_NOW(_x, _s)\
+       {\
+               _x->silent_cleanup = _s;\
+               _x->when = header->ts;\
+               rs_packet_cleanup(_x);\
+               _x = NULL;\
+       } while (0)
+
+static void rs_packet_process(uint64_t count, rs_event_t *event, struct pcap_pkthdr const *header, uint8_t const *data)
+{
+       rs_stats_t              *stats = event->stats;
+       struct timeval          elapsed = {0, 0};
+       struct timeval          latency;
+
+       /*
+        *      Pointers into the packet data we just received
+        */
+       ssize_t len;
+       uint8_t const           *p = data;
+
+       ip_header_t const       *ip = NULL;             /* The IP header */
+       ip_header6_t const      *ip6 = NULL;            /* The IPv6 header */
+       udp_header_t const      *udp;                   /* The UDP header */
+       uint8_t                 version;                /* IP header version */
+       bool                    response;               /* Was it a response code */
+
+       decode_fail_t           reason;                 /* Why we failed decoding the packet */
+       static uint64_t         captured = 0;
+
+       rs_status_t             status = RS_NORMAL;     /* Any special conditions (RTX, Unlinked, ID-Reused) */
+       RADIUS_PACKET           *current;               /* Current packet were processing */
+       rs_request_t            *original = NULL;
+
+       rs_request_t            search;
+
+       memset(&search, 0, sizeof(search));
+
+       if (!start_pcap.tv_sec) {
+               start_pcap = header->ts;
+       }
+
+       if (RIDEBUG_ENABLED()) {
+               rs_time_print(timestr, sizeof(timestr), &header->ts);
+       }
+
+       len = fr_pcap_link_layer_offset(data, header->caplen, event->in->link_type);
+       if (len < 0) {
+               REDEBUG("Failed determining link layer header offset");
+               return;
+       }
+       p += len;
+
+       version = (p[0] & 0xf0) >> 4;
+       switch (version) {
+       case 4:
+               ip = (ip_header_t const *)p;
+               len = (0x0f & ip->ip_vhl) * 4;  /* ip_hl specifies length in 32bit words */
+               p += len;
+               break;
+
+       case 6:
+               ip6 = (ip_header6_t const *)p;
+               p += sizeof(ip_header6_t);
+
+               break;
+
+       default:
+               REDEBUG("IP version invalid %i", version);
+               return;
+       }
+
+       /*
+        *      End of variable length bits, do basic check now to see if packet looks long enough
+        */
+       len = (p - data) + sizeof(udp_header_t) + (sizeof(radius_packet_t) - 1);        /* length value */
+       if ((size_t) len > header->caplen) {
+               REDEBUG("Packet too small, we require at least %zu bytes, captured %i bytes",
+                       (size_t) len, header->caplen);
+               return;
+       }
+
+       /*
+        *      UDP header validation.
+        */
+       udp = (udp_header_t const *)p;
+       {
+               uint16_t udp_len;
+               ssize_t diff;
+
+               udp_len = ntohs(udp->len);
+               diff = udp_len - (header->caplen - (p - data));
+               /* Truncated data */
+               if (diff > 0) {
+                       REDEBUG("Packet too small by %zi bytes, UDP header + Payload should be %hu bytes",
+                               diff, udp_len);
+                       return;
+               }
+               /* Trailing data */
+               else if (diff < 0) {
+                       REDEBUG("Packet too big by %zi bytes, UDP header + Payload should be %hu bytes",
+                               diff * -1, udp_len);
+                       return;
+               }
+       }
+       if ((version == 4) && conf->verify_udp_checksum) {
+               uint16_t expected;
+
+               expected = fr_udp_checksum((uint8_t const *) udp, ntohs(udp->len), udp->checksum,
+                                          ip->ip_src, ip->ip_dst);
+               if (udp->checksum != expected) {
+                       REDEBUG("UDP checksum invalid, packet: 0x%04hx calculated: 0x%04hx",
+                               ntohs(udp->checksum), ntohs(expected));
+                       /* Not a fatal error */
+               }
+       }
+       p += sizeof(udp_header_t);
+
+       /*
+        *      With artificial talloc memory limits there's a good chance we can
+        *      recover once some requests timeout, so make an effort to deal
+        *      with allocation failures gracefully.
+        */
+       current = rad_alloc(conf, 0);
+       if (!current) {
+               REDEBUG("Failed allocating memory to hold decoded packet");
+               rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
+               return;
+       }
+
+       current->timestamp = header->ts;
+       current->data_len = header->caplen - (p - data);
+       memcpy(&current->data, &p, sizeof(current->data));
+
+       /*
+        *      Populate IP/UDP fields from PCAP data
+        */
+       if (ip) {
+               current->src_ipaddr.af = AF_INET;
+               current->src_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_src.s_addr;
+
+               current->dst_ipaddr.af = AF_INET;
+               current->dst_ipaddr.ipaddr.ip4addr.s_addr = ip->ip_dst.s_addr;
+       } else {
+               current->src_ipaddr.af = AF_INET6;
+               memcpy(&current->src_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_src.s6_addr,
+                      sizeof(current->src_ipaddr.ipaddr.ip6addr.s6_addr));
+
+               current->dst_ipaddr.af = AF_INET6;
+               memcpy(&current->dst_ipaddr.ipaddr.ip6addr.s6_addr, &ip6->ip_dst.s6_addr,
+                      sizeof(current->dst_ipaddr.ipaddr.ip6addr.s6_addr));
+       }
+
+       current->src_port = ntohs(udp->src);
+       current->dst_port = ntohs(udp->dst);
+
+       if (!rad_packet_ok(current, 0, &reason)) {
+               REDEBUG("%s", fr_strerror());
+               if (conf->event_flags & RS_ERROR) {
+                       conf->logger(count, RS_ERROR, event->in, current, &elapsed, NULL, false, false);
+               }
+               rad_free(&current);
+
+               return;
+       }
+
+       switch (current->code) {
+       case PW_CODE_ACCOUNTING_RESPONSE:
+       case PW_CODE_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_ACCESS_CHALLENGE:
+       case PW_CODE_COA_NAK:
+       case PW_CODE_COA_ACK:
+       case PW_CODE_DISCONNECT_NAK:
+       case PW_CODE_DISCONNECT_ACK:
+       case PW_CODE_STATUS_CLIENT:
+       {
+               /* look for a matching request and use it for decoding */
+               search.expect = current;
+               original = rbtree_finddata(request_tree, &search);
+
+               /*
+                *      Verify this code is allowed
+                */
+               if (conf->filter_response_code && (conf->filter_response_code != current->code)) {
+                       drop_response:
+                       RDEBUG2("Response dropped by filter");
+                       rad_free(&current);
+
+                       /* We now need to cleanup the original request too */
+                       if (original) {
+                               RS_CLEANUP_NOW(original, true);
+                       }
+                       return;
+               }
+
+               /*
+                *      Only decode attributes if we want to print them or filter on them
+                *      rad_packet_ok does checks to verify the packet is actually valid.
+                */
+               if (conf->decode_attrs) {
+                       int ret;
+                       FILE *log_fp = fr_log_fp;
+
+                       fr_log_fp = NULL;
+                       ret = rad_decode(current, original ? original->expect : NULL, conf->radius_secret);
+                       fr_log_fp = log_fp;
+                       if (ret != 0) {
+                               rad_free(&current);
+                               REDEBUG("Failed decoding");
+                               return;
+                       }
+               }
+
+               /*
+                *      Check if we've managed to link it to a request
+                */
+               if (original) {
+                       /*
+                        *      Now verify the packet passes the attribute filter
+                        */
+                       if (conf->filter_response_vps) {
+                               pairsort(&current->vps, attrtagcmp);
+                               if (!pairvalidate_relaxed(NULL, conf->filter_response_vps, current->vps)) {
+                                       goto drop_response;
+                               }
+                       }
+
+                       /*
+                        *      Is this a retransmission?
+                        */
+                       if (original->linked) {
+                               status = RS_RTX;
+                               original->rt_rsp++;
+
+                               rad_free(&original->linked);
+                               fr_event_delete(event->list, &original->event);
+                       /*
+                        *      ...nope it's the first response to a request.
+                        */
+                       } else {
+                               original->stats_rsp = &stats->exchange[current->code];
+                       }
+
+                       /*
+                        *      Insert a callback to remove the request and response
+                        *      from the tree after the timeout period.
+                        *      The delay is so we can detect retransmissions.
+                        */
+                       original->linked = talloc_steal(original, current);
+                       rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
+                       if (!fr_event_insert(event->list, _rs_event, original, &original->when,
+                                            &original->event)) {
+                               REDEBUG("Failed inserting new event");
+                               /*
+                                *      Delete the original request/event, it's no longer valid
+                                *      for statistics.
+                                */
+                               talloc_free(original);
+                               return;
+                       }
+               /*
+                *      No request seen, or request was dropped by attribute filter
+                */
+               } else {
+                       /*
+                        *      If conf->filter_request_vps are set assume the original request was dropped,
+                        *      the alternative is maintaining another 'filter', but that adds
+                        *      complexity, reduces max capture rate, and is generally a PITA.
+                        */
+                       if (conf->filter_request) {
+                               rad_free(&current);
+                               RDEBUG2("Original request dropped by filter");
+                               return;
+                       }
+
+                       status = RS_UNLINKED;
+                       stats->exchange[current->code].interval.unlinked_total++;
+               }
+
+               response = true;
+               break;
+       }
+
+       case PW_CODE_ACCOUNTING_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
+       case PW_CODE_COA_REQUEST:
+       case PW_CODE_DISCONNECT_REQUEST:
+       case PW_CODE_STATUS_SERVER:
+       {
+               /*
+                *      Verify this code is allowed
+                */
+               if (conf->filter_request_code && (conf->filter_request_code != current->code)) {
+                       drop_request:
+
+                       RDEBUG2("Request dropped by filter");
+                       rad_free(&current);
+
+                       return;
+               }
+
+               /*
+                *      Only decode attributes if we want to print them or filter on them
+                *      rad_packet_ok does checks to verify the packet is actually valid.
+                */
+               if (conf->decode_attrs) {
+                       int ret;
+                       FILE *log_fp = fr_log_fp;
+
+                       fr_log_fp = NULL;
+                       ret = rad_decode(current, NULL, conf->radius_secret);
+                       fr_log_fp = log_fp;
+
+                       if (ret != 0) {
+                               rad_free(&current);
+                               REDEBUG("Failed decoding");
+                               return;
+                       }
+
+                       pairsort(&current->vps, attrtagcmp);
+               }
+
+               /*
+                *      Save the request for later matching
+                */
+               search.expect = rad_alloc_reply(current, current);
+               if (!search.expect) {
+                       REDEBUG("Failed allocating memory to hold expected reply");
+                       rs_tv_add_ms(&header->ts, conf->stats.timeout, &stats->quiet);
+                       rad_free(&current);
+                       return;
+               }
+               search.expect->code = current->code;
+
+               if ((conf->link_da_num > 0) && current->vps) {
+                       int ret;
+                       ret = rs_get_pairs(current, &search.link_vps, current->vps, conf->link_da,
+                                          conf->link_da_num);
+                       if (ret < 0) {
+                               ERROR("Failed extracting RTX linking pairs from request");
+                               rad_free(&current);
+                               return;
+                       }
+               }
+
+               /*
+                *      If we have linking attributes set, attempt to find a request in the linking tree.
+                */
+               if (search.link_vps) {
+                       rs_request_t *tuple;
+
+                       original = rbtree_finddata(link_tree, &search);
+                       tuple = rbtree_finddata(request_tree, &search);
+
+                       /*
+                        *      If the packet we matched using attributes is not the same
+                        *      as the packet in the request tree, then we need to clean up
+                        *      the packet in the request tree.
+                        */
+                       if (tuple && (original != tuple)) {
+                               RS_CLEANUP_NOW(tuple, true);
+                       }
+               /*
+                *      Detect duplicates using the normal 5-tuple of src/dst ips/ports id
+                */
+               } else {
+                       original = rbtree_finddata(request_tree, &search);
+                       if (original && memcmp(original->expect->vector, current->vector,
+                           sizeof(original->expect->vector)) != 0) {
+                               /*
+                                *      ID reused before the request timed out (which may be an issue)...
+                                */
+                               if (!original->linked) {
+                                       status = RS_REUSED;
+                                       stats->exchange[current->code].interval.reused_total++;
+                                       /* Occurs regularly downstream of proxy servers (so don't complain) */
+                                       RS_CLEANUP_NOW(original, true);
+                               /*
+                                *      ...and before we saw a response (which may be a bigger issue).
+                                */
+                               } else {
+                                       RS_CLEANUP_NOW(original, false);
+                               }
+                               /* else it's a proper RTX with the same src/dst id authenticator/nonce */
+                       }
+               }
+
+               /*
+                *      Now verify the packet passes the attribute filter
+                */
+               if (conf->filter_request_vps) {
+                       if (!pairvalidate_relaxed(NULL, conf->filter_request_vps, current->vps)) {
+                               goto drop_request;
+                       }
+               }
+
+               /*
+                *      Is this a retransmission?
+                */
+               if (original) {
+                       status = RS_RTX;
+                       original->rt_req++;
+
+                       rad_free(&original->packet);
+
+                       /* We may of seen the response, but it may of been lost upstream */
+                       rad_free(&original->linked);
+
+                       original->packet = talloc_steal(original, current);
+
+                       /* Request may need to be reinserted as the 5 tuple of the response may of changed */
+                       if (rs_packet_cmp(original, &search) != 0) {
+                               rbtree_deletebydata(request_tree, original);
+                       }
+
+                       rad_free(&original->expect);
+                       original->expect = talloc_steal(original, search.expect);
+
+                       /* Disarm the timer for the cleanup event for the original request */
+                       fr_event_delete(event->list, &original->event);
+               /*
+                *      ...nope it's a new request.
+                */
+               } else {
+                       original = talloc_zero(conf, rs_request_t);
+                       talloc_set_destructor(original, _request_free);
+
+                       original->id = count;
+                       original->in = event->in;
+                       original->stats_req = &stats->exchange[current->code];
+
+                       original->packet = talloc_steal(original, current);
+                       original->expect = talloc_steal(original, search.expect);
+
+                       if (search.link_vps) {
+                               original->link_vps = pairsteal(original, search.link_vps);
+
+                               /* We should never have conflicts */
+                               assert(rbtree_insert(link_tree, original));
+                               original->in_link_tree = true;
+                       }
+
+                       /*
+                        *      Special case for when were filtering by response,
+                        *      we never count any requests as lost, because we
+                        *      don't know what the response to that request would
+                        *      of been.
+                        */
+                       if (conf->filter_response_vps) {
+                               original->silent_cleanup = true;
+                       }
+               }
+
+               if (!original->in_request_tree) {
+                       /* We should never have conflicts */
+                       assert(rbtree_insert(request_tree, original));
+                       original->in_request_tree = true;
+               }
+
+               /*
+                *      Insert a callback to remove the request from the tree
+                */
+               original->packet->timestamp = header->ts;
+               rs_tv_add_ms(&header->ts, conf->stats.timeout, &original->when);
+               if (!fr_event_insert(event->list, _rs_event, original,
+                                    &original->when, &original->event)) {
+                       REDEBUG("Failed inserting new event");
+
+                       talloc_free(original);
+                       return;
+               }
+               response = false;
+               break;
+       }
+
+       default:
+               REDEBUG("Unsupported code %i", current->code);
+               rad_free(&current);
+
+               return;
+       }
+
+       if (event->out) {
+               pcap_dump((void *) (event->out->dumper), header, data);
+       }
+
+       rs_tv_sub(&header->ts, &start_pcap, &elapsed);
+
+       /*
+        *      Increase received count
+        */
+       stats->exchange[current->code].interval.received_total++;
+
+       /*
+        *      It's a linked response
+        */
+       if (original && original->linked) {
+               rs_tv_sub(&current->timestamp, &original->packet->timestamp, &latency);
+
+               /*
+                *      Update stats for both the request and response types.
+                *
+                *      This isn't useful for things like Access-Requests, but will be useful for
+                *      CoA and Disconnect Messages, as we get the average latency across both
+                *      response types.
+                *
+                *      It also justifies allocating PW_CODE_MAX instances of rs_latency_t.
+                */
+               rs_stats_update_latency(&stats->exchange[current->code], &latency);
+               rs_stats_update_latency(&stats->exchange[original->expect->code], &latency);
+
+               /*
+                *      Were filtering on response, now print out the full data from the request
+                */
+               if (conf->filter_response && RIDEBUG_ENABLED() && (conf->event_flags & RS_NORMAL)) {
+                       rs_time_print(timestr, sizeof(timestr), &original->packet->timestamp);
+                       rs_tv_sub(&original->packet->timestamp, &start_pcap, &elapsed);
+                       conf->logger(original->id, RS_NORMAL, original->in, original->packet, &elapsed, NULL, false, true);
+                       rs_tv_sub(&header->ts, &start_pcap, &elapsed);
+                       rs_time_print(timestr, sizeof(timestr), &header->ts);
+               }
+
+               if (conf->event_flags & status) {
+                       conf->logger(count, status, event->in, current, &elapsed, &latency, response, true);
+               }
+       /*
+        *      It's the original request
+        *
+        *      If were filtering on responses we can only indicate we received it on response, or timeout.
+        */
+       } else if (!conf->filter_response && (conf->event_flags & status)) {
+               conf->logger(original ? original->id : count, status, event->in,
+                            current, &elapsed, NULL, response, true);
+       }
+
+       fflush(fr_log_fp);
+
+       /*
+        *      If it's a unlinked response, we need to free it explicitly, as it will
+        *      not be done by the event queue.
+        */
+       if (response && !original) {
+               rad_free(&current);
+       }
+
+       captured++;
+       /*
+        *      We've hit our capture limit, break out of the event loop
+        */
+       if ((conf->limit > 0) && (captured >= conf->limit)) {
+               INFO("Captured %" PRIu64 " packets, exiting...", captured);
+               fr_event_loop_exit(events, 1);
+       }
+}
+
+static void rs_got_packet(UNUSED fr_event_list_t *el, int fd, void *ctx)
+{
+       static uint64_t count = 0;      /* Packets seen */
+       rs_event_t      *event = ctx;
+       pcap_t          *handle = event->in->handle;
+
+       int i;
+       int ret;
+       const uint8_t *data;
+       struct pcap_pkthdr *header;
+
+       /*
+        *      Consume entire capture, interleaving not currently possible
+        */
+       if ((event->in->type == PCAP_FILE_IN) || (event->in->type == PCAP_STDIO_IN)) {
+               while (!fr_event_loop_exiting(el)) {
+                       struct timeval now;
+
+                       ret = pcap_next_ex(handle, &header, &data);
+                       if (ret == 0) {
+                               /* No more packets available at this time */
+                               return;
+                       }
+                       if (ret == -2) {
+                               DEBUG("Done reading packets (%s)", event->in->name);
+                               fr_event_fd_delete(events, 0, fd);
+
+                               /* Signal pipe takes one slot which is why this is == 1 */
+                               if (fr_event_list_num_fds(events) == 1) {
+                                       fr_event_loop_exit(events, 1);
+                               }
+
+                               return;
+                       }
+                       if (ret < 0) {
+                               ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
+                               return;
+                       }
+
+                       do {
+                               now = header->ts;
+                       } while (fr_event_run(el, &now) == 1);
+                       count++;
+
+                       rs_packet_process(count, event, header, data);
+               }
+               return;
+       }
+
+       /*
+        *      Consume multiple packets from the capture buffer.
+        *      We occasionally need to yield to allow events to run.
+        */
+       for (i = 0; i < RS_FORCE_YIELD; i++) {
+               ret = pcap_next_ex(handle, &header, &data);
+               if (ret == 0) {
+                       /* No more packets available at this time */
+                       return;
+               }
+               if (ret < 0) {
+                       ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle));
+                       return;
+               }
+
+               count++;
+               rs_packet_process(count, event, header, data);
+       }
+}
+
+static void _rs_event_status(struct timeval *wake)
+{
+       if (wake && ((wake->tv_sec != 0) || (wake->tv_usec >= 100000))) {
+               DEBUG2("Waking up in %d.%01u seconds.", (int) wake->tv_sec, (unsigned int) wake->tv_usec / 100000);
+
+               if (RIDEBUG_ENABLED()) {
+                       rs_time_print(timestr, sizeof(timestr), wake);
+               }
+       }
+}
+
+/** Compare requests using packet info and lists of attributes
+ *
+ */
+static int rs_rtx_cmp(rs_request_t const *a, rs_request_t const *b)
+{
+       int rcode;
+
+       assert(a->link_vps);
+       assert(b->link_vps);
+
+       rcode = (int) a->expect->code - (int) b->expect->code;
+       if (rcode != 0) return rcode;
+
+       rcode = a->expect->sockfd - b->expect->sockfd;
+       if (rcode != 0) return rcode;
+
+       rcode = fr_ipaddr_cmp(&a->expect->src_ipaddr, &b->expect->src_ipaddr);
+       if (rcode != 0) return rcode;
+
+       rcode = fr_ipaddr_cmp(&a->expect->dst_ipaddr, &b->expect->dst_ipaddr);
+       if (rcode != 0) return rcode;
+
+       return pairlistcmp(a->link_vps, b->link_vps);
+}
+
+static int rs_build_dict_list(DICT_ATTR const **out, size_t len, char *list)
+{
+       size_t i = 0;
+       char *p, *tok;
+
+       p = list;
+       while ((tok = strsep(&p, "\t ,")) != NULL) {
+               DICT_ATTR const *da;
+               if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
+                       continue;
+               }
+
+               if (i == len) {
+                       ERROR("Too many attributes, maximum allowed is %zu", len);
+                       return -1;
+               }
+
+               da = dict_attrbyname(tok);
+               if (!da) {
+                       ERROR("Error parsing attribute name \"%s\"", tok);
+                       return -1;
+               }
+
+               out[i] = da;
+               i++;
+       }
+
+       /*
+        *      This allows efficient list comparisons later
+        */
+       if (i > 1) fr_quick_sort((void const **)out, 0, i - 1, fr_pointer_cmp);
+
+       return i;
+}
+
+static int rs_build_filter(VALUE_PAIR **out, char const *filter)
+{
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
+       FR_TOKEN code;
+
+       code = userparse(conf, filter, out);
+       if (code == T_OP_INVALID) {
+               ERROR("Invalid RADIUS filter \"%s\" (%s)", filter, fr_strerror());
+               return -1;
+       }
+
+       if (!*out) {
+               ERROR("Empty RADIUS filter '%s'", filter);
+               return -1;
+       }
+
+       for (vp = fr_cursor_init(&cursor, out);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               /*
+                *      xlat expansion isn't support here
+                */
+               if (vp->type == VT_XLAT) {
+                       vp->type = VT_DATA;
+                       vp->vp_strvalue = vp->value.xlat;
+               }
        }
 
-       INFO(log_dst, "%s Id %d\t", fr_packet_codes[packet->code], packet->id);
-
        /*
-        *  Print the RADIUS packet
+        *      This allows efficient list comparisons later
         */
-       INFO(log_dst, "%s:%d -> ", inet_ntoa(ip->ip_src), ntohs(udp->udp_sport));
-       INFO(log_dst, "%s:%d", inet_ntoa(ip->ip_dst), ntohs(udp->udp_dport));
+       pairsort(out, attrtagcmp);
 
-       DEBUG1(log_dst, "\t(%d packets)", count++);
+       return 0;
+}
 
-       if (!start_pcap.tv_sec) {
-               start_pcap = header->ts;
-       }
+static int rs_build_event_flags(int *flags, FR_NAME_NUMBER const *map, char *list)
+{
+       size_t i = 0;
+       char *p, *tok;
 
-       tv_sub(&header->ts, &start_pcap, &elapsed);
+       p = list;
+       while ((tok = strsep(&p, "\t ,")) != NULL) {
+               int flag;
 
-       INFO(log_dst, "\t+%u.%03u", (unsigned int) elapsed.tv_sec,
-              (unsigned int) elapsed.tv_usec / 1000);
+               if ((*tok == '\t') || (*tok == ' ') || (*tok == '\0')) {
+                       continue;
+               }
 
-       if (fr_debug_flag > 1) {
-               DEBUG(log_dst, "\n");
-               if (packet->vps) {
-                       if (do_sort) {
-                               pairsort(&packet->vps, true);
-                       }
-                       vp_printlist(log_dst, packet->vps);
-                       pairfree(&packet->vps);
+               *flags |= flag = fr_str2int(map, tok, -1);
+               if (flag < 0) {
+                       ERROR("Invalid flag \"%s\"", tok);
+                       return -1;
                }
+
+               i++;
        }
 
-       INFO(log_dst, "\n");
+       return i;
+}
+
+/** Callback for when the request is removed from the request tree
+ *
+ * @param request being removed.
+ */
+static void _unmark_request(void *request)
+{
+       rs_request_t *this = request;
+       this->in_request_tree = false;
+}
+
+/** Callback for when the request is removed from the link tree
+ *
+ * @param request being removed.
+ */
+static void _unmark_link(void *request)
+{
+       rs_request_t *this = request;
+       this->in_link_tree = false;
+}
+
+#ifdef HAVE_COLLECTDC_H
+/** Re-open the collectd socket
+ *
+ */
+static void rs_collectd_reopen(void *ctx)
+{
+       fr_event_list_t *list = ctx;
+       static fr_event_t *event;
+       struct timeval now, when;
 
-       if (!to_stdout && (fr_debug_flag > 4)) {
-               rad_print_hex(packet);
+       if (rs_stats_collectd_open(conf) == 0) {
+               DEBUG2("Stats output socket (re)opened");
+               return;
        }
 
-       fflush(log_dst);
+       ERROR("Will attempt to re-establish connection in %i ms", RS_SOCKET_REOPEN_DELAY);
 
- check_filter:
-       /*
-        *  If we're doing filtering, Access-Requests are cached in the
-        *  filter tree.
-        */
-       if (!filter_vps ||
-           ((packet->code != PW_AUTHENTICATION_REQUEST) &&
-            (packet->code != PW_ACCOUNTING_REQUEST))) {
-               rad_free(&packet);
+       gettimeofday(&now, NULL);
+       rs_tv_add_ms(&now, RS_SOCKET_REOPEN_DELAY, &when);
+       if (!fr_event_insert(list, rs_collectd_reopen, list, &when, &event)) {
+               ERROR("Failed inserting re-open event");
+               assert(0);
        }
 }
+#endif
 
+/** Write the last signal to the signal pipe
+ *
+ * @param sig raised
+ */
+static void rs_signal_self(int sig)
+{
+       if (write(self_pipe[1], &sig, sizeof(sig)) < 0) {
+               ERROR("Failed writing signal %s to pipe: %s", strsignal(sig), fr_syserror(errno));
+               exit(EXIT_FAILURE);
+       }
+}
 
-/** Wrapper function to allow rad_free to be called as an rbtree destructor callback
+/** Read the last signal from the signal pipe
  *
- * @param packet to free.
  */
-static void _rb_rad_free(void *packet)
+static void rs_signal_action(UNUSED fr_event_list_t *list, int fd, UNUSED void *ctx)
 {
-       rad_free((RADIUS_PACKET **) &packet);
+       int sig;
+       ssize_t ret;
+
+       ret = read(fd, &sig, sizeof(sig));
+       if (ret < 0) {
+               ERROR("Failed reading signal from pipe: %s", fr_syserror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       if (ret != sizeof(sig)) {
+               ERROR("Failed reading signal from pipe: "
+                     "Expected signal to be %zu bytes but only read %zu byes", sizeof(sig), ret);
+               exit(EXIT_FAILURE);
+       }
+
+       switch (sig) {
+#ifdef HAVE_COLLECTDC_H
+       case SIGPIPE:
+               rs_collectd_reopen(list);
+               break;
+#endif
+
+       case SIGINT:
+       case SIGTERM:
+       case SIGQUIT:
+               DEBUG2("Signalling event loop to exit");
+               fr_event_loop_exit(events, 1);
+               break;
+
+       default:
+               ERROR("Unhandled signal %s", strsignal(sig));
+               exit(EXIT_FAILURE);
+       }
 }
 
 static void NEVER_RETURNS usage(int status)
 {
        FILE *output = status ? stderr : stdout;
-       fprintf(output, "Usage: radsniff [options]\n");
+       fprintf(output, "Usage: radsniff [options][stats options] -- [pcap files]\n");
        fprintf(output, "options:\n");
-       fprintf(output, "  -c <count>      Number of packets to capture.\n");
-       fprintf(output, "  -d <directory>  Set dictionary directory.\n");
-       fprintf(output, "  -F              Filter PCAP file from stdin to stdout.\n");
-       fprintf(output, "  -f <filter>     PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
-       fprintf(output, "  -h              This help message.\n");
-       fprintf(output, "  -i <interface>  Capture packets from interface (defaults to any if supported).\n");
-       fprintf(output, "  -I <file>       Read packets from file (overrides input of -F).\n");
-       fprintf(output, "  -p <port>       Filter packets by port (default is 1812).\n");
-       fprintf(output, "  -q              Print less debugging information.\n");
-       fprintf(output, "  -r <filter>     RADIUS attribute filter.\n");
-       fprintf(output, "  -s <secret>     RADIUS secret.\n");
-       fprintf(output, "  -S              Sort attributes in the packet (useful for diffing responses).\n");
-       fprintf(output, "  -v              Show program version information.\n");
-       fprintf(output, "  -w <file>       Write output packets to file (overrides output of -F).\n");
-       fprintf(output, "  -x              Print more debugging information (defaults to -xx).\n");
+       fprintf(output, "  -a                    List all interfaces available for capture.\n");
+       fprintf(output, "  -c <count>            Number of packets to capture.\n");
+       fprintf(output, "  -C                    Enable UDP checksum validation.\n");
+       fprintf(output, "  -d <directory>        Set dictionary directory.\n");
+       fprintf(output, "  -d <raddb>            Set configuration directory (defaults to " RADDBDIR ").\n");
+       fprintf(output, "  -D <dictdir>          Set main dictionary directory (defaults to " DICTDIR ").\n");
+       fprintf(output, "  -e <event>[,<event>]  Only log requests with these event flags.\n");
+       fprintf(output, "                        Event may be one of the following:\n");
+       fprintf(output, "                        - received - a request or response.\n");
+       fprintf(output, "                        - norsp    - seen for a request.\n");
+       fprintf(output, "                        - rtx      - of a request that we've seen before.\n");
+       fprintf(output, "                        - noreq    - could be matched with the response.\n");
+       fprintf(output, "                        - reused   - ID too soon.\n");
+       fprintf(output, "                        - error    - decoding the packet.\n");
+       fprintf(output, "  -f <filter>           PCAP filter (default is 'udp port <port> or <port + 1> or 3799')\n");
+       fprintf(output, "  -h                    This help message.\n");
+       fprintf(output, "  -i <interface>        Capture packets from interface (defaults to all if supported).\n");
+       fprintf(output, "  -I <file>             Read packets from file (overrides input of -F).\n");
+       fprintf(output, "  -l <attr>[,<attr>]    Output packet sig and a list of attributes.\n");
+       fprintf(output, "  -L <attr>[,<attr>]    Detect retransmissions using these attributes to link requests.\n");
+       fprintf(output, "  -m                    Don't put interface(s) into promiscuous mode.\n");
+       fprintf(output, "  -p <port>             Filter packets by port (default is 1812).\n");
+       fprintf(output, "  -P <pidfile>          Daemonize and write out <pidfile>.\n");
+       fprintf(output, "  -q                    Print less debugging information.\n");
+       fprintf(output, "  -r <filter>           RADIUS attribute request filter.\n");
+       fprintf(output, "  -R <filter>           RADIUS attribute response filter.\n");
+       fprintf(output, "  -s <secret>           RADIUS secret.\n");
+       fprintf(output, "  -S                    Write PCAP data to stdout.\n");
+       fprintf(output, "  -v                    Show program version information.\n");
+       fprintf(output, "  -w <file>             Write output packets to file.\n");
+       fprintf(output, "  -x                    Print more debugging information.\n");
+       fprintf(output, "stats options:\n");
+       fprintf(output, "  -W <interval>         Periodically write out statistics every <interval> seconds.\n");
+       fprintf(output, "  -T <timeout>          How many milliseconds before the request is counted as lost "
+               "(defaults to %i).\n", RS_DEFAULT_TIMEOUT);
+#ifdef HAVE_COLLECTDC_H
+       fprintf(output, "  -N <prefix>           The instance name passed to the collectd plugin.\n");
+       fprintf(output, "  -O <server>           Write statistics to this collectd server.\n");
+#endif
        exit(status);
 }
 
 int main(int argc, char *argv[])
 {
-       char const *from_dev = NULL;                    /* Capture from device */
-       char const *from_file = NULL;                   /* Read from pcap file */
-       int from_stdin = 0;                             /* Read from stdin */
+       fr_pcap_t *in = NULL, *in_p;
+       fr_pcap_t **in_head = &in;
+       fr_pcap_t *out = NULL;
 
-       pcap_t *in = NULL;                              /* PCAP input handle */
-
-       int limit = -1;                                 /* How many packets to sniff */
+       int ret = 1;                                    /* Exit status */
 
        char errbuf[PCAP_ERRBUF_SIZE];                  /* Error buffer */
-
-       char *to_file = NULL;                           /* PCAP output file */
-
-       char *pcap_filter = NULL;                       /* PCAP filter string */
-       char *radius_filter = NULL;
        int port = 1812;
 
-       struct bpf_program fp;                          /* Holds compiled filter */
-       bpf_u_int32 ip_mask = PCAP_NETMASK_UNKNOWN;     /* Device Subnet mask */
-       bpf_u_int32 ip_addr = 0;                        /* Device IP */
-
        char buffer[1024];
 
        int opt;
-       FR_TOKEN parsecode;
-       char const *radius_dir = RADIUS_DIR;
+       char const *radius_dir = RADDBDIR;
+       char const *dict_dir = DICTDIR;
 
-       fr_debug_flag = 2;
-       log_dst = stdout;
+       rs_stats_t stats;
+
+       fr_debug_flag = 1;
+       fr_log_fp = stdout;
+
+       /*
+        *      Useful if using radsniff as a long running stats daemon
+        */
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radsniff");
+               exit(EXIT_FAILURE);
+       }
+#endif
 
        talloc_set_log_stderr();
 
+       conf = talloc_zero(NULL, rs_t);
+       if (!fr_assert(conf)) {
+               exit (1);
+       }
+
+       /*
+        *  We don't really want probes taking down machines
+        */
+#ifdef HAVE_TALLOC_SET_MEMLIMIT
+       /*
+        *      @fixme causes hang in talloc steal
+        */
+        //talloc_set_memlimit(conf, 524288000);                /* 50 MB */
+#endif
+
+       /*
+        *      Set some defaults
+        */
+       conf->print_packet = true;
+       conf->limit = 0;
+       conf->promiscuous = true;
+#ifdef HAVE_COLLECTDC_H
+       conf->stats.prefix = RS_DEFAULT_PREFIX;
+#endif
+       conf->radius_secret = RS_DEFAULT_SECRET;
+       conf->logger = rs_packet_print_null;
+
+#ifdef HAVE_COLLECTDC_H
+       conf->stats.prefix = RS_DEFAULT_PREFIX;
+#endif
+
        /*
         *  Get options
         */
-       while ((opt = getopt(argc, argv, "c:d:Ff:hi:I:p:qr:s:Svw:xX")) != EOF) {
+       while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
                switch (opt) {
+               case 'a':
+               {
+                       pcap_if_t *all_devices = NULL;
+                       pcap_if_t *dev_p;
+
+                       if (pcap_findalldevs(&all_devices, errbuf) < 0) {
+                               ERROR("Error getting available capture devices: %s", errbuf);
+                               goto finish;
+                       }
+
+                       int i = 1;
+                       for (dev_p = all_devices;
+                            dev_p;
+                            dev_p = dev_p->next) {
+                               INFO("%i.%s", i++, dev_p->name);
+                       }
+                       ret = 0;
+                       goto finish;
+               }
+
+               /* super secret option */
+               case 'b':
+                       conf->buffer_pkts = atoi(optarg);
+                       if (conf->buffer_pkts == 0) {
+                               ERROR("Invalid buffer length \"%s\"", optarg);
+                               usage(1);
+                       }
+                       break;
+
                case 'c':
-                       limit = atoi(optarg);
-                       if (limit <= 0) {
-                               fprintf(stderr, "radsniff: Invalid number of packets \"%s\"\n", optarg);
-                               exit(1);
+                       conf->limit = atoi(optarg);
+                       if (conf->limit == 0) {
+                               ERROR("Invalid number of packets \"%s\"", optarg);
+                               usage(1);
                        }
                        break;
+
+               /* udp checksum */
+               case 'C':
+                       conf->verify_udp_checksum = true;
+                       break;
+
                case 'd':
                        radius_dir = optarg;
                        break;
-               case 'F':
-                       from_stdin = 1;
-                       to_stdout = 1;
+
+               case 'D':
+                       dict_dir = optarg;
+                       break;
+
+               case 'e':
+                       if (rs_build_event_flags((int *) &conf->event_flags, rs_events, optarg) < 0) {
+                               usage(64);
+                       }
                        break;
+
                case 'f':
-                       pcap_filter = optarg;
+                       conf->pcap_filter = optarg;
                        break;
+
                case 'h':
                        usage(0);
                        break;
+
                case 'i':
-                       from_dev = optarg;
+                       *in_head = fr_pcap_init(conf, optarg, PCAP_INTERFACE_IN);
+                       if (!*in_head) {
+                               goto finish;
+                       }
+                       in_head = &(*in_head)->next;
+                       conf->from_dev = true;
                        break;
+
                case 'I':
-                       from_file = optarg;
+                       *in_head = fr_pcap_init(conf, optarg, PCAP_FILE_IN);
+                       if (!*in_head) {
+                               goto finish;
+                       }
+                       in_head = &(*in_head)->next;
+                       conf->from_file = true;
+                       break;
+
+               case 'l':
+                       conf->list_attributes = optarg;
+                       break;
+
+               case 'L':
+                       conf->link_attributes = optarg;
+                       break;
+
+               case 'm':
+                       conf->promiscuous = false;
                        break;
+
                case 'p':
                        port = atoi(optarg);
                        break;
+
+               case 'P':
+                       conf->daemonize = true;
+                       conf->pidfile = optarg;
+                       break;
+
                case 'q':
                        if (fr_debug_flag > 0) {
                                fr_debug_flag--;
                        }
                        break;
+
                case 'r':
-                       radius_filter = optarg;
+                       conf->filter_request = optarg;
                        break;
+
+               case 'R':
+                       conf->filter_response = optarg;
+                       break;
+
                case 's':
-                       radius_secret = optarg;
+                       conf->radius_secret = optarg;
                        break;
+
                case 'S':
-                       do_sort = 1;
+                       conf->to_stdout = true;
                        break;
+
                case 'v':
-                       INFO(log_dst, "%s %s\n", radsniff_version, pcap_lib_version());
-                       exit(0);
+#ifdef HAVE_COLLECTDC_H
+                       INFO("%s, %s, collectdclient version %s", radsniff_version, pcap_lib_version(),
+                            lcc_version_string());
+#else
+                       INFO("%s %s", radsniff_version, pcap_lib_version());
+#endif
+                       exit(EXIT_SUCCESS);
                        break;
+
                case 'w':
-                       to_file = optarg;
+                       out = fr_pcap_init(conf, optarg, PCAP_FILE_OUT);
+                       if (!out) {
+                               ERROR("Failed creating pcap file \"%s\"", optarg);
+                               exit(EXIT_FAILURE);
+                       }
+                       conf->to_file = true;
                        break;
+
                case 'x':
                case 'X':
-                       fr_debug_flag++;
+                       fr_debug_flag++;
+                       break;
+
+               case 'W':
+                       conf->stats.interval = atoi(optarg);
+                       conf->print_packet = false;
+                       if (conf->stats.interval <= 0) {
+                               ERROR("Stats interval must be > 0");
+                               usage(64);
+                       }
+                       break;
+
+               case 'T':
+                       conf->stats.timeout = atoi(optarg);
+                       if (conf->stats.timeout <= 0) {
+                               ERROR("Timeout value must be > 0");
+                               usage(64);
+                       }
+                       break;
+
+#ifdef HAVE_COLLECTDC_H
+               case 'N':
+                       conf->stats.prefix = optarg;
+                       break;
+
+               case 'O':
+                       conf->stats.collectd = optarg;
+                       conf->stats.out = RS_STATS_OUT_COLLECTD;
                        break;
+#endif
                default:
                        usage(64);
                }
        }
 
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radsniff");
+               exit(EXIT_FAILURE);
+       }
+
+       /* Useful for file globbing */
+       while (optind < argc) {
+               *in_head = fr_pcap_init(conf, argv[optind], PCAP_FILE_IN);
+               if (!*in_head) {
+                       goto finish;
+               }
+               in_head = &(*in_head)->next;
+               conf->from_file = true;
+               optind++;
+       }
+
+       /* Is stdin not a tty? If so it's probably a pipe */
+       if (!isatty(fileno(stdin))) {
+               conf->from_stdin = true;
+       }
+
        /* What's the point in specifying -F ?! */
-       if (from_stdin && from_file && to_file) {
+       if (conf->from_stdin && conf->from_file && conf->to_file) {
                usage(64);
        }
 
        /* Can't read from both... */
-       if (from_file && from_dev) {
+       if (conf->from_file && conf->from_dev) {
                usage(64);
        }
 
        /* Reading from file overrides stdin */
-       if (from_stdin && (from_file || from_dev)) {
-               from_stdin = 0;
+       if (conf->from_stdin && (conf->from_file || conf->from_dev)) {
+               conf->from_stdin = false;
        }
 
        /* Writing to file overrides stdout */
-       if (to_file && to_stdout) {
-               to_stdout = 0;
+       if (conf->to_file && conf->to_stdout) {
+               conf->to_stdout = false;
+       }
+
+       if (conf->to_stdout) {
+               out = fr_pcap_init(conf, "stdout", PCAP_STDIO_OUT);
+               if (!out) {
+                       goto finish;
+               }
+       }
+
+       if (conf->from_stdin) {
+               *in_head = fr_pcap_init(conf, "stdin", PCAP_STDIO_IN);
+               if (!*in_head) {
+                       goto finish;
+               }
+               in_head = &(*in_head)->next;
+       }
+
+       if (conf->stats.interval && !conf->stats.out) {
+               conf->stats.out = RS_STATS_OUT_STDIO;
+       }
+
+       if (conf->stats.timeout == 0) {
+               conf->stats.timeout = RS_DEFAULT_TIMEOUT;
        }
 
        /*
-        *  If were writing pcap data stdout we *really* don't want to send
-        *  logging there as well.
+        *      If were writing pcap data, or CSV to stdout we *really* don't want to send
+        *      logging there as well.
         */
-       log_dst = to_stdout ? stderr : stdout;
+       if (conf->to_stdout || conf->list_attributes) {
+               fr_log_fp = stderr;
+       }
+
+       if (conf->list_attributes) {
+               conf->logger = rs_packet_print_csv;
+       } else if (fr_debug_flag > 0) {
+               conf->logger = rs_packet_print_fancy;
+       }
 
 #if !defined(HAVE_PCAP_FOPEN_OFFLINE) || !defined(HAVE_PCAP_DUMP_FOPEN)
-       if (from_stdin || to_stdout) {
-               fprintf(stderr, "radsniff: PCAP streams not supported.\n");
-               exit(64);
+       if (conf->from_stdin || conf->to_stdout) {
+               ERROR("PCAP streams not supported");
+               goto finish;
        }
 #endif
 
-       if (!pcap_filter) {
-               pcap_filter = buffer;
+       if (!conf->pcap_filter) {
                snprintf(buffer, sizeof(buffer), "udp port %d or %d or %d",
                         port, port + 1, 3799);
+               conf->pcap_filter = buffer;
        }
 
-       /*
-        *  There are times when we don't need the dictionaries.
-        */
-       if (!to_stdout) {
-               if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-                       fr_perror("radsniff");
-                       exit(64);
+       if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
+               fr_perror("radsniff");
+               ret = 64;
+               goto finish;
+       }
+
+       if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
+               fr_perror("radsniff");
+               ret = 64;
+               goto finish;
+       }
+
+       fr_strerror();  /* Clear out any non-fatal errors */
+
+       if (conf->list_attributes) {
+               conf->list_da_num = rs_build_dict_list(conf->list_da, sizeof(conf->list_da) / sizeof(*conf->list_da),
+                                                      conf->list_attributes);
+               if (conf->list_da_num < 0) {
+                       usage(64);
+               }
+               rs_packet_print_csv_header();
+       }
+
+       if (conf->link_attributes) {
+               conf->link_da_num = rs_build_dict_list(conf->link_da, sizeof(conf->link_da) / sizeof(*conf->link_da),
+                                                      conf->link_attributes);
+               if (conf->link_da_num < 0) {
+                       usage(64);
+               }
+
+               link_tree = rbtree_create((rbcmp) rs_rtx_cmp, _unmark_link, 0);
+               if (!link_tree) {
+                       ERROR("Failed creating RTX tree");
+                       goto finish;
                }
        }
 
-       if (radius_filter) {
-               parsecode = userparse(NULL, radius_filter, &filter_vps);
-               if (parsecode == T_OP_INVALID) {
-                       fprintf(stderr, "radsniff: Invalid RADIUS filter \"%s\" (%s)\n", radius_filter, fr_strerror());
-                       exit(64);
+       if (conf->filter_request) {
+               vp_cursor_t cursor;
+               VALUE_PAIR *type;
+
+               if (rs_build_filter(&conf->filter_request_vps, conf->filter_request) < 0) {
+                       usage(64);
+               }
+
+               fr_cursor_init(&cursor, &conf->filter_request_vps);
+               type = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
+               if (type) {
+                       fr_cursor_remove(&cursor);
+                       conf->filter_request_code = type->vp_integer;
+                       talloc_free(type);
                }
+       }
 
-               if (!filter_vps) {
-                       fprintf(stderr, "radsniff: Empty RADIUS filter \"%s\"\n", radius_filter);
-                       exit(64);
+       if (conf->filter_response) {
+               vp_cursor_t cursor;
+               VALUE_PAIR *type;
+
+               if (rs_build_filter(&conf->filter_response_vps, conf->filter_response) < 0) {
+                       usage(64);
                }
 
-               filter_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
-               if (!filter_tree) {
-                       fprintf(stderr, "radsniff: Failed creating filter tree\n");
-                       exit(1);
+               fr_cursor_init(&cursor, &conf->filter_response_vps);
+               type = fr_cursor_next_by_num(&cursor, PW_PACKET_TYPE, 0, TAG_ANY);
+               if (type) {
+                       fr_cursor_remove(&cursor);
+                       conf->filter_response_code = type->vp_integer;
+                       talloc_free(type);
                }
        }
 
        /*
-        *  Setup the request tree
+        *      Default to logging and capturing all events
         */
-       request_tree = rbtree_create((rbcmp) fr_packet_cmp, _rb_rad_free, 0);
-       if (!request_tree) {
-               fprintf(stderr, "radsniff: Failed creating request tree\n");
-               exit(1);
+       if (conf->event_flags == 0) {
+               DEBUG("Logging all events");
+               memset(&conf->event_flags, 0xff, sizeof(conf->event_flags));
+       }
+
+       /*
+        *      If we need to list attributes, link requests using attributes, filter attributes
+        *      or print the packet contents, we need to decode the attributes.
+        *
+        *      But, if were just logging requests, or graphing packets, we don't need to decode
+        *      attributes.
+        */
+       if (conf->list_da_num || conf->link_da_num || conf->filter_response_vps || conf->filter_request_vps ||
+           conf->print_packet) {
+               conf->decode_attrs = true;
        }
 
        /*
-        *  Allocate a null packet for decrypting attributes in CoA requests
+        *      Setup the request tree
         */
-       nullpacket = rad_alloc(NULL, 0);
-       if (!nullpacket) {
-               fprintf(stderr, "radsniff: Out of memory\n");
-               exit(1);
+       request_tree = rbtree_create((rbcmp) rs_packet_cmp, _unmark_request, 0);
+       if (!request_tree) {
+               ERROR("Failed creating request tree");
+               goto finish;
        }
 
        /*
-        *  Get the default capture device
+        *      Get the default capture device
         */
-       if (!from_stdin && !from_file && !from_dev) {
-               from_dev = pcap_lookupdev(errbuf);
-               if (!from_dev) {
-                       fprintf(stderr, "radsniff: Failed discovering default interface (%s)\n", errbuf);
-                       exit(1);
+       if (!conf->from_stdin && !conf->from_file && !conf->from_dev) {
+               pcap_if_t *all_devices;                 /* List of all devices libpcap can listen on */
+               pcap_if_t *dev_p;
+
+               if (pcap_findalldevs(&all_devices, errbuf) < 0) {
+                       ERROR("Error getting available capture devices: %s", errbuf);
+                       goto finish;
+               }
+
+               if (!all_devices) {
+                       ERROR("No capture files specified and no live interfaces available");
+                       ret = 64;
+                       goto finish;
                }
 
-               INFO(log_dst, "Capturing from interface \"%s\"\n", from_dev);
+               for (dev_p = all_devices;
+                    dev_p;
+                    dev_p = dev_p->next) {
+                       /* Don't use the any devices, it's horribly broken */
+                       if (!strcmp(dev_p->name, "any")) continue;
+                       *in_head = fr_pcap_init(conf, dev_p->name, PCAP_INTERFACE_IN);
+                       in_head = &(*in_head)->next;
+               }
+               conf->from_auto = true;
+               conf->from_dev = true;
+               INFO("Defaulting to capture on all interfaces");
        }
 
        /*
-        *  Print captures values which will be used
+        *      Print captures values which will be used
         */
        if (fr_debug_flag > 2) {
-                               DEBUG1(log_dst, "Sniffing with options:\n");
-               if (from_dev)   DEBUG1(log_dst, "  Device                   : [%s]\n", from_dev);
-               if (limit > 0)  DEBUG1(log_dst, "  Capture limit (packets)  : [%d]\n", limit);
-                               DEBUG1(log_dst, "  PCAP filter              : [%s]\n", pcap_filter);
-                               DEBUG1(log_dst, "  RADIUS secret            : [%s]\n", radius_secret);
-               if (filter_vps){DEBUG1(log_dst, "  RADIUS filter            :\n");
-                       vp_printlist(log_dst, filter_vps);
+               DEBUG2("Sniffing with options:");
+               if (conf->from_dev)     {
+                       char *buff = fr_pcap_device_names(conf, in, ' ');
+                       DEBUG2("  Device(s)               : [%s]", buff);
+                       talloc_free(buff);
+               }
+               if (out) {
+                       DEBUG2("  Writing to              : [%s]", out->name);
+               }
+               if (conf->limit > 0)    {
+                       DEBUG2("  Capture limit (packets) : [%" PRIu64 "]", conf->limit);
+               }
+                       DEBUG2("  PCAP filter             : [%s]", conf->pcap_filter);
+                       DEBUG2("  RADIUS secret           : [%s]", conf->radius_secret);
+
+               if (conf->filter_request_code) {
+                       DEBUG2("  RADIUS request code     : [%s]", fr_packet_codes[conf->filter_request_code]);
+               }
+
+               if (conf->filter_request_vps){
+                       DEBUG2("  RADIUS request filter   :");
+                       vp_printlist(fr_log_fp, conf->filter_request_vps);
+               }
+
+               if (conf->filter_response_code) {
+                       DEBUG2("  RADIUS response code    : [%s]", fr_packet_codes[conf->filter_response_code]);
+               }
+
+               if (conf->filter_response_vps){
+                       DEBUG2("  RADIUS response filter  :");
+                       vp_printlist(fr_log_fp, conf->filter_response_vps);
                }
        }
 
        /*
-        *  Figure out whether were doing a reading from a file, doing a live
-        *  capture or reading from stdin.
+        *      Setup collectd templates
         */
-       if (from_file) {
-               in = pcap_open_offline(from_file, errbuf);
-#ifdef HAVE_PCAP_FOPEN_OFFLINE
-       } else if (from_stdin) {
-               in = pcap_fopen_offline(stdin, errbuf);
-#endif
-       } else if (from_dev) {
-               pcap_lookupnet(from_dev, &ip_addr, &ip_mask, errbuf);
-               in = pcap_open_live(from_dev, 65536, 1, 1, errbuf);
-       } else {
-               fprintf(stderr, "radsniff: No capture devices available\n");
+#ifdef HAVE_COLLECTDC_H
+       if (conf->stats.out == RS_STATS_OUT_COLLECTD) {
+               size_t i;
+               rs_stats_tmpl_t *tmpl, **next;
+
+               if (rs_stats_collectd_open(conf) < 0) {
+                       exit(EXIT_FAILURE);
+               }
+
+               next = &conf->stats.tmpl;
+
+               for (i = 0; i < (sizeof(rs_useful_codes) / sizeof(*rs_useful_codes)); i++) {
+                       tmpl = rs_stats_collectd_init_latency(conf, next, conf, "exchanged",
+                                                             &stats.exchange[rs_useful_codes[i]],
+                                                             rs_useful_codes[i]);
+                       if (!tmpl) {
+                               ERROR("Error allocating memory for stats template");
+                               goto finish;
+                       }
+                       next = &(tmpl->next);
+               }
        }
+#endif
 
-       if (!in) {
-               fprintf(stderr, "radsniff: Failed opening input (%s)\n", errbuf);
-               exit(1);
+       /*
+        *      This actually opens the capture interfaces/files (we just allocated the memory earlier)
+        */
+       {
+               fr_pcap_t *tmp;
+               fr_pcap_t **tmp_p = &tmp;
+
+               for (in_p = in;
+                    in_p;
+                    in_p = in_p->next) {
+                       in_p->promiscuous = conf->promiscuous;
+                       in_p->buffer_pkts = conf->buffer_pkts;
+                       if (fr_pcap_open(in_p) < 0) {
+                               ERROR("Failed opening pcap handle (%s): %s", in_p->name, fr_strerror());
+                               if (conf->from_auto || (in_p->type == PCAP_FILE_IN)) {
+                                       continue;
+                               }
+
+                               goto finish;
+                       }
+
+                       if (conf->pcap_filter) {
+                               if (fr_pcap_apply_filter(in_p, conf->pcap_filter) < 0) {
+                                       ERROR("Failed applying filter");
+                                       goto finish;
+                               }
+                       }
+
+                       *tmp_p = in_p;
+                       tmp_p = &(in_p->next);
+               }
+               *tmp_p = NULL;
+               in = tmp;
+
+               if (!in) {
+                       ERROR("No PCAP sources available");
+                       exit(EXIT_FAILURE);
+               }
+
+               /* Clear any irrelevant errors */
+               fr_strerror();
        }
 
-       if (to_file) {
-               out = pcap_dump_open(in, to_file);
-               if (!out) {
-                       fprintf(stderr, "radsniff: Failed opening output file (%s)\n", pcap_geterr(in));
-                       exit(1);
+       /*
+        *      Open our output interface (if we have one);
+        */
+       if (out) {
+               out->link_type = -1;    /* Infer output link type from input */
+
+               for (in_p = in;
+                    in_p;
+                    in_p = in_p->next) {
+                       if (out->link_type < 0) {
+                               out->link_type = in_p->link_type;
+                               continue;
+                       }
+
+                       if (out->link_type != in_p->link_type) {
+                               ERROR("Asked to write to output file, but inputs do not have the same link type");
+                               ret = 64;
+                               goto finish;
+                       }
                }
-#ifdef HAVE_PCAP_DUMP_FOPEN
-       } else if (to_stdout) {
-               out = pcap_dump_fopen(in, stdout);
-               if (!out) {
-                       fprintf(stderr, "radsniff: Failed opening stdout (%s)\n", pcap_geterr(in));
-                       exit(1);
+
+               assert(out->link_type > 0);
+
+               if (fr_pcap_open(out) < 0) {
+                       ERROR("Failed opening pcap output (%s): %s", out->name, fr_strerror());
+                       goto finish;
                }
-#endif
        }
 
        /*
-        *  Apply the rules
+        *      Setup and enter the main event loop. Who needs libev when you can roll your own...
         */
-       if (pcap_compile(in, &fp, pcap_filter, 0, ip_mask) < 0) {
-               fprintf(stderr, "radsniff: Failed compiling PCAP filter (%s)\n", pcap_geterr(in));
-               exit(1);
+        {
+               struct timeval now;
+               rs_update_t update;
+
+               char *buff;
+
+               memset(&stats, 0, sizeof(stats));
+               memset(&update, 0, sizeof(update));
+
+               events = fr_event_list_create(conf, _rs_event_status);
+               if (!events) {
+                       ERROR();
+                       goto finish;
+               }
+
+               /*
+                *  Initialise the signal handler pipe
+                */
+               if (pipe(self_pipe) < 0) {
+                       ERROR("Couldn't open signal pipe: %s", fr_syserror(errno));
+                       exit(EXIT_FAILURE);
+               }
+
+               if (!fr_event_fd_insert(events, 0, self_pipe[0], rs_signal_action, events)) {
+                       ERROR("Failed inserting signal pipe descriptor: %s", fr_strerror());
+                       goto finish;
+               }
+
+               /*
+                *  Now add fd's for each of the pcap sessions we opened
+                */
+               for (in_p = in;
+                    in_p;
+                    in_p = in_p->next) {
+                       rs_event_t *event;
+
+                       event = talloc_zero(events, rs_event_t);
+                       event->list = events;
+                       event->in = in_p;
+                       event->out = out;
+                       event->stats = &stats;
+
+                       if (!fr_event_fd_insert(events, 0, in_p->fd, rs_got_packet, event)) {
+                               ERROR("Failed inserting file descriptor");
+                               goto finish;
+                       }
+               }
+
+               buff = fr_pcap_device_names(conf, in, ' ');
+               DEBUG("Sniffing on (%s)", buff);
+               talloc_free(buff);
+
+               gettimeofday(&now, NULL);
+
+               /*
+                *  Insert our stats processor
+                */
+               if (conf->stats.interval) {
+                       static fr_event_t *event;
+
+                       update.list = events;
+                       update.stats = &stats;
+                       update.in = in;
+
+                       now.tv_sec += conf->stats.interval;
+                       now.tv_usec = 0;
+                       if (!fr_event_insert(events, rs_stats_process, (void *) &update, &now, &event)) {
+                               ERROR("Failed inserting stats event");
+                       }
+
+                       INFO("Muting stats for the next %i milliseconds (warmup)", conf->stats.timeout);
+                       rs_tv_add_ms(&now, conf->stats.timeout, &stats.quiet);
+               }
        }
 
-       if (pcap_setfilter(in, &fp) < 0) {
-               fprintf(stderr, "radsniff: Failed applying PCAP filter (%s)\n", pcap_geterr(in));
-               exit(1);
+
+       /*
+        *      Do this as late as possible so we can return an error code if something went wrong.
+        */
+       if (conf->daemonize) {
+               rs_daemonize(conf->pidfile);
        }
 
        /*
-        *  Enter the main capture loop...
+        *      Setup signal handlers so we always exit gracefully, ensuring output buffers are always
+        *      flushed.
         */
-       pcap_loop(in, limit, got_packet, NULL);
+       fr_set_signal(SIGPIPE, rs_signal_self);
+       fr_set_signal(SIGINT, rs_signal_self);
+       fr_set_signal(SIGTERM, rs_signal_self);
+#ifdef SIGQUIT
+       fr_set_signal(SIGQUIT, rs_signal_self);
+#endif
+
+       fr_event_loop(events);  /* Enter the main event loop */
+
+       DEBUG("Done sniffing");
+
+       finish:
+
+       cleanup = true;
 
        /*
-        *  ...were done capturing.
+        *      Free all the things! This also closes all the sockets and file descriptors
         */
-       pcap_close(in);
-       if (out) {
-               pcap_dump_close(out);
-       }
+       talloc_free(conf);
 
-       if (filter_tree) {
-               rbtree_free(filter_tree);
+       if (conf->daemonize) {
+               unlink(conf->pidfile);
        }
 
-       INFO(log_dst, "Done sniffing\n");
-
-       return 0;
+       return ret;
 }
index 03b9abd..3de7ebc 100644 (file)
@@ -6,7 +6,8 @@ else
 TARGET         :=
 endif
 
-SOURCES                := radsniff.c
+SOURCES                := radsniff.c collectd.c
 
 TGT_PREREQS    := libfreeradius-radius.a
-TGT_LDLIBS     := $(LIBS) $(PCAP_LIBS)
+TGT_LDLIBS     := $(LIBS) $(PCAP_LIBS) $(COLLECTDC_LIBS)
+TGT_LDFLAGS     := $(LDFLAGS) $(PCAP_LDFLAGS) $(COLLECTDC_LDFLAGS)
index 9f3f6b0..7f009ae 100644 (file)
@@ -57,11 +57,11 @@ do
                ;;
        -d) 
                OPTIONS="$OPTIONS -d $2"
-               shift;shift
+               shift;shift
                ;;
        -P) 
                OPTIONS="$OPTIONS -P $2"
-               shift;shift
+               shift;shift
                ;;
        -x)
                OPTIONS="$OPTIONS -x"
@@ -96,7 +96,7 @@ do
                shift
                ;;
 
-        *)
+       *)
                usage
                ;;
   esac
index fe69113..6bc75e7 100644 (file)
@@ -64,7 +64,7 @@ bool log_stripped_names;
 /*
  *     Global, for log.c to use.
  */
-struct main_config_t mainconfig;
+struct main_config_t main_config;
 
 #include <sys/wait.h>
 pid_t rad_fork(void)
@@ -80,11 +80,11 @@ pid_t rad_waitpid(pid_t pid, int *status)
 #endif
 
 struct radutmp_config_t {
-  char *radutmp_fn;
+  char const *radutmp_fn;
 } radutmpconfig;
 
 static const CONF_PARSER module_config[] = {
-  { "filename", PW_TYPE_FILE_INPUT, 0, &radutmpconfig.radutmp_fn,  RADUTMP },
+  { "filename", FR_CONF_POINTER(PW_TYPE_FILE_INPUT, &radutmpconfig.radutmp_fn), RADUTMP },
   { NULL, -1, 0, NULL, NULL }
 };
 
@@ -220,6 +220,13 @@ int main(int argc, char **argv)
 
        raddb_dir = RADIUS_DIR;
 
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("radwho");
+               exit(EXIT_FAILURE);
+       }
+#endif
+
        talloc_set_log_stderr();
 
        while((c = getopt(argc, argv, "d:fF:nN:sSipP:crRu:U:Z")) != EOF) switch(c) {
@@ -283,6 +290,14 @@ int main(int argc, char **argv)
        }
 
        /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radwho");
+               return 1;
+       }
+
+       /*
         *      Be safe.
         */
        if (zap && !radiusoutput) zap = 0;
@@ -306,9 +321,9 @@ int main(int argc, char **argv)
        if (radutmp_file) goto have_radutmp;
 
        /*
-        *      Initialize mainconfig
+        *      Initialize main_config
         */
-       memset(&mainconfig, 0, sizeof(mainconfig));
+       memset(&main_config, 0, sizeof(main_config));
 
        /* Read radiusd.conf */
        snprintf(buffer, sizeof(buffer), "%.200s/radiusd.conf", raddb_dir);
@@ -339,7 +354,7 @@ int main(int argc, char **argv)
         */
        if ((fp = fopen(radutmp_file, "r")) == NULL) {
                fprintf(stderr, "%s: Error reading %s: %s\n",
-                       progname, radutmp_file, strerror(errno));
+                       progname, radutmp_file, fr_syserror(errno));
                return 0;
        }
 
index a30189c..a06bb5c 100755 (executable)
@@ -5,9 +5,9 @@
 
 usage() {
        echo "Usage: radzap [options] server[:port] secret" >&2
-        echo "       -h Print usage help information."
-        echo "       -d raddb_directory: directory where radiusd.conf is located."
-        echo "       -N nas_ip_address: IP address of the NAS to zap."
+       echo "       -h Print usage help information."
+       echo "       -d raddb_directory: directory where radiusd.conf is located."
+       echo "       -N nas_ip_address: IP address of the NAS to zap."
        echo "       -P nas_port: NAS port that the user is logged into."
        echo "       -u username: Name of user to zap (case insensitive)."
        echo "       -U username: like -u, but case-sensitive."
index 278ee44..e339d90 100644 (file)
@@ -45,9 +45,9 @@ static realm_regex_t *realms_regex = NULL;
 
 typedef struct realm_config_t {
        CONF_SECTION    *cs;
-       int             dead_time;
-       int             retry_count;
-       int             retry_delay;
+       uint32_t        dead_time;
+       uint32_t        retry_count;
+       uint32_t        retry_delay;
        bool            fallback;
        bool            wake_all_if_all_dead;
 } realm_config_t;
@@ -68,25 +68,15 @@ static rbtree_t     *home_pools_byname = NULL;
  *  Map the proxy server configuration parameters to variables.
  */
 static const CONF_PARSER proxy_config[] = {
-       { "retry_delay",  PW_TYPE_INTEGER,
-         offsetof(realm_config_t, retry_delay),
-         NULL, STRINGIFY(RETRY_DELAY) },
+       { "retry_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, realm_config_t, retry_delay), STRINGIFY(RETRY_DELAY)  },
 
-       { "retry_count",  PW_TYPE_INTEGER,
-         offsetof(realm_config_t, retry_count),
-         NULL, STRINGIFY(RETRY_COUNT) },
+       { "retry_count", FR_CONF_OFFSET(PW_TYPE_INTEGER, realm_config_t, retry_count), STRINGIFY(RETRY_COUNT)  },
 
-       { "default_fallback", PW_TYPE_BOOLEAN,
-         offsetof(realm_config_t, fallback),
-         NULL, "no" },
+       { "default_fallback", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, realm_config_t, fallback), "no" },
 
-       { "dead_time",    PW_TYPE_INTEGER,
-         offsetof(realm_config_t, dead_time),
-         NULL, STRINGIFY(DEAD_TIME) },
+       { "dead_time", FR_CONF_OFFSET(PW_TYPE_INTEGER, realm_config_t, dead_time), STRINGIFY(DEAD_TIME)  },
 
-       { "wake_all_if_all_dead", PW_TYPE_BOOLEAN,
-         offsetof(realm_config_t, wake_all_if_all_dead),
-         NULL, "no" },
+       { "wake_all_if_all_dead", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, realm_config_t, wake_all_if_all_dead), "no" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -104,15 +94,15 @@ static int realm_name_cmp(void const *one, void const *two)
 #ifdef WITH_PROXY
 static void home_server_free(void *data)
 {
-       home_server *home = data;
+       home_server_t *home = data;
 
        talloc_free(home);
 }
 
 static int home_server_name_cmp(void const *one, void const *two)
 {
-       home_server const *a = one;
-       home_server const *b = two;
+       home_server_t const *a = one;
+       home_server_t const *b = two;
 
        if (a->type < b->type) return -1;
        if (a->type > b->type) return +1;
@@ -123,8 +113,8 @@ static int home_server_name_cmp(void const *one, void const *two)
 static int home_server_addr_cmp(void const *one, void const *two)
 {
        int rcode;
-       home_server const *a = one;
-       home_server const *b = two;
+       home_server_t const *a = one;
+       home_server_t const *b = two;
 
        if (a->server && !b->server) return -1;
        if (!a->server && b->server) return +1;
@@ -151,8 +141,8 @@ static int home_server_addr_cmp(void const *one, void const *two)
 #ifdef WITH_STATS
 static int home_server_number_cmp(void const *one, void const *two)
 {
-       home_server const *a = one;
-       home_server const *b = two;
+       home_server_t const *a = one;
+       home_server_t const *b = two;
 
        return (a->number - b->number);
 }
@@ -170,8 +160,7 @@ static int home_pool_name_cmp(void const *one, void const *two)
 }
 
 
-static size_t xlat_cs(CONF_SECTION *cs, char const *fmt, char *out, size_t outlen)
-
+static size_t CC_HINT(nonnull) xlat_cs(CONF_SECTION *cs, char const *fmt, char *out, size_t outlen)
 {
        char const *value = NULL;
 
@@ -203,12 +192,10 @@ static size_t xlat_cs(CONF_SECTION *cs, char const *fmt, char *out, size_t outle
 /*
  *     Xlat for %{home_server:foo}
  */
-static ssize_t xlat_home_server(UNUSED void *instance, REQUEST *request,
-                               char const *fmt, char *out, size_t outlen)
+static ssize_t CC_HINT(nonnull) xlat_home_server(UNUSED void *instance, REQUEST *request,
+                                                char const *fmt, char *out, size_t outlen)
 {
-       if (!fmt || !out || (outlen < 1)) return 0;
-
-       if (!request || !request->home_server) {
+       if (!request->home_server) {
                RWDEBUG("No home_server associated with this request");
 
                *out = '\0';
@@ -222,12 +209,10 @@ static ssize_t xlat_home_server(UNUSED void *instance, REQUEST *request,
 /*
  *     Xlat for %{home_server_pool:foo}
  */
-static ssize_t xlat_server_pool(UNUSED void *instance, REQUEST *request,
-                               char const *fmt, char *out, size_t outlen)
+static ssize_t CC_HINT(nonnull) xlat_server_pool(UNUSED void *instance, REQUEST *request,
+                                                char const *fmt, char *out, size_t outlen)
 {
-       if (!fmt || !out || (outlen < 1)) return 0;
-
-       if (!request || !request->home_pool) {
+       if (!request->home_pool) {
                RWDEBUG("No home_pool associated with this request");
 
                *out = '\0';
@@ -279,109 +264,77 @@ void realms_free(void)
 
 #ifdef WITH_PROXY
 static CONF_PARSER limit_config[] = {
-       { "max_connections", PW_TYPE_INTEGER,
-         offsetof(home_server, limit.max_connections), NULL,   "16" },
-
-       { "max_requests", PW_TYPE_INTEGER,
-         offsetof(home_server, limit.max_requests), NULL,   "0" },
-
-       { "lifetime", PW_TYPE_INTEGER,
-         offsetof(home_server, limit.lifetime), NULL,   "0" },
-
-       { "idle_timeout", PW_TYPE_INTEGER,
-         offsetof(home_server, limit.idle_timeout), NULL,   "0" },
+       { "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.max_connections), "16" },
+       { "max_requests", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.max_requests), "0" },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.lifetime), "0" },
+       { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.idle_timeout), "0" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
-static struct in_addr hs_ip4addr;
-static struct in6_addr hs_ip6addr;
-static char *hs_srcipaddr = NULL;
-static char *hs_type = NULL;
-static char *hs_check = NULL;
-static char *hs_virtual_server = NULL;
+static fr_ipaddr_t hs_ipaddr;
+static char const *hs_srcipaddr = NULL;
+static char const *hs_type = NULL;
+static char const *hs_check = NULL;
+static char const *hs_virtual_server = NULL;
 #ifdef WITH_TCP
-static char *hs_proto = NULL;
+static char const *hs_proto = NULL;
 #endif
 
 #ifdef WITH_COA
 static CONF_PARSER home_server_coa[] = {
-       { "irt",  PW_TYPE_INTEGER,
-         offsetof(home_server, coa_irt), 0, STRINGIFY(2) },
-       { "mrt",  PW_TYPE_INTEGER,
-         offsetof(home_server, coa_mrt), 0, STRINGIFY(16) },
-       { "mrc",  PW_TYPE_INTEGER,
-         offsetof(home_server, coa_mrc), 0, STRINGIFY(5) },
-       { "mrd",  PW_TYPE_INTEGER,
-         offsetof(home_server, coa_mrd), 0, STRINGIFY(30) },
+       { "irt",  FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, coa_irt), STRINGIFY(2) },
+       { "mrt",  FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, coa_mrt), STRINGIFY(16) },
+       { "mrc",  FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, coa_mrc), STRINGIFY(5) },
+       { "mrd",  FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, coa_mrd), STRINGIFY(30) },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 #endif
 
 static CONF_PARSER home_server_config[] = {
-       { "ipaddr",  PW_TYPE_IPADDR,
-         0, &hs_ip4addr,  NULL },
-       { "ipv6addr",  PW_TYPE_IPV6ADDR,
-         0, &hs_ip6addr, NULL },
-       { "virtual_server",  PW_TYPE_STRING_PTR,
-         0, &hs_virtual_server, NULL },
+       { "ipaddr", FR_CONF_POINTER(PW_TYPE_IP_ADDR, &hs_ipaddr), NULL },
+       { "ipv4addr", FR_CONF_POINTER(PW_TYPE_IPV4_ADDR, &hs_ipaddr), NULL },
+       { "ipv6addr", FR_CONF_POINTER(PW_TYPE_IPV6_ADDR, &hs_ipaddr), NULL },
+       { "virtual_server", FR_CONF_POINTER(PW_TYPE_STRING, &hs_virtual_server), NULL },
 
-       { "port", PW_TYPE_INTEGER,
-         offsetof(home_server,port), NULL,   "0" },
+       { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, home_server_t, port), "0" },
 
-       { "type",  PW_TYPE_STRING_PTR,
-         0, &hs_type, NULL },
+       { "type", FR_CONF_POINTER(PW_TYPE_STRING, &hs_type), NULL },
 
 #ifdef WITH_TCP
-       { "proto",  PW_TYPE_STRING_PTR,
-         0, &hs_proto, NULL },
+       { "proto", FR_CONF_POINTER(PW_TYPE_STRING, &hs_proto), NULL },
 #endif
 
-       { "secret",  PW_TYPE_STRING_PTR,
-         offsetof(home_server,secret), NULL,  NULL},
-
-       { "src_ipaddr",  PW_TYPE_STRING_PTR,
-         0, &hs_srcipaddr,  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" },
-       { "status_check", PW_TYPE_STRING_PTR,
-         0, &hs_check,   "none" },
-       { "ping_check", PW_TYPE_STRING_PTR,
-         0, &hs_check,   NULL },
-
-       { "ping_interval", PW_TYPE_INTEGER,
-         offsetof(home_server,ping_interval), NULL,   "30" },
-       { "check_interval", PW_TYPE_INTEGER,
-         offsetof(home_server,ping_interval), NULL,   "30" },
-       { "num_answers_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" },
-       { "status_check_timeout", PW_TYPE_INTEGER,
-         offsetof(home_server,ping_timeout), NULL,   "4" },
-
-       { "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},
+       { "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, home_server_t, secret), NULL },
+
+       { "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &hs_srcipaddr), NULL },
+
+       { "response_window", FR_CONF_OFFSET(PW_TYPE_TIMEVAL, home_server_t, response_window), "30" },
+       { "max_outstanding", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, max_outstanding), "65536" },
+
+       { "zombie_period", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, zombie_period), "40" },
+       { "status_check", FR_CONF_POINTER(PW_TYPE_STRING, &hs_check), "none" },
+       { "ping_check", FR_CONF_POINTER(PW_TYPE_STRING, &hs_check), NULL },
+
+       { "ping_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, ping_interval), "30" },
+       { "check_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, ping_interval), "30" },
+       { "num_answers_to_alive", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, num_pings_to_alive), "3" },
+       { "revive_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, revive_interval), "300" },
+       { "status_check_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, ping_timeout), "4" },
+
+       { "username", FR_CONF_OFFSET(PW_TYPE_STRING, home_server_t, ping_user_name), NULL },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING, home_server_t, ping_user_password), NULL },
 
 #ifdef WITH_STATS
-       { "historic_average_window", PW_TYPE_INTEGER,
-         offsetof(home_server,ema.window), NULL,  NULL },
+       { "historic_average_window", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, ema.window), NULL },
 #endif
 
 #ifdef WITH_COA
-       {  "coa", PW_TYPE_SUBSECTION, 0, NULL, (void const *) home_server_coa },
+       { "coa", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) home_server_coa },
 #endif
 
-       { "limit", PW_TYPE_SUBSECTION, 0, NULL, (void const *) limit_config },
+       { "limit", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) limit_config },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -392,7 +345,7 @@ static void null_free(UNUSED void *data)
 }
 
 
-int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual)
+int realms_home_server_add(home_server_t *home, CONF_SECTION *cs, int dual)
 {
         CONF_SECTION *parent = NULL;
        const char * name2 = home->name;
@@ -449,47 +402,51 @@ int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual)
        }
 #endif
 
-       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;
+       FR_INTEGER_BOUND_CHECK("max_outstanding", home->max_outstanding, >=, 8);
+       FR_INTEGER_BOUND_CHECK("max_outstanding", home->max_outstanding, <=, 65536*16);
 
-       if (home->response_window < 1) home->response_window = 1;
-       if (home->response_window > 60) home->response_window = 60;
-       if (home->response_window > mainconfig.max_request_time) home->response_window = mainconfig.max_request_time;
+       FR_INTEGER_BOUND_CHECK("ping_interval", home->ping_interval, >=, 6);
+       FR_INTEGER_BOUND_CHECK("ping_interval", home->ping_interval, <=, 120);
 
-       if (home->zombie_period < 1) home->zombie_period = 1;
-       if (home->zombie_period > 120) home->zombie_period = 120;
+       FR_TIMEVAL_BOUND_CHECK("response_window", &home->response_window, >=, 0, 1000);
+       FR_TIMEVAL_BOUND_CHECK("response_window", &home->response_window, <=, 60, 0);
+       FR_TIMEVAL_BOUND_CHECK("response_window", &home->response_window, <=,
+                               main_config.max_request_time, 0);
 
-       if (home->zombie_period < home->response_window) {
-               home->zombie_period = home->response_window;
+       /*
+        *      Track the minimum response window, so that we can
+        *      correctly set the timers in process.c
+        */
+       if (timercmp(&main_config.init_delay, &home->response_window, >)) {
+               main_config.init_delay = 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;
+       FR_INTEGER_BOUND_CHECK("zombie_period", home->zombie_period, >=, 1);
+       FR_INTEGER_BOUND_CHECK("zombie_period", home->zombie_period, <=, 120);
+       FR_INTEGER_BOUND_CHECK("zombie_period", home->zombie_period, >=, (uint32_t) home->response_window.tv_sec);
+
+       FR_INTEGER_BOUND_CHECK("num_pings_to_alive", home->num_pings_to_alive, >=, 3);
+       FR_INTEGER_BOUND_CHECK("num_pings_to_alive", home->num_pings_to_alive, <=, 10);
 
-       if (home->ping_timeout < 3) home->ping_timeout = 3;
-       if (home->ping_timeout > 10) home->ping_timeout = 10;
+       FR_INTEGER_BOUND_CHECK("ping_timeout", home->ping_timeout, >=, 3);
+       FR_INTEGER_BOUND_CHECK("ping_timeout", home->ping_timeout, <=, 10);
 
-       if (home->revive_interval < 60) home->revive_interval = 60;
-       if (home->revive_interval > 3600) home->revive_interval = 3600;
+       FR_INTEGER_BOUND_CHECK("revive_interval", home->revive_interval, >=, 60);
+       FR_INTEGER_BOUND_CHECK("revive_interval", home->revive_interval, <=, 3600);
 
 #ifdef WITH_COA
-       if (home->coa_irt < 1) home->coa_irt = 1;
-       if (home->coa_irt > 5) home->coa_irt = 5;
+       FR_INTEGER_BOUND_CHECK("coa_irt", home->coa_irt, >=, 1);
+       FR_INTEGER_BOUND_CHECK("coa_irt", home->coa_irt, <=, 5);
 
-       if (home->coa_mrc < 0) home->coa_mrc = 0;
-       if (home->coa_mrc > 20 ) home->coa_mrc = 20;
+       FR_INTEGER_BOUND_CHECK("coa_mrc", home->coa_mrc, <=, 20);
 
-       if (home->coa_mrt < 0) home->coa_mrt = 0;
-       if (home->coa_mrt > 30 ) home->coa_mrt = 30;
+       FR_INTEGER_BOUND_CHECK("coa_mrt", home->coa_mrt, <=, 30);
 
-       if (home->coa_mrd < 5) home->coa_mrd = 5;
-       if (home->coa_mrd > 60 ) home->coa_mrd = 60;
+       FR_INTEGER_BOUND_CHECK("coa_mrd", home->coa_mrd, >=, 5);
+       FR_INTEGER_BOUND_CHECK("coa_mrd", home->coa_mrd, <=, 60);
 #endif
 
-       if (home->limit.max_connections > 1024) home->limit.max_connections = 1024;
+       FR_INTEGER_BOUND_CHECK("max_connections", home->limit.max_connections, <=, 1024);
 
 #ifdef WITH_TCP
        /*
@@ -506,12 +463,12 @@ int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual)
                home->limit.idle_timeout = 0;
 
        parent = cf_item_parent(cf_sectiontoitem(cs));
-       if (parent && strcmp(cf_section_name1(parent), "server") == 0) {
+       if (strcmp(cf_section_name1(parent), "server") == 0) {
                home->parent_server = cf_section_name2(parent);
        }
 
        if (dual) {
-               home_server *home2 = rad_malloc(sizeof(*home2));
+               home_server_t *home2 = talloc(home, home_server_t);
 
                memcpy(home2, home, sizeof(*home2));
 
@@ -528,7 +485,7 @@ int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual)
                        free(home2);
                        return 0;
                }
-               
+
                if (!home->server &&
                    !rbtree_insert(home_servers_byaddr, home2)) {
                        rbtree_deletebydata(home_servers_byname, home2);
@@ -564,8 +521,8 @@ int realms_home_server_add(home_server *home, CONF_SECTION *cs, int dual)
 static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
 {
        char const *name2;
-       home_server *home;
-       int dual = false;
+       home_server_t *home;
+       bool dual = false;
        CONF_PAIR *cp;
        CONF_SECTION *tls;
 
@@ -573,12 +530,11 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
 
        name2 = cf_section_name2(cs);
        if (!name2) {
-               cf_log_err_cs(cs,
-                          "Home server section is missing a name.");
+               cf_log_err_cs(cs, "Home server section is missing a name");
                return 0;
        }
 
-       home = talloc_zero(rc, home_server);
+       home = talloc_zero(rc, home_server_t);
 
        home->name = name2;
        home->cs = cs;
@@ -588,8 +544,7 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
         *      Last packet sent / received are zero.
         */
 
-       memset(&hs_ip4addr, 0, sizeof(hs_ip4addr));
-       memset(&hs_ip6addr, 0, sizeof(hs_ip6addr));
+       memset(&hs_ipaddr, 0, sizeof(hs_ipaddr));
        if (cf_section_parse(cs, home, home_server_config) < 0) {
                goto error;
        }
@@ -597,14 +552,12 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
        /*
         *      Figure out which one to use.
         */
-       if (cf_pair_find(cs, "ipaddr")) {
-               home->ipaddr.af = AF_INET;
-               home->ipaddr.ipaddr.ip4addr = hs_ip4addr;
-
-       } else if (cf_pair_find(cs, "ipv6addr")) {
-               home->ipaddr.af = AF_INET6;
-               home->ipaddr.ipaddr.ip6addr = hs_ip6addr;
-
+       if (cf_pair_find(cs, "ipaddr") || cf_pair_find(cs, "ipv4addr") || cf_pair_find(cs, "ipv6addr")) {
+               if (is_wildcard(&hs_ipaddr)) {
+                       cf_log_err_cs(cs, "Wildcard '*' addresses are not permitted for home servers");
+                       goto error;
+               }
+               home->ipaddr = hs_ipaddr;
        } else if ((cp = cf_pair_find(cs, "virtual_server")) != NULL) {
                home->ipaddr.af = AF_UNSPEC;
                home->server = cf_pair_value(cp);
@@ -630,11 +583,8 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                goto skip_port;
 
        } else {
-               cf_log_err_cs(cs,
-                          "No ipaddr, ipv6addr, or virtual_server defined for home server \"%s\".",
-                          name2);
+               cf_log_err_cs(cs, "No ipaddr, ipv4addr, ipv6addr, or virtual_server defined for home server \"%s\"", name2);
        error:
-               talloc_free(hs_type);
                hs_type = NULL;
                hs_check = NULL;
                hs_srcipaddr = NULL;
@@ -644,24 +594,8 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                return 0;
        }
 
-       if (!home->port || (home->port > 65535)) {
-               cf_log_err_cs(cs,
-                          "No port, or invalid port defined for home server %s.",
-                          name2);
-               goto error;
-       }
-
-       if (0) {
-               cf_log_err_cs(cs,
-                          "Fatal error!  Home server %s is ourselves!",
-                          name2);
-               goto error;
-       }
-
-       if (!home->secret) {
-               cf_log_err_cs(cs,
-                          "No shared secret defined for home server %s.",
-                          name2);
+       if (home->port == 0) {
+               cf_log_err_cs(cs, "No port, or invalid port defined for home server %s", name2);
                goto error;
        }
 
@@ -669,37 +603,29 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
         *      Use a reasonable default.
         */
  skip_port:
-       if (!hs_type) hs_type = talloc_strdup(cs, "auth+acct");
-
-       if (strcasecmp(hs_type, "auth") == 0) {
+       if (!hs_type || (strcasecmp(hs_type, "auth+acct") == 0)) {
+               home->type = HOME_TYPE_AUTH;
+               dual = true;
+       } else if (strcasecmp(hs_type, "auth") == 0) {
                home->type = HOME_TYPE_AUTH;
 
        } else if (strcasecmp(hs_type, "acct") == 0) {
                home->type = HOME_TYPE_ACCT;
-
-       } else if (strcasecmp(hs_type, "auth+acct") == 0) {
-               home->type = HOME_TYPE_AUTH;
-               dual = true;
-
 #ifdef WITH_COA
        } else if (strcasecmp(hs_type, "coa") == 0) {
                home->type = HOME_TYPE_COA;
                dual = false;
 
                if (home->server != NULL) {
-                       cf_log_err_cs(cs,
-                                  "Home servers of type \"coa\" cannot point to a virtual server");
+                       cf_log_err_cs(cs, "Home servers of type \"coa\" cannot point to a virtual server");
                        goto error;
                }
 #endif
 
        } else {
-               cf_log_err_cs(cs,
-                          "Invalid type \"%s\" for home server %s.",
-                          hs_type, name2);
+               cf_log_err_cs(cs, "Invalid type \"%s\" for home server %s.", hs_type, name2);
                goto error;
        }
-       if (hs_type) talloc_free(hs_type);
        hs_type = NULL;
 
        if (!hs_check || (strcasecmp(hs_check, "none") == 0)) {
@@ -726,7 +652,7 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
 
        } else {
                cf_log_err_cs(cs,
-                          "Invalid status__check \"%s\" for home server %s.",
+                          "Invalid status_check \"%s\" for home server %s.",
                           hs_check, name2);
                goto error;
        }
@@ -782,6 +708,22 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
        tls = cf_section_sub_find(cs, "tls");
 
        /*
+        *      If were doing RADSEC (tls+tcp) the secret should default
+        *      to radsec, else a secret must be set.
+        */
+       if (!home->secret) {
+#ifdef WITH_TLS
+               if (tls && (home->proto == IPPROTO_TCP)) {
+                       home->secret = "radsec";
+               } else
+#endif
+               {
+                       cf_log_err_cs(cs, "No shared secret defined for home server %s", name2);
+                       goto error;
+               }
+       }
+
+       /*
         *      If the home is a virtual server, don't look up source IP.
         */
        if (!home->server) {
@@ -792,7 +734,7 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                 *      address family as the destination IP.
                 */
                if (hs_srcipaddr) {
-                       if (ip_hton(hs_srcipaddr, home->ipaddr.af, &home->src_ipaddr) < 0) {
+                       if (ip_hton(&home->src_ipaddr, home->ipaddr.af, hs_srcipaddr, false) < 0) {
                                cf_log_err_cs(cs, "Failed parsing src_ipaddr");
                                goto error;
                        }
@@ -807,14 +749,14 @@ static int home_server_add(realm_config_t *rc, CONF_SECTION *cs)
                }
 
                if (tls && (home->proto != IPPROTO_TCP)) {
-                       cf_log_err_cs(cs, "TLS transport is not available for UDP sockets.");
+                       cf_log_err_cs(cs, "TLS transport is not available for UDP sockets");
                        goto error;
                }
 
 #ifndef WITH_TLS
 
                if (tls) {
-                       cf_log_err_cs(cs, "TLS transport is not available in this executable.");
+                       cf_log_err_cs(cs, "TLS transport is not available in this executable");
                        goto error;
                }
 #else
@@ -873,13 +815,13 @@ static home_pool_t *server_pool_alloc(char const *name, home_pool_type_t type,
  */
 static int pool_check_home_server(UNUSED realm_config_t *rc, CONF_PAIR *cp,
                                  char const *name, int server_type,
-                                 home_server **phome)
+                                 home_server_t **phome)
 {
-       home_server myhome, *home;
+       home_server_t myhome, *home;
 
        if (!name) {
                cf_log_err_cp(cp,
-                          "No value given for home_server.");
+                          "No value given for home_server");
                return 0;
        }
 
@@ -914,20 +856,20 @@ static int server_pool_add(realm_config_t *rc,
        char const *value;
        CONF_PAIR *cp;
        int num_home_servers;
-       home_server *home;
+       home_server_t *home;
 
        name2 = cf_section_name1(cs);
        if (!name2 || ((strcasecmp(name2, "server_pool") != 0) &&
                       (strcasecmp(name2, "home_server_pool") != 0))) {
                cf_log_err_cs(cs,
-                          "Section is not a home_server_pool.");
+                          "Section is not a home_server_pool");
                return 0;
        }
 
        name2 = cf_section_name2(cs);
        if (!name2) {
                cf_log_err_cs(cs,
-                          "Server pool section is missing a name.");
+                          "Server pool section is missing a name");
                return 0;
        }
 
@@ -970,7 +912,7 @@ static int server_pool_add(realm_config_t *rc,
        if (cp) {
 #ifdef WITH_COA
                if (server_type == HOME_TYPE_COA) {
-                       cf_log_err_cs(cs, "Home server pools of type \"coa\" cannot have a fallback virtual server.");
+                       cf_log_err_cs(cs, "Home server pools of type \"coa\" cannot have a fallback virtual server");
                        goto error;
                }
 #endif
@@ -1009,7 +951,7 @@ static int server_pool_add(realm_config_t *rc,
                value = cf_pair_value(cp);
                if (!value) {
                        cf_log_err_cp(cp,
-                                  "No value given for type.");
+                                  "No value given for type");
                        goto error;
                }
 
@@ -1049,7 +991,7 @@ static int server_pool_add(realm_config_t *rc,
        for (cp = cf_pair_find(cs, "home_server");
             cp != NULL;
             cp = cf_pair_find_next(cs, cp, "home_server")) {
-               home_server myhome;
+               home_server_t myhome;
 
                value = cf_pair_value(cp);
 
@@ -1064,7 +1006,7 @@ static int server_pool_add(realm_config_t *rc,
                }
 
                if (0) {
-                       WDEBUG2("Duplicate home server %s in server pool %s", home->name, pool->name);
+                       WARN("Duplicate home server %s in server pool %s", home->name, pool->name);
                        continue;
                }
 
@@ -1103,7 +1045,7 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
 {
 #ifdef WITH_PROXY
        int i, insert_point, num_home_servers;
-       home_server myhome, *home;
+       home_server_t myhome, *home;
        home_pool_t mypool, *pool;
        CONF_SECTION *subcs;
 #else
@@ -1202,9 +1144,7 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
                char const *p;
                char *q;
 
-               home = rad_malloc(sizeof(*home));
-               memset(home, 0, sizeof(*home));
-
+               home = talloc_zero(rc, home_server_t);
                home->name = name;
                home->hostname = name;
                home->type = type;
@@ -1224,35 +1164,31 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
                        q = NULL;
 
                } else if (p == name) {
-                               cf_log_err_cs(cs,
-                                          "Invalid hostname %s.",
-                                          name);
-                               free(home);
-                               return 0;
-
+                       cf_log_err_cs(cs, "Invalid hostname %s", name);
+                       talloc_free(home);
+                       return 0;
                } else {
-                       home->port = atoi(p + 1);
-                       if ((home->port == 0) || (home->port > 65535)) {
-                               cf_log_err_cs(cs,
-                                          "Invalid port %s.",
-                                          p + 1);
-                               free(home);
+                       unsigned long port = strtoul(p + 1, NULL, 0);
+                       if ((port == 0) || (port > 65535)) {
+                               cf_log_err_cs(cs, "Invalid port %s", p + 1);
+                               talloc_free(home);
                                return 0;
                        }
 
-                       q = rad_malloc((p - name) + 1);
+                       home->port = (uint16_t)port;
+                       q = talloc_array(home, char, (p - name) + 1);
                        memcpy(q, name, (p - name));
                        q[p - name] = '\0';
                        p = q;
                }
 
                if (!server) {
-                       if (ip_hton(p, AF_UNSPEC, &home->ipaddr) < 0) {
+                       if (ip_hton(&home->ipaddr, AF_UNSPEC, p, false) < 0) {
                                cf_log_err_cs(cs,
                                           "Failed looking up hostname %s.",
                                           p);
-                               free(home);
-                               free(q);
+                               talloc_free(home);
+                               talloc_free(q);
                                return 0;
                        }
                        home->src_ipaddr.af = home->ipaddr.af;
@@ -1260,15 +1196,16 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
                        home->ipaddr.af = AF_UNSPEC;
                        home->server = server;
                }
-               free(q);
+               talloc_free(q);
 
                /*
                 *      Use the old-style configuration.
                 */
                home->max_outstanding = 65535*16;
                home->zombie_period = rc->retry_delay * rc->retry_count;
-               if (home->zombie_period == 0) home->zombie_period =30;
-               home->response_window = home->zombie_period - 1;
+               if (home->zombie_period < 2) home->zombie_period = 30;
+               home->response_window.tv_sec = home->zombie_period - 1;
+               home->response_window.tv_usec = 0;
 
                home->ping_check = HOME_PING_CHECK_NONE;
 
@@ -1276,20 +1213,20 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
 
                if (rbtree_finddata(home_servers_byaddr, home)) {
                        cf_log_err_cs(cs, "Home server %s has the same IP address and/or port as another home server.", name);
-                       free(home);
+                       talloc_free(home);
                        return 0;
                }
 
                if (!rbtree_insert(home_servers_byname, home)) {
                        cf_log_err_cs(cs, "Internal error %d adding home server %s.", __LINE__, name);
-                       free(home);
+                       talloc_free(home);
                        return 0;
                }
 
                if (!rbtree_insert(home_servers_byaddr, home)) {
                        rbtree_deletebydata(home_servers_byname, home);
                        cf_log_err_cs(cs, "Internal error %d adding home server %s.", __LINE__, name);
-                       free(home);
+                       talloc_free(home);
                        return 0;
                }
 
@@ -1303,7 +1240,7 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
                        cf_log_err_cs(cs,
                                   "Internal error %d adding home server %s.",
                                   __LINE__, name);
-                       free(home);
+                       talloc_free(home);
                        return 0;
                }
 #endif
@@ -1337,11 +1274,16 @@ static int old_server_add(realm_config_t *rc, CONF_SECTION *cs,
 
        if (num_home_servers == 0) {
                cf_log_err_cs(cs, "Internal error counting pools for home server %s.", name);
-               free(home);
+               talloc_free(home);
                return 0;
        }
 
        pool = server_pool_alloc(realm, ldflag, type, num_home_servers);
+       if (!pool) {
+               cf_log_err_cs(cs, "Out of memory");
+               return 0;
+       }
+
        pool->cs = cs;
 
        pool->servers[0] = home;
@@ -1579,13 +1521,13 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
 
        name2 = cf_section_name1(cs);
        if (!name2 || (strcasecmp(name2, "realm") != 0)) {
-               cf_log_err_cs(cs, "Section is not a realm.");
+               cf_log_err_cs(cs, "Section is not a realm");
                return 0;
        }
 
        name2 = cf_section_name2(cs);
        if (!name2) {
-               cf_log_err_cs(cs, "Realm section is missing the realm name.");
+               cf_log_err_cs(cs, "Realm section is missing the realm name");
                return 0;
        }
 
@@ -1621,7 +1563,7 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
        if (cp) auth_pool_name = cf_pair_value(cp);
        if (cp && auth_pool_name) {
                if (auth_pool) {
-                       cf_log_err_cs(cs, "Cannot use \"pool\" and \"auth_pool\" at the same time.");
+                       cf_log_err_cs(cs, "Cannot use \"pool\" and \"auth_pool\" at the same time");
                        return 0;
                }
                if (!add_pool_to_realm(rc, cs,
@@ -1634,10 +1576,10 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
        cp = cf_pair_find(cs, "acct_pool");
        if (cp) acct_pool_name = cf_pair_value(cp);
        if (cp && acct_pool_name) {
-               int do_print = true;
+               bool do_print = true;
 
                if (acct_pool) {
-                       cf_log_err_cs(cs, "Cannot use \"pool\" and \"acct_pool\" at the same time.");
+                       cf_log_err_cs(cs, "Cannot use \"pool\" and \"acct_pool\" at the same time");
                        return 0;
                }
 
@@ -1658,7 +1600,7 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
        cp = cf_pair_find(cs, "coa_pool");
        if (cp) coa_pool_name = cf_pair_value(cp);
        if (cp && coa_pool_name) {
-               int do_print = true;
+               bool do_print = true;
 
                if (!add_pool_to_realm(rc, cs,
                                       coa_pool_name, &coa_pool,
@@ -1755,7 +1697,7 @@ static int realm_add(realm_config_t *rc, CONF_SECTION *cs)
                    ((cp = cf_pair_find(cs, "accthost")) != NULL) ||
                    ((cp = cf_pair_find(cs, "secret")) != NULL) ||
                    ((cp = cf_pair_find(cs, "ldflag")) != NULL)) {
-                       WDEBUG2("Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
+                       WARN("Ignoring old-style configuration entry \"%s\" in realm \"%s\"", cf_pair_attr(cp), r->name);
                }
 
 
@@ -1888,10 +1830,7 @@ int realms_init(CONF_SECTION *config)
        if (cs) {
                if (cf_section_parse(cs, rc, proxy_config) < 0) {
                        ERROR("Failed parsing proxy section");
-
-                       talloc_free(rc);
-                       realms_free();
-                       return 0;
+                       goto error;
                }
        } else {
                rc->dead_time = DEAD_TIME;
@@ -1904,11 +1843,7 @@ int realms_init(CONF_SECTION *config)
        for (cs = cf_subsection_find_next(config, NULL, "home_server");
             cs != NULL;
             cs = cf_subsection_find_next(config, cs, "home_server")) {
-               if (!home_server_add(rc, cs)) {
-                       talloc_free(rc);
-                       realms_free();
-                       return 0;
-               }
+               if (!home_server_add(rc, cs)) goto error;
        }
 
        /*
@@ -1921,11 +1856,7 @@ int realms_init(CONF_SECTION *config)
                for (cs = cf_subsection_find_next(server_cs, NULL, "home_server");
                     cs != NULL;
                     cs = cf_subsection_find_next(server_cs, cs, "home_server")) {
-                       if (!home_server_add(rc, cs)) {
-                               talloc_free(rc);
-                               realms_free();
-                               return 0;
-                       }
+                       if (!home_server_add(rc, cs)) goto error;
                }
        }
 #endif
@@ -1934,8 +1865,15 @@ int realms_init(CONF_SECTION *config)
             cs != NULL;
             cs = cf_subsection_find_next(config, cs, "realm")) {
                if (!realm_add(rc, cs)) {
-                       talloc_free(rc);
+#if defined (WITH_PROXY) || defined (WITH_COA)
+               error:
+#endif
                        realms_free();
+                       /*
+                        *      Must be called after realms_free as home_servers
+                        *      parented by rc are in trees freed by realms_free()
+                        */
+                       talloc_free(rc);
                        return 0;
                }
        }
@@ -1955,17 +1893,8 @@ int realms_init(CONF_SECTION *config)
                if (cf_data_find(cs, "home_server_pool")) continue;
 
                type = pool_peek_type(config, cs);
-               if (type == HOME_TYPE_INVALID) {
-                       talloc_free(rc);
-                       realms_free();
-                       return 0;
-               }
-
-               if (!server_pool_add(rc, cs, type, true)) {
-                       talloc_free(rc);
-                       realms_free();
-                       return 0;
-               }
+               if (type == HOME_TYPE_INVALID) goto error;
+               if (!server_pool_add(rc, cs, type, true)) goto error;
        }
 #endif
 
@@ -1980,7 +1909,7 @@ int realms_init(CONF_SECTION *config)
         */
        old_rc = realm_config;
        realm_config = rc;
-       free(old_rc);
+       talloc_free(old_rc);
 
        return 1;
 }
@@ -2071,12 +2000,12 @@ REALM *realm_find(char const *name)
  *     VPs into it. Setup src/dst IP addresses based on home server, and
  *     calculate and add the message-authenticator.
  *
- *     This is a distinct function from home_server_ldb, as not all home_server
+ *     This is a distinct function from home_server_ldb, as not all home_server_t
  *     lookups result in the *CURRENT* request being proxied,
  *     as in rlm_replicate, and this may trigger asserts elsewhere in the
  *     server.
  */
-void home_server_update_request(home_server *home, REQUEST *request)
+void home_server_update_request(home_server_t *home, REQUEST *request)
 {
 
        /*
@@ -2101,7 +2030,7 @@ void home_server_update_request(home_server *home, REQUEST *request)
        if (!request->proxy) {
                request->proxy = rad_alloc(request, true);
                if (!request->proxy) {
-                       ERROR("no memory");                     
+                       ERROR("no memory");
                        fr_exit(1);
                        _exit(1);
                }
@@ -2127,13 +2056,16 @@ void home_server_update_request(home_server *home, REQUEST *request)
        request->proxy->src_port = 0;
        request->proxy->dst_ipaddr = home->ipaddr;
        request->proxy->dst_port = home->port;
+#ifdef WITH_TCP
+       request->proxy->proto = home->proto;
+#endif
        request->home_server = home;
 
        /*
         *      Access-Requests have a Message-Authenticator added,
         *      unless one already exists.
         */
-       if ((request->packet->code == PW_AUTHENTICATION_REQUEST) &&
+       if ((request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) &&
            !pairfind(request->proxy->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY)) {
                pairmake(request->proxy, &request->proxy->vps,
                         "Message-Authenticator", "0x00",
@@ -2141,13 +2073,13 @@ void home_server_update_request(home_server *home, REQUEST *request)
        }
 }
 
-home_server *home_server_ldb(char const *realmname,
+home_server_t *home_server_ldb(char const *realmname,
                             home_pool_t *pool, REQUEST *request)
 {
        int             start;
        int             count;
-       home_server     *found = NULL;
-       home_server     *zombie = NULL;
+       home_server_t   *found = NULL;
+       home_server_t   *zombie = NULL;
        VALUE_PAIR      *vp;
 
        /*
@@ -2227,7 +2159,7 @@ home_server *home_server_ldb(char const *realmname,
         *      Otherwise, use it.
         */
        for (count = 0; count < pool->num_home_servers; count++) {
-               home_server *home = pool->servers[(start + count) % pool->num_home_servers];
+               home_server_t *home = pool->servers[(start + count) % pool->num_home_servers];
 
                if (!home) continue;
 
@@ -2255,7 +2187,7 @@ home_server *home_server_ldb(char const *realmname,
                 *      there.
                 */
                if ((request->listener->type == RAD_LISTEN_DETAIL) &&
-                   (request->packet->code == PW_ACCOUNTING_REQUEST) &&
+                   (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) &&
                    (fr_ipaddr_cmp(&home->ipaddr, &request->packet->src_ipaddr) == 0)) {
                        continue;
                }
@@ -2361,7 +2293,7 @@ home_server *home_server_ldb(char const *realmname,
        if (!found && pool->fallback) {
                found = pool->fallback;
 
-               WDEBUG("Home server pool %s failing over to fallback %s",
+               WARN("Home server pool %s failing over to fallback %s",
                      pool->name, found->server);
                if (pool->in_fallback) goto update_and_return;
 
@@ -2397,7 +2329,7 @@ home_server *home_server_ldb(char const *realmname,
        if (!realm_config->fallback &&
            realm_config->wake_all_if_all_dead) {
                for (count = 0; count < pool->num_home_servers; count++) {
-                       home_server *home = pool->servers[count];
+                       home_server_t *home = pool->servers[count];
 
                        if (!home) continue;
 
@@ -2424,10 +2356,10 @@ home_server *home_server_ldb(char const *realmname,
                if (!rd) return NULL;
 
                pool = NULL;
-               if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+               if (request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) {
                        pool = rd->auth_pool;
 
-               } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+               } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
                        pool = rd->acct_pool;
                }
                if (!pool) return NULL;
@@ -2443,9 +2375,9 @@ home_server *home_server_ldb(char const *realmname,
 }
 
 
-home_server *home_server_find(fr_ipaddr_t *ipaddr, int port, int proto)
+home_server_t *home_server_find(fr_ipaddr_t *ipaddr, uint16_t port, int proto)
 {
-       home_server myhome;
+       home_server_t myhome;
 
        memset(&myhome, 0, sizeof(myhome));
        myhome.ipaddr = *ipaddr;
@@ -2462,9 +2394,9 @@ home_server *home_server_find(fr_ipaddr_t *ipaddr, int port, int proto)
 }
 
 #ifdef WITH_COA
-home_server *home_server_byname(char const *name, int type)
+home_server_t *home_server_byname(char const *name, int type)
 {
-       home_server myhome;
+       home_server_t myhome;
 
        memset(&myhome, 0, sizeof(myhome));
        myhome.type = type;
@@ -2475,9 +2407,9 @@ home_server *home_server_byname(char const *name, int type)
 #endif
 
 #ifdef WITH_STATS
-home_server *home_server_bynumber(int number)
+home_server_t *home_server_bynumber(int number)
 {
-       home_server myhome;
+       home_server_t myhome;
 
        memset(&myhome, 0, sizeof(myhome));
        myhome.number = number;
index 10f3d13..b6c7b05 100644 (file)
@@ -34,7 +34,7 @@ RCSID("$Id$")
 /*
  *     End a session by faking a Stop packet to all accounting modules.
  */
-int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port,
+int session_zap(REQUEST *request, uint32_t nasaddr, uint32_t nas_port,
                char const *user,
                char const *sessionid, uint32_t cliaddr, char proto,
                int session_time)
@@ -44,7 +44,7 @@ int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port,
        int ret;
 
        stopreq = request_alloc_fake(request);
-       stopreq->packet->code = PW_ACCOUNTING_REQUEST; /* just to be safe */
+       stopreq->packet->code = PW_CODE_ACCOUNTING_REQUEST; /* just to be safe */
        stopreq->listener = request->listener;
        rad_assert(stopreq != NULL);
 
@@ -77,7 +77,7 @@ int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port,
        INTPAIR(PW_ACCT_DELAY_TIME, 0);
        STRINGPAIR(PW_USER_NAME, user);
        userpair = vp;
-       INTPAIR(PW_NAS_PORT, port);
+       INTPAIR(PW_NAS_PORT, nas_port);
        STRINGPAIR(PW_ACCT_SESSION_ID, sessionid);
        if(proto == 'P') {
                INTPAIR(PW_SERVICE_TYPE, PW_FRAMED_USER);
@@ -119,7 +119,7 @@ int session_zap(REQUEST *request, uint32_t nasaddr, unsigned int port,
  *             1 The user is logged in.
  *             2 Some error occured.
  */
-int rad_check_ts(uint32_t nasaddr, unsigned int portnum, char const *user,
+int rad_check_ts(uint32_t nasaddr, uint32_t nas_port, char const *user,
                 char const *session_id)
 {
        pid_t   pid, child_pid;
@@ -195,7 +195,7 @@ int rad_check_ts(uint32_t nasaddr, unsigned int portnum, char const *user,
        closefrom(3);
 
        ip_ntoa(address, nasaddr);
-       snprintf(port, 11, "%u", portnum);
+       snprintf(port, 11, "%u", nas_port);
 
 #ifdef __EMX__
        /* OS/2 can't directly execute scripts then we call the command
@@ -204,10 +204,10 @@ int rad_check_ts(uint32_t nasaddr, unsigned int portnum, char const *user,
        execl(getenv("COMSPEC"), "", "/C","checkrad", cl->nas_type, address, port,
                user, session_id, NULL);
 #else
-       execl(mainconfig.checkrad, "checkrad", cl->nas_type, address, port,
+       execl(main_config.checkrad, "checkrad", cl->nas_type, address, port,
                user, session_id, NULL);
 #endif
-       ERROR("Check-TS: exec %s: %s", mainconfig.checkrad, strerror(errno));
+       ERROR("Check-TS: exec %s: %s", main_config.checkrad, fr_syserror(errno));
 
        /*
         *      Exit - 2 means "some error occured".
@@ -216,7 +216,7 @@ int rad_check_ts(uint32_t nasaddr, unsigned int portnum, char const *user,
        return 2;
 }
 #else
-int rad_check_ts(UNUSED uint32_t nasaddr, UNUSED unsigned int portnum,
+int rad_check_ts(UNUSED uint32_t nasaddr, UNUSED unsigned int nas_port,
                 UNUSED char const *user, UNUSED char const *session_id)
 {
        ERROR("Simultaneous-Use is not supported");
@@ -227,7 +227,7 @@ int rad_check_ts(UNUSED uint32_t nasaddr, UNUSED unsigned int portnum,
 #else
 /* WITH_SESSION_MGMT */
 
-int session_zap(UNUSED REQUEST *request, UNUSED uint32_t nasaddr, UNUSED unsigned int port,
+int session_zap(UNUSED REQUEST *request, UNUSED uint32_t nasaddr, UNUSED uint32_t nas_port,
                UNUSED char const *user,
                UNUSED char const *sessionid, UNUSED uint32_t cliaddr, UNUSED char proto,
                UNUSED int session_time)
@@ -235,7 +235,7 @@ int session_zap(UNUSED REQUEST *request, UNUSED uint32_t nasaddr, UNUSED unsigne
        return RLM_MODULE_FAIL;
 }
 
-int rad_check_ts(UNUSED uint32_t nasaddr, UNUSED unsigned int portnum,
+int rad_check_ts(UNUSED uint32_t nasaddr, UNUSED unsigned int nas_port,
                 UNUSED char const *user, UNUSED char const *session_id)
 {
        ERROR("Simultaneous-Use is not supported");
index c1a38f1..ddb85fa 100644 (file)
@@ -27,6 +27,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/soh.h>
+#include <freeradius-devel/rad_assert.h>
 
 /*
  * This code implements parsing of MS-SOH data into FreeRadius AVPs
@@ -140,7 +141,7 @@ uint32_t soh_pull_be_32(uint8_t const *p) {
  * @param data_len length of blob
  * @return 1 on success, 0 on failure
  */
-static int eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len) {
+static int CC_HINT(nonnull) eapsoh_mstlv(REQUEST *request, uint8_t const *p, unsigned int data_len) {
        VALUE_PAIR *vp;
        uint8_t c;
        int t;
@@ -381,6 +382,8 @@ int soh_verify(REQUEST *request, uint8_t const *data, unsigned int data_len) {
        soh_tlv tlv;
        int curr_shid=-1, curr_shid_c=-1, curr_hc=-1;
 
+       rad_assert(request->packet != NULL);
+
        hdr.tlv_type = soh_pull_be_16(data); data += 2;
        hdr.tlv_len = soh_pull_be_16(data); data += 2;
        hdr.tlv_vendor = soh_pull_be_32(data); data += 4;
index c3421b7..1b1c223 100644 (file)
@@ -127,7 +127,7 @@ void request_stats_final(REQUEST *request)
            (request->listener->type != RAD_LISTEN_AUTH)) return;
 
        /* don't count statistic requests */
-       if (request->packet->code == PW_STATUS_SERVER)
+       if (request->packet->code == PW_CODE_STATUS_SERVER)
                return;
 
 #undef INC_AUTH
@@ -163,8 +163,8 @@ void request_stats_final(REQUEST *request)
         *      deleted, because only the main server thread calls
         *      this function, which makes it thread-safe.
         */
-       if (request->reply) switch (request->reply->code) {
-       case PW_AUTHENTICATION_ACK:
+       if (request->reply && (request->packet->code != PW_CODE_STATUS_SERVER)) switch (request->reply->code) {
+       case PW_CODE_AUTHENTICATION_ACK:
                INC_AUTH(total_access_accepts);
 
                auth_stats:
@@ -184,16 +184,16 @@ void request_stats_final(REQUEST *request)
                           &request->reply->timestamp);
                break;
 
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_REJECT:
                INC_AUTH(total_access_rejects);
                goto auth_stats;
 
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_CHALLENGE:
                INC_AUTH(total_access_challenges);
                goto auth_stats;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_RESPONSE:
+       case PW_CODE_ACCOUNTING_RESPONSE:
                INC_ACCT(total_responses);
                stats_time(&radius_acct_stats,
                           &request->packet->timestamp,
@@ -205,7 +205,7 @@ void request_stats_final(REQUEST *request)
 #endif
 
 #ifdef WITH_COA
-       case PW_COA_ACK:
+       case PW_CODE_COA_ACK:
                INC_COA(total_access_accepts);
          coa_stats:
                INC_COA(total_responses);
@@ -214,11 +214,11 @@ void request_stats_final(REQUEST *request)
                           &request->reply->timestamp);
                break;
 
-       case PW_COA_NAK:
+       case PW_CODE_COA_NAK:
                INC_COA(total_access_rejects);
                goto coa_stats;
 
-       case PW_DISCONNECT_ACK:
+       case PW_CODE_DISCONNECT_ACK:
                INC_DSC(total_access_accepts);
          dsc_stats:
                INC_DSC(total_responses);
@@ -227,7 +227,7 @@ void request_stats_final(REQUEST *request)
                           &request->reply->timestamp);
                break;
 
-       case PW_DISCONNECT_NAK:
+       case PW_CODE_DISCONNECT_NAK:
                INC_DSC(total_access_rejects);
                goto dsc_stats;
 #endif
@@ -237,13 +237,13 @@ void request_stats_final(REQUEST *request)
                 *      authenticator.
                 */
        case 0:
-               if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+               if (request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) {
                        if (request->reply->offset == -2) {
                                INC_AUTH(total_bad_authenticators);
                        } else {
                                INC_AUTH(total_packets_dropped);
                        }
-               } else if (request->packet->code == PW_ACCOUNTING_REQUEST) {
+               } else if (request->packet->code == PW_CODE_ACCOUNTING_REQUEST) {
                        if (request->reply->offset == -2) {
                                INC_ACCT(total_bad_authenticators);
                        } else {
@@ -260,14 +260,14 @@ void request_stats_final(REQUEST *request)
        if (!request->proxy || !request->proxy_listener) goto done;     /* simplifies formatting */
 
        switch (request->proxy->code) {
-       case PW_AUTHENTICATION_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
                proxy_auth_stats.total_requests += request->num_proxied_requests;
                request->proxy_listener->stats.total_requests += request->num_proxied_requests;
                request->home_server->stats.total_requests += request->num_proxied_requests;
                break;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
                proxy_acct_stats.total_requests++;
                request->proxy_listener->stats.total_requests += request->num_proxied_requests;
                request->home_server->stats.total_requests += request->num_proxied_requests;
@@ -284,7 +284,7 @@ void request_stats_final(REQUEST *request)
 #define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->proxy_listener->stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
 
        switch (request->proxy_reply->code) {
-       case PW_AUTHENTICATION_ACK:
+       case PW_CODE_AUTHENTICATION_ACK:
                INC(total_access_accepts);
        proxy_stats:
                INC(total_responses);
@@ -296,16 +296,16 @@ void request_stats_final(REQUEST *request)
                           &request->proxy_reply->timestamp);
                break;
 
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_REJECT:
                INC(total_access_rejects);
                goto proxy_stats;
 
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_CHALLENGE:
                INC(total_access_challenges);
                goto proxy_stats;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_RESPONSE:
+       case PW_CODE_ACCOUNTING_RESPONSE:
                proxy_acct_stats.total_responses++;
                request->proxy_listener->stats.total_responses++;
                request->home_server->stats.total_responses++;
@@ -438,7 +438,7 @@ static void request_stats_addvp(REQUEST *request,
        VALUE_PAIR *vp;
 
        for (i = 0; table[i].attribute != 0; i++) {
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       table[i].attribute, VENDORPEC_FREERADIUS);
                if (!vp) continue;
 
@@ -455,7 +455,7 @@ void request_stats_reply(REQUEST *request)
        /*
         *      Statistics are available ONLY on a "status" port.
         */
-       rad_assert(request->packet->code == PW_STATUS_SERVER);
+       rad_assert(request->packet->code == PW_CODE_STATUS_SERVER);
        rad_assert(request->listener->type == RAD_LISTEN_NONE);
 
        flag = pairfind(request->packet->vps, 127, VENDORPEC_FREERADIUS, TAG_ANY);
@@ -503,10 +503,10 @@ void request_stats_reply(REQUEST *request)
         *      Internal server statistics
         */
        if ((flag->vp_integer & 0x10) != 0) {
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       176, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = start_time.tv_sec;
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       177, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = hup_time.tv_sec;
 
@@ -516,7 +516,7 @@ void request_stats_reply(REQUEST *request)
                thread_pool_queue_stats(array, pps);
 
                for (i = 0; i <= 4; i++) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               162 + i, VENDORPEC_FREERADIUS);
 
                        if (!vp) continue;
@@ -524,7 +524,7 @@ void request_stats_reply(REQUEST *request)
                }
 
                for (i = 0; i < 2; i++) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               181 + i, VENDORPEC_FREERADIUS);
 
                        if (!vp) continue;
@@ -595,19 +595,19 @@ void request_stats_reply(REQUEST *request)
                         */
                        if ((vp->da->type == PW_TYPE_INTEGER) &&
                            (client->ipaddr.af == AF_INET)) {
-                               vp = radius_paircreate(request,
+                               vp = radius_paircreate(request->reply,
                                                       &request->reply->vps,
                                                       167, VENDORPEC_FREERADIUS);
                                if (vp) {
                                        vp->vp_ipaddr = client->ipaddr.ipaddr.ip4addr.s_addr;
                                }
 
-                               if (client->prefix != 32) {
-                                       vp = radius_paircreate(request,
+                               if (client->ipaddr.prefix != 32) {
+                                       vp = radius_paircreate(request->reply,
                                                               &request->reply->vps,
                                                               169, VENDORPEC_FREERADIUS);
                                        if (vp) {
-                                               vp->vp_integer = client->prefix;
+                                               vp->vp_integer = client->ipaddr.prefix;
                                        }
                                }
                        }
@@ -690,7 +690,7 @@ void request_stats_reply(REQUEST *request)
         */
        if (((flag->vp_integer & 0x80) != 0) &&
            ((flag->vp_integer & 0x03) != 0)) {
-               home_server *home;
+               home_server_t *home;
                VALUE_PAIR *server_ip, *server_port;
                fr_ipaddr_t ipaddr;
 
@@ -722,32 +722,32 @@ void request_stats_reply(REQUEST *request)
                pairadd(&request->reply->vps,
                        paircopyvp(request->reply, server_port));
 
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       172, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_integer = home->currently_outstanding;
 
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       173, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_integer = home->state;
 
                if ((home->state == HOME_STATE_ALIVE) &&
                    (home->revive_time.tv_sec != 0)) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               175, VENDORPEC_FREERADIUS);
                        if (vp) vp->vp_date = home->revive_time.tv_sec;
                }
 
                if ((home->state == HOME_STATE_ALIVE) &&
                    (home->ema.window > 0)) {
-                               vp = radius_paircreate(request,
+                               vp = radius_paircreate(request->reply,
                                                       &request->reply->vps,
                                                       178, VENDORPEC_FREERADIUS);
                                if (vp) vp->vp_integer = home->ema.window;
-                               vp = radius_paircreate(request,
+                               vp = radius_paircreate(request->reply,
                                                       &request->reply->vps,
                                                       179, VENDORPEC_FREERADIUS);
                                if (vp) vp->vp_integer = home->ema.ema1 / EMA_SCALE;
-                               vp = radius_paircreate(request,
+                               vp = radius_paircreate(request->reply,
                                                       &request->reply->vps,
                                                       180, VENDORPEC_FREERADIUS);
                                if (vp) vp->vp_integer = home->ema.ema10 / EMA_SCALE;
@@ -755,7 +755,7 @@ void request_stats_reply(REQUEST *request)
                }
 
                if (home->state == HOME_STATE_IS_DEAD) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               174, VENDORPEC_FREERADIUS);
                        if (vp) vp->vp_date = home->zombie_period_start.tv_sec + home->zombie_period;
                }
@@ -765,11 +765,11 @@ void request_stats_reply(REQUEST *request)
                 *
                 *      FIXME: do this for clients, too!
                 */
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       184, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = home->last_packet_recv;
 
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       185, VENDORPEC_FREERADIUS);
                if (vp) vp->vp_date = home->last_packet_sent;
 
index 2f57b27..c30461a 100644 (file)
@@ -112,10 +112,10 @@ typedef struct thread_fork_t {
 
 #ifdef WITH_STATS
 typedef struct fr_pps_t {
-       int     pps_old;
-       int     pps_now;
-       int     pps;
-       time_t  time_old;
+       uint32_t        pps_old;
+       uint32_t        pps_now;
+       uint32_t        pps;
+       time_t          time_old;
 } fr_pps_t;
 #endif
 
@@ -127,24 +127,25 @@ typedef struct fr_pps_t {
  */
 typedef struct THREAD_POOL {
 #ifndef WITH_GCD
-       THREAD_HANDLE *head;
-       THREAD_HANDLE *tail;
-
-       int active_threads;     /* protected by queue_mutex */
-       int exited_threads;
-       int total_threads;
-       int max_thread_num;
-       int start_threads;
-       int max_threads;
-       int min_spare_threads;
-       int max_spare_threads;
-       unsigned int max_requests_per_thread;
-       unsigned long request_count;
-       time_t time_last_spawned;
-       int cleanup_delay;
-       int stop_flag;
+       THREAD_HANDLE   *head;
+       THREAD_HANDLE   *tail;
+
+       uint32_t        active_threads; /* protected by queue_mutex */
+       uint32_t        total_threads;
+
+       uint32_t        exited_threads;
+       uint32_t        max_thread_num;
+       uint32_t        start_threads;
+       uint32_t        max_threads;
+       uint32_t        min_spare_threads;
+       uint32_t        max_spare_threads;
+       uint32_t        max_requests_per_thread;
+       uint32_t        request_count;
+       time_t          time_last_spawned;
+       uint32_t        cleanup_delay;
+       bool            stop_flag;
 #endif /* WITH_GCD */
-       int spawn_flag;
+       bool            spawn_flag;
 
 #ifdef WNOHANG
        pthread_mutex_t wait_mutex;
@@ -173,14 +174,14 @@ typedef struct THREAD_POOL {
         */
        pthread_mutex_t queue_mutex;
 
-       int             max_queue_size;
-       int             num_queued;
+       uint32_t        max_queue_size;
+       uint32_t        num_queued;
        fr_fifo_t       *fifo[NUM_FIFOS];
 #endif /* WITH_GCD */
 } THREAD_POOL;
 
 static THREAD_POOL thread_pool;
-static int pool_initialized = false;
+static bool pool_initialized = false;
 
 #ifndef WITH_GCD
 static time_t last_cleaned = 0;
@@ -193,16 +194,16 @@ static void thread_pool_manage(time_t now);
  *     A mapping of configuration file names to internal integers
  */
 static const CONF_PARSER thread_config[] = {
-       { "start_servers",         PW_TYPE_INTEGER, 0, &thread_pool.start_threads,         "5" },
-       { "max_servers",             PW_TYPE_INTEGER, 0, &thread_pool.max_threads,           "32" },
-       { "min_spare_servers",       PW_TYPE_INTEGER, 0, &thread_pool.min_spare_threads,       "3" },
-       { "max_spare_servers",       PW_TYPE_INTEGER, 0, &thread_pool.max_spare_threads,       "10" },
-       { "max_requests_per_server", PW_TYPE_INTEGER, 0, &thread_pool.max_requests_per_thread, "0" },
-       { "cleanup_delay",         PW_TYPE_INTEGER, 0, &thread_pool.cleanup_delay,         "5" },
-       { "max_queue_size",       PW_TYPE_INTEGER, 0, &thread_pool.max_queue_size,        "65536" },
+       { "start_servers", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.start_threads), "5" },
+       { "max_servers", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.max_threads), "32" },
+       { "min_spare_servers", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.min_spare_threads), "3" },
+       { "max_spare_servers", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.max_spare_threads), "10" },
+       { "max_requests_per_server", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.max_requests_per_thread), "0" },
+       { "cleanup_delay", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.cleanup_delay), "5" },
+       { "max_queue_size", FR_CONF_POINTER(PW_TYPE_INTEGER, &thread_pool.max_queue_size), "65536" },
 #ifdef WITH_STATS
 #ifdef WITH_ACCOUNTING
-       { "auto_limit_acct",         PW_TYPE_BOOLEAN, 0, &thread_pool.auto_limit_acct, NULL },
+       { "auto_limit_acct", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &thread_pool.auto_limit_acct), NULL },
 #endif
 #endif
        { NULL, -1, 0, NULL, NULL }
@@ -243,13 +244,6 @@ static int setup_ssl_mutexes(void)
 {
        int i;
 
-#ifdef HAVE_OPENSSL_EVP_H
-       /*
-        *      Enable all ciphers and digests.
-        */
-       OpenSSL_add_all_algorithms();
-#endif
-
        ssl_mutexes = rad_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t));
        if (!ssl_mutexes) {
                ERROR("Error allocating memory for SSL mutexes!");
@@ -321,6 +315,8 @@ static void reap_children(void)
  */
 int request_enqueue(REQUEST *request)
 {
+       rad_assert(pool_initialized == true);
+
        /*
         *      If we haven't checked the number of child threads
         *      in a while, OR if the thread pool appears to be full,
@@ -370,11 +366,11 @@ int request_enqueue(REQUEST *request)
                 *      A probabilistic approach allows us to process
                 *      SOME of the new accounting packets.
                 */
-               if ((request->packet->code == PW_ACCOUNTING_REQUEST) &&
+               if ((request->packet->code == PW_CODE_ACCOUNTING_REQUEST) &&
                    (thread_pool.num_queued > (thread_pool.max_queue_size / 2)) &&
                    (thread_pool.pps_in.pps_now > thread_pool.pps_out.pps_now)) {
                        uint32_t prob;
-                       int keep;
+                       uint32_t keep;
 
                        /*
                         *      Take a random value of how full we
@@ -417,28 +413,18 @@ int request_enqueue(REQUEST *request)
        thread_pool.request_count++;
 
        if (thread_pool.num_queued >= thread_pool.max_queue_size) {
-               int complain = false;
-               time_t now;
-               static time_t last_complained = 0;
-
-               now = time(NULL);
-               if (last_complained != now) {
-                       last_complained = now;
-                       complain = true;
-               }
-
                pthread_mutex_unlock(&thread_pool.queue_mutex);
 
                /*
                 *      Mark the request as done.
                 */
-               if (complain) {
-                       ERROR("Something is blocking the server.  There are %d packets in the queue, waiting to be processed.  Ignoring the new request.", thread_pool.max_queue_size);
-               }
+               RATE_LIMIT(ERROR("Something is blocking the server.  There are %d packets in the queue, "
+                                "waiting to be processed.  Ignoring the new request.", thread_pool.num_queued));
                return 0;
        }
        request->component = "<core>";
        request->module = "<queue>";
+       request->child_state = REQUEST_QUEUED;
 
        /*
         *      Push the request onto the appropriate fifo for that
@@ -480,6 +466,8 @@ static int request_dequeue(REQUEST **prequest)
        REQUEST *request;
        reap_children();
 
+       rad_assert(pool_initialized == true);
+
        pthread_mutex_lock(&thread_pool.queue_mutex);
 
 #ifdef WITH_STATS
@@ -559,6 +547,7 @@ static int request_dequeue(REQUEST **prequest)
 
        request->component = "<core>";
        request->module = "";
+       request->child_state = REQUEST_RUNNING;
 
        /*
         *      If the request has sat in the queue for too long,
@@ -580,7 +569,7 @@ static int request_dequeue(REQUEST **prequest)
        thread_pool.active_threads++;
 
        blocked = time(NULL);
-       if ((blocked - request->timestamp) > 5) {
+       if (!request->proxy && (blocked - request->timestamp) > 5) {
                total_blocked++;
                if (last_complained < blocked) {
                        last_complained = blocked;
@@ -635,14 +624,14 @@ static void *request_handler_thread(void *arg)
                                goto re_wait;
                        }
                        ERROR("Thread %d failed waiting for semaphore: %s: Exiting\n",
-                              self->thread_num, strerror(errno));
+                              self->thread_num, fr_syserror(errno));
                        break;
                }
 
                DEBUG2("Thread %d got semaphore", self->thread_num);
 
 #ifdef HAVE_OPENSSL_ERR_H
-               /*
+               /*
                 *      Clear the error queue for the current thread.
                 */
                ERR_clear_error ();
@@ -670,7 +659,7 @@ static void *request_handler_thread(void *arg)
                       self->request_count);
 
 #ifdef WITH_ACCOUNTING
-               if ((self->request->packet->code == PW_ACCOUNTING_REQUEST) &&
+               if ((self->request->packet->code == PW_CODE_ACCOUNTING_REQUEST) &&
                    thread_pool.auto_limit_acct) {
                        VALUE_PAIR *vp;
                        REQUEST *request = self->request;
@@ -828,8 +817,9 @@ static THREAD_HANDLE *spawn_thread(time_t now, int do_trigger)
        rcode = pthread_create(&handle->pthread_id, 0,
                        request_handler_thread, handle);
        if (rcode != 0) {
+               free(handle);
                ERROR("Thread create failed: %s",
-                      strerror(rcode));
+                      fr_syserror(rcode));
                return NULL;
        }
 
@@ -889,10 +879,11 @@ static int pid_cmp(void const *one, void const *two)
  *
  *     FIXME: What to do on a SIGHUP???
  */
-int thread_pool_init(UNUSED CONF_SECTION *cs, int *spawn_flag)
+int thread_pool_init(UNUSED CONF_SECTION *cs, bool *spawn_flag)
 {
 #ifndef WITH_GCD
-       int             i, rcode;
+       uint32_t        i;
+       int             rcode;
        CONF_SECTION    *pool_cf;
 #endif
        time_t          now;
@@ -918,7 +909,7 @@ int thread_pool_init(UNUSED CONF_SECTION *cs, int *spawn_flag)
        thread_pool.total_threads = 0;
        thread_pool.max_thread_num = 1;
        thread_pool.cleanup_delay = 5;
-       thread_pool.stop_flag = 0;
+       thread_pool.stop_flag = false;
 #endif
        thread_pool.spawn_flag = *spawn_flag;
 
@@ -931,7 +922,7 @@ int thread_pool_init(UNUSED CONF_SECTION *cs, int *spawn_flag)
 #ifdef WNOHANG
        if ((pthread_mutex_init(&thread_pool.wait_mutex,NULL) != 0)) {
                ERROR("FATAL: Failed to initialize wait mutex: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                return -1;
        }
 
@@ -967,6 +958,12 @@ int thread_pool_init(UNUSED CONF_SECTION *cs, int *spawn_flag)
                ERROR("FATAL: max_queue_size value must be in range 2-1048576");
                return -1;
        }
+
+       if (thread_pool.start_threads > thread_pool.max_threads) {
+               ERROR("FATAL: start_servers (%i) must be <= max_servers (%i)",
+                     thread_pool.start_threads, thread_pool.max_threads);
+               return -1;
+       }
 #endif /* WITH_GCD */
 
        /*
@@ -985,14 +982,14 @@ int thread_pool_init(UNUSED CONF_SECTION *cs, int *spawn_flag)
        rcode = sem_init(&thread_pool.semaphore, 0, SEMAPHORE_LOCKED);
        if (rcode != 0) {
                ERROR("FATAL: Failed to initialize semaphore: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                return -1;
        }
 
        rcode = pthread_mutex_init(&thread_pool.queue_mutex,NULL);
        if (rcode != 0) {
                ERROR("FATAL: Failed to initialize queue mutex: %s",
-                      strerror(errno));
+                      fr_syserror(errno));
                return -1;
        }
 
@@ -1034,7 +1031,7 @@ int thread_pool_init(UNUSED CONF_SECTION *cs, int *spawn_flag)
 #else
        thread_pool.queue = dispatch_queue_create("org.freeradius.threads", NULL);
        if (!thread_pool.queue) {
-               ERROR("Failed creating dispatch queue: %s", strerror(errno));
+               ERROR("Failed creating dispatch queue: %s", fr_syserror(errno));
                fr_exit(1);
        }
 #endif
@@ -1056,10 +1053,12 @@ void thread_pool_stop(void)
        THREAD_HANDLE *handle;
        THREAD_HANDLE *next;
 
+       if (!pool_initialized) return;
+
        /*
         *      Set pool stop flag.
         */
-       thread_pool.stop_flag = 1;
+       thread_pool.stop_flag = true;
 
        /*
         *      Wakeup all threads to make them see stop flag.
@@ -1105,10 +1104,10 @@ int request_enqueue(REQUEST *request)
  */
 static void thread_pool_manage(time_t now)
 {
-       int spare;
+       uint32_t spare;
        int i, total;
        THREAD_HANDLE *handle, *next;
-       int active_threads;
+       uint32_t active_threads;
 
        /*
         *      Loop over the thread pool, deleting exited threads.
@@ -1138,13 +1137,12 @@ static void thread_pool_manage(time_t now)
        active_threads = thread_pool.active_threads;
        spare = thread_pool.total_threads - active_threads;
        if (debug_flag) {
-               static int old_total = -1;
-               static int old_active = -1;
+               static uint32_t old_total = 0;
+               static uint32_t old_active = 0;
 
-               if ((old_total != thread_pool.total_threads) ||
-                               (old_active != active_threads)) {
+               if ((old_total != thread_pool.total_threads) || (old_active != active_threads)) {
                        DEBUG2("Threads: total/active/spare threads = %d/%d/%d",
-                                       thread_pool.total_threads, active_threads, spare);
+                              thread_pool.total_threads, active_threads, spare);
                        old_total = thread_pool.total_threads;
                        old_active = active_threads;
                }
@@ -1190,7 +1188,7 @@ static void thread_pool_manage(time_t now)
         *      passed since we last created one.  This helps to minimize
         *      the amount of create/delete cycles.
         */
-       if ((now - thread_pool.time_last_spawned) < thread_pool.cleanup_delay) {
+       if ((now - thread_pool.time_last_spawned) < (int)thread_pool.cleanup_delay) {
                return;
        }
 
@@ -1393,7 +1391,7 @@ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quen
         *      Use global "trigger" section if no local config is given.
         */
        if (!cs) {
-               cs = mainconfig.config;
+               cs = main_config.config;
                attr = name;
        } else {
                /*
@@ -1413,21 +1411,21 @@ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quen
         *      reference to the full path, rather than the sub-path.
         */
        subcs = cf_section_sub_find(cs, "trigger");
-       if (!subcs && (cs != mainconfig.config)) {
-               subcs = cf_section_sub_find(mainconfig.config, "trigger");
+       if (!subcs && (cs != main_config.config)) {
+               subcs = cf_section_sub_find(main_config.config, "trigger");
                attr = name;
        }
 
        if (!subcs) return;
 
-       ci = cf_reference_item(subcs, mainconfig.config, attr);
+       ci = cf_reference_item(subcs, main_config.config, attr);
        if (!ci) {
-               RDEBUG3("No such item in trigger section: %s", attr);
+               ERROR("No such item in trigger section: %s", attr);
                return;
        }
 
        if (!cf_item_is_pair(ci)) {
-               RDEBUG2("Trigger is not a configuration variable: %s", attr);
+               ERROR("Trigger is not a configuration variable: %s", attr);
                return;
        }
 
@@ -1436,7 +1434,7 @@ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quen
 
        value = cf_pair_value(cp);
        if (!value) {
-               RDEBUG2("Trigger has no value: %s", name);
+               ERROR("Trigger has no value: %s", name);
                return;
        }
 
@@ -1474,6 +1472,6 @@ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, int quen
                }
        }
 
-       RDEBUG("Trigger %s -> %s", name, value);
+       DEBUG("Trigger %s -> %s", name, value);
        radius_exec_program(request, value, false, true, NULL, 0, EXEC_TIMEOUT, vp, NULL);
 }
index cdff110..c44122e 100644 (file)
@@ -51,7 +51,26 @@ USES_APPLE_DEPRECATED_API    /* OpenSSL API has been deprecated by Apple */
 #include <openssl/ocsp.h>
 #endif
 
-static void tls_server_conf_free(fr_tls_server_conf_t *conf);
+typedef struct libssl_defect {
+       uint64_t        high;
+       uint64_t        low;
+
+       char const      *id;
+       char const      *name;
+       char const      *comment;
+} libssl_defect_t;
+
+/* Record critical defects in libssl here (newest first)*/
+static libssl_defect_t libssl_defects[] =
+{
+       {
+               .low            = 0x010001000,          /* 1.0.1  */
+               .high           = 0x01000106f,          /* 1.0.1f */
+               .id             = "CVE-2014-0160",
+               .name           = "Heartbleed",
+               .comment        = "For more information see http://heartbleed.com"
+       }
+};
 
 /* record */
 static void            record_init(record_t *buf);
@@ -147,13 +166,23 @@ tls_session_t *tls_new_client_session(fr_tls_server_conf_t *conf, int fd)
 {
        int verify_mode;
        tls_session_t *ssn = NULL;
+       REQUEST *request;
 
        ssn = talloc_zero(conf, tls_session_t);
        if (!ssn) return NULL;
 
        ssn->ctx = conf->ctx;
+
+       SSL_CTX_set_mode(ssn->ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | SSL_MODE_AUTO_RETRY);
+
        ssn->ssl = SSL_new(ssn->ctx);
-       rad_assert(ssn->ssl != NULL);
+       if (!ssn->ssl) {
+               talloc_free(ssn);
+               return NULL;
+       }
+
+       request = talloc_zero(ssn, REQUEST);
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request);
 
        /*
         *      Add the message callback to identify what type of
@@ -177,10 +206,11 @@ tls_session_t *tls_new_client_session(fr_tls_server_conf_t *conf, int fd)
        if (SSL_connect(ssn->ssl) <= 0) {
                int err;
                while ((err = ERR_get_error())) {
-                       DEBUG("OpenSSL Err says %s",
-                             ERR_error_string(err, NULL));
+                       ERROR("tls: %s", ERR_error_string(err, NULL));
                }
+               SSL_free(ssn->ssl);
                talloc_free(ssn);
+
                return NULL;
        }
 
@@ -207,7 +237,7 @@ tls_session_t *tls_new_session(fr_tls_server_conf_t *conf, REQUEST *request,
         *      FIXME: Also do it every N sessions?
         */
        if (conf->session_cache_enable &&
-           ((conf->session_last_flushed + (conf->session_timeout * 1800)) <= request->timestamp)){
+           ((conf->session_last_flushed + ((int)conf->session_timeout * 1800)) <= request->timestamp)){
                RDEBUG2("Flushing SSL sessions (of #%ld)",
                        SSL_CTX_sess_number(conf->ctx));
 
@@ -388,6 +418,8 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
 {
        int err;
 
+       if (ssn->invalid_hb_used) return 0;
+
        err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used);
        if (err != (int) ssn->dirty_in.used) {
                RDEBUG("Failed writing %d to SSL BIO: %d", ssn->dirty_in.used,
@@ -412,16 +444,16 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
        if (SSL_is_init_finished(ssn->ssl)) {
                DEBUG2("SSL Connection Established\n");
        }
-       if (SSL_in_init(ssn->ssl)) {
+       if (SSL_in_init(ssn->ssl)) {
                DEBUG2("In SSL Handshake Phase\n");
        }
-       if (SSL_in_before(ssn->ssl)) {
+       if (SSL_in_before(ssn->ssl)) {
                DEBUG2("Before SSL Handshake Phase\n");
        }
-       if (SSL_in_accept_init(ssn->ssl)) {
+       if (SSL_in_accept_init(ssn->ssl)) {
                DEBUG2("In SSL Accept mode \n");
        }
-       if (SSL_in_connect_init(ssn->ssl)) {
+       if (SSL_in_connect_init(ssn->ssl)) {
                DEBUG2("In SSL Connect mode \n");
        }
 
@@ -454,7 +486,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
 }
 
 /*
- *     Take clear-text user data, and encrypt it into the output buffer,
+ *     Take cleartext user data, and encrypt it into the output buffer,
  *     to send to the client at the other end of the SSL connection.
  */
 int tls_handshake_send(REQUEST *request, tls_session_t *ssn)
@@ -513,8 +545,10 @@ void session_close(tls_session_t *ssn)
        SSL_set_quiet_shutdown(ssn->ssl, 1);
        SSL_shutdown(ssn->ssl);
 
-       if(ssn->ssl)
+       if (ssn->ssl) {
                SSL_free(ssn->ssl);
+               ssn->ssl = NULL;
+       }
 
        record_close(&ssn->clean_in);
        record_close(&ssn->clean_out);
@@ -782,117 +816,81 @@ void tls_session_information(tls_session_t *tls_session)
                 str_details1, str_details2);
 
        request = SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST);
-
-       RDEBUG2("%s\n", tls_session->info.info_description);
+       if (request) {
+               RDEBUG2("%s", tls_session->info.info_description);
+       } else {
+               DEBUG2("%s", tls_session->info.info_description);
+       }
 }
 
 static CONF_PARSER cache_config[] = {
-       { "enable", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, session_cache_enable), NULL, "no" },
-       { "lifetime", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, session_timeout), NULL, "24" },
-       { "max_entries", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, session_cache_size), NULL, "255" },
-       { "name", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, session_id_name), NULL, NULL},
-       { "persist_dir", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, session_cache_path), NULL, NULL},
+       { "enable", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, session_cache_enable), "no" },
+       { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, session_timeout), "24" },
+       { "max_entries", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, session_cache_size), "255" },
+       { "name", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, session_id_name), NULL },
+       { "persist_dir", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, session_cache_path), NULL },
        { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 static CONF_PARSER verify_config[] = {
-       { "tmpdir", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, verify_tmp_dir), NULL, NULL},
-       { "client", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, verify_client_cert_cmd), NULL, NULL},
+       { "tmpdir", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, verify_tmp_dir), NULL },
+       { "client", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, verify_client_cert_cmd), NULL },
        { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 #ifdef HAVE_OPENSSL_OCSP_H
 static CONF_PARSER ocsp_config[] = {
-       { "enable", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_enable), NULL, "no"},
-       { "override_cert_url", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_override_url), NULL, "no"},
-       { "url", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, ocsp_url), NULL, NULL },
-       { "use_nonce", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_use_nonce), NULL, "yes"},
-       { "timeout", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, ocsp_timeout), NULL, "yes"},
-       { "softfail", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, ocsp_softfail), NULL, "yes"},
+       { "enable", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_enable), "no" },
+       { "override_cert_url", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_override_url), "no" },
+       { "url", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ocsp_url), NULL },
+       { "use_nonce", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_use_nonce), "yes" },
+       { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, ocsp_timeout), "yes" },
+       { "softfail", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, ocsp_softfail), "yes" },
        { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 #endif
 
 static CONF_PARSER tls_server_config[] = {
-       { "rsa_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, rsa_key), NULL, "no" },
-       { "dh_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, dh_key), NULL, "yes" },
-       { "rsa_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, rsa_key_length), NULL, "512" },
-       { "dh_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, dh_key_length), NULL, "512" },
-       { "verify_depth", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, verify_depth), NULL, "0" },
-       { "CA_path", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED,
-         offsetof(fr_tls_server_conf_t, ca_path), NULL, NULL },
-       { "ca_path", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_path), NULL, NULL },
-       { "pem_file_type", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, file_type), NULL, "yes" },
-       { "private_key_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, certificate_file), NULL, NULL },
-       { "CA_file", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "ca_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, private_key_password), NULL, NULL },
+       { "rsa_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, rsa_key), "no" },
+       { "dh_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, dh_key), "yes" },
+       { "rsa_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, rsa_key_length), "512" },
+       { "dh_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, dh_key_length), "512" },
+       { "verify_depth", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, verify_depth), "0" },
+       { "CA_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, fr_tls_server_conf_t, ca_path), NULL },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_path), NULL },
+       { "pem_file_type", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, file_type), "yes" },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, private_key_file), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, certificate_file), NULL },
+       { "CA_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, fr_tls_server_conf_t, ca_file), NULL },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_file), NULL },
+       { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, private_key_password), NULL },
 #ifdef PSK_MAX_IDENTITY_LEN
-       { "psk_identity", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, psk_identity), NULL, NULL },
-       { "psk_hexphrase", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, psk_password), NULL, NULL },
+       { "psk_identity", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, psk_identity), NULL },
+       { "psk_hexphrase", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, psk_password), NULL },
 #endif
-       { "dh_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, dh_file), NULL, NULL },
-       { "random_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, random_file), NULL, NULL },
-       { "fragment_size", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, fragment_size), NULL, "1024" },
-       { "include_length", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, include_length), NULL, "yes" },
-       { "check_crl", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, check_crl), NULL, "no"},
-       { "allow_expired_crl", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, allow_expired_crl), NULL, NULL},
-       { "check_cert_cn", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_cn), NULL, NULL},
-       { "cipher_list", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, cipher_list), NULL, NULL},
-       { "check_cert_issuer", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_issuer), NULL, NULL},
-       { "require_client_cert", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, require_client_cert), NULL, NULL },
+       { "dh_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, dh_file), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, random_file), NULL },
+       { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, fragment_size), "1024" },
+       { "include_length", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, include_length), "yes" },
+       { "check_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_crl), "no" },
+       { "allow_expired_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, allow_expired_crl), NULL },
+       { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_cn), NULL },
+       { "cipher_list", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, cipher_list), NULL },
+       { "check_cert_issuer", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_issuer), NULL },
+       { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, require_client_cert), NULL },
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
-       { "ecdh_curve", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, ecdh_curve), NULL, "prime256v1"},
+       { "ecdh_curve", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ecdh_curve), "prime256v1" },
 #endif
 #endif
 
-       { "cache", PW_TYPE_SUBSECTION, 0, NULL, (void const *) cache_config },
+       { "cache", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) cache_config },
 
-       { "verify", PW_TYPE_SUBSECTION, 0, NULL, (void const *) verify_config },
+       { "verify", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) verify_config },
 
 #ifdef HAVE_OPENSSL_OCSP_H
-       { "ocsp", PW_TYPE_SUBSECTION, 0, NULL, (void const *) ocsp_config },
+       { "ocsp", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) ocsp_config },
 #endif
 
        { NULL, -1, 0, NULL, NULL }        /* end the list */
@@ -900,59 +898,33 @@ static CONF_PARSER tls_server_config[] = {
 
 
 static CONF_PARSER tls_client_config[] = {
-       { "rsa_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, rsa_key), NULL, "no" },
-       { "dh_key_exchange", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, dh_key), NULL, "yes" },
-       { "rsa_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, rsa_key_length), NULL, "512" },
-       { "dh_key_length", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, dh_key_length), NULL, "512" },
-       { "verify_depth", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, verify_depth), NULL, "0" },
-       { "ca_path", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_path), NULL, NULL },
-       { "pem_file_type", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, file_type), NULL, "yes" },
-       { "private_key_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, private_key_file), NULL, NULL },
-       { "certificate_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, certificate_file), NULL, NULL },
-       { "ca_file", PW_TYPE_FILE_INPUT,
-         offsetof(fr_tls_server_conf_t, ca_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, private_key_password), NULL, NULL },
-#ifdef PSK_MAX_IDENTITY_LEN
-       { "psk_identity", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, psk_identity), NULL, NULL },
-       { "psk_hexphrase", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, psk_password), NULL, NULL },
-#endif
-       { "dh_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, dh_file), NULL, NULL },
-       { "random_file", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, random_file), NULL, NULL },
-       { "fragment_size", PW_TYPE_INTEGER,
-         offsetof(fr_tls_server_conf_t, fragment_size), NULL, "1024" },
-       { "include_length", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, include_length), NULL, "yes" },
-       { "check_crl", PW_TYPE_BOOLEAN,
-         offsetof(fr_tls_server_conf_t, check_crl), NULL, "no"},
-       { "check_cert_cn", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_cn), NULL, NULL},
-       { "cipher_list", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, cipher_list), NULL, NULL},
-       { "check_cert_issuer", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, check_cert_issuer), NULL, NULL},
+       { "rsa_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, rsa_key), "no" },
+       { "dh_key_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, dh_key), "yes" },
+       { "rsa_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, rsa_key_length), "512" },
+       { "dh_key_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, dh_key_length), "512" },
+       { "verify_depth", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, verify_depth), "0" },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_path), NULL },
+       { "pem_file_type", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, file_type), "yes" },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, private_key_file), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, certificate_file), NULL },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_file), NULL },
+       { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, private_key_password), NULL },
+       { "dh_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, dh_file), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, random_file), NULL },
+       { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, fragment_size), "1024" },
+       { "include_length", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, include_length), "yes" },
+       { "check_crl", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, fr_tls_server_conf_t, check_crl), "no" },
+       { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_cn), NULL },
+       { "cipher_list", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, cipher_list), NULL },
+       { "check_cert_issuer", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, check_cert_issuer), NULL },
 
 #if OPENSSL_VERSION_NUMBER >= 0x0090800fL
 #ifndef OPENSSL_NO_ECDH
-       { "ecdh_curve", PW_TYPE_STRING_PTR,
-         offsetof(fr_tls_server_conf_t, ecdh_curve), NULL, "prime256v1"},
+       { "ecdh_curve", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, ecdh_curve), "prime256v1" },
 #endif
 #endif
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 
@@ -964,6 +936,8 @@ static int load_dh_params(SSL_CTX *ctx, char *file)
        DH *dh = NULL;
        BIO *bio;
 
+       if (!file) return 0;
+
        if ((bio = BIO_new_file(file, "r")) == NULL) {
                ERROR("tls: Unable to open DH file - %s", file);
                return -1;
@@ -972,8 +946,8 @@ static int load_dh_params(SSL_CTX *ctx, char *file)
        dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
        BIO_free(bio);
        if (!dh) {
-               WDEBUG2("tls: Unable to set DH parameters.  DH cipher suites may not work!");
-               WDEBUG2("Fix this by running the OpenSSL command listed in eap.conf");
+               WARN("tls: Unable to set DH parameters.  DH cipher suites may not work!");
+               WARN("Fix this by running the OpenSSL command listed in eap.conf");
                return 0;
        }
 
@@ -1010,6 +984,7 @@ static int generate_eph_rsa_key(SSL_CTX *ctx)
  * needs to be dynamic so we can supply a "free" function
  */
 static int FR_TLS_EX_INDEX_VPS = -1;
+int FR_TLS_EX_INDEX_CERTS = -1;
 
 /*
  *     Print debugging messages, and free data.
@@ -1042,7 +1017,7 @@ static void cbtls_remove_session(SSL_CTX *ctx, SSL_SESSION *sess)
                         conf->session_cache_path, FR_DIR_SEP, buffer);
                rv = unlink(filename);
                if (rv != 0) {
-                       DEBUG2("  SSL: could not remove persisted session file %s: %s", filename, strerror(errno));
+                       DEBUG2("  SSL: could not remove persisted session file %s: %s", filename, fr_syserror(errno));
                }
                /* VPs might be absent; might not have been written to disk yet */
                snprintf(filename, sizeof(filename), "%s%c%s.vps",
@@ -1100,7 +1075,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                         conf->session_cache_path, FR_DIR_SEP, buffer);
                fd = open(filename, O_RDWR|O_CREAT|O_EXCL, 0600);
                if (fd < 0) {
-                       DEBUG2("  SSL: could not open session file %s: %s", filename, strerror(errno));
+                       DEBUG2("  SSL: could not open session file %s: %s", filename, fr_syserror(errno));
                        goto error;
                }
 
@@ -1109,7 +1084,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
                while (todo > 0) {
                        rv = write(fd, p, todo);
                        if (rv < 1) {
-                               DEBUG2("  SSL: failed writing session: %s", strerror(errno));
+                               DEBUG2("  SSL: failed writing session: %s", fr_syserror(errno));
                                close(fd);
                                goto error;
                        }
@@ -1133,6 +1108,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
        size_t size;
        char buffer[2 * MAX_SESSION_SIZE + 1];
        fr_tls_server_conf_t *conf;
+       TALLOC_CTX *talloc_ctx;
 
        SSL_SESSION *sess = NULL;
        unsigned char *sess_data = NULL;
@@ -1146,6 +1122,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
        DEBUG2("  SSL: Client requested cached session %s", buffer);
 
        conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CONF);
+       talloc_ctx = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TALLOC);
        if (conf && conf->session_cache_path) {
                int rv, fd, todo;
                char filename[256];
@@ -1168,13 +1145,13 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                         conf->session_cache_path, FR_DIR_SEP, buffer);
                fd = open(filename, O_RDONLY);
                if (fd < 0) {
-                       DEBUG2("  SSL: could not find persisted session file %s: %s", filename, strerror(errno));
+                       DEBUG2("  SSL: could not find persisted session file %s: %s", filename, fr_syserror(errno));
                        goto err;
                }
 
                rv = fstat(fd, &st);
                if (rv < 0) {
-                       DEBUG2("  SSL: could not stat persisted session file %s: %s", filename, strerror(errno));
+                       DEBUG2("  SSL: could not stat persisted session file %s: %s", filename, fr_syserror(errno));
                        close(fd);
                        goto err;
                }
@@ -1191,7 +1168,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                while (todo > 0) {
                        rv = read(fd, p, todo);
                        if (rv < 1) {
-                               DEBUG2("  SSL: could not read from persisted session: %s", strerror(errno));
+                               DEBUG2("  SSL: could not read from persisted session: %s", fr_syserror(errno));
                                close(fd);
                                goto err;
                        }
@@ -1210,7 +1187,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl,
                }
 
                /* cache the VPs into the session */
-               vp = paircopy(NULL, pairlist->reply);
+               vp = paircopy(talloc_ctx, pairlist->reply);
                SSL_SESSION_set_ex_data(sess, FR_TLS_EX_INDEX_VPS, vp);
                DEBUG2("  SSL: Successfully restored session %s", buffer);
        }
@@ -1297,15 +1274,19 @@ static int ocsp_check(X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
         */
 
        /* Get OCSP responder URL */
-       if(conf->ocsp_override_url) {
-               OCSP_parse_url(conf->ocsp_url, &host, &port, &path, &use_ssl);
+       if (conf->ocsp_override_url) {
+               char *url;
+
+               memcpy(&url, &conf->ocsp_url, sizeof(url));
+               /* Reading the libssl src, they do a strdup on the URL, so it could of been const *sigh* */
+               OCSP_parse_url(url, &host, &port, &path, &use_ssl);
        }
        else {
                ocsp_parse_cert_url(client_cert, &host, &port, &path, &use_ssl);
        }
 
        if (!host || !port || !path) {
-               DEBUG2("[ocsp] - Host / port / path missing.  Not doing OCSP.");
+               DEBUG2("[ocsp] - Host / port / path missing.  Not doing OCSP");
                ocsp_ok = 2;
                goto ocsp_skip;
        }
@@ -1445,8 +1426,8 @@ ocsp_end:
                break;
        case 2:
                if (conf->ocsp_softfail) {
-                       DEBUG2("[ocsp] --> Unable to check certificate; assuming valid.");
-                       DEBUG2("[ocsp] --> Warning! This may be insecure.");
+                       DEBUG2("[ocsp] --> Unable to check certificate; assuming valid");
+                       DEBUG2("[ocsp] --> Warning! This may be insecure");
                        ocsp_ok = 1;
                } else {
                        DEBUG2("[ocsp] --> Unable to check certificate; failing!");
@@ -1465,13 +1446,15 @@ ocsp_end:
 /*
  *     For creating certificate attributes.
  */
-static char const *cert_attr_names[6][2] = {
+static char const *cert_attr_names[8][2] = {
   { "TLS-Client-Cert-Serial",          "TLS-Cert-Serial" },
   { "TLS-Client-Cert-Expiration",      "TLS-Cert-Expiration" },
   { "TLS-Client-Cert-Subject",         "TLS-Cert-Subject" },
   { "TLS-Client-Cert-Issuer",          "TLS-Cert-Issuer" },
   { "TLS-Client-Cert-Common-Name",     "TLS-Cert-Common-Name" },
-  { "TLS-Client-Cert-Subject-Alt-Name-Email",  "TLS-Cert-Subject-Alt-Name-Email" }
+  { "TLS-Client-Cert-Subject-Alt-Name-Email",  "TLS-Cert-Subject-Alt-Name-Email" },
+  { "TLS-Client-Cert-Subject-Alt-Name-Dns",    "TLS-Cert-Subject-Alt-Name-Dns" },
+  { "TLS-Client-Cert-Subject-Alt-Name-Upn",    "TLS-Cert-Subject-Alt-Name-Upn" }
 };
 
 #define FR_TLS_SERIAL          (0)
@@ -1480,6 +1463,8 @@ static char const *cert_attr_names[6][2] = {
 #define FR_TLS_ISSUER          (3)
 #define FR_TLS_CN              (4)
 #define FR_TLS_SAN_EMAIL               (5)
+#define FR_TLS_SAN_DNS          (6)
+#define FR_TLS_SAN_UPN          (7)
 
 /*
  *     Before trusting a certificate, you must make sure that the
@@ -1531,6 +1516,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        X509_STORE *ocsp_store = NULL;
        X509 *issuer_cert;
 #endif
+       TALLOC_CTX *talloc_ctx;
 
        client_cert = X509_STORE_CTX_get_current_cert(ctx);
        err = X509_STORE_CTX_get_error(ctx);
@@ -1553,17 +1539,16 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        if (!conf) return 1;
 
        request = (REQUEST *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_REQUEST);
-
-       if (!request) return 1; /* FIXME: outbound TLS */
-
        rad_assert(request != NULL);
        certs = (VALUE_PAIR **)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_CERTS);
-       rad_assert(certs != NULL);
+
        identity = (char **)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_IDENTITY);
 #ifdef HAVE_OPENSSL_OCSP_H
        ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_STORE);
 #endif
 
+       talloc_ctx = SSL_get_ex_data(ssl, FR_TLS_EX_INDEX_TALLOC);
+
        /*
         *      Get the Serial Number
         */
@@ -1576,7 +1561,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
         *      have a user identity.  i.e. we don't create the
         *      attributes for RadSec connections.
         */
-       if (identity &&
+       if (certs && identity &&
            (lookup <= 1) && sn && ((size_t) sn->length < (sizeof(buf) / 2))) {
                char *p = buf;
                int i;
@@ -1585,7 +1570,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        sprintf(p, "%02x", (unsigned int)sn->data[i]);
                        p += 2;
                }
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_SERIAL][lookup], buf, T_OP_SET);
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SERIAL][lookup], buf, T_OP_SET);
        }
 
 
@@ -1594,11 +1579,11 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
         */
        buf[0] = '\0';
        asn_time = X509_get_notAfter(client_cert);
-       if (identity && (lookup <= 1) && asn_time &&
+       if (certs && identity && (lookup <= 1) && asn_time &&
            (asn_time->length < (int) sizeof(buf))) {
                memcpy(buf, (char*) asn_time->data, asn_time->length);
                buf[asn_time->length] = '\0';
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_EXPIRATION][lookup], buf, T_OP_SET);
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_EXPIRATION][lookup], buf, T_OP_SET);
        }
 
        /*
@@ -1608,15 +1593,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        X509_NAME_oneline(X509_get_subject_name(client_cert), subject,
                          sizeof(subject));
        subject[sizeof(subject) - 1] = '\0';
-       if (identity && (lookup <= 1) && subject[0]) {
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_SUBJECT][lookup], subject, T_OP_SET);
+       if (certs && identity && (lookup <= 1) && subject[0]) {
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SUBJECT][lookup], subject, T_OP_SET);
        }
 
        X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer,
                          sizeof(issuer));
        issuer[sizeof(issuer) - 1] = '\0';
-       if (identity && (lookup <= 1) && issuer[0]) {
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_ISSUER][lookup], issuer, T_OP_SET);
+       if (certs && identity && (lookup <= 1) && issuer[0]) {
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_ISSUER][lookup], issuer, T_OP_SET);
        }
 
        /*
@@ -1625,16 +1610,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
        X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert),
                                  NID_commonName, common_name, sizeof(common_name));
        common_name[sizeof(common_name) - 1] = '\0';
-       if (identity && (lookup <= 1) && common_name[0] && subject[0]) {
-               pairmake(NULL, certs, cert_attr_names[FR_TLS_CN][lookup], common_name, T_OP_SET);
+       if (certs && identity && (lookup <= 1) && common_name[0] && subject[0]) {
+               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_CN][lookup], common_name, T_OP_SET);
        }
 
-#ifdef GEN_EMAIL
        /*
         *      Get the RFC822 Subject Alternative Name
         */
        loc = X509_get_ext_by_NID(client_cert, NID_subject_alt_name, 0);
-       if (lookup <= 1 && loc >= 0) {
+       if (certs && (lookup <= 1) && (loc >= 0)) {
                X509_EXTENSION *ext = NULL;
                GENERAL_NAMES *names = NULL;
                int i;
@@ -1645,10 +1629,34 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
 
                                switch (name->type) {
+#ifdef GEN_EMAIL
                                case GEN_EMAIL:
-                                       pairmake(NULL, certs, cert_attr_names[FR_TLS_SAN_EMAIL][lookup],
+                                       pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_EMAIL][lookup],
                                                 (char *) ASN1_STRING_data(name->d.rfc822Name), T_OP_SET);
                                        break;
+#endif /* GEN_EMAIL */
+#ifdef GEN_DNS
+                               case GEN_DNS:
+                                       pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_DNS][lookup],
+                                                (char *) ASN1_STRING_data(name->d.dNSName), T_OP_SET);
+                                       break;
+#endif /* GEN_DNS */
+#ifdef GEN_OTHERNAME
+                               case GEN_OTHERNAME:
+                                       /* look for a MS UPN */
+                                       if (NID_ms_upn == OBJ_obj2nid(name->d.otherName->type_id)) {
+                                           /* we've got a UPN - Must be ASN1-encoded UTF8 string */
+                                           if (name->d.otherName->value->type == V_ASN1_UTF8STRING) {
+                                               pairmake(talloc_ctx, certs, cert_attr_names[FR_TLS_SAN_UPN][lookup],
+                                                        (char *) ASN1_STRING_data(name->d.otherName->value->value.utf8string), T_OP_SET);
+                                               break;
+                                           } else {
+                                               RWARN("Invalid UPN in Subject Alt Name (should be UTF-8)\n");
+                                               break;
+                                           }
+                                       }
+                                       break;
+#endif /* GEN_OTHERNAME */
                                default:
                                        /* XXX TODO handle other SAN types */
                                        break;
@@ -1658,7 +1666,6 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                if (names != NULL)
                        sk_GENERAL_NAME_free(names);
        }
-#endif /* GEN_EMAIL */
 
        /*
         *      If the CRL has expired, that might still be OK.
@@ -1724,7 +1731,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                                if (*p == ' ') *p = '-';
                        }
 
-                       vp = pairmake(NULL, certs, attribute, value, T_OP_ADD);
+                       vp = pairmake(talloc_ctx, certs, attribute, value, T_OP_ADD);
                        if (vp) debug_pair_list(vp);
                }
 
@@ -1806,14 +1813,15 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
                        fd = mkstemp(filename);
                        if (fd < 0) {
                                RDEBUG("Failed creating file in %s: %s",
-                                      conf->verify_tmp_dir, strerror(errno));
+                                      conf->verify_tmp_dir, fr_syserror(errno));
                                break;
                        }
 
                        fp = fdopen(fd, "w");
                        if (!fp) {
+                               close(fd);
                                RDEBUG("Failed opening file %s: %s",
-                                      filename, strerror(errno));
+                                      filename, fr_syserror(errno));
                                break;
                        }
 
@@ -1939,21 +1947,86 @@ static void sess_free_vps(UNUSED void *parent, void *data_ptr,
        VALUE_PAIR *vp = data_ptr;
        if (!vp) return;
 
-       DEBUG2("  Freeing cached session VPs %p", vp);
+       DEBUG2("  Freeing cached session VPs");;
 
        pairfree(&vp);
 }
 
-/*
- *     Add all the default ciphers and message digests
- *     Create our context.
+static void sess_free_certs(UNUSED void *parent, void *data_ptr,
+                               UNUSED CRYPTO_EX_DATA *ad, UNUSED int idx,
+                               UNUSED long argl, UNUSED void *argp)
+{
+       VALUE_PAIR **certs = data_ptr;
+       if (!certs) return;
+
+       DEBUG2("  Freeing cached session Certificates");
+
+       pairfree(certs);
+}
+
+/** Add all the default ciphers and message digests reate our context.
  *
- *     This should be called exactly once from main.
+ * This should be called exactly once from main, before reading the main config
+ * or initialising any modules.
  */
 void tls_global_init(void)
 {
-       SSL_library_init();
-       SSL_load_error_strings();
+       SSL_load_error_strings();       /* readable error messages (examples show call before library_init) */
+       SSL_library_init();             /* initialize library */
+       OpenSSL_add_all_algorithms();   /* required for SHA2 in OpenSSL < 0.9.8o and 1.0.0.a */
+       OPENSSL_config(NULL);
+}
+
+/** Check for vulnerable versions of libssl
+ *
+ * @param acknowledged The highest CVE number a user has confirmed is not present in the system's libssl.
+ * @return 0 if the CVE specified by the user matches the most recent CVE we have, else -1.
+ */
+int tls_global_version_check(char const *acknowledged)
+{
+       uint64_t v;
+
+       if ((strcmp(acknowledged, libssl_defects[0].id) != 0) && (strcmp(acknowledged, "yes") != 0)) {
+               bool bad = false;
+               size_t i;
+
+               /* Check for bad versions */
+               v = (uint64_t) SSLeay();
+
+               for (i = 0; i < (sizeof(libssl_defects) / sizeof(*libssl_defects)); i++) {
+                       libssl_defect_t *defect = &libssl_defects[i];
+
+                       if ((v >= defect->low) && (v <= defect->high)) {
+                               ERROR("Refusing to start with libssl version %s (in range %s)",
+                                     ssl_version(), ssl_version_range(defect->low, defect->high));
+                               ERROR("Security advisory %s (%s)", defect->id, defect->name);
+                               ERROR("%s", defect->comment);
+
+                               bad = true;
+                       }
+               }
+
+               if (bad) {
+                       INFO("Once you have verified libssl has been correctly patched, "
+                            "set security.allow_vulnerable_openssl = '%s'", libssl_defects[0].id);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/** Free any memory alloced by libssl
+ *
+ */
+void tls_global_cleanup(void)
+{
+       ERR_remove_state(0);
+       ENGINE_cleanup();
+       CONF_modules_unload(1);
+       ERR_free_strings();
+       EVP_cleanup();
+       CRYPTO_cleanup_all_ex_data();
 }
 
 /*
@@ -1981,6 +2054,14 @@ void tls_global_init(void)
 #endif
 
        ctx = SSL_CTX_new(TLSv1_method());
+       if (!ctx) {
+               int err;
+               while ((err = ERR_get_error())) {
+                       DEBUG("Failed creating SSL context: %s",
+                             ERR_error_string(err, NULL));
+                       return NULL;
+               }
+       }
 
        /*
         * Save the config on the context so that callbacks which
@@ -2008,15 +2089,12 @@ void tls_global_init(void)
                 * programmatically.
                 */
                char const* special_string = "Apple:UseCertAdmin";
-               if (strncmp(conf->private_key_password,
-                                       special_string,
-                                       strlen(special_string)) == 0)
-               {
+               if (strncmp(conf->private_key_password, special_string, strlen(special_string)) == 0) {
                        char cmd[256];
+                       char *password;
                        long const max_password_len = 128;
-                       snprintf(cmd, sizeof(cmd) - 1,
-                                        "/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
-                                        conf->private_key_file);
+                       snprintf(cmd, sizeof(cmd) - 1, "/usr/sbin/certadmin --get-private-key-passphrase \"%s\"",
+                                conf->private_key_file);
 
                        DEBUG2("tls: Getting private key passphrase using command \"%s\"", cmd);
 
@@ -2027,25 +2105,33 @@ void tls_global_init(void)
                                return NULL;
                        }
 
-                       talloc_free(conf->private_key_password);
-                       conf->private_key_password = talloc_array(conf, char, max_password_len);
-                       if (!conf->private_key_password) {
+                       rad_const_free(conf->private_key_password);
+                       password = talloc_array(conf, char, max_password_len);
+                       if (!password) {
                                ERROR("TLS: Can't allocate space for private_key_password");
                                ERROR("TLS: Error reading private_key_file %s", conf->private_key_file);
                                pclose(cmd_pipe);
                                return NULL;
                        }
 
-                       fgets(conf->private_key_password, max_password_len, cmd_pipe);
+                       fgets(password, max_password_len, cmd_pipe);
                        pclose(cmd_pipe);
 
                        /* Get rid of newline at end of password. */
-                       conf->private_key_password[strlen(conf->private_key_password) - 1] = '\0';
-                       DEBUG2("tls:  Password from command = \"%s\"", conf->private_key_password);
+                       password[strlen(password) - 1] = '\0';
+
+                       DEBUG3("tls:  Password from command = \"%s\"", password);
+                       conf->private_key_password = password;
                }
 #endif
-               SSL_CTX_set_default_passwd_cb_userdata(ctx, conf->private_key_password);
-               SSL_CTX_set_default_passwd_cb(ctx, cbtls_password);
+
+               {
+                       char *password;
+
+                       memcpy(&password, &conf->private_key_password, sizeof(password));
+                       SSL_CTX_set_default_passwd_cb_userdata(ctx, password);
+                       SSL_CTX_set_default_passwd_cb(ctx, cbtls_password);
+               }
        }
 
 #ifdef PSK_MAX_IDENTITY_LEN
@@ -2232,6 +2318,8 @@ post_ca:
                SSL_CTX_set_quiet_shutdown(ctx, 1);
                if (FR_TLS_EX_INDEX_VPS < 0)
                        FR_TLS_EX_INDEX_VPS = SSL_SESSION_get_ex_new_index(0, NULL, NULL, NULL, sess_free_vps);
+               if (FR_TLS_EX_INDEX_CERTS < 0)
+                       FR_TLS_EX_INDEX_CERTS = SSL_SESSION_get_ex_new_index(0, NULL, NULL, NULL, sess_free_certs);
        }
 
        /*
@@ -2264,7 +2352,7 @@ post_ca:
 
        /* Load randomness */
        if (conf->random_file) {
-               if (!(RAND_load_file(conf->random_file, 1024*1024))) {
+               if (!(RAND_load_file(conf->random_file, 1024*10))) {
                        ERROR("tls: SSL error %s", ERR_error_string(ERR_get_error(), NULL));
                        ERROR("tls: Error loading randomness");
                        return NULL;
@@ -2333,10 +2421,8 @@ post_ca:
  *     added to automatically free the data when the CONF_SECTION
  *     is freed.
  */
-static void tls_server_conf_free(fr_tls_server_conf_t *conf)
+static int tls_server_conf_free(fr_tls_server_conf_t *conf)
 {
-       if (!conf) return;
-
        if (conf->ctx) SSL_CTX_free(conf->ctx);
 
 #ifdef HAVE_OPENSSL_OCSP_H
@@ -2347,7 +2433,7 @@ static void tls_server_conf_free(fr_tls_server_conf_t *conf)
 #ifndef NDEBUG
        memset(conf, 0, sizeof(*conf));
 #endif
-       talloc_free(conf);
+       return 0;
 }
 
 
@@ -2371,9 +2457,11 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
                return NULL;
        }
 
+       talloc_set_destructor(conf, tls_server_conf_free);
+
        if (cf_section_parse(cs, conf, tls_server_config) < 0) {
        error:
-               tls_server_conf_free(conf);
+               talloc_free(conf);
                return NULL;
        }
 
@@ -2409,9 +2497,13 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
                if (conf->ocsp_store == NULL) goto error;
        }
 #endif /*HAVE_OPENSSL_OCSP_H*/
+       {
+               char *dh_file;
 
-       if (load_dh_params(conf->ctx, conf->dh_file) < 0) {
-               goto error;
+               memcpy(&dh_file, &conf->dh_file, sizeof(dh_file));
+               if (load_dh_params(conf->ctx, dh_file) < 0) {
+                       goto error;
+               }
        }
 
        if (generate_eph_rsa_key(conf->ctx) < 0) {
@@ -2420,7 +2512,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
 
        if (conf->verify_tmp_dir) {
                if (chmod(conf->verify_tmp_dir, S_IRWXU) < 0) {
-                       ERROR("Failed changing permissions on %s: %s", conf->verify_tmp_dir, strerror(errno));
+                       ERROR("Failed changing permissions on %s: %s", conf->verify_tmp_dir, fr_syserror(errno));
                        goto error;
                }
        }
@@ -2433,7 +2525,7 @@ fr_tls_server_conf_t *tls_server_conf_parse(CONF_SECTION *cs)
        /*
         *      Cache conf in cs in case we're asked to parse this again.
         */
-       cf_data_add(cs, "tls-conf", conf, (void *)(void *) tls_server_conf_free);
+       cf_data_add(cs, "tls-conf", conf, NULL);
 
        return conf;
 }
@@ -2454,9 +2546,11 @@ fr_tls_server_conf_t *tls_client_conf_parse(CONF_SECTION *cs)
                return NULL;
        }
 
+       talloc_set_destructor(conf, tls_server_conf_free);
+
        if (cf_section_parse(cs, conf, tls_client_config) < 0) {
        error:
-               tls_server_conf_free(conf);
+               talloc_free(conf);
                return NULL;
        }
 
@@ -2473,15 +2567,20 @@ fr_tls_server_conf_t *tls_client_conf_parse(CONF_SECTION *cs)
                goto error;
        }
 
-       if (load_dh_params(conf->ctx, conf->dh_file) < 0) {
-               goto error;
+       {
+               char *dh_file;
+
+               memcpy(&dh_file, &conf->dh_file, sizeof(dh_file));
+               if (load_dh_params(conf->ctx, dh_file) < 0) {
+                       goto error;
+               }
        }
 
        if (generate_eph_rsa_key(conf->ctx) < 0) {
                goto error;
        }
 
-       cf_data_add(cs, "tls-conf", conf, (void *)(void *) tls_server_conf_free);
+       cf_data_add(cs, "tls-conf", conf, NULL);
 
        return conf;
 }
@@ -2490,10 +2589,13 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 {
        VALUE_PAIR *vp, *vps = NULL;
        fr_tls_server_conf_t *conf;
+       TALLOC_CTX *talloc_ctx;
 
        conf = (fr_tls_server_conf_t *)SSL_get_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CONF);
        rad_assert(conf != NULL);
 
+       talloc_ctx = SSL_get_ex_data(ssn->ssl, FR_TLS_EX_INDEX_TALLOC);
+
        /*
         *      If there's no session resumption, delete the entry
         *      from the cache.  This means either it's disabled
@@ -2515,7 +2617,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                 *      not allowed,
                 */
                if (SSL_session_reused(ssn->ssl)) {
-                       RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed.");
+                       RDEBUG("FAIL: Forcibly stopping session resumption as it is not allowed");
                        return -1;
                }
 
@@ -2533,13 +2635,16 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
 
                fr_bin2hex(buffer, ssn->ssl->session->session_id, size);
 
-               vp = paircopy2(NULL, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_USER_NAME, 0, TAG_ANY);
+               if (vp) pairadd(&vps, vp);
+
+               vp = paircopy2(talloc_ctx, request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
-               vp = paircopy2(NULL, request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_CHARGEABLE_USER_IDENTITY, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
-               vp = paircopy2(NULL, request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
+               vp = paircopy2(talloc_ctx, request->reply->vps, PW_CACHED_SESSION_POLICY, 0, TAG_ANY);
                if (vp) pairadd(&vps, vp);
 
                certs = (VALUE_PAIR **)SSL_get_ex_data(ssn->ssl, FR_TLS_EX_INDEX_CERTS);
@@ -2552,7 +2657,7 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                         *      @todo: some go into reply, others into
                         *      request
                         */
-                       pairadd(&vps, paircopy(NULL, *certs));
+                       pairadd(&vps, paircopy(talloc_ctx, *certs));
                }
 
                if (vps) {
@@ -2569,17 +2674,17 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                                        );
                                vp_file = fopen(filename, "w");
                                if (vp_file == NULL) {
-                                       RDEBUG2("Could not write session VPs to persistent cache: %s", strerror(errno));
+                                       RDEBUG2("Could not write session VPs to persistent cache: %s", fr_syserror(errno));
                                } else {
                                        vp_cursor_t cursor;
                                        /* generate a dummy user-style entry which is easy to read back */
                                        fprintf(vp_file, "# SSL cached session\n");
                                        fprintf(vp_file, "%s\n", buffer);
-                                       for (vp = paircursor(&cursor, &vps);
+                                       for (vp = fr_cursor_init(&cursor, &vps);
                                             vp;
-                                            vp = pairnext(&cursor)) {
+                                            vp = fr_cursor_next(&cursor)) {
                                                vp_prints(buf, sizeof(buf), vp);
-                                               fprintf(vp_file, "\t%s%s\n", buf, ",");
+                                               fprintf(vp_file, "\t%s,\n", buf);
                                        }
                                        fclose(vp_file);
                                }
@@ -2615,9 +2720,9 @@ int tls_success(tls_session_t *ssn, REQUEST *request)
                        RDEBUG("Adding cached attributes for session %s:", buffer);
                        debug_pair_list(vps);
 
-                       for (vp = paircursor(&cursor, &vps);
+                       for (vp = fr_cursor_init(&cursor, &vps);
                             vp;
-                            vp = pairnext(&cursor)) {
+                            vp = fr_cursor_next(&cursor)) {
                                /*
                                 *      TLS-* attrs get added back to
                                 *      the request list.
@@ -2715,7 +2820,8 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
                        break;
 
                default:
-                       DEBUG("Error in fragmentation logic: ?");
+                       DEBUG("Error in fragmentation logic: %s",
+                             ERR_error_string(code, NULL));
 
                        /*
                         *      FIXME: Call int_ssl_check?
@@ -2726,7 +2832,7 @@ fr_tls_status_t tls_application_data(tls_session_t *ssn,
        }
 
        if (err == 0) {
-               RWDEBUG("No data inside of the tunnel.");
+               RWDEBUG("No data inside of the tunnel");
        }
 
        /*
@@ -2749,16 +2855,16 @@ fr_tls_status_t tls_ack_handler(tls_session_t *ssn, REQUEST *request)
        RDEBUG2("Received TLS ACK");
 
        if (ssn == NULL){
-               RERROR("FAIL: Unexpected ACK received.  Could not obtain session information.");
+               RERROR("FAIL: Unexpected ACK received.  Could not obtain session information");
                return FR_TLS_INVALID;
        }
        if (ssn->info.initialized == 0) {
-               RDEBUG("No SSL info available. Waiting for more SSL data.");
+               RDEBUG("No SSL info available. Waiting for more SSL data");
                return FR_TLS_REQUEST;
        }
        if ((ssn->info.content_type == handshake) &&
            (ssn->info.origin == 0)) {
-               RERROR("FAIL: ACK without earlier message.");
+               RERROR("FAIL: ACK without earlier message");
                return FR_TLS_INVALID;
        }
 
index 711f52b..705c0e8 100644 (file)
@@ -72,26 +72,17 @@ static void tls_socket_close(rad_listen_t *listener)
 {
        listen_socket_t *sock = listener->data;
 
+       SSL_shutdown(sock->ssn->ssl);
+
+
        listener->status = RAD_LISTEN_STATUS_EOL;
        listener->tls = NULL; /* parent owns this! */
 
-       if (sock->parent) {
-               /*
-                *      Decrement the number of connections.
-                */
-               if (sock->parent->limit.num_connections > 0) {
-                       sock->parent->limit.num_connections--;
-               }
-               if (sock->client->limit.num_connections > 0) {
-                       sock->client->limit.num_connections--;
-               }
-       }
-
        /*
         *      Tell the event handler that an FD has disappeared.
         */
        DEBUG("Client has closed connection");
-       event_new_fd(listener);
+       radius_update_listener(listener);
 
        /*
         *      Do NOT free the listener here.  It's in use by
@@ -102,7 +93,7 @@ static void tls_socket_close(rad_listen_t *listener)
         */
 }
 
-static int tls_socket_write(rad_listen_t *listener, REQUEST *request)
+static int CC_HINT(nonnull) tls_socket_write(rad_listen_t *listener, REQUEST *request)
 {
        uint8_t *p;
        ssize_t rcode;
@@ -115,7 +106,7 @@ static int tls_socket_write(rad_listen_t *listener, REQUEST *request)
                rcode = write(request->packet->sockfd, p,
                              (sock->ssn->dirty_out.data + sock->ssn->dirty_out.used) - p);
                if (rcode <= 0) {
-                       RDEBUG("Error writing to TLS socket: %s", strerror(errno));
+                       RDEBUG("Error writing to TLS socket: %s", fr_syserror(errno));
 
                        tls_socket_close(listener);
                        return 0;
@@ -131,7 +122,7 @@ static int tls_socket_write(rad_listen_t *listener, REQUEST *request)
 
 static int tls_socket_recv(rad_listen_t *listener)
 {
-       int doing_init = false;
+       bool doing_init = false;
        ssize_t rcode;
        RADIUS_PACKET *packet;
        REQUEST *request;
@@ -191,6 +182,7 @@ static int tls_socket_recv(rad_listen_t *listener)
                (void) talloc_steal(sock, sock->ssn);
                SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_REQUEST, (void *)request);
                SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_CERTS, (void *)&request->packet->vps);
+               SSL_set_ex_data(sock->ssn->ssl, FR_TLS_EX_INDEX_TALLOC, sock->parent);
 
                doing_init = true;
        }
@@ -209,13 +201,14 @@ static int tls_socket_recv(rad_listen_t *listener)
                     sizeof(sock->ssn->dirty_in.data));
        if ((rcode < 0) && (errno == ECONNRESET)) {
        do_close:
-               PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+               DEBUG("Closing TLS socket from client port %u", sock->other_port);
                tls_socket_close(listener);
+               PTHREAD_MUTEX_UNLOCK(&sock->mutex);
                return 0;
        }
 
        if (rcode < 0) {
-               RDEBUG("Error reading TLS socket: %s", strerror(errno));
+               RDEBUG("Error reading TLS socket: %s", fr_syserror(errno));
                goto do_close;
        }
 
@@ -293,9 +286,12 @@ app:
        packet->vps = NULL;
        PTHREAD_MUTEX_UNLOCK(&sock->mutex);
 
-       if (!rad_packet_ok(packet, 0)) {
+       if (!rad_packet_ok(packet, 0, NULL)) {
                RDEBUG("Received bad packet: %s", fr_strerror());
+               DEBUG("Closing TLS socket from client");
+               PTHREAD_MUTEX_LOCK(&sock->mutex);
                tls_socket_close(listener);
+               PTHREAD_MUTEX_UNLOCK(&sock->mutex);
                return 0;       /* do_close unlocks the mutex */
        }
 
@@ -305,7 +301,7 @@ app:
        if (fr_debug_flag) {
                char host_ipaddr[128];
 
-               if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+               if (is_radius_code(packet->code)) {
                        RDEBUG("tls_recv: %s packet from host %s port %d, id=%d, length=%d",
                               fr_packet_codes[packet->code],
                               inet_ntop(packet->src_ipaddr.af,
@@ -360,14 +356,14 @@ int dual_tls_recv(rad_listen_t *listener)
         *      set.
         */
        switch(packet->code) {
-       case PW_AUTHENTICATION_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
                if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
                FR_STATS_INC(auth, total_requests);
                fun = rad_authenticate;
                break;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
                if (listener->type != RAD_LISTEN_ACCT) {
                        /*
                         *      Allow auth + dual.  Disallow
@@ -383,10 +379,10 @@ int dual_tls_recv(rad_listen_t *listener)
                break;
 #endif
 
-       case PW_STATUS_SERVER:
-               if (!mainconfig.status_server) {
+       case PW_CODE_STATUS_SERVER:
+               if (!main_config.status_server) {
                        FR_STATS_INC(auth, total_unknown_types);
-                       WDEBUG("Ignoring Status-Server request due to security configuration");
+                       WARN("Ignoring Status-Server request due to security configuration");
                        rad_free(&sock->packet);
                        request->packet = NULL;
                        return 0;
@@ -426,6 +422,8 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request)
 {
        listen_socket_t *sock = listener->data;
 
+       VERIFY_REQUEST(request);
+
        rad_assert(request->listener == listener);
        rad_assert(listener->send == dual_tls_send);
 
@@ -444,7 +442,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request)
         */
        if (rad_encode(request->reply, request->packet,
                       request->client->secret) < 0) {
-               RDEBUG("Failed encoding packet: %s", fr_strerror());
+               RERROR("Failed encoding packet: %s", fr_strerror());
                return 0;
        }
 
@@ -453,7 +451,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request)
         */
        if (rad_sign(request->reply, request->packet,
                       request->client->secret) < 0) {
-               RDEBUG("Failed signing packet: %s", fr_strerror());
+               RERROR("Failed signing packet: %s", fr_strerror());
                return 0;
        }
 
@@ -484,84 +482,152 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request)
 
 
 #ifdef WITH_PROXY
-int proxy_tls_recv(rad_listen_t *listener)
+/*
+ *     Read from the SSL socket.  Safe with either blocking or
+ *     non-blocking IO.  This level of complexity is probably not
+ *     necessary, as each packet gets put into one SSL application
+ *     record.  When SSL has a full record, we should be able to read
+ *     the entire packet via one SSL_read().
+ *
+ *     When SSL has a partial record, SSL_read() will return
+ *     WANT_READ or WANT_WRITE, and zero application data.
+ *
+ *     Called with the mutex held.
+ */
+static ssize_t proxy_tls_read(rad_listen_t *listener)
 {
        int rcode;
        size_t length;
-       listen_socket_t *sock = listener->data;
-       char buffer[256];
-       RADIUS_PACKET *packet;
        uint8_t *data;
+       listen_socket_t *sock = listener->data;
 
        /*
         *      Get the maximum size of data to receive.
         */
        if (!sock->data) sock->data = talloc_array(sock, uint8_t,
                                                   sock->ssn->offset);
+
        data = sock->data;
 
-       if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
-       DEBUG3("Proxy SSL socket has data to read");
-       PTHREAD_MUTEX_LOCK(&sock->mutex);
-redo:
-       rcode = SSL_read(sock->ssn->ssl, data, 4);
-       if (rcode <= 0) {
-               int err = SSL_get_error(sock->ssn->ssl, rcode);
-               switch (err) {
-               case SSL_ERROR_WANT_READ:
-               case SSL_ERROR_WANT_WRITE:
-                       goto redo;
+       if (sock->partial < 4) {
+               rcode = SSL_read(sock->ssn->ssl, data + sock->partial,
+                                4 - sock->partial);
+               if (rcode <= 0) {
+                       int err = SSL_get_error(sock->ssn->ssl, rcode);
+                       switch (err) {
+                       case SSL_ERROR_WANT_READ:
+                       case SSL_ERROR_WANT_WRITE:
+                               return 0; /* do some more work later */
+
+                       case SSL_ERROR_ZERO_RETURN:
+                               /* remote end sent close_notify, send one back */
+                               SSL_shutdown(sock->ssn->ssl);
+
+                       case SSL_ERROR_SYSCALL:
+                       do_close:
+                               return -1;
+
+                       default:
+                               while ((err = ERR_get_error())) {
+                                       DEBUG("proxy recv says %s",
+                                             ERR_error_string(err, NULL));
+                               }
+
+                               goto do_close;
+                       }
+               }
 
-               case SSL_ERROR_ZERO_RETURN:
-                       /* remote end sent close_notify, send one back */
-                       SSL_shutdown(sock->ssn->ssl);
+               sock->partial = rcode;
+       } /* try reading the packet header */
 
-               case SSL_ERROR_SYSCALL:
-               do_close:
-                       PTHREAD_MUTEX_UNLOCK(&sock->mutex);
-                       tls_socket_close(listener);
-                       return 0;
+       if (sock->partial < 4) return 0; /* read more data */
 
-               default:
-                       while ((err = ERR_get_error())) {
-                               DEBUG("proxy recv says %s",
-                                     ERR_error_string(err, NULL));
-                       }
+       length = (data[2] << 8) | data[3];
+
+       /*
+        *      Do these checks only once, when we read the header.
+        */
+       if (sock->partial == 4) {
+               DEBUG3("Proxy received header saying we have a packet of %u bytes",
+                      (unsigned int) length);
 
+               /*
+                *      FIXME: allocate a RADIUS_PACKET, and set
+                *      "data" to be as large as necessary.
+                */
+               if (length > sock->ssn->offset) {
+                       INFO("Received packet will be too large! Set \"fragment_size = %u\"",
+                            (data[2] << 8) | data[3]);
                        goto do_close;
                }
        }
 
-       length = (data[2] << 8) | data[3];
-       DEBUG3("Proxy received header saying we have a packet of %u bytes",
-              (unsigned int) length);
+       /*
+        *      Try to read some more.
+        */
+       if (sock->partial < length) {
+               rcode = SSL_read(sock->ssn->ssl, data + sock->partial,
+                                length - sock->partial);
+               if (rcode <= 0) {
+                       switch (SSL_get_error(sock->ssn->ssl, rcode)) {
+                       case SSL_ERROR_WANT_READ:
+                       case SSL_ERROR_WANT_WRITE:
+                               return 0;
+
+                       case SSL_ERROR_ZERO_RETURN:
+                               /* remote end sent close_notify, send one back */
+                               SSL_shutdown(sock->ssn->ssl);
+                               goto do_close;
+                       default:
+                               goto do_close;
+                       }
+               }
 
-       if (length > sock->ssn->offset) {
-               INFO("Received packet will be too large! Set \"fragment_size=%u\"",
-                      (data[2] << 8) | data[3]);
-               goto do_close;
+               sock->partial += rcode;
        }
 
-       rcode = SSL_read(sock->ssn->ssl, data + 4, length);
-       if (rcode <= 0) {
-               switch (SSL_get_error(sock->ssn->ssl, rcode)) {
-               case SSL_ERROR_WANT_READ:
-               case SSL_ERROR_WANT_WRITE:
-                       break;
+       /*
+        *      If we're not done, say so.
+        *
+        *      Otherwise, reset the partially read data flag, and say
+        *      we have a packet.
+        */
+       if (sock->partial < length) {
+               return 0;
+       }
 
+       sock->partial = 0;      /* we've now read the packet */
+       return length;
+}
 
-               case SSL_ERROR_ZERO_RETURN:
-                       /* remote end sent close_notify, send one back */
-                       SSL_shutdown(sock->ssn->ssl);
-                       goto do_close;
-               default:
-                       goto do_close;
-               }
 
+int proxy_tls_recv(rad_listen_t *listener)
+{
+       listen_socket_t *sock = listener->data;
+       char buffer[256];
+       RADIUS_PACKET *packet;
+       uint8_t *data;
+       ssize_t data_len;
+
+       if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
 
-       }
+       DEBUG3("Proxy SSL socket has data to read");
+       PTHREAD_MUTEX_LOCK(&sock->mutex);
+       data_len = proxy_tls_read(listener);
        PTHREAD_MUTEX_UNLOCK(&sock->mutex);
 
+       if (data_len < 0) {
+               DEBUG("Closing TLS socket to home server");
+               PTHREAD_MUTEX_LOCK(&sock->mutex);
+               tls_socket_close(listener);
+               PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+               return 0;
+       }
+
+       if (data_len == 0) return 0; /* not done yet */
+
+       data = sock->data;
+
        packet = rad_alloc(sock, 0);
        packet->sockfd = listener->fd;
        packet->src_ipaddr = sock->other_ipaddr;
@@ -570,7 +636,7 @@ redo:
        packet->dst_port = sock->my_port;
        packet->code = data[0];
        packet->id = data[1];
-       packet->data_len = length;
+       packet->data_len = data_len;
        packet->data = talloc_array(packet, uint8_t, packet->data_len);
        memcpy(packet->data, data, packet->data_len);
        memcpy(packet->vector, packet->data + 4, 16);
@@ -579,13 +645,13 @@ redo:
         *      FIXME: Client MIB updates?
         */
        switch(packet->code) {
-       case PW_AUTHENTICATION_ACK:
-       case PW_ACCESS_CHALLENGE:
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_ACK:
+       case PW_CODE_ACCESS_CHALLENGE:
+       case PW_CODE_AUTHENTICATION_REJECT:
                break;
 
 #ifdef WITH_ACCOUNTING
-       case PW_ACCOUNTING_RESPONSE:
+       case PW_CODE_ACCOUNTING_RESPONSE:
                break;
 #endif
 
@@ -610,12 +676,16 @@ redo:
        return 1;
 }
 
+
 int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
 {
        int rcode;
        listen_socket_t *sock = listener->data;
 
-       if (listener->status != RAD_LISTEN_STATUS_KNOWN) return 0;
+       VERIFY_REQUEST(request);
+
+       if ((listener->status != RAD_LISTEN_STATUS_INIT) &&
+           (listener->status != RAD_LISTEN_STATUS_KNOWN)) return 0;
 
        /*
         *      Normal proxying calls us with the data already
@@ -630,16 +700,26 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
        DEBUG3("Proxy is writing %u bytes to SSL",
               (unsigned int) request->proxy->data_len);
        PTHREAD_MUTEX_LOCK(&sock->mutex);
-       while ((rcode = SSL_write(sock->ssn->ssl, request->proxy->data,
-                                 request->proxy->data_len)) < 0) {
+       rcode = SSL_write(sock->ssn->ssl, request->proxy->data,
+                         request->proxy->data_len);
+       if (rcode < 0) {
                int err;
-               while ((err = ERR_get_error())) {
+
+               err = ERR_get_error();
+               switch (err) {
+               case SSL_ERROR_NONE:
+               case SSL_ERROR_WANT_READ:
+               case SSL_ERROR_WANT_WRITE:
+                       break;  /* let someone else retry */
+
+               default:
                        DEBUG("proxy SSL_write says %s",
                              ERR_error_string(err, NULL));
+                       DEBUG("Closing TLS socket to home server");
+                       tls_socket_close(listener);
+                       PTHREAD_MUTEX_UNLOCK(&sock->mutex);
+                       return 0;
                }
-               PTHREAD_MUTEX_UNLOCK(&sock->mutex);
-               tls_socket_close(listener);
-               return 0;
        }
        PTHREAD_MUTEX_UNLOCK(&sock->mutex);
 
index a330445..0252c75 100644 (file)
@@ -44,11 +44,11 @@ bool memory_report = false;
 bool check_config = false;
 bool log_stripped_names = false;
 
-int filedone = 0;
+bool filedone = false;
 
 char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
-" (git #" RADIUSD_VERSION_COMMIT ")"
+" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
 #endif
 ", for host " HOSTINFO ", built on " __DATE__ " at " __TIME__;
 
@@ -57,14 +57,6 @@ char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
  */
 static void usage(int);
 
-#ifdef WITH_VERIFY_PTR
-static void die_horribly(char const *reason)
-{
-       ERROR("talloc abort: %s\n", reason);
-       abort();
-}
-#endif
-
 void listen_free(UNUSED rad_listen_t **head)
 {
        /* do nothing */
@@ -139,17 +131,17 @@ static REQUEST *request_setup(FILE *fp)
        request->number = 0;
 
        request->master_state = REQUEST_ACTIVE;
-       request->child_state = REQUEST_ACTIVE;
+       request->child_state = REQUEST_RUNNING;
        request->handle = NULL;
-       request->server = talloc_strdup(request, "default");
+       request->server = talloc_typed_strdup(request, "default");
 
-       request->root = &mainconfig;
+       request->root = &main_config;
 
        /*
         *      Read packet from fp
         */
-       request->packet->vps = readvp2(request->packet, fp, &filedone, "radiusd:");
-       if (!request->packet->vps) {
+       if (readvp2(&request->packet->vps, request->packet, fp, &filedone) < 0) {
+               fr_perror("unittest");
                talloc_free(request);
                return NULL;
        }
@@ -157,7 +149,7 @@ static REQUEST *request_setup(FILE *fp)
        /*
         *      Set the defaults for IPs, etc.
         */
-       request->packet->code = PW_AUTHENTICATION_REQUEST;
+       request->packet->code = PW_CODE_AUTHENTICATION_REQUEST;
 
        request->packet->src_ipaddr.af = AF_INET;
        request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
@@ -172,9 +164,9 @@ static REQUEST *request_setup(FILE *fp)
         *
         *      Fix up Digest-Attributes issues
         */
-       for (vp = paircursor(&cursor, &request->packet->vps);
+       for (vp = fr_cursor_init(&cursor, &request->packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                /*
                 *      Double quoted strings get marked up as xlat expansions,
                 *      but we don't support that here.
@@ -280,7 +272,7 @@ static REQUEST *request_setup(FILE *fp)
                                /* overlapping! */
                        {
                                DICT_ATTR const *da;
-                               uint8_t *p;
+                               uint8_t *p, *q;
 
                                p = talloc_array(vp, uint8_t, vp->length + 2);
 
@@ -289,11 +281,26 @@ static REQUEST *request_setup(FILE *fp)
                                vp->length += 2;
                                p[1] = vp->length;
 
-                               pairmemsteal(vp, p);
-
                                da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
                                rad_assert(da != NULL);
                                vp->da = da;
+
+                               /*
+                                *      Re-do pairmemsteal ourselves,
+                                *      because we play games with
+                                *      vp->da, and pairmemsteal goes
+                                *      to GREAT lengths to sanitize
+                                *      and fix and change and
+                                *      double-check the various
+                                *      fields.
+                                */
+                               memcpy(&q, &vp->vp_octets, sizeof(q));
+                               talloc_free(q);
+
+                               vp->vp_octets = talloc_steal(vp, p);
+                               vp->type = VT_DATA;
+
+                               VERIFY_VP(vp);
                        }
 
                        break;
@@ -301,19 +308,19 @@ static REQUEST *request_setup(FILE *fp)
        } /* loop over the VP's we read in */
 
        if (debug_flag) {
-               for (vp = paircursor(&cursor, &request->packet->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        /*
                         *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
                         */
                        if (!talloc_get_type(vp, VALUE_PAIR)) {
                                ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
-                               
-                               log_talloc_report(vp);
+
+                               fr_log_talloc_report(vp);
                                rad_assert(0);
                        }
-                       
+
                        vp_print(fr_log_fp, vp);
                }
                fflush(fr_log_fp);
@@ -338,8 +345,8 @@ static REQUEST *request_setup(FILE *fp)
        /*
         *      Debugging
         */
-       request->options = debug_flag;
-       request->radlog = radlog_request;
+       request->log.lvl = debug_flag;
+       request->log.func = vradlog_request;
 
        request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
        request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
@@ -360,16 +367,16 @@ static void print_packet(FILE *fp, RADIUS_PACKET *packet)
 
        fprintf(fp, "%s\n", fr_packet_codes[packet->code]);
 
-       for (vp = paircursor(&cursor, &packet->vps);
+       for (vp = fr_cursor_init(&cursor, &packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                /*
                 *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
                 */
                if (!talloc_get_type(vp, VALUE_PAIR)) {
                        ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
 
-                       log_talloc_report(vp);
+                       fr_log_talloc_report(vp);
                        rad_assert(0);
                }
 
@@ -389,25 +396,36 @@ int main(int argc, char *argv[])
        const char *output_file = NULL;
        const char *filter_file = NULL;
        FILE *fp;
-       REQUEST *request;
+       REQUEST *request = NULL;
        VALUE_PAIR *vp;
        VALUE_PAIR *filter_vps = NULL;
 
+       /*
+        *      If the server was built with debugging enabled always install
+        *      the basic fatal signal handlers.
+        */
+#ifndef NDEBUG
+       if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
+               fr_perror("unittest");
+               exit(EXIT_FAILURE);
+       }
+#endif
+
        if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL)
                progname = argv[0];
        else
                progname++;
 
        debug_flag = 0;
-       radius_dir = talloc_strdup(NULL, RADIUS_DIR);
+       set_radius_dir(NULL, RADIUS_DIR);
 
        /*
         *      Ensure that the configuration is initialized.
         */
-       memset(&mainconfig, 0, sizeof(mainconfig));
-       mainconfig.myip.af = AF_UNSPEC;
-       mainconfig.port = -1;
-       mainconfig.name = "radiusd";
+       memset(&main_config, 0, sizeof(main_config));
+       main_config.myip.af = AF_UNSPEC;
+       main_config.port = 0;
+       main_config.name = "radiusd";
 
        /*
         *      The tests should have only IPs, not host names.
@@ -418,7 +436,7 @@ int main(int argc, char *argv[])
         *      We always log to stdout.
         */
        fr_log_fp = stdout;
-       default_log.dest = L_DST_STDOUT;
+       default_log.dst = L_DST_STDOUT;
        default_log.fd = STDOUT_FILENO;
 
        /*  Process the options.  */
@@ -426,14 +444,11 @@ int main(int argc, char *argv[])
 
                switch(argval) {
                        case 'd':
-                               if (radius_dir) {
-                                       rad_const_free(radius_dir);
-                               }
-                               radius_dir = talloc_strdup(NULL, optarg);
+                               set_radius_dir(NULL, optarg);
                                break;
 
                        case 'D':
-                               mainconfig.dictionary_dir = talloc_strdup(NULL, optarg);
+                               main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
                                break;
 
                        case 'f':
@@ -449,16 +464,16 @@ int main(int argc, char *argv[])
                                break;
 
                        case 'm':
-                               mainconfig.debug_memory = 1;
+                               main_config.debug_memory = true;
                                break;
 
                        case 'M':
-                               memory_report = 1;
-                               mainconfig.debug_memory = 1;
+                               memory_report = true;
+                               main_config.debug_memory = true;
                                break;
 
                        case 'n':
-                               mainconfig.name = optarg;
+                               main_config.name = optarg;
                                break;
 
                        case 'o':
@@ -467,9 +482,9 @@ int main(int argc, char *argv[])
 
                        case 'X':
                                debug_flag += 2;
-                               mainconfig.log_auth = true;
-                               mainconfig.log_auth_badpass = true;
-                               mainconfig.log_auth_goodpass = true;
+                               main_config.log_auth = true;
+                               main_config.log_auth_badpass = true;
+                               main_config.log_auth_goodpass = true;
                                break;
 
                        case 'x':
@@ -482,24 +497,43 @@ int main(int argc, char *argv[])
                }
        }
 
-       if (memory_report) {
-               talloc_enable_null_tracking();
-#ifdef WITH_VERIFY_PTR
-               talloc_set_abort_fn(die_horribly);
-#endif
-       }
-       talloc_set_log_fn(log_talloc);
-
        if (debug_flag) {
                version();
        }
        fr_debug_flag = debug_flag;
 
-       /*  Read the configuration files, BEFORE doing anything else.  */
-       if (read_mainconfig(0) < 0) {
+       /*
+        *      Mismatch between the binary and the libraries it depends on
+        */
+       if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
+               fr_perror("radiusd");
                exit(EXIT_FAILURE);
        }
 
+       /*  Read the configuration files, BEFORE doing anything else.  */
+       if (main_config_init() < 0) {
+               rcode = EXIT_FAILURE;
+               goto finish;
+       }
+
+       /*
+        *  Load the modules
+        */
+       if (modules_init(main_config.config) < 0) {
+               rcode = EXIT_FAILURE;
+               goto finish;
+       }
+
+       /* Set the panic action (if required) */
+       if (main_config.panic_action &&
+#ifndef NDEBUG
+           !getenv("PANIC_ACTION") &&
+#endif
+           (fr_fault_setup(main_config.panic_action, argv[0]) < 0)) {
+               rcode = EXIT_FAILURE;
+               goto finish;
+       }
+
        setlinebuf(stdout); /* unbuffered output */
 
        if (!input_file || (strcmp(input_file, "-") == 0)) {
@@ -508,8 +542,8 @@ int main(int argc, char *argv[])
                fp = fopen(input_file, "r");
                if (!fp) {
                        fprintf(stderr, "Failed reading %s: %s\n",
-                               input_file, strerror(errno));
-                       exit(EXIT_FAILURE);
+                               input_file, fr_syserror(errno));
+                       goto finish;
                }
        }
 
@@ -519,7 +553,8 @@ int main(int argc, char *argv[])
        request = request_setup(fp);
        if (!request) {
                fprintf(stderr, "Failed reading input: %s\n", fr_strerror());
-               exit(EXIT_FAILURE);
+               rcode = EXIT_FAILURE;
+               goto finish;
        }
 
        /*
@@ -533,7 +568,7 @@ int main(int argc, char *argv[])
                        fclose(fp);
                        fp = NULL;
                }
-               filedone = 0;
+               filedone = false;
        }
 
        /*
@@ -545,17 +580,18 @@ int main(int argc, char *argv[])
                if (!fp) {
                        fp = fopen(filter_file, "r");
                        if (!fp) {
-                               fprintf(stderr, "Failed reading %s: %s\n",
-                                       filter_file, strerror(errno));
-                               exit(EXIT_FAILURE);
+                               fprintf(stderr, "Failed reading %s: %s\n", filter_file, strerror(errno));
+                               rcode = EXIT_FAILURE;
+                               goto finish;
                        }
                }
 
-               filter_vps = readvp2(request, fp, &filedone, "radiusd");
-               if (!filter_vps) {
+
+               if (readvp2(&filter_vps, request, fp, &filedone) < 0) {
                        fprintf(stderr, "Failed reading attributes from %s: %s\n",
                                filter_file, fr_strerror());
-                       exit(EXIT_FAILURE);
+                       rcode = EXIT_FAILURE;
+                       goto finish;
                }
 
                /*
@@ -572,7 +608,7 @@ int main(int argc, char *argv[])
                fp = fopen(output_file, "w");
                if (!fp) {
                        fprintf(stderr, "Failed writing %s: %s\n",
-                               output_file, strerror(errno));
+                               output_file, fr_syserror(errno));
                        exit(EXIT_FAILURE);
                }
        }
@@ -584,37 +620,42 @@ int main(int argc, char *argv[])
        /*
         *      Update the list with the response type.
         */
-       vp = radius_paircreate(request, &request->reply->vps,
+       vp = radius_paircreate(request->reply, &request->reply->vps,
                               PW_RESPONSE_PACKET_TYPE, 0);
        vp->vp_integer = request->reply->code;
 
-       if (filter_vps && !pairvalidate(filter_vps, request->reply->vps)) {
-               fprintf(stderr, "Output file %s does not match attributes in filter %s\n",
-                       output_file ? output_file : input_file, filter_file);
-               exit(EXIT_FAILURE);
+       {
+               VALUE_PAIR const *failed[2];
+
+               if (filter_vps && !pairvalidate(failed, filter_vps, request->reply->vps)) {
+                       pairvalidate_debug(request, failed);
+                       fr_perror("Output file %s does not match attributes in filter %s",
+                                 output_file ? output_file : input_file, filter_file);
+                       rcode = EXIT_FAILURE;
+                       goto finish;
+               }
        }
 
-       talloc_free(request);
+       INFO("Exiting normally");
 
-       INFO("Exiting normally.");
+finish:
+       talloc_free(request);
 
        /*
         *      Detach any modules.
         */
-       detach_modules();
+       modules_free();
 
        xlat_free();            /* modules may have xlat's */
 
        /*
         *      Free the configuration items.
         */
-       free_mainconfig();
-
-       rad_const_free(radius_dir);
+       main_config_free();
 
        if (memory_report) {
                INFO("Allocated memory at time of report:");
-               log_talloc_report(NULL);
+               fr_log_talloc_report(NULL);
        }
 
        return rcode;
index c345985..4f4bdcd 100644 (file)
@@ -26,7 +26,6 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #include <ctype.h>
-#include <signal.h>
 
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -243,45 +242,6 @@ int request_opaque_free(REQUEST *request)
 }
 
 /*
- *     Check a filename for sanity.
- *
- *     Allow only uppercase/lowercase letters, numbers, and '-_/.'
- */
-int rad_checkfilename(char const *filename)
-{
-       if (strspn(filename, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.") == strlen(filename)) {
-               return 0;
-       }
-
-       return -1;
-}
-
-/** Check if file exists
- *
- * @param filename to check.
- * @return 0 if the file does not exist, 1 if the file exists, -1 if the file
- *     exists but there was an error opening it. errno value should be usable
- *     for error messages.
- */
-int rad_file_exists(char const *filename)
-{
-       int des;
-       int ret = 1;
-
-       if ((des = open(filename, O_RDONLY)) == -1) {
-               if (errno == ENOENT) {
-                       ret = 0;
-               } else {
-                       ret = -1;
-               }
-       } else {
-               close(des);
-       }
-
-       return ret;
-}
-
-/*
  *     Create possibly many directories.
  *
  *     Note that the input directory name is NOT a constant!
@@ -292,60 +252,46 @@ int rad_mkdir(char *directory, mode_t mode)
 {
        int rcode;
        char *p;
-       struct stat st;
-
-       /*
-        *      If the directory exists, don't do anything.
-        */
-       if (stat(directory, &st) == 0) {
-               return 0;
-       }
 
        /*
-        *      Look for the LAST directory name.  Try to create that,
-        *      failing on any error.
+        *      Try to make the directory.  If it exists, chmod it.
+        *      If a path doesn't exist, that's OK.  Otherwise
+        *      return with an error.
         */
-       p = strrchr(directory, FR_DIR_SEP);
-       if (p != NULL) {
-               *p = '\0';
-               rcode = rad_mkdir(directory, mode);
+       rcode = mkdir(directory, mode & 0777);
+       if (rcode < 0) {
+               if (errno == EEXIST) {
+                       return chmod(directory, mode);
+               }
 
-               /*
-                *      On error, we leave the directory name as the
-                *      one which caused the error.
-                */
-               if (rcode < 0) {
-                       if (errno == EEXIST) return 0;
+               if (errno != ENOENT) {
                        return rcode;
                }
 
                /*
-                *      Reset the directory delimiter, and go ask
-                *      the system to make the directory.
+                *      A component in the directory path doesn't
+                *      exist.  Look for the LAST directory name.  Try
+                *      to create that.  If there's an error, we leave
+                *      the directory path as the one at which the
+                *      error occured.
                 */
-               *p = FR_DIR_SEP;
-       } else {
-               return 0;
-       }
+               p = strrchr(directory, FR_DIR_SEP);
+               if (!p || (p == directory)) return -1;
 
-       /*
-        *      Having done everything successfully, we do the
-        *      system call to actually go create the directory.
-        */
-       rcode = mkdir(directory, mode & 0777);
-       if (rcode < 0) {
-               return rcode;
-       }
+               *p = '\0';
+               rcode = rad_mkdir(directory, mode);
+               if (rcode < 0) return rcode;
 
-       /*
-        *      Set things like sticky bits that aren't supported by
-        *      mkdir.
-        */
-       if (mode & ~0777) {
-               rcode = chmod(directory, mode);
-       }
+               /*
+                *      Reset the directory path, and try again to
+                *      make the directory.
+                */
+               *p = FR_DIR_SEP;
+               rcode = mkdir(directory, mode & 0777);
+               if (rcode < 0) return rcode;
+       } /* else we successfully created the directory */
 
-       return rcode;
+       return chmod(directory, mode);
 }
 
 
@@ -367,13 +313,6 @@ void *rad_malloc(size_t size)
 }
 
 
-void *rad_calloc(size_t size)
-{
-       void *ptr = rad_malloc(size);
-       memset(ptr, 0, size);
-       return ptr;
-}
-
 void rad_const_free(void const *ptr)
 {
        void *tmp;
@@ -389,11 +328,11 @@ void rad_const_free(void const *ptr)
  *
  */
 
-void NEVER_RETURNS rad_assert_fail (char const *file, unsigned int line,
-                                   char const *expr)
+void NEVER_RETURNS rad_assert_fail(char const *file, unsigned int line, char const *expr)
 {
        ERROR("ASSERT FAILED %s[%u]: %s", file, line, expr);
-       abort();
+       fr_fault(SIGABRT);
+       fr_exit_now(1);
 }
 
 
@@ -419,11 +358,11 @@ REQUEST *request_alloc(TALLOC_CTX *ctx)
        request->username = NULL;
        request->password = NULL;
        request->timestamp = time(NULL);
-       request->options = debug_flag; /* Default to global debug level */
+       request->log.lvl = debug_flag; /* Default to global debug level */
 
        request->module = "";
        request->component = "<core>";
-       request->radlog = radlog_request;
+       request->log.func = vradlog_request;
 
        return request;
 }
@@ -458,13 +397,13 @@ REQUEST *request_alloc_fake(REQUEST *request)
         */
        fake->server = request->server;
 
-       fake->packet = rad_alloc(request, 1);
+       fake->packet = rad_alloc(fake, 1);
        if (!fake->packet) {
                request_free(&fake);
                return NULL;
        }
 
-       fake->reply = rad_alloc(request, 0);
+       fake->reply = rad_alloc(fake, 0);
        if (!fake->reply) {
                request_free(&fake);
                return NULL;
@@ -511,8 +450,7 @@ REQUEST *request_alloc_fake(REQUEST *request)
        /*
         *      Copy debug information.
         */
-       fake->options = request->options;
-       fake->radlog = request->radlog;
+       memcpy(&(fake->log), &(request->log), sizeof(fake->log));
 
        return fake;
 }
@@ -525,8 +463,8 @@ REQUEST *request_alloc_coa(REQUEST *request)
        /*
         *      Originate CoA requests only when necessary.
         */
-       if ((request->packet->code != PW_AUTHENTICATION_REQUEST) &&
-           (request->packet->code != PW_ACCOUNTING_REQUEST)) return NULL;
+       if ((request->packet->code != PW_CODE_AUTHENTICATION_REQUEST) &&
+           (request->packet->code != PW_CODE_ACCOUNTING_REQUEST)) return NULL;
 
        request->coa = request_alloc_fake(request);
        if (!request->coa) return NULL;
@@ -663,9 +601,9 @@ int rad_copy_variable(char *to, char const *from)
 #define USEC 1000000
 #endif
 
-int rad_pps(int *past, int *present, time_t *then, struct timeval *now)
+uint32_t rad_pps(uint32_t *past, uint32_t *present, time_t *then, struct timeval *now)
 {
-       int pps;
+       uint32_t pps;
 
        if (*then != now->tv_sec) {
                *then = now->tv_sec;
@@ -813,7 +751,7 @@ int rad_expand_xlat(REQUEST *request, char const *cmd,
         *      We have to have SOMETHING, at least.
         */
        if (argc <= 0) {
-               ERROR("rad_expand_xlat: Empty command line.");
+               ERROR("rad_expand_xlat: Empty command line");
                return -1;
        }
 
@@ -854,7 +792,7 @@ int rad_expand_xlat(REQUEST *request, char const *cmd,
                left--;
 
                if (left <= 0) {
-                       ERROR("rad_expand_xlat: Ran out of space while expanding arguments.");
+                       ERROR("rad_expand_xlat: Ran out of space while expanding arguments");
                        return -1;
                }
        }
@@ -866,8 +804,8 @@ int rad_expand_xlat(REQUEST *request, char const *cmd,
 const FR_NAME_NUMBER pair_lists[] = {
        { "request",            PAIR_LIST_REQUEST },
        { "reply",              PAIR_LIST_REPLY },
+       { "control",            PAIR_LIST_CONTROL },            /* New name should have priority */
        { "config",             PAIR_LIST_CONTROL },
-       { "control",            PAIR_LIST_CONTROL },
 #ifdef WITH_PROXY
        { "proxy-request",      PAIR_LIST_PROXY_REQUEST },
        { "proxy-reply",        PAIR_LIST_PROXY_REPLY },
@@ -905,10 +843,10 @@ const FR_NAME_NUMBER request_refs[] = {
  * @see dict_attrbyname
  *
  * @param[in,out] name of attribute.
- * @param[in] unknown the list to return if no qualifiers were found.
+ * @param[in] default_list the list to return if no qualifiers were found.
  * @return PAIR_LIST_UNKOWN if qualifiers couldn't be resolved to a list.
  */
-pair_lists_t radius_list_name(char const **name, pair_lists_t unknown)
+pair_lists_t radius_list_name(char const **name, pair_lists_t default_list)
 {
        char const *p = *name;
        char const *q;
@@ -918,34 +856,79 @@ pair_lists_t radius_list_name(char const **name, pair_lists_t unknown)
        rad_assert(name && *name);
 
        /*
-        *      We couldn't determine the list if:
-        *
-        *      A colon delimiter was found, but the next char was a
-        *      number, indicating a tag, not a list qualifier.
-        *
-        *      No colon was found and the first char was upper case
-        *      indicating an attribute.
-        *
+        *      Unfortunately, ':' isn't a definitive separator for
+        *      the list name.  We may have numeric tags, too.
         */
        q = strchr(p, ':');
-       if (((q && (q[1] >= '0') && (q[1] <= '9'))) ||
-           (!q && isupper((int) *p))) {
-               return unknown;
-       }
-
        if (q) {
-               *name = (q + 1);        /* Consume the list and delimiter */
-               return fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+               /*
+                *      Check for tagged attributes.  They have
+                *      "name:tag", where tag is a decimal number.
+                *      Valid tags are invalid attributes, so that's
+                *      OK.
+                *
+                *      Also allow "name:tag[#]" as a tag.
+                *
+                *      However, "request:" is allowed, too, and
+                *      shouldn't be interpreted as a tag.
+                *
+                *      We do this check first rather than just
+                *      looking up the request name, because this
+                *      check is cheap, and looking up the request
+                *      name is expensive.
+                */
+               if (isdigit((int) q[1])) {
+                       char const *d = q + 1;
+
+                       while (isdigit((int) *d)) {
+                               d++;
+                       }
+
+                       /*
+                        *      Return the DEFAULT list as supplied by
+                        *      the caller.  This is usually
+                        *      PAIRLIST_REQUEST.
+                        */
+                       if (!*d || (*d == '[')) {
+                               return default_list;
+                       }
+               }
+
+               /*
+                *      If the first part is a list name, then treat
+                *      it as a list.  This means that we CANNOT have
+                *      an attribute which is named "request",
+                *      "reply", etc.  Allowing a tagged attribute
+                *      "request:3" would just be insane.
+                */
+               output = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
+               if (output != PAIR_LIST_UNKNOWN) {
+                       *name = (q + 1);        /* Consume the list and delimiter */
+                       return output;
+               }
+
+               /*
+                *      It's not a known list, say so.
+                */
+               return PAIR_LIST_UNKNOWN;
        }
 
-       q = (p + strlen(p));    /* Consume the entire string */
+       /*
+        *      The input string may be just a list name,
+        *      e.g. "request".  Check for that.
+        */
+       q = (p + strlen(p));
        output = fr_substr2int(pair_lists, p, PAIR_LIST_UNKNOWN, (q - p));
        if (output != PAIR_LIST_UNKNOWN) {
                *name = q;
                return output;
        }
 
-       return unknown;
+       /*
+        *      It's just an attribute name.  Return the default list
+        *      as supplied by the caller.
+        */
+       return default_list;
 }
 
 
@@ -1090,3 +1073,68 @@ void rad_regcapture(REQUEST *request, int compare, char const *value, regmatch_t
        }
 }
 
+#ifndef NDEBUG
+/*
+ *     Verify a packet.
+ */
+static void verify_packet(REQUEST *request, RADIUS_PACKET *packet)
+{
+       TALLOC_CTX *parent;
+
+       if (!packet) return;
+
+       parent = talloc_parent(packet);
+       if (parent != request) {
+               ERROR("Expected RADIUS_PACKET to be parented by %p (%s), "
+                     "but parented by %p (%s)",
+                     request, talloc_get_name(request),
+                     parent, parent ? talloc_get_name(parent) : "NULL");
+
+               fr_log_talloc_report(packet);
+               if (parent) fr_log_talloc_report(parent);
+
+               rad_assert(0);
+       }
+
+       VERIFY_PACKET(packet);
+
+       if (!packet->vps) return;
+
+#ifdef WITH_VERIFY_PTR
+       fr_verify_list(packet, packet->vps);
+#endif
+}
+/*
+ *     Catch horrible talloc errors.
+ */
+void verify_request(REQUEST *request)
+{
+       if (!request) return;
+
+       (void) talloc_get_type_abort(request, REQUEST);
+
+#ifdef WITH_VERIFY_PTR
+       fr_verify_list(request, request->config_items);
+#endif
+
+       if (request->packet) verify_packet(request, request->packet);
+       if (request->reply) verify_packet(request, request->reply);
+#ifdef WITH_PROXY
+       if (request->proxy) verify_packet(request, request->proxy);
+       if (request->proxy_reply) verify_packet(request, request->proxy_reply);
+#endif
+
+#ifdef WITH_COA
+       if (request->coa) {
+               void *parent;
+
+               (void) talloc_get_type_abort(request->coa, REQUEST);
+               parent = talloc_parent(request->coa);
+
+               rad_assert(parent == request);
+
+               verify_request(request->coa);
+       }
+#endif
+}
+#endif
index ff123f5..c89cf91 100644 (file)
@@ -145,9 +145,9 @@ int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
         *      Tagged attributes are equal if and only if both the
         *      tag AND value match.
         */
-       if (check->da->flags.has_tag) {
+       if (check->da->flags.has_tag && !TAG_EQ(check->tag, vp->tag)) {
                ret = ((int) vp->tag) - ((int) check->tag);
-               goto finish;
+               if (ret != 0) goto finish;
        }
 
        /*
@@ -208,16 +208,16 @@ int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp)
                        ret = vp->vp_date - check->vp_date;
                        break;
 
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr);
                        break;
 
-               case PW_TYPE_IPV6ADDR:
+               case PW_TYPE_IPV6_ADDR:
                        ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr,
                                     sizeof(vp->vp_ipv6addr));
                        break;
 
-               case PW_TYPE_IPV6PREFIX:
+               case PW_TYPE_IPV6_PREFIX:
                        ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix,
                                     sizeof(vp->vp_ipv6prefix));
                        break;
@@ -287,7 +287,6 @@ int radius_callback_compare(REQUEST *request, VALUE_PAIR *req,
 
 /** Find a comparison function for two attributes.
  *
- * @todo this should probably take DA's.
  * @param da to find comparison function for.
  * @return true if a comparison function was found, else false.
  */
@@ -432,9 +431,9 @@ int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
        int compare;
        bool first_only;
 
-       for (check_item = paircursor(&cursor, &check);
+       for (check_item = fr_cursor_init(&cursor, &check);
             check_item;
-            check_item = pairnext(&cursor)) {
+            check_item = fr_cursor_next(&cursor)) {
                /*
                 *      If the user is setting a configuration value,
                 *      then don't bother comparing it to any attributes
@@ -470,9 +469,9 @@ int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
                         */
                        case PW_USER_PASSWORD:
                                if (check_item->op == T_OP_CMP_EQ) {
-                                       WDEBUG("Found User-Password == \"...\".");
-                                       WDEBUG("Are you sure you don't mean Cleartext-Password?");
-                                       WDEBUG("See \"man rlm_pap\" for more information.");
+                                       WARN("Found User-Password == \"...\"");
+                                       WARN("Are you sure you don't mean Cleartext-Password?");
+                                       WARN("See \"man rlm_pap\" for more information");
                                }
                                if (pairfind(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
                                        continue;
@@ -608,7 +607,6 @@ int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
 
        len = radius_xlat(buffer, sizeof(buffer), request, vp->value.xlat, NULL, NULL);
 
-
        rad_const_free(vp->value.xlat);
        vp->value.xlat = NULL;
        if (len < 0) {
@@ -618,166 +616,31 @@ int radius_xlat_do(REQUEST *request, VALUE_PAIR *vp)
        /*
         *      Parse the string into a new value.
         */
-       if (!pairparsevalue(vp, buffer)){
+       if (pairparsevalue(vp, buffer, 0) < 0){
                return -2;
        }
 
        return 0;
 }
 
-/** Move pairs, replacing/over-writing them, and doing xlat.
- *
- * Move attributes from one list to the other if not already present.
- */
-void radius_xlat_move(REQUEST *request, VALUE_PAIR **to, VALUE_PAIR **from)
-{
-       VALUE_PAIR **tailto, *i, *j, *next;
-       VALUE_PAIR *tailfrom = NULL;
-       VALUE_PAIR *found;
-
-       /*
-        *      Point "tailto" to the end of the "to" list.
-        */
-       tailto = to;
-       for (i = *to; i; i = i->next) {
-               tailto = &i->next;
-       }
-
-       /*
-        *      Loop over the "from" list.
-        */
-       for (i = *from; i; i = next) {
-               next = i->next;
-
-               /*
-                *      Don't move 'fallthrough' over.
-                */
-               if (!i->da->vendor && i->da->attr == PW_FALL_THROUGH) {
-                       tailfrom = i;
-                       continue;
-               }
-
-               /*
-                *      We've got to xlat the string before moving
-                *      it over.
-                */
-               radius_xlat_do(request, i);
-
-               found = pairfind(*to, i->da->attr, i->da->vendor, TAG_ANY);
-               switch (i->op) {
-
-                       /*
-                        *      If a similar attribute is found,
-                        *      delete it.
-                        */
-                       case T_OP_SUB:          /* -= */
-                               if (found) {
-                                       if (!i->vp_strvalue[0] ||
-                                           (strcmp(found->vp_strvalue,
-                                                   i->vp_strvalue) == 0)) {
-                                               pairdelete(to, found->da->attr,
-                                                       found->da->vendor,
-                                                       found->tag);
-
-                                       /*
-                                        *      'tailto' may have been
-                                        *      deleted...
-                                        */
-                                       tailto = to;
-                                       for (j = *to; j; j = j->next) {
-                                               tailto = &j->next;
-                                       }
-                               }
-                       }
-                       tailfrom = i;
-                       continue;
-
-                       /*
-                        *      Add it, if it's not already there.
-                        */
-                       case T_OP_EQ:           /* = */
-                               if (found) {
-                                       tailfrom = i;
-                                       continue; /* with the loop */
-                               }
-                               break;
-
-                       /*
-                        *      If a similar attribute is found,
-                        *      replace it with the new one.  Otherwise,
-                        *      add the new one to the list.
-                        */
-                       case T_OP_SET:          /* := */
-                               if (found) {
-                                       VALUE_PAIR *vp;
-
-                                       vp = found->next;
-                                       memcpy(found, i, sizeof(*found));
-                                       found->next = vp;
-                                       tailfrom = i;
-                                       continue;
-                               }
-                               break;
-
-                       /*
-                        *      FIXME: Add support for <=, >=, <, >
-                        *
-                        *      which will mean (for integers)
-                        *      'make the attribute the smaller, etc'
-                        */
-
-                       /*
-                        *  Add the new element to the list, even
-                        *  if similar ones already exist.
-                        */
-                       default:
-                       case T_OP_ADD:          /* += */
-                               break;
-               }
-
-               if (tailfrom) {
-                       tailfrom->next = next;
-               } else {
-                       *from = next;
-               }
-
-               /*
-                *      If ALL of the 'to' attributes have been deleted,
-                *      then ensure that the 'tail' is updated to point
-                *      to the head.
-                */
-               if (!*to) {
-                       tailto = to;
-               }
-               *tailto = i;
-               if (i) {
-                       i->next = NULL;
-                       tailto = &i->next;
-               }
-       } /* loop over the 'from' list */
-}
-
 /** Create a VALUE_PAIR and add it to a list of VALUE_PAIR s
  *
  * @note This function ALWAYS returns. If we're OOM, then it causes the
  * @note server to exit, so you don't need to check the return value.
  *
- * @param[in] request Current request.
+ * @param[in] ctx for talloc
  * @param[out] vps List to add new VALUE_PAIR to, if NULL will just
  *     return VALUE_PAIR.
  * @param[in] attribute number.
  * @param[in] vendor number.
  * @return a new VLAUE_PAIR or causes server to exit on error.
  */
-VALUE_PAIR *radius_paircreate(REQUEST *request, VALUE_PAIR **vps,
+VALUE_PAIR *radius_paircreate(TALLOC_CTX *ctx, VALUE_PAIR **vps,
                              unsigned int attribute, unsigned int vendor)
 {
        VALUE_PAIR *vp;
 
-       /*
-        *      FIXME: the context should ideally be the packet...
-        */
-       vp = paircreate(request, attribute, vendor);
+       vp = paircreate(ctx, attribute, vendor);
        if (!vp) {
                ERROR("No memory!");
                rad_assert("No memory" == NULL);
@@ -809,19 +672,9 @@ void debug_pair_list(VALUE_PAIR *vp)
        vp_cursor_t cursor;
        if (!vp || !debug_flag || !fr_log_fp) return;
 
-       for (vp = paircursor(&cursor, &vp);
+       for (vp = fr_cursor_init(&cursor, &vp);
             vp;
-            vp = pairnext(&cursor)) {
-               /*
-                *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
-                */
-               if (!talloc_get_type(vp, VALUE_PAIR)) {
-                       ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
-
-                       log_talloc_report(vp);
-                       rad_assert(0);
-               }
-
+            vp = fr_cursor_next(&cursor)) {
                vp_print(fr_log_fp, vp);
        }
        fflush(fr_log_fp);
@@ -837,24 +690,23 @@ void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp)
 {
        vp_cursor_t cursor;
        char buffer[256];
-       if (!vp || !request || !request->radlog) return;
+       if (!vp || !request || !request->log.func) return;
 
-       for (vp = paircursor(&cursor, &vp);
+       for (vp = fr_cursor_init(&cursor, &vp);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                /*
                 *      Take this opportunity to verify all the VALUE_PAIRs are still valid.
                 */
                if (!talloc_get_type(vp, VALUE_PAIR)) {
                        REDEBUG("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));
 
-                       log_talloc_report(vp);
+                       fr_log_talloc_report(vp);
                        rad_assert(0);
                }
 
                vp_prints(buffer, sizeof(buffer), vp);
-
-               request->radlog(L_DBG, level, request, "\t%s", buffer);
+               RDEBUGX(level, "\t%s", buffer);
        }
 }
 
@@ -898,14 +750,14 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
 #ifdef WITH_COA
                case PAIR_LIST_COA:
                        if (request->coa &&
-                           (request->coa->proxy->code == PW_COA_REQUEST)) {
+                           (request->coa->proxy->code == PW_CODE_COA_REQUEST)) {
                                return &request->coa->proxy->vps;
                        }
                        break;
 
                case PAIR_LIST_COA_REPLY:
                        if (request->coa && /* match reply with request */
-                           (request->coa->proxy->code == PW_COA_REQUEST) &&
+                           (request->coa->proxy->code == PW_CODE_COA_REQUEST) &&
                            request->coa->proxy_reply) {
                                return &request->coa->proxy_reply->vps;
                        }
@@ -913,16 +765,16 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
 
                case PAIR_LIST_DM:
                        if (request->coa &&
-                           (request->coa->proxy->code == PW_DISCONNECT_REQUEST)) {
+                           (request->coa->proxy->code == PW_CODE_DISCONNECT_REQUEST)) {
                                return &request->coa->proxy->vps;
                        }
                        break;
 
                case PAIR_LIST_DM_REPLY:
                        if (request->coa && /* match reply with request */
-                           (request->coa->proxy->code == PW_DISCONNECT_REQUEST) &&
+                           (request->coa->proxy->code == PW_CODE_DISCONNECT_REQUEST) &&
                            request->coa->proxy_reply) {
-                               return &request->coa->proxy->vps;
+                               return &request->coa->proxy->vps;
                        }
                        break;
 #endif
@@ -934,6 +786,151 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
        return NULL;
 }
 
+
+TALLOC_CTX *radius_list_ctx(REQUEST *request, pair_lists_t list_name)
+{
+       if (!request) return NULL;
+
+               switch (list_name) {
+               case PAIR_LIST_REQUEST:
+                       return request->packet;
+
+               case PAIR_LIST_REPLY:
+                       return request->reply;
+
+               case PAIR_LIST_CONTROL:
+                       return request;
+
+#ifdef WITH_PROXY
+               case PAIR_LIST_PROXY_REQUEST:
+                       return request->proxy;
+
+               case PAIR_LIST_PROXY_REPLY:
+                       return request->proxy_reply;
+#endif
+
+#ifdef WITH_COA
+               case PAIR_LIST_COA:
+                       if (!request->coa) return NULL;
+                       rad_assert(request->coa->proxy != NULL);
+                       if (request->coa->proxy->code != PW_CODE_COA_REQUEST) return NULL;
+                       return request->coa->proxy;
+
+               case PAIR_LIST_COA_REPLY:
+                       if (!request->coa) return NULL;
+                       rad_assert(request->coa->proxy != NULL);
+                       if (request->coa->proxy->code != PW_CODE_COA_REQUEST) return NULL;
+                       return request->coa->proxy_reply;
+
+               case PAIR_LIST_DM:
+                       if (!request->coa) return NULL;
+                       rad_assert(request->coa->proxy != NULL);
+                       if (request->coa->proxy->code != PW_CODE_DISCONNECT_REQUEST) return NULL;
+                       return request->coa->proxy;
+
+               case PAIR_LIST_DM_REPLY:
+                       if (!request->coa) return NULL;
+                       rad_assert(request->coa->proxy != NULL);
+                       if (request->coa->proxy->code != PW_CODE_DISCONNECT_REQUEST) return NULL;
+                       return request->coa->proxy_reply;
+#endif
+
+               default:
+                       break;
+               }
+
+               return NULL;
+}
+
+/*
+ *     Debug print a map / VP
+ */
+void radius_map_debug(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR const *vp)
+{
+       char *value;
+       char buffer[1024];
+
+       rad_assert(vp || (map->src->type == VPT_TYPE_NULL));
+
+       switch (map->src->type) {
+               /*
+                *      Just print the value being assigned
+                */
+               default:
+               case VPT_TYPE_LITERAL:
+                       vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+                       value = buffer;
+                       break;
+
+               case VPT_TYPE_XLAT:
+               case VPT_TYPE_XLAT_STRUCT:
+                       vp_prints_value(buffer, sizeof(buffer), vp, '"');
+                       value = buffer;
+                       break;
+
+               case VPT_TYPE_DATA:
+                       vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+                       value = buffer;
+                       break;
+
+               /*
+                *      Just printing the value doesn't make sense, but we still
+                *      want to know what it was...
+                */
+               case VPT_TYPE_LIST:
+                       vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+
+                       if (map->src->vpt_request == REQUEST_OUTER) {
+                               value = talloc_typed_asprintf(request, "&outer.%s:%s -> %s",
+                                                             fr_int2str(pair_lists, map->src->vpt_list, "<INVALID>"),
+                                                             vp->da->name, buffer);
+                       } else {
+                               value = talloc_typed_asprintf(request, "&%s:%s -> %s",
+                                                             fr_int2str(pair_lists, map->src->vpt_list, "<INVALID>"),
+                                                             vp->da->name, buffer);
+                       }
+                       break;
+
+               case VPT_TYPE_ATTR:
+                       vp_prints_value(buffer, sizeof(buffer), vp, '\'');
+                       value = talloc_typed_asprintf(request, "&%s -> %s", map->src->vpt_da->name, buffer);
+                       break;
+
+               case VPT_TYPE_NULL:
+                       strcpy(buffer, "ANY");
+                       value = buffer;
+                       break;
+       }
+
+       switch (map->dst->type) {
+               case VPT_TYPE_LIST:
+                       RDEBUG("\t%s%s %s %s", map->dst->name, vp ? vp->da->name : "",
+                              fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
+                       break;
+
+               case VPT_TYPE_ATTR:
+                       RDEBUG("\t%s %s %s", map->dst->name,
+                              fr_int2str(fr_tokens, vp ? vp->op : map->op, "<INVALID>"), value);
+                       break;
+
+               default:
+                       break;
+       }
+
+       if (value != buffer) talloc_free(value);
+}
+
+#define DEBUG_OVERWRITE(_old, _new) \
+do {\
+       if (RDEBUG_ENABLED3) {\
+               char *old = vp_aprint_value(request, _old);\
+               char *new = vp_aprint_value(request, _new);\
+               RDEBUG3("Overwriting value \"%s\" with \"%s\"", old, new);\
+               talloc_free(old);\
+               talloc_free(new);\
+       }\
+} while (0)
+
 /** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST.
  *
  * Takes a single value_pair_map_t, resolves request and list identifiers
@@ -946,95 +943,355 @@ VALUE_PAIR **radius_list(REQUEST *request, pair_lists_t list)
  * @param func to retrieve module specific values and convert them to
  *     VALUE_PAIRS.
  * @param ctx to be passed to func.
- * @param src name to be used in debugging if different from map value.
  * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success.
  */
-int radius_map2request(REQUEST *request, value_pair_map_t const *map,
-                      UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx)
+int radius_map2request(REQUEST *request, value_pair_map_t const *map, radius_tmpl_getvalue_t func, void *ctx)
 {
-       int rcode;
-       vp_cursor_t cursor;
-       VALUE_PAIR **list, *vp, *head = NULL;
+       int rcode = 0;
+       int num;
+       VALUE_PAIR **list, *vp, *dst, *head = NULL;
+       bool found = false;
        REQUEST *context;
-       char buffer[1024];
+       TALLOC_CTX *parent;
+       vp_cursor_t dst_list, src_list;
+
+       /*
+        *      Sanity check inputs.  We can have a list or attribute
+        *      as a destination.
+        */
+       if ((map->dst->type != VPT_TYPE_LIST) &&
+           (map->dst->type != VPT_TYPE_ATTR)) {
+               REDEBUG("Invalid mapping destination");
+               return -2;
+       }
 
        context = request;
-       if (radius_request(&context, map->dst->request) < 0) {
+       if (radius_request(&context, map->dst->vpt_request) < 0) {
                REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
-
                return -2;
        }
 
-       list = radius_list(context, map->dst->list);
+       /*
+        *      If there's no CoA packet and we're updating it,
+        *      auto-allocate it.
+        */
+       if (((map->dst->vpt_list == PAIR_LIST_COA) ||
+            (map->dst->vpt_list == PAIR_LIST_DM)) && !request->coa) {
+               request_alloc_coa(context);
+               context->coa->proxy->code = (map->dst->vpt_list == PAIR_LIST_COA) ?
+                                           PW_CODE_COA_REQUEST :
+                                           PW_CODE_DISCONNECT_REQUEST;
+       }
+
+       list = radius_list(context, map->dst->vpt_list);
        if (!list) {
                REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name);
 
                return -2;
        }
 
+       parent = radius_list_ctx(context, map->dst->vpt_list);
+       rad_assert(parent);
 
        /*
-        *      The callback should either return -1 to signify operations error, -2 when it can't find the
-        *      attribute or list being referenced, or 0 to signify success.
-        *      It may return "sucess", but still have no VPs to work with.
-        *      Only if it returned an error code should it not write anything to the head pointer.
+        *      The callback should either return -1 to signify operations error,
+        *      -2 when it can't find the attribute or list being referenced, or
+        *      0 to signify success. It may return "sucess", but still have no
+        *      VPs to work with.
         */
-       rcode = func(&head, request, map, ctx);
-       if (rcode < 0) {
-               rad_assert(!head);
+       if (map->src->type != VPT_TYPE_NULL) {
+               rcode = func(&head, request, map, ctx);
+               if (rcode < 0) {
+                       rad_assert(!head);
+                       return rcode;
+               }
+               if (!head) return rcode;
+       } else {
+               if (debug_flag) radius_map_debug(request, map, NULL);
+       }
 
-               return rcode;
+       /*
+        *      Reparent the VPs (func may return multiple)
+        */
+       for (vp = fr_cursor_init(&src_list, &head);
+            vp;
+            vp = fr_cursor_next(&src_list)) {
+               VERIFY_VP(vp);
+
+               if (debug_flag) radius_map_debug(request, map, vp);
+               (void) talloc_steal(parent, vp);
        }
 
-       if (!head) return 0;
+       /*
+        *      The destination is a list (which is a completely different set of operations)
+        */
+       if (map->dst->type == VPT_TYPE_LIST) {
+               switch (map->op) {
+               case T_OP_CMP_FALSE:
+                       /* We don't need the src VPs (should just be 'ANY') */
+                       rad_assert(!head);
+
+                       /* Clear the entire dst list */
+                       pairfree(list);
+
+                       if (map->dst->vpt_list == PAIR_LIST_REQUEST) {
+                               context->username = NULL;
+                               context->password = NULL;
+                       }
+                       return 0;
 
-       VERIFY_VP(head);
+               case T_OP_SET:
+                       if (map->src->type == VPT_TYPE_LIST) {
+                               pairfree(list);
+                               *list = head;
+                       } else {
+               case T_OP_EQ:
+                               rad_assert(map->src->type == VPT_TYPE_EXEC);
+                               pairmove(parent, list, &head);
+                               pairfree(&head);
+                       }
+                       goto finish;
 
-       if (debug_flag) for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) {
-               char *value;
+               case T_OP_ADD:
+                       pairadd(list, head);
+                       head = NULL;
+                       goto finish;
 
-               switch (map->src->type) {
-                       /*
-                        *      Just print the value being assigned
-                        */
-                       default:
+               default:
+                       pairfree(&head);
+                       return -1;
+               }
+       }
 
-                       case VPT_TYPE_LITERAL:
-                               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
-                               value = buffer;
-                               break;
-                       case VPT_TYPE_XLAT:
-                               vp_prints_value(buffer, sizeof(buffer), vp, '"');
-                               value = buffer;
-                               break;
-                       case VPT_TYPE_DATA:
-                               vp_prints_value(buffer, sizeof(buffer), vp, 0);
-                               value = buffer;
-                               break;
-                       /*
-                        *      Just printing the value doesn't make sense, but we still
-                        *      want to know what it was...
-                        */
-                       case VPT_TYPE_LIST:
-                               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
-                               value = talloc_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer);
-                               break;
-                       case VPT_TYPE_ATTR:
-                               vp_prints_value(buffer, sizeof(buffer), vp, '\'');
-                               value = talloc_asprintf(request, "&%s -> %s", map->src->name, buffer);
-                               break;
+       /*
+        *      Find the destination attribute.  We leave with either
+        *      the dst_list and vp pointing to the attribute or the VP
+        *      being NULL (no attribute at that index).
+        */
+       num = map->dst->vpt_num;
+       (void) fr_cursor_init(&dst_list, list);
+       if (num != NUM_ANY) {
+               while ((dst = fr_cursor_next_by_da(&dst_list, map->dst->vpt_da, map->dst->vpt_tag))) {
+                       if (num-- == 0) break;
+               }
+       } else {
+               dst = fr_cursor_next_by_da(&dst_list, map->dst->vpt_da, map->dst->vpt_tag);
+       }
+       rad_assert(!dst || (map->dst->vpt_da == dst->da));
+
+       /*
+        *      The destination is an attribute
+        */
+       switch (map->op) {
+       default:
+               break;
+       /*
+        *      !* - Remove all attributes which match dst in the specified list.
+        *      This doesn't use attributes returned by the func(), and immediately frees them.
+        */
+       case T_OP_CMP_FALSE:
+               /* We don't need the src VPs (should just be 'ANY') */
+               rad_assert(!head);
+               if (!dst) return 0;
+
+               /*
+                *      Wildcard: delete all of the matching ones, based on tag.
+                */
+               if (map->dst->vpt_num == NUM_ANY) {
+                       pairdelete(list, map->dst->vpt_da->attr, map->dst->vpt_da->vendor, map->dst->vpt_tag);
+                       dst = NULL;
+               /*
+                *      We've found the Nth one.  Delete it, and only it.
+                */
+               } else {
+                       dst = fr_cursor_remove(&dst_list);
+                       pairfree(&dst);
                }
 
+               /*
+                *      Check that the User-Name and User-Password
+                *      caches point to the correct attribute.
+                */
+               goto finish;
+
+       /*
+        *      -= - Delete attributes in the dst list which match any of the
+        *      src_list attributes.
+        *
+        *      This operation has two modes:
+        *      - If map->dst->vpt_num > 0, we check each of the src_list attributes against
+        *        the dst attribute, to see if any of their values match.
+        *      - If map->dst->vpt_num == NUM_ANY, we compare all instances of the dst attribute
+        *        against each of the src_list attributes.
+        */
+       case T_OP_SUB:
+               /* We didn't find any attributes earlier */
+               if (!dst) {
+                       pairfree(&head);
+                       return 0;
+               }
+
+               /*
+                *      Instance specific[n] delete
+                */
+               if (map->dst->vpt_num != NUM_ANY) {
+                       for (vp = fr_cursor_first(&src_list);
+                            vp;
+                            vp = fr_cursor_next(&src_list)) {
+                               head->op = T_OP_CMP_EQ;
+                               rcode = radius_compare_vps(request, vp, dst);
+                               if (rcode == 0) {
+                                       dst = fr_cursor_remove(&dst_list);
+                                       pairfree(&dst);
+                                       found = true;
+                               }
+                       }
+                       pairfree(&head);
+                       if (!found) return 0;
+                       goto finish;
+               }
 
-               RDEBUG("\t\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);
+               /*
+                *      All instances[*] delete
+                */
+               for (dst = fr_cursor_current(&dst_list);
+                    dst;
+                    dst = fr_cursor_next_by_da(&dst_list, map->dst->vpt_da, map->dst->vpt_tag)) {
+                       for (vp = fr_cursor_first(&src_list);
+                            vp;
+                            vp = fr_cursor_next(&src_list)) {
+                               head->op = T_OP_CMP_EQ;
+                               rcode = radius_compare_vps(request, vp, dst);
+                               if (rcode == 0) {
+                                       dst = fr_cursor_remove(&dst_list);
+                                       pairfree(&dst);
+                                       found = true;
+                               }
+                       }
+               }
+               pairfree(&head);
+               if (!found) return 0;
+               goto finish;
+       }
 
-               if (value != buffer) talloc_free(value);
+       /*
+        *      Another fixup pass to set tags on attributes were about to insert
+        */
+       if (map->dst->vpt_tag != TAG_ANY) {
+               for (vp = fr_cursor_init(&src_list, &head);
+                    vp;
+                    vp = fr_cursor_next(&src_list)) {
+                       vp->tag = map->dst->vpt_tag;
+               }
        }
 
+       switch (map->op) {
        /*
-        *      Use pairmove so the operator is respected
+        *      = - Set only if not already set
         */
-       radius_pairmove(context, list, head);
+       case T_OP_EQ:
+               if (dst) {
+                       RDEBUG3("Refusing to overwrite (use :=)");
+                       pairfree(&head);
+                       return 0;
+               }
+
+               /* Insert first instance (if multiple) */
+               fr_cursor_first(&src_list);
+               fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
+               /* Free any we didn't insert */
+               pairfree(&head);
+               break;
+
+       /*
+        *      := - Overwrite existing attribute with last src_list attribute
+        */
+       case T_OP_SET:
+               /* Wind to last instance */
+               fr_cursor_last(&src_list);
+               if (dst) {
+                       dst = fr_cursor_remove(&dst_list);
+                       DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
+                       pairfree(&dst);
+               }
+               fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
+               /* Free any we didn't insert */
+               pairfree(&head);
+               break;
+
+       /*
+        *      += - Add all src_list attributes to the destination
+        */
+       case T_OP_ADD:
+               /* Insert all the instances! (if multiple) */
+               pairadd(list, head);
+               head = NULL;
+               break;
+
+       /*
+        *      Filtering operators
+        */
+       default:
+               /*
+                *      If the dst doesn't exist, the filters will add
+                *      it with the given value.
+                */
+               if (!dst) {
+                       RDEBUG3("No existing attribute to filter, adding instead");
+                       fr_cursor_insert(&dst_list, head);
+                       head = NULL;
+                       goto finish;
+               }
+
+               /*
+                *      The LHS exists.  We need to limit it's value based on
+                *      the operator, and the value of the RHS.
+                */
+               found = false;
+               for (vp = fr_cursor_first(&src_list);
+                    vp;
+                    vp = fr_cursor_next(&src_list)) {
+                       vp->op = map->op;
+                       rcode = radius_compare_vps(request, vp, dst);
+                       vp->op = T_OP_SET;
+
+                       switch (map->op) {
+                       case T_OP_CMP_EQ:
+                               if (rcode == 0) continue;
+                       replace:
+                               dst = fr_cursor_remove(&dst_list);
+                               DEBUG_OVERWRITE(dst, fr_cursor_current(&src_list));
+                               pairfree(&dst);
+                               fr_cursor_insert(&dst_list, fr_cursor_remove(&src_list));
+                               found = true;
+                               continue;
+
+                       case T_OP_LE:
+                               if (rcode <= 0) continue;
+                               goto replace;
+
+                       case T_OP_GE:
+                               if (rcode >= 0) continue;
+                               goto replace;
+
+                       default:
+                               pairfree(&head);
+                               return -1;
+                       }
+               }
+               pairfree(&head);
+               if (!found) return 0;
+
+               break;
+       }
+
+finish:
+       rad_assert(!head);
+
+       if (map->dst->vpt_list == PAIR_LIST_REQUEST) {
+               context->username = pairfind(*list, PW_USER_NAME, 0, TAG_ANY);
+               context->password = pairfind(*list, PW_USER_PASSWORD, 0, TAG_ANY);
+       }
        return 0;
 }
 
@@ -1054,7 +1311,7 @@ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *m
        char *expanded = NULL;
        char answer[1024];
        VALUE_PAIR **input_pairs = NULL;
-       VALUE_PAIR **output_pairs = NULL;
+       VALUE_PAIR *output_pairs = NULL;
 
        *out = NULL;
 
@@ -1072,14 +1329,12 @@ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *m
         *      if dst is an attribute, then we create an attribute of that type and then
         *      call pairparsevalue on the output of the script.
         */
-       out[0] = '\0';
        result = radius_exec_program(request, map->src->name, true, true,
                                     answer, sizeof(answer), EXEC_TIMEOUT,
                                     input_pairs ? *input_pairs : NULL,
-                                    (map->dst->type == VPT_TYPE_LIST) ? output_pairs : NULL);
+                                    (map->dst->type == VPT_TYPE_LIST) ? &output_pairs : NULL);
        talloc_free(expanded);
        if (result != 0) {
-               REDEBUG("%s", answer);
                talloc_free(output_pairs);
                return -1;
        }
@@ -1087,26 +1342,27 @@ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *m
        switch (map->dst->type) {
        case VPT_TYPE_LIST:
                if (!output_pairs) {
+                       REDEBUG("No valid attributes received from program");
                        return -2;
                }
-               *out = *output_pairs;
+               *out = output_pairs;
 
                return 0;
        case VPT_TYPE_ATTR:
-               {
-                       VALUE_PAIR *vp;
+       {
+               VALUE_PAIR *vp;
 
-                       vp = pairalloc(request, map->dst->da);
-                       if (!vp) return -1;
-                       vp->op = map->op;
-                       if (!pairparsevalue(vp, answer)) {
-                               pairfree(&vp);
-                               return -2;
-                       }
-                       *out = vp;
-
-                       return 0;
+               vp = pairalloc(request, map->dst->vpt_da);
+               if (!vp) return -1;
+               vp->op = map->op;
+               if (pairparsevalue(vp, answer, 0) < 0) {
+                       pairfree(&vp);
+                       return -2;
                }
+               *out = vp;
+
+               return 0;
+       }
        default:
                rad_assert(0);
        }
@@ -1116,57 +1372,49 @@ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *m
 
 /** Convert a map to a VALUE_PAIR.
  *
- * @param[out] out Where to write the VALUE_PAIR(s).
+ * @param[out] out Where to write the VALUE_PAIR(s), which may be NULL if not found
  * @param[in] request structure (used only for talloc)
  * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST.
  * @param[in] ctx unused
- * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent
+ * @return 0 on success, -1 on failure
  */
 int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx)
 {
        int rcode = 0;
        VALUE_PAIR *vp = NULL, *found, **from = NULL;
        DICT_ATTR const *da;
-       REQUEST *context;
+       REQUEST *context = request;
        vp_cursor_t cursor;
 
-       rad_assert(request != NULL);
-       rad_assert(map != NULL);
-
        *out = NULL;
 
        /*
-        *      Special case for !*, we don't need to parse the value, just allocate an attribute with
-        *      the right operator.
+        *      Special case for !*, we don't need to parse RHS as this is a unary operator.
         */
-       if (map->op == T_OP_CMP_FALSE) {
-               vp = pairalloc(request, map->dst->da);
-               if (!vp) return -1;
-               vp->op = map->op;
-               *out = vp;
-
-               return 0;
-       }
+       if (map->op == T_OP_CMP_FALSE) return 0;
 
        /*
         *      List to list found, this is a special case because we don't need
-        *      to allocate any attributes, just found the current list, and change
+        *      to allocate any attributes, just finding the current list, and change
         *      the op.
         */
        if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) {
-               from = radius_list(request, map->src->list);
-               if (!from) return -2;
+               if (radius_request(&context, map->src->vpt_request) == 0) {
+                       from = radius_list(context, map->src->vpt_list);
+               }
+               if (!from) return 0;
 
                found = paircopy(request, *from);
+
                /*
-                *      List to list copy is invalid if the src list has no attributes.
+                *      List to list copy is empty if the src list has no attributes.
                 */
-               if (!found) return -2;
+               if (!found) return 0;
 
-               for (vp = paircursor(&cursor, &found);
+               for (vp = fr_cursor_init(&cursor, &found);
                     vp;
-                    vp = pairnext(&cursor)) {
-                       vp->op = T_OP_ADD;
+                    vp = fr_cursor_next(&cursor)) {
+                       vp->op = T_OP_ADD;
                }
 
                *out = found;
@@ -1175,12 +1423,13 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
        }
 
        /*
-        *      Deal with all non-list founding operations.
+        *      Deal with all non-list operations.
         */
-       da = map->dst->da ? map->dst->da : map->src->da;
+       da = map->dst->vpt_da ? map->dst->vpt_da : map->src->vpt_da;
 
        switch (map->src->type) {
        case VPT_TYPE_XLAT:
+       case VPT_TYPE_XLAT_STRUCT:
        case VPT_TYPE_LITERAL:
        case VPT_TYPE_DATA:
                vp = pairalloc(request, da);
@@ -1196,73 +1445,96 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
         *      And parse the RHS
         */
        switch (map->src->type) {
-       case VPT_TYPE_XLAT:
-               rad_assert(map->dst->da);       /* Need to know where were going to write the new attribute */
-               /*
-                *      Don't call unnecessary expansions
-                */
-               if (strchr(map->src->name, '%') != NULL) {
-                       ssize_t slen;
-                       char *str = NULL;
+               ssize_t slen;
+               char *str;
 
-                       slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
-                       if (slen < 0) {
-                               rcode = slen;
-                               goto error;
-                       }
-                       rcode = pairparsevalue(vp, str);
-                       talloc_free(str);
-                       if (!rcode) {
-                               pairfree(&vp);
-                               rcode = -1;
-                               goto error;
-                       }
+       case VPT_TYPE_XLAT_STRUCT:
+               rad_assert(map->dst->vpt_da);   /* Need to know where were going to write the new attribute */
+               rad_assert(map->src->vpt_xlat != NULL);
 
-                       break;
+               str = NULL;
+               slen = radius_axlat_struct(&str, request, map->src->vpt_xlat, NULL, NULL);
+               if (slen < 0) {
+                       rcode = slen;
+                       goto error;
                }
-               /* FALL-THROUGH */
 
-       case VPT_TYPE_LITERAL:
-               if (!pairparsevalue(vp, map->src->name)) {
-                       rcode = -2;
+               /*
+                *      We do the debug printing because radius_axlat_struct
+                *      doesn't have access to the original string.  It's been
+                *      mangled during the parsing to xlat_exp_t
+                */
+               RDEBUG2("EXPAND %s", map->src->name);
+               RDEBUG2("   --> %s", str);
+
+               rcode = pairparsevalue(vp, str, 0);
+               talloc_free(str);
+               if (rcode < 0) {
+                       pairfree(&vp);
                        goto error;
                }
                break;
 
-       case VPT_TYPE_ATTR:
-               rad_assert(!map->dst->da ||
-                          (map->src->da->type == map->dst->da->type) ||
-                          (map->src->da->type == PW_TYPE_OCTETS) ||
-                          (map->dst->da->type == PW_TYPE_OCTETS));
-               context = request;
-
-               if (radius_request(&context, map->src->request) == 0) {
-                       from = radius_list(context, map->src->list);
+       case VPT_TYPE_XLAT:
+               rad_assert(map->dst->vpt_da);   /* Need to know where were going to write the new attribute */
+
+               str = NULL;
+               slen = radius_axlat(&str, request, map->src->name, NULL, NULL);
+               if (slen < 0) {
+                       rcode = slen;
+                       goto error;
                }
+               rcode = pairparsevalue(vp, str, 0);
+               talloc_free(str);
+               if (rcode < 0) {
+                       pairfree(&vp);
+                       goto error;
+               }
+               break;
 
-               /*
-                *      Can't add the attribute if the list isn't
-                *      valid.
-                */
-               if (!from) {
-                       rcode = -2;
+       case VPT_TYPE_LITERAL:
+               if (pairparsevalue(vp, map->src->name, 0) < 0) {
+                       rcode = 0;
                        goto error;
                }
+               break;
+
+       case VPT_TYPE_ATTR:
+               rad_assert(!map->dst->vpt_da ||
+                          (map->src->vpt_da->type == map->dst->vpt_da->type) ||
+                          (map->src->vpt_da->type == PW_TYPE_OCTETS) ||
+                          (map->dst->vpt_da->type == PW_TYPE_OCTETS));
 
                /*
                 *      Special case, destination is a list, found all instance of an attribute.
                 */
                if (map->dst->type == VPT_TYPE_LIST) {
-                       found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
+                       context = request;
+
+                       if (radius_request(&context, map->src->vpt_request) == 0) {
+                               from = radius_list(context, map->src->vpt_list);
+                       }
+
+                       /*
+                        *      Can't add the attribute if the list isn't
+                        *      valid.
+                        */
+                       if (!from) {
+                               rcode = 0;
+                               goto error;
+                       }
+
+                       found = paircopy2(request, *from, map->src->vpt_da->attr, map->src->vpt_da->vendor,
+                                         map->src->vpt_tag);
                        if (!found) {
                                REDEBUG("Attribute \"%s\" not found in request", map->src->name);
-                               rcode = -2;
+                               rcode = 0;
                                goto error;
                        }
 
-                       for (vp = paircursor(&cursor, &found);
+                       for (vp = fr_cursor_init(&cursor, &found);
                             vp;
-                            vp = pairnext(&cursor)) {
+                            vp = fr_cursor_next(&cursor)) {
                                vp->op = T_OP_ADD;
                        }
 
@@ -1270,13 +1542,9 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
                        return 0;
                }
 
-               /*
-                *      FIXME: allow tag references?
-                */
-               found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
-               if (!found) {
+               if (radius_tmpl_get_vp(&found, request, map->src) < 0) {
                        REDEBUG("Attribute \"%s\" not found in request", map->src->name);
-                       rcode = -2;
+                       rcode = 0;
                        goto error;
                }
 
@@ -1284,7 +1552,6 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
                 *      Copy the data over verbatim, assuming it's
                 *      actually data.
                 */
-//             rad_assert(found->type == VT_DATA);
                vp = paircopyvpdata(request, da, found);
                if (!vp) {
                        return -1;
@@ -1294,9 +1561,11 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
                break;
 
        case VPT_TYPE_DATA:
-               rad_assert(map->src->da->type == map->dst->da->type);
-               memcpy(&vp->data, map->src->vpd, sizeof(vp->data));
-               vp->length = map->src->length;
+               rad_assert(map->src && map->src->vpt_da);
+               rad_assert(map->dst && map->dst->vpt_da);
+               rad_assert(map->src->vpt_da->type == map->dst->vpt_da->type);
+               memcpy(&vp->data, map->src->vpt_value, sizeof(vp->data));
+               vp->length = map->src->vpt_length;
                break;
 
        /*
@@ -1309,7 +1578,7 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
        case VPT_TYPE_EXEC:
                return radius_mapexec(out, request, map);
        default:
-               rad_assert(0);  /* Should of been caught at parse time */
+               rad_assert(0);  /* Should have been caught at parse time */
        error:
                pairfree(&vp);
                return rcode;
@@ -1319,12 +1588,12 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
        return 0;
 }
 
-
-/** Convert a valuepair string to VALUE_PAIR and insert it into a list
+/** Convert a valuepair string to valuepair map
  *
- * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR
- * and inserts it into the appropriate list.
+ * Takes a valuepair string with list and request qualifiers, converts it into a
+ * value_pair_map_t and inserts it into the appropriate list.
  *
+ * @param out Where to write the new map (must be freed with talloc_free()).
  * @param request Current request.
  * @param raw string to parse.
  * @param dst_request_def to use if attribute isn't qualified.
@@ -1333,13 +1602,12 @@ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *ma
  * @param src_list_def to use if attribute isn't qualified.
  * @return 0 on success, < 0 on error.
  */
-int radius_str2vp(REQUEST *request, char const *raw,
-                 request_refs_t dst_request_def, pair_lists_t dst_list_def,
-                 request_refs_t src_request_def, pair_lists_t src_list_def)
+int radius_strpair2map(value_pair_map_t **out, REQUEST *request, char const *raw,
+                      request_refs_t dst_request_def, pair_lists_t dst_list_def,
+                      request_refs_t src_request_def, pair_lists_t src_list_def)
 {
        char const *p = raw;
        FR_TOKEN ret;
-       int rcode;
 
        VALUE_PAIR_RAW tokens;
        value_pair_map_t *map;
@@ -1351,107 +1619,234 @@ int radius_str2vp(REQUEST *request, char const *raw,
        }
 
        map = radius_str2map(request, tokens.l_opand, T_BARE_WORD, tokens.op, tokens.r_opand, tokens.quote,
-                            dst_request_def, dst_list_def, src_request_def, src_list_def);
+                            dst_request_def, dst_list_def, src_request_def, src_list_def);
        if (!map) {
                REDEBUG("Failed parsing attribute string: %s", fr_strerror());
                return -1;
        }
+       *out = map;
 
-       rcode = radius_map2request(request, map, NULL, radius_map2vp, NULL);
-       talloc_free(map);
-       return rcode;
+       return 0;
 }
 
+/** Check whether the destination of a map is currently valid
+ *
+ * @param request The current request.
+ * @param map to check.
+ * @return true if the map resolves to a request and list else false.
+ */
+bool radius_map_dst_valid(REQUEST *request, value_pair_map_t const *map)
+{
+       REQUEST *context = request;
+
+       if (radius_request(&context, map->dst->vpt_request) < 0) return false;
+       if (!radius_list(context, map->dst->vpt_list)) return false;
+
+       return true;
+}
 
 /** Return a VP from a value_pair_tmpl_t
  *
+ * @param out where to write the retrieved vp.
  * @param request current request.
  * @param vpt the value pair template
- * @return NULL if not found, or the VPs.
+ * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
  */
-VALUE_PAIR *radius_vpt_get_vp(REQUEST *request, value_pair_tmpl_t const *vpt)
+int radius_tmpl_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
 {
-       VALUE_PAIR **vps;
+       VALUE_PAIR **vps, *vp;
 
-       if (radius_request(&request, vpt->request) < 0) {
-               return NULL;
+       if (out) *out = NULL;
+
+       if (radius_request(&request, vpt->vpt_request) < 0) {
+               return -3;
        }
 
-       vps = radius_list(request, vpt->list);
+       vps = radius_list(request, vpt->vpt_list);
        if (!vps) {
-               return NULL;
+               return -2;
        }
 
        switch (vpt->type) {
-       /*
-        *      May not may not be found, but it *is* a known name.
-        */
+               /*
+                *      May not may not be found, but it *is* a known
+                *      name.
+                */
        case VPT_TYPE_ATTR:
-               return pairfind(*vps, vpt->da->attr, vpt->da->vendor, TAG_ANY);
+       {
+               int num;
+               vp_cursor_t cursor;
+
+               if (vpt->vpt_num == NUM_ANY) {
+                       vp = pairfind(*vps, vpt->vpt_da->attr, vpt->vpt_da->vendor, vpt->vpt_tag);
+                       if (!vp) return -1;
+                       break;
+               }
+
+               (void) fr_cursor_init(&cursor, vps);
+               num = vpt->vpt_num;
+               while((vp = fr_cursor_next_by_da(&cursor, vpt->vpt_da, vpt->vpt_tag))) {
+                       VERIFY_VP(vp);
+                       if (num-- == 0) goto finish;
+               }
+               return -1;
+       }
 
        case VPT_TYPE_LIST:
-               return *vps;
+               vp = *vps;
+               break;
 
        default:
-               break;
+               /*
+                *      literal, xlat, regex, exec, data.
+                *      no attribute.
+                */
+               return -1;
        }
 
-       return NULL;
-}
-
+finish:
+       if (out) *out = vp;
 
+       return 0;
+}
 
 /** Return a VP from the specified request.
  *
- * @param vp_p where to write the pointer to the resolved VP.
+ * @param out where to write the pointer to the resolved VP.
  *     Will be NULL if the attribute couldn't be resolved.
  * @param request current request.
  * @param name attribute name including qualifiers.
- * @return -1 if either the attribute or qualifier were invalid, else 0
+ * @return -4 if either the attribute or qualifier were invalid, and the same error codes as radius_tmpl_get_vp for other
+ *     error conditions.
  */
-int radius_get_vp(VALUE_PAIR **vp_p, REQUEST *request, char const *name)
+int radius_get_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
 {
        value_pair_tmpl_t vpt;
 
-       *vp_p = NULL;
+       *out = NULL;
+
+       if (radius_parse_attr(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               return -4;
+       }
 
-       if (radius_parse_attr(name, &vpt, REQUEST_CURRENT,
-           PAIR_LIST_REQUEST) < 0) {
+       return radius_tmpl_get_vp(out, request, &vpt);
+}
+
+/** Copy pairs matching a VPT in the current request
+ *
+ * @param out where to write the copied vps.
+ * @param request current request.
+ * @param vpt the value pair template
+ * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found.
+ */
+int radius_tmpl_copy_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt)
+{
+       VALUE_PAIR **vps, *vp;
+       REQUEST *current = request;
+
+       if (out) *out = NULL;
+
+       if (radius_request(&current, vpt->vpt_request) < 0) {
+               return -3;
+       }
+
+       vps = radius_list(request, vpt->vpt_list);
+       if (!vps) {
+               return -2;
+       }
+
+       switch (vpt->type) {
+       /*
+        *      May not may not be found, but it *is* a known name.
+        */
+       case VPT_TYPE_ATTR:
+               vp = paircopy2(request, *vps, vpt->vpt_da->attr, vpt->vpt_da->vendor, vpt->vpt_tag);
+               if (!vp) {
+                       return -1;
+               }
+               break;
+
+       case VPT_TYPE_LIST:
+               vp = paircopy(request, *vps);
+
+               break;
+
+       default:
+               /*
+                *      literal, xlat, regex, exec, data.
+                *      no attribute.
+                */
                return -1;
        }
 
-       *vp_p = radius_vpt_get_vp(request, &vpt);
+       if (out) {
+               *out = vp;
+       }
+
        return 0;
 }
 
-/** Add a module failure message VALUE_PAIR to the request
+/** Copy a VP from the specified request.
+ *
+ * @param out where to write the pointer to the copied VP.
+ *     Will be NULL if the attribute couldn't be resolved.
+ * @param request current request.
+ * @param name attribute name including qualifiers.
+ * @return -4 if either the attribute or qualifier were invalid, and the same error codes as radius_tmpl_get_vp for other
+ *     error conditions.
  */
+int radius_copy_vp(VALUE_PAIR **out, REQUEST *request, char const *name)
+{
+       value_pair_tmpl_t vpt;
+
+       *out = NULL;
+
+       if (radius_parse_attr(&vpt, name, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               return -4;
+       }
+
+       return radius_tmpl_copy_vp(out, request, &vpt);
+}
+
 void module_failure_msg(REQUEST *request, char const *fmt, ...)
 {
        va_list ap;
+
+       va_start(ap, fmt);
+       vmodule_failure_msg(request, fmt, ap);
+       va_end(ap);
+}
+
+/** Add a module failure message VALUE_PAIR to the request
+ */
+void vmodule_failure_msg(REQUEST *request, char const *fmt, va_list ap)
+{
        char *p;
        VALUE_PAIR *vp;
+       va_list aq;
 
        if (!fmt || !request->packet) {
-               va_start(ap, fmt);
-               va_end(ap);
                return;
        }
 
-       va_start(ap, fmt);
-       vp = paircreate(request->packet, PW_MODULE_FAILURE_MESSAGE, 0);
-       if (!vp) {
-               va_end(ap);
-               return;
-       }
-
-       p = talloc_vasprintf(vp, fmt, ap);
+       /*
+        *  If we don't copy the original ap we get a segfault from vasprintf. This is apparently
+        *  due to ap sometimes being implemented with a stack offset which is invalidated if
+        *  ap is passed into another function. See here:
+        *  http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
+        *
+        *  I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
+        *  running unit tests which generate errors under CI.
+        */
+       va_copy(aq, ap);
+       p = talloc_vasprintf(request, fmt, aq);
+       va_end(aq);
 
-       if (request->module && *request->module) {
+       MEM(vp = pairmake_packet("Module-Failure-Message", NULL, T_OP_ADD));
+       if (request->module && (request->module[0] != '\0')) {
                pairsprintf(vp, "%s: %s", request->module, p);
        } else {
                pairsprintf(vp, "%s", p);
        }
        talloc_free(p);
-       pairadd(&request->packet->vps, vp);
 }
index cffacb4..fb72c86 100644 (file)
@@ -17,7 +17,7 @@
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 1999-2012  The FreeRADIUS server project
+ * Copyright 1999-2014  The FreeRADIUS server project
  * Copyright 2012  Alan DeKok <aland@ox.org>
  * Copyright 2000  Chris Parker <cparker@starnetusa.com>
  */
@@ -27,10 +27,11 @@ RCSID("$Id$")
 #include <freeradius-devel/radiusd.h>
 USES_APPLE_DEPRECATED_API      /* OpenSSL API has been deprecated by Apple */
 
-#ifdef HAVE_OPENSSL_CRYPTO_H
+static uint64_t libmagic = RADIUSD_MAGIC_NUMBER;
 
-#include <openssl/crypto.h>
-#include <openssl/opensslv.h>
+#ifdef HAVE_OPENSSL_CRYPTO_H
+#  include <openssl/crypto.h>
+#  include <openssl/opensslv.h>
 
 static long ssl_built = OPENSSL_VERSION_NUMBER;
 
@@ -41,15 +42,14 @@ static long ssl_built = OPENSSL_VERSION_NUMBER;
  *
  * @return 0 if ok, else -1
  */
-int ssl_check_version(void)
+int ssl_check_consistency(void)
 {
        long ssl_linked;
 
        ssl_linked = SSLeay();
 
        if (ssl_linked != ssl_built) {
-               ERROR("libssl version mismatch."
-                      "  Built with: %lx\n  Linked: %lx",
+               ERROR("libssl version mismatch.  built: %lx linked: %lx",
                       (unsigned long) ssl_built,
                       (unsigned long) ssl_linked);
 
@@ -59,16 +59,76 @@ int ssl_check_version(void)
        return 0;
 }
 
+/** Convert a version number to a text string
+ *
+ * @note Not thread safe.
+ *
+ * @param v version to convert.
+ * @return pointer to a static buffer containing the version string.
+ */
+char const *ssl_version_by_num(uint64_t v)
+{
+       /* 2 (%s) + 1 (.) + 2 (%i) + 1 (.) + 2 (%i) + 1 (c) + 1 (-) + 2 (%i) + \0 */
+       static char buffer[13];
+       char *p = buffer;
+
+       p += sprintf(p, "%i.%i.%i",
+                    (int) ((0xff0000000 & v) >> 28),
+                    (int) ((0x00ff00000 & v) >> 20),
+                    (int) ((0x0000ff000 & v) >> 12));
+
+       if ((0x000000ff0 & v) >> 4) {
+               *p++ =  (char) (0x60 + ((0x000000ff0 & v) >> 4));
+       }
+
+       sprintf(p, "-%i", (int) (0x00000000f & v));
+
+       return buffer;
+}
+
+/** Convert two openssl version numbers into a range string
+ *
+ * @note Not thread safe.
+ *
+ * @param low version to convert.
+ * @param high version to convert.
+ * @return pointer to a static buffer containing the version range string.
+ */
+char const *ssl_version_range(uint64_t low, uint64_t high)
+{
+       /* 12 (version) + 3 ( - ) + 12 (version) */
+       static char buffer[28];
+       char *p = buffer;
+
+       p += strlcpy(p, ssl_version_by_num(low), sizeof(buffer));
+       p += strlcpy(p, " - ", sizeof(buffer) - (p - buffer));
+       strlcpy(p, ssl_version_by_num(high), sizeof(buffer) - (p - buffer));
+
+       return buffer;
+}
+
 /** Print the current linked version of Openssl
  *
  * Print the currently linked version of the OpenSSL library.
+ *
+ * @note Not thread safe.
+ * @return pointer to a static buffer containing libssl version information.
  */
 char const *ssl_version(void)
 {
-       return SSLeay_version(SSLEAY_VERSION);
+       static char buffer[256];
+
+       uint64_t v = (uint64_t) SSLeay();
+
+       snprintf(buffer, sizeof(buffer), "%s 0x%.9" PRIx64 " (%s)",
+                SSLeay_version(SSLEAY_VERSION),                /* Not all builds include a useful version number */
+                v,
+                ssl_version_by_num((uint64_t) v));
+
+       return buffer;
 }
-#else
-int ssl_check_version(void) {
+#  else
+int ssl_check_consistency(void) {
        return 0;
 }
 
@@ -76,7 +136,39 @@ char const *ssl_version()
 {
        return "not linked";
 }
-#endif
+#endif /* ifdef HAVE_OPENSSL_CRYPTO_H */
+
+
+/** Check if the application linking to the library has the correct magic number
+ *
+ * @param magic number as defined by RADIUSD_MAGIC_NUMBER
+ * @returns 0 on success, -1 on prefix mismatch, -2 on version mismatch -3 on commit mismatch.
+ */
+int rad_check_lib_magic(uint64_t magic)
+{
+       if (MAGIC_PREFIX(magic) != MAGIC_PREFIX(libmagic)) {
+               ERROR("Application and libfreeradius-server magic number (prefix) mismatch."
+                     "  application: %x library: %x",
+                     MAGIC_PREFIX(magic), MAGIC_PREFIX(libmagic));
+               return -1;
+       }
+
+       if (MAGIC_VERSION(magic) != MAGIC_VERSION(libmagic)) {
+               ERROR("Application and libfreeradius-server magic number (version) mismatch."
+                     "  application: %lx library: %lx",
+                     (unsigned long) MAGIC_VERSION(magic), (unsigned long) MAGIC_VERSION(libmagic));
+               return -2;
+       }
+
+       if (MAGIC_COMMIT(magic) != MAGIC_COMMIT(libmagic)) {
+               ERROR("Application and libfreeradius-server magic number (commit) mismatch."
+                     "  application: %lx library: %lx",
+                     (unsigned long) MAGIC_COMMIT(magic), (unsigned long) MAGIC_COMMIT(libmagic));
+               return -3;
+       }
+
+       return 0;
+}
 
 /*
  *     Display the revision number for this program
@@ -145,12 +237,26 @@ void version(void)
 #ifdef WITH_VMPS
        DEBUG3("  vmps");
 #endif
+#ifndef NDEBUG
+       DEBUG3("  developer");
+#endif
 
        DEBUG3("Server core libs:");
-       DEBUG3("  talloc : %i.%i.*", talloc_version_major(),
-              talloc_version_minor());
+       DEBUG3("  talloc : %i.%i.*", talloc_version_major(), talloc_version_minor());
        DEBUG3("  ssl    : %s", ssl_version());
 
+       DEBUG3("Library magic number:");
+       DEBUG3("  0x%llx", (unsigned long long) libmagic);
+
+       DEBUG3("Endianess:");
+#if defined(LITTLE_ENDIAN)
+       DEBUG3("  little");
+#elif defined(BIG_ENDIAN)
+       DEBUG3("  big");
+#else
+       DEBUG3("  unknown");
+#endif
+
        INFO("Copyright (C) 1999-2014 The FreeRADIUS server project and contributors");
        INFO("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A");
        INFO("PARTICULAR PURPOSE");
index f3a7638..8fda26d 100644 (file)
@@ -32,15 +32,13 @@ RCSID("$Id$")
 #include       <freeradius-devel/base64.h>
 
 #include       <ctype.h>
-#include       <limits.h>
-
 typedef struct xlat_t {
        char                    name[MAX_STRING_LEN];   //!< Name of the xlat expansion.
        int                     length;                 //!< Length of name.
        void                    *instance;              //!< Module instance passed to xlat and escape functions.
        RAD_XLAT_FUNC           func;                   //!< xlat function.
        RADIUS_ESCAPE_STRING    escape;                 //!< Escape function to apply to dynamic input to func.
-       int                     internal;               //!< If true, cannot be redefined.
+       bool                    internal;               //!< If true, cannot be redefined.
 } xlat_t;
 
 typedef enum {
@@ -60,8 +58,9 @@ struct xlat_exp {
        size_t len;             //!< Length of the format string.
 
        DICT_ATTR const *da;    //!< the name of the dictionary attribute
+
        int num;                //!< attribute number
-       int tag;                //!< attribute tag
+       int8_t tag;             //!< attribute tag
        pair_lists_t list;      //!< list of which attribute
        request_refs_t ref;     //!< outer / this / ...
 
@@ -100,50 +99,33 @@ static char const * const xlat_foreach_names[] = {"Foreach-Variable-0",
 #endif
 static int xlat_inst[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };        /* up to 8 for regex */
 
+char const *radiusd_short_version = RADIUSD_VERSION_STRING;
 
-#ifdef WITH_UNLANG
-/** Convert the value on a VALUE_PAIR to string
+/** Print length of its RHS.
  *
  */
-static int valuepair2str(char * out,int outlen,VALUE_PAIR * pair, int type)
+static ssize_t xlat_strlen(UNUSED void *instance, UNUSED REQUEST *request,
+                          char const *fmt, char *out, size_t outlen)
 {
-       if (pair != NULL) {
-               vp_prints_value(out, outlen, pair, -1);
-               return strlen(out);
-       }
-
-       switch (type) {
-       case PW_TYPE_STRING :
-               strlcpy(out,"_",outlen);
-               break;
-       case PW_TYPE_INTEGER64:
-       case PW_TYPE_SIGNED:
-       case PW_TYPE_INTEGER:
-               strlcpy(out,"0",outlen);
-               break;
-       case PW_TYPE_IPADDR :
-               strlcpy(out,"?.?.?.?",outlen);
-               break;
-       case PW_TYPE_IPV6ADDR :
-               strlcpy(out,":?:",outlen);
-               break;
-       case PW_TYPE_DATE :
-               strlcpy(out,"0",outlen);
-               break;
-       default :
-               strlcpy(out,"unknown_type",outlen);
-       }
+       snprintf(out, outlen, "%u", (unsigned int) strlen(fmt));
        return strlen(out);
 }
-#endif
 
-/** Print length of its RHS.
+/** Print the size of the attribute in bytes.
  *
  */
-static ssize_t xlat_strlen(UNUSED void *instance, UNUSED REQUEST *request,
+static ssize_t xlat_length(UNUSED void *instance, UNUSED REQUEST *request,
                           char const *fmt, char *out, size_t outlen)
 {
-       snprintf(out, outlen, "%u", (unsigned int) strlen(fmt));
+       VALUE_PAIR *vp;
+       while (isspace((int) *fmt)) fmt++;
+
+       if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+               *out = '\0';
+               return 0;
+       }
+
+       snprintf(out, outlen, "%zu", vp->length);
        return strlen(out);
 }
 
@@ -155,7 +137,8 @@ static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
 {
        VALUE_PAIR      *vp;
 
-       uint64_t        integer;
+       uint64_t        int64 = 0;      /* Needs to be initialised to zero */
+       uint32_t        int32 = 0;      /* Needs to be initialised to zero */
 
        while (isspace((int) *fmt)) fmt++;
 
@@ -171,25 +154,56 @@ static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
                        break;
                }
 
-               memcpy(&integer, &(vp->vp_octets), vp->length);
+               if (vp->length > 4) {
+                       memcpy(&int64, vp->vp_octets, vp->length);
+                       return snprintf(out, outlen, "%" PRIu64, htonll(int64));
+               }
 
-               return snprintf(out, outlen, "%" PRIu64, ntohll(integer));
+               memcpy(&int32, vp->vp_octets, vp->length);
+               return snprintf(out, outlen, "%i", htonl(int32));
 
        case PW_TYPE_INTEGER64:
                return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);
 
-       case PW_TYPE_IPADDR:
+       /*
+        *      IP addresses are treated specially, as parsing functions assume the value
+        *      is bigendian and will convert it for us.
+        */
+       case PW_TYPE_IPV4_ADDR:
+               return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr));
+
+       case PW_TYPE_IPV4_PREFIX:
+               return snprintf(out, outlen, "%u", htonl((*(uint32_t *)(vp->vp_ipv4prefix + 2))));
+
        case PW_TYPE_INTEGER:
-       case PW_TYPE_SHORT:
-       case PW_TYPE_BYTE:
        case PW_TYPE_DATE:
+       case PW_TYPE_BYTE:
+       case PW_TYPE_SHORT:
                return snprintf(out, outlen, "%u", vp->vp_integer);
+
+       /*
+        *      Ethernet is weird... It's network related, so we assume to it should be
+        *      bigendian.
+        */
+       case PW_TYPE_ETHERNET:
+               memcpy(&int64, &vp->vp_ether, vp->length);
+               return snprintf(out, outlen, "%" PRIu64, htonll(int64));
+
+       case PW_TYPE_SIGNED:
+               return snprintf(out, outlen, "%i", vp->vp_signed);
+
+       case PW_TYPE_IPV6_ADDR:
+               return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &vp->vp_ipv6addr));
+
+       case PW_TYPE_IPV6_PREFIX:
+               return fr_prints_uint128(out, outlen, ntohlll(*(uint128_t const *) &(vp->vp_ipv6prefix[2])));
+
        default:
                break;
        }
 
-       REDEBUG("Type \"%s\" cannot be converted to integer",
-               fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID));
+       REDEBUG("Type '%s' of length %zu cannot be converted to integer",
+               fr_int2str(dict_attr_types, vp->da->type, "???"), vp->length);
        *out = '\0';
 
        return -1;
@@ -199,11 +213,11 @@ static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
  *
  */
 static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
-                       char const *fmt, char *out, size_t outlen)
+                       char const *fmt, char *out, size_t outlen)
 {
        size_t i;
        VALUE_PAIR *vp;
-       uint8_t buffer[MAX_STRING_LEN];
+       uint8_t const *p;
        ssize_t ret;
        size_t  len;
 
@@ -214,7 +228,10 @@ static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
                return -1;
        }
 
-       ret = rad_vp2data(vp, buffer, sizeof(buffer));
+       ret = rad_vp2data(&p, vp);
+       if (ret < 0) {
+               return ret;
+       }
        len = (size_t) ret;
 
        /*
@@ -226,21 +243,19 @@ static ssize_t xlat_hex(UNUSED void *instance, REQUEST *request,
        }
 
        for (i = 0; i < len; i++) {
-               snprintf(out + 2*i, 3, "%02x", buffer[i]);
+               snprintf(out + 2*i, 3, "%02x", p[i]);
        }
 
        return len * 2;
 }
 
-/** Print data as base64, not as VALUE
+/** Return the tag of an attribute reference
  *
  */
-static ssize_t xlat_base64(UNUSED void *instance, REQUEST *request,
-                          char const *fmt, char *out, size_t outlen)
+static ssize_t xlat_tag(UNUSED void *instance, REQUEST *request,
+                       char const *fmt, char *out, size_t outlen)
 {
        VALUE_PAIR *vp;
-       uint8_t buffer[MAX_STRING_LEN];
-       ssize_t ret;
 
        while (isspace((int) *fmt)) fmt++;
 
@@ -249,13 +264,12 @@ static ssize_t xlat_base64(UNUSED void *instance, REQUEST *request,
                return 0;
        }
 
-       ret = rad_vp2data(vp, buffer, sizeof(buffer));
-       if (ret < 0) {
+       if (!vp->da->flags.has_tag || !TAG_VALID(vp->tag)) {
                *out = '\0';
-               return ret;
+               return 0;
        }
 
-       return fr_base64_encode(buffer, (size_t) ret, out, outlen);
+       return snprintf(out, outlen, "%u", vp->tag);
 }
 
 /** Print out attribute info
@@ -287,25 +301,24 @@ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char con
 
        while (isspace((int) *fmt)) fmt++;
 
-       if (*fmt == '&') fmt++;
-
-       if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+       if (radius_parse_attr(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+               RDEBUG("%s", fr_strerror());
                return -1;
        }
 
        current = request;
-       if (radius_request(&current, vpt.request) < 0) return -2;
+       if (radius_request(&current, vpt.vpt_request) < 0) return -2;
 
-       vps = radius_list(current, vpt.list);
+       vps = radius_list(current, vpt.vpt_list);
        if (!vps) {
                return -2;
        }
 
        RIDEBUG("Attributes matching \"%s\"", fmt);
-       vp = paircursor(&cursor, vps);
+       vp = fr_cursor_init(&cursor, vps);
 
-       if (vpt.da) {
-               vp = pairfindnext(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
+       if (vpt.vpt_da) {
+               vp = fr_cursor_next_by_da(&cursor, vpt.vpt_da, TAG_ANY);
        }
        while (vp) {
                DICT_ATTR *dac = NULL;
@@ -315,14 +328,14 @@ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char con
 
                if (vp->da->flags.has_tag) {
                        RIDEBUG2("\t%s:%s:%i %s %s",
-                               fr_int2str(pair_lists, vpt.list, "<INVALID>"),
+                               fr_int2str(pair_lists, vpt.vpt_list, "<INVALID>"),
                                vp->da->name,
                                vp->tag,
                                fr_int2str(fr_tokens, vp->op, "<INVALID>"),
                                buffer);
                } else {
                        RIDEBUG2("\t%s:%s %s %s",
-                               fr_int2str(pair_lists, vpt.list, "<INVALID>"),
+                               fr_int2str(pair_lists, vpt.vpt_list, "<INVALID>"),
                                vp->da->name,
                                fr_int2str(fr_tokens, vp->op, "<INVALID>"),
                                buffer);
@@ -340,19 +353,17 @@ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char con
                RDEBUG3("\t\tlength        : %zu", vp->length);
 
                dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
-               if (!dac) {
-                       return -1;
-               }
+               if (!dac) return -1;
+               talloc_set_type(dac, DICT_ATTR);
                dac->flags.vp_free = 0;
 
        next_vp:
-
                talloc_free(dac);
 
-               if (vpt.da) {
-                       vp = pairfindnext(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
+               if (vpt.vpt_da) {
+                       vp = fr_cursor_next_by_da(&cursor, vpt.vpt_da, TAG_ANY);
                } else {
-                       vp = pairnext(&cursor);
+                       vp = fr_cursor_next(&cursor);
                }
        }
 
@@ -380,6 +391,7 @@ static ssize_t xlat_foreach(void *instance, REQUEST *request,
                            UNUSED char const *fmt, char *out, size_t outlen)
 {
        VALUE_PAIR      **pvp;
+       size_t          len;
 
        /*
         *      See modcall, "FOREACH" for how this works.
@@ -390,7 +402,13 @@ static ssize_t xlat_foreach(void *instance, REQUEST *request,
                return 0;
        }
 
-       return valuepair2str(out, outlen, (*pvp), (*pvp)->da->type);
+       len = vp_prints_value(out, outlen, *pvp, 0);
+       if (is_truncated(len, outlen)) {
+               RDEBUG("Insufficient buffer space to write foreach value");
+               return -1;
+       }
+
+       return len;
 }
 #endif
 
@@ -403,8 +421,10 @@ static ssize_t xlat_foreach(void *instance, REQUEST *request,
 static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
                           char const *fmt, char *out, size_t outlen)
 {
-       int len;
+       size_t len;
+       ssize_t ret;
        VALUE_PAIR *vp;
+       uint8_t const *p;
 
        while (isspace((int) *fmt)) fmt++;
 
@@ -416,10 +436,24 @@ static ssize_t xlat_string(UNUSED void *instance, REQUEST *request,
 
        if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) goto nothing;
 
-       if (vp->da->type != PW_TYPE_OCTETS) goto nothing;
+       ret = rad_vp2data(&p, vp);
+       if (ret < 0) {
+               return ret;
+       }
 
-       len = fr_print_string(vp->vp_strvalue, vp->length, out, outlen);
-       out[len] = '\0';
+       switch (vp->da->type) {
+               case PW_TYPE_OCTETS:
+                       len = fr_print_string((char const *) p, vp->length, out, outlen);
+                       break;
+
+               case PW_TYPE_STRING:
+                       len = strlcpy(out, vp->vp_strvalue, outlen);
+                       break;
+
+               default:
+                       len = fr_print_string((char const *) p, ret, out, outlen);
+                       break;
+       }
 
        return len;
 }
@@ -457,7 +491,7 @@ static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
        /*
         *  Expand to previous (or current) level
         */
-       snprintf(out, outlen, "%d", request->options & RAD_REQUEST_OPTION_DEBUG4);
+       snprintf(out, outlen, "%d", request->log.lvl & RAD_REQUEST_OPTION_DEBUG4);
 
        /*
         *  Assume we just want to get the current value and NOT set it to 0
@@ -467,13 +501,13 @@ static ssize_t xlat_debug(UNUSED void *instance, REQUEST *request,
 
        level = atoi(fmt);
        if (level == 0) {
-               request->options = RAD_REQUEST_OPTION_NONE;
-               request->radlog = NULL;
+               request->log.lvl = RAD_REQUEST_OPTION_NONE;
+               request->log.func = NULL;
        } else {
                if (level > 4) level = 4;
 
-               request->options = level;
-               request->radlog = radlog_request;
+               request->log.lvl = level;
+               request->log.func = vradlog_request;
        }
 
        done:
@@ -520,8 +554,9 @@ static xlat_t *xlat_find(char const *name)
  */
 int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING escape, void *instance)
 {
-       xlat_t  *c;
-       xlat_t  my_xlat;
+       xlat_t   *c;
+       xlat_t   my_xlat;
+       rbnode_t *node;
 
        if (!name || !*name) {
                DEBUG("xlat_register: Invalid xlat name");
@@ -541,7 +576,7 @@ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING esc
 
                xlat_root = rbtree_create(xlat_cmp, free, 0);
                if (!xlat_root) {
-                       DEBUG("xlat_register: Failed to create tree.");
+                       DEBUG("xlat_register: Failed to create tree");
                        return -1;
                }
 
@@ -562,8 +597,9 @@ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING esc
 
                XLAT_REGISTER(integer);
                XLAT_REGISTER(strlen);
+               XLAT_REGISTER(length);
                XLAT_REGISTER(hex);
-               XLAT_REGISTER(base64);
+               XLAT_REGISTER(tag);
                XLAT_REGISTER(string);
                XLAT_REGISTER(xlat);
                XLAT_REGISTER(module);
@@ -605,7 +641,11 @@ int xlat_register(char const *name, RAD_XLAT_FUNC func, RADIUS_ESCAPE_STRING esc
        c->length = strlen(c->name);
        c->instance = instance;
 
-       rbtree_insert(xlat_root, c);
+       node = rbtree_insert_node(xlat_root, c);
+       if (!node) {
+               talloc_free(c);
+               return -1;
+       }
 
        return 0;
 }
@@ -637,6 +677,42 @@ void xlat_unregister(char const *name, UNUSED RAD_XLAT_FUNC func, void *instance
        rbtree_deletebydata(xlat_root, c);
 }
 
+
+/** Crappy temporary function to add attribute ref support to xlats
+ *
+ * This needs to die, and hopefully will die, when xlat functions accept
+ * xlat node structures.
+ *
+ * Provides either a pointer to a buffer which contains the value of the reference VALUE_PAIR
+ * in an architecture independent format. Or a pointer to the start of the fmt string.
+ *
+ * The pointer is only guaranteed to be valid between calls to xlat_fmt_to_ref,
+ * and so long as the source VALUE_PAIR is not freed.
+ *
+ * @param out where to write a pointer to the buffer to the data the xlat function needs to work on.
+ * @param request current request.
+ * @param fmt string.
+ * @returns the length of the data or -1 on error.
+ */
+ssize_t xlat_fmt_to_ref(uint8_t const **out, REQUEST *request, char const *fmt)
+{
+       VALUE_PAIR *vp;
+
+       while (isspace((int) *fmt)) fmt++;
+
+       if (fmt[0] == '&') {
+               if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
+                       *out = NULL;
+                       return -1;
+               }
+
+               return rad_vp2data(out, vp);
+       }
+
+       *out = (uint8_t const *)fmt;
+       return strlen(fmt);
+}
+
 /** De-register all xlat functions, used mainly for debugging.
  *
  */
@@ -645,17 +721,12 @@ void xlat_free(void)
        rbtree_free(xlat_root);
 }
 
-#if 0
-#define XLAT_DEBUG(fmt, ...) printf(fmt, ## __VA_ARGS__);printf("\n")
-#endif
 
-#ifndef XLAT_DEBUG
-#if 0
+#ifdef DEBUG_XLAT
 #define XLAT_DEBUG DEBUG3
 #else
 #define XLAT_DEBUG(...)
 #endif
-#endif
 
 static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
                                       char const **error);
@@ -676,7 +747,7 @@ static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t
        rad_assert(fmt[2] == '%');
        rad_assert(fmt[3] == '{');
 
-       XLAT_DEBUG("ALTERNATE: %s", fmt);
+       XLAT_DEBUG("ALTERNATE <-- %s", fmt);
 
        node = talloc_zero(ctx, xlat_exp_t);
        node->type = XLAT_ALTERNATE;
@@ -712,7 +783,7 @@ static ssize_t xlat_tokenize_alternation(TALLOC_CTX *ctx, char *fmt, xlat_exp_t
                 */
                node->alternate = talloc_zero(node, xlat_exp_t);
                node->alternate->type = XLAT_LITERAL;
-               node->alternate->fmt = talloc_strdup(node->alternate, "");
+               node->alternate->fmt = talloc_typed_strdup(node->alternate, "");
                *(p++) = '\0';
 
        } else {
@@ -752,7 +823,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                return xlat_tokenize_alternation(ctx, fmt, head, error);
        }
 
-       XLAT_DEBUG("EXPANSION: %s", fmt);
+       XLAT_DEBUG("EXPANSION <-- %s", fmt);
        node = talloc_zero(ctx, xlat_exp_t);
        attrname = node->fmt = fmt + 2;
        node->len = 0;
@@ -768,7 +839,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                        return -2;
                }
 
-               XLAT_DEBUG("REGEX: %s", fmt);
+               XLAT_DEBUG("REGEX <-- %s", fmt);
                fmt[3] = '\0';
                node->num = fmt[2] - '0'; /* ASCII */
 
@@ -788,7 +859,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
         *      %{request:Tunnel-Password:1[#]}
         *      %{mod:foo}
         */
-       q = brace = NULL;
+        brace = NULL;
        for (p = fmt + 2; *p != '\0'; p++) {
                if (*p == ':') break;
 
@@ -814,7 +885,8 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                if (node->xlat) {
                        node->type = XLAT_MODULE;
 
-                       XLAT_DEBUG("MOD: %s --> %s", node->fmt, p);
+                       XLAT_DEBUG("MOD <-- %s ... %s", node->fmt, p + 1);
+
                        slen = xlat_tokenize_literal(node, p + 1, &node->child, true, error);
                        if (slen <= 0) {
                                talloc_free(node);
@@ -922,7 +994,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                        node->type = XLAT_VIRTUAL;
                        node->fmt = attrname;
 
-                       XLAT_DEBUG("VIRTUAL: %s", node->fmt);
+                       XLAT_DEBUG("VIRTUAL <-- %s", node->fmt);
                        *head = node;
                        rad_assert(node->next == NULL);
                        brace++;
@@ -979,18 +1051,18 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
 
                p = q;
                if (*p== '#') {
-                       node->num = 65536;
+                       node->num = NUM_COUNT;
                        p++;
 
                } else if (*p == '*') {
-                       node->num = 65537;
+                       node->num = NUM_JOIN;
                        p++;
 
                } else if (isdigit((int) *p)) {
                        num = strtoul(p, &end, 10);
-                       if ((num == ULONG_MAX) || (num > 65535)) {
+                       if (num > 1000) {
                                talloc_free(node);
-                               *error = "Invalid number";
+                               *error = "Invalid array reference";
                                return - (p - fmt);
                        }
                        p = end;
@@ -1015,6 +1087,8 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **
                        *error = "Unexpected text after array reference";
                        return - (p - fmt);
                }
+       } else {
+               node->num = NUM_ANY;
        }
 
        rad_assert(!p || (p == brace));
@@ -1036,7 +1110,7 @@ static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **he
 
        if (!*fmt) return 0;
 
-       XLAT_DEBUG("LITERAL: %s", fmt);
+       XLAT_DEBUG("LITERAL <-- %s", fmt);
 
        node = talloc_zero(ctx, xlat_exp_t);
        node->fmt = fmt;
@@ -1105,7 +1179,8 @@ static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **he
                if ((p[0] == '%') && (p[1] == '{')) {
                        ssize_t slen;
 
-                       XLAT_DEBUG("LITERAL: %s --> %s", node->fmt, p);
+                       XLAT_DEBUG("LITERAL <-- %s", node->fmt);
+
                        slen = xlat_tokenize_expansion(node, p, &node->next, error);
                        if (slen <= 0) {
                                talloc_free(node);
@@ -1147,18 +1222,28 @@ static ssize_t xlat_tokenize_literal(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **he
                        ssize_t slen;
                        xlat_exp_t *next;
 
-                       if (!p[1] || !strchr("%dlmtDGHISTY", p[1])) {
+                       if (!p[1] || !strchr("%dlmtDGHISTYv", p[1])) {
                                        talloc_free(node);
                                        *error = "Invalid variable expansion";
                                        p++;
                                        return - (p - fmt);
                        }
 
-                       XLAT_DEBUG("PERCENT: %s --> %c", node->fmt, p[1]);
                        next = talloc_zero(node, xlat_exp_t);
-                       next->fmt = p + 1;
                        next->len = 1;
-                       next->type = XLAT_PERCENT;
+
+                       if (p[1] == '%') {
+                               next->fmt = talloc_typed_strdup(next, "%");
+
+                               XLAT_DEBUG("LITERAL <-- %s", next->fmt);
+                               next->type = XLAT_LITERAL;
+
+                       } else {
+                               next->fmt = p + 1;
+
+                               XLAT_DEBUG("PERCENT <-- %c", *next->fmt);
+                               next->type = XLAT_PERCENT;
+                       }
 
                        node->next = next;
                        *p = '\0';
@@ -1218,28 +1303,30 @@ static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
        while (node) {
                switch (node->type) {
                case XLAT_LITERAL:
-                       DEBUG("%.*sliteral: '%s'", lvl, xlat_tabs, node->fmt);
+                       DEBUG("%.*sliteral --> %s", lvl, xlat_tabs, node->fmt);
                        break;
 
                case XLAT_PERCENT:
-                       DEBUG("%.*sliteral (with %%): '%c'", lvl, xlat_tabs, node->fmt[0]);
+                       DEBUG("%.*spercent --> %c", lvl, xlat_tabs, node->fmt[0]);
                        break;
 
                case XLAT_ATTRIBUTE:
                        rad_assert(node->da != NULL);
-                       DEBUG("%.*sattribute: %s", lvl, xlat_tabs, node->da->name);
+                       DEBUG("%.*sattribute --> %s", lvl, xlat_tabs, node->da->name);
                        rad_assert(node->child == NULL);
-                       if ((node->tag != 0) || (node->num != 0)) {
+                       if ((node->tag != TAG_ANY) || (node->num != NUM_ANY)) {
                                DEBUG("%.*s{", lvl, xlat_tabs);
 
                                DEBUG("%.*sref  %d", lvl + 1, xlat_tabs, node->ref);
                                DEBUG("%.*slist %d", lvl + 1, xlat_tabs, node->list);
 
-                               if (node->tag) DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->tag);
-                               if (node->num) {
-                                       if (node->num == 65536) {
+                               if (node->tag != TAG_ANY) {
+                                       DEBUG("%.*stag %d", lvl + 1, xlat_tabs, node->tag);
+                               }
+                               if (node->num != NUM_ANY) {
+                                       if (node->num == NUM_COUNT) {
                                                DEBUG("%.*s[#]", lvl + 1, xlat_tabs);
-                                       } else if (node->num == 65537) {
+                                       } else if (node->num == NUM_JOIN) {
                                                DEBUG("%.*s[*]", lvl + 1, xlat_tabs);
                                        } else {
                                                DEBUG("%.*s[%d]", lvl + 1, xlat_tabs, node->num);
@@ -1252,12 +1339,12 @@ static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
 
                case XLAT_VIRTUAL:
                        rad_assert(node->fmt != NULL);
-                       DEBUG("%.*svirtual: %s", lvl, xlat_tabs, node->fmt);
+                       DEBUG("%.*svirtual --> %s", lvl, xlat_tabs, node->fmt);
                        break;
 
                case XLAT_MODULE:
                        rad_assert(node->xlat != NULL);
-                       DEBUG("%.*sxlat: %s", lvl, xlat_tabs, node->xlat->name);
+                       DEBUG("%.*sxlat --> %s", lvl, xlat_tabs, node->xlat->name);
                        if (node->child) {
                                DEBUG("%.*s{", lvl, xlat_tabs);
                                xlat_tokenize_debug(node->child, lvl + 1);
@@ -1267,7 +1354,7 @@ static void xlat_tokenize_debug(xlat_exp_t const *node, int lvl)
 
 #ifdef HAVE_REGEX_H
                case XLAT_REGEX:
-                       DEBUG("%.*sregex-var: %d", lvl, xlat_tabs, node->num);
+                       DEBUG("%.*sregex-var --> %d", lvl, xlat_tabs, node->num);
                        break;
 #endif
 
@@ -1336,16 +1423,18 @@ size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
                                p += strlen(p);
                        }
 
-                       if (node->num != 0) {
+                       if (node->num != NUM_ANY) {
                                *(p++) = '[';
-
-                               if (node->num == 65536) {
+                               switch (node->num) {
+                               case NUM_COUNT:
                                        *(p++) = '#';
+                                       break;
 
-                               } else if (node->num == 65537) {
+                               case NUM_JOIN:
                                        *(p++) = '*';
+                                       break;
 
-                               } else {
+                               default:
                                        snprintf(p, end - p, "%u", node->num);
                                        p += strlen(p);
                                }
@@ -1407,10 +1496,6 @@ size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
        return p - buffer;
 }
 
-
-static char const xlat_spaces[] = "                                                                                                                                                                                                                                                                ";
-
-
 ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head,
                      char const **error)
 {
@@ -1437,10 +1522,11 @@ static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp
         *      the later functions can mangle it in-place, which is
         *      much faster.
         */
-       tokens = talloc_strdup(request, fmt);
+       tokens = talloc_typed_strdup(request, fmt);
        if (!tokens) return -1;
 
        slen = xlat_tokenize_literal(request, tokens, head, false, &error);
+
        /*
         *      Zero length expansion, return a zero length node.
         */
@@ -1455,14 +1541,10 @@ static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp
         *      "       ^ error was here"
         */
        if (slen < 0) {
-               size_t indent = -slen;
                talloc_free(tokens);
-
                rad_assert(error != NULL);
-               if (indent < sizeof(xlat_spaces)) {
-                       REDEBUG("%s", fmt);
-                       REDEBUG("%.*s^ %s", (int) -slen, xlat_spaces, error);
-               }
+
+               REMARKER(fmt, -slen, error);
                return slen;
        }
 
@@ -1483,13 +1565,13 @@ static ssize_t xlat_tokenize_request(REQUEST *request, char const *fmt, xlat_exp
 }
 
 
-static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da, int8_t tag,
-                       int num, bool return_null)
+static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DICT_ATTR const *da,
+                       int8_t tag, int num, bool return_null)
 {
-       VALUE_PAIR *vp, *vps = NULL;
+       VALUE_PAIR *vp, *vps = NULL, *myvp = NULL;
        RADIUS_PACKET *packet = NULL;
        DICT_VALUE *dv;
-       VALUE_PAIR myvp;
+       char *ret = NULL;
 
        /*
         *      Arg.  Too much abstraction is annoying.
@@ -1497,7 +1579,7 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DI
        switch (list) {
        default:
                if (return_null) return NULL;
-               return vp_aprinttype(ctx, da->type);
+               return vp_aprint_type(ctx, da->type);
 
        case PAIR_LIST_CONTROL:
                vps = request->config_items;
@@ -1542,17 +1624,15 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DI
        }
 
        /*
-        *      Now that we have the list, etc. handled,
-        *      find the VP and print it.
+        *      Now we have the list, check to see if we have an attribute in
+        *      the request, if we do, it takes precedence over the virtual
+        *      attributes.
+        *
+        *      This allows users to manipulate virtual attributes as if they
+        *      were real ones.
         */
-       if ((da->vendor != 0) || (da->attr < 256) || (list == PAIR_LIST_CONTROL)) {
-       print_vp:
-               vp = pairfind(vps, da->attr, da->vendor, tag);
-               if (!vp) {
-                       return NULL;
-               }
-               goto do_print;
-       }
+       vp = pairfind(vps, da->attr, da->vendor, tag);
+       if (vp) goto do_print;
 
        /*
         *      Some non-packet expansions
@@ -1562,47 +1642,49 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DI
                break;          /* ignore them */
 
        case PW_CLIENT_SHORTNAME:
+               if (num == NUM_COUNT) goto count;
                if (request->client && request->client->shortname) {
-                       return talloc_strdup(ctx, request->client->shortname);
+                       return talloc_typed_strdup(ctx, request->client->shortname);
                }
-               return talloc_strdup(ctx, "<UNKNOWN-CLIENT>");
+               return talloc_typed_strdup(ctx, "<UNKNOWN-CLIENT>");
 
        case PW_REQUEST_PROCESSING_STAGE:
+               if (num == NUM_COUNT) goto count;
                if (request->component) {
-                       return talloc_strdup(ctx, request->component);
+                       return talloc_typed_strdup(ctx, request->component);
                }
-               return talloc_strdup(ctx, "server_core");
+               return talloc_typed_strdup(ctx, "server_core");
 
        case PW_VIRTUAL_SERVER:
+               if (num == NUM_COUNT) goto count;
                if (!request->server) return NULL;
-               return talloc_strdup(ctx, request->server);
+               return talloc_typed_strdup(ctx, request->server);
 
        case PW_MODULE_RETURN_CODE:
-               return talloc_asprintf(ctx, "%d", request->simul_max); /* hack */
+               if (num == NUM_COUNT) goto count;
+               if (!request->rcode) return NULL;
+               return talloc_typed_strdup(ctx, fr_int2str(modreturn_table, request->rcode, ""));
        }
 
        /*
-        *      All of the attributes must now refer to a packet.  If
-        *      there's no packet, we can't print any attribute
+        *      All of the attributes must now refer to a packet.
+        *      If there's no packet, we can't print any attribute
         *      referencing it.
         */
        if (!packet) {
                if (return_null) return NULL;
-               return vp_aprinttype(ctx, da->type);
+               return vp_aprint_type(ctx, da->type);
        }
 
-       memset(&myvp, 0, sizeof(myvp));
-       myvp.da = da;
        vp = NULL;
-
        switch (da->attr) {
        default:
-               goto print_vp;
+               break;
 
        case PW_PACKET_TYPE:
                dv = dict_valbyattr(PW_PACKET_TYPE, 0, packet->code);
-               if (dv) return talloc_strdup(ctx, dv->name);
-               return talloc_asprintf(ctx, "%d", packet->code);
+               if (dv) return talloc_typed_strdup(ctx, dv->name);
+               return talloc_typed_asprintf(ctx, "%d", packet->code);
 
        case PW_RESPONSE_PACKET_TYPE:
        {
@@ -1617,124 +1699,139 @@ static char *xlat_getvp(TALLOC_CTX *ctx, REQUEST *request, pair_lists_t list, DI
                                code = request->reply->code;
                        }
 
-               return talloc_strdup(ctx, fr_packet_codes[code]);
+               return talloc_typed_strdup(ctx, fr_packet_codes[code]);
        }
 
+       /*
+        *      Virtual attributes which require a temporary VALUE_PAIR
+        *      to be allocated. We can't use stack allocated memory
+        *      because of the talloc checks sprinkled throughout the
+        *      various VP functions.
+        */
        case PW_PACKET_AUTHENTICATION_VECTOR:
-               myvp.length = sizeof(packet->vector);
-               memcpy(&myvp.vp_octets, packet->vector, sizeof(packet->vector));
-               vp = &myvp;
+               myvp = pairalloc(ctx, da);
+               pairmemcpy(myvp, packet->vector, sizeof(packet->vector));
+               vp = myvp;
                break;
 
        case PW_CLIENT_IP_ADDRESS:
        case PW_PACKET_SRC_IP_ADDRESS:
                if (packet->src_ipaddr.af == AF_INET) {
-                       myvp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
-                       vp = &myvp;
+                       myvp = pairalloc(ctx, da);
+                       myvp->vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+                       vp = myvp;
                }
                break;
 
        case PW_PACKET_DST_IP_ADDRESS:
                if (packet->dst_ipaddr.af == AF_INET) {
-                       myvp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
-                       vp = &myvp;
+                       myvp = pairalloc(ctx, da);
+                       myvp->vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+                       vp = myvp;
                }
                break;
 
        case PW_PACKET_SRC_IPV6_ADDRESS:
                if (packet->src_ipaddr.af == AF_INET6) {
-                       memcpy(&myvp.vp_ipv6addr,
+                       myvp = pairalloc(ctx, da);
+                       memcpy(&myvp->vp_ipv6addr,
                               &packet->src_ipaddr.ipaddr.ip6addr,
                               sizeof(packet->src_ipaddr.ipaddr.ip6addr));
-                       vp = &myvp;
+                       vp = myvp;
                }
                break;
 
        case PW_PACKET_DST_IPV6_ADDRESS:
                if (packet->dst_ipaddr.af == AF_INET6) {
-                       memcpy(&myvp.vp_ipv6addr,
+                       myvp = pairalloc(ctx, da);
+                       memcpy(&myvp->vp_ipv6addr,
                               &packet->dst_ipaddr.ipaddr.ip6addr,
                               sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
-                       vp = &myvp;
+                       vp = myvp;
                }
                break;
 
        case PW_PACKET_SRC_PORT:
-               myvp.vp_integer = packet->src_port;
-               vp = &myvp;
+               myvp = pairalloc(ctx, da);
+               myvp->vp_integer = packet->src_port;
+               vp = myvp;
                break;
 
        case PW_PACKET_DST_PORT:
-               myvp.vp_integer = packet->dst_port;
-               vp = &myvp;
+               myvp = pairalloc(ctx, da);
+               myvp->vp_integer = packet->dst_port;
+               vp = myvp;
                break;
        }
 
-do_print:
        /*
-        *      Hack up the virtual attributes.
+        *      Fake various operations for virtual attributes.
         */
-       if (num && (vp == &myvp)) {
-               char *p, *q;
-
+       if (myvp) {
+               if (num != NUM_ANY) switch (num) {
+               /*
+                *      [n] is NULL (we only have [0])
+                */
+               default:
+                       goto finish;
                /*
                 *      [*] means only one.
                 */
-               if (num == 65537) num = 0;
+               case NUM_JOIN:
+                       break;
 
                /*
-                *      [n] means NULL, as there's only one.
+                *      [#] means 1 (as there's only one)
                 */
-               if ((num > 0) && (num < 65536)) {
-                       return NULL;
-               }
-
-               p = vp_aprint(ctx, vp);
-               rad_assert(p != NULL);
+               case NUM_COUNT:
+               count:
+                       ret = talloc_strdup(ctx, "1");
+                       goto finish;
 
                /*
-                *      Get the length of it.
+                *      [0] is fine (get the first instance)
                 */
-               if (num == 65536) {
-                       q = talloc_asprintf(ctx, "%d", (int) strlen(p));
-                       talloc_free(p);
-                       return q;
+               case 0:
+                       break;
                }
-
-               return p;
+               goto print;
        }
 
+do_print:
        /*
         *      We want the N'th VP.
         */
-       if (num) {
+       if (num != NUM_ANY) {
                int count = 0;
                vp_cursor_t cursor;
 
+               switch (num) {
                /*
                 *      Return a count of the VPs.
                 */
-               if (num == 65536) {
-                       paircursor(&cursor, &vp);
-                       while (pairfindnext(&cursor, da->attr, da->vendor, tag) != NULL) {
+               case NUM_COUNT:
+                       fr_cursor_init(&cursor, &vp);
+                       while (fr_cursor_next_by_da(&cursor, da, tag) != NULL) {
                                count++;
                        }
-
-                       return talloc_asprintf(ctx, "%d", count);
-               }
+                       return talloc_typed_asprintf(ctx, "%d", count);
 
                /*
                 *      Ugly, but working.
                 */
-               if (num == 65537) {
+               case NUM_JOIN:
+               {
                        char *p, *q;
 
-                       vp = paircursor(&cursor, &vp);
-                       vp = pairfindnext(&cursor, da->attr, da->vendor, tag);
+                       (void) fr_cursor_init(&cursor, &vp);
+                       vp = fr_cursor_next_by_da(&cursor, da, tag);
                        if (!vp) return NULL;
-                       p = vp_aprint(ctx, vp);
-                       while ((vp = pairfindnext(&cursor, da->attr, da->vendor, tag)) != NULL) {
-                               q = vp_aprint(ctx, vp);
+
+                       p = vp_aprint_value(ctx, vp);
+                       if (!p) return NULL;
+                       while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
+                               q = vp_aprint_value(ctx, vp);
+                               if (!q) return NULL;
                                p = talloc_strdup_append(p, ",");
                                p = talloc_strdup_append(p, q);
                        }
@@ -1742,23 +1839,32 @@ do_print:
                        return p;
                }
 
-               paircursor(&cursor, &vp);
-               while (pairfindnext(&cursor, da->attr, da->vendor, tag) != NULL) {
-                       if (count == num) {
-                               break;
+               default:
+                       fr_cursor_init(&cursor, &vp);
+                       while ((vp = fr_cursor_next_by_da(&cursor, da, tag)) != NULL) {
+                               if (count++ == num) break;
                        }
-                       count++;
+                       break;
                }
        }
 
        if (!vp) {
                if (return_null) return NULL;
-               return vp_aprinttype(ctx, da->type);
+               return vp_aprint_type(ctx, da->type);
        }
 
-       return vp_aprint(ctx, vp);
+print:
+       ret = vp_aprint_value(ctx, vp);
+
+finish:
+       talloc_free(myvp);
+       return ret;
 }
 
+#ifdef DEBUG_XLAT
+static const char *xlat_spaces = "                                                                                                                                                                                                                                                                ";
+#endif
+
 static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node,
                         RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl)
 {
@@ -1774,7 +1880,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                 */
        case XLAT_LITERAL:
                XLAT_DEBUG("xlat_aprint LITERAL");
-               return talloc_strdup(ctx, node->fmt);
+               return talloc_typed_strdup(ctx, node->fmt);
 
                /*
                 *      Do a one-character expansion.
@@ -1818,6 +1924,10 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        strftime(str, freespace, "%m", &ts);
                        break;
 
+               case 'n': /* Request Number*/
+                       snprintf(str, freespace, "%i", request->number);
+                       break;
+
                case 't': /* request timestamp */
                        CTIME_R(&when, str, freespace);
                        nl = strchr(str, '\n');
@@ -1860,6 +1970,10 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        strftime(str, freespace, "%Y", &ts);
                        break;
 
+               case 'v': /* Version of code */
+                       snprintf(str, freespace, "%s", radiusd_short_version);
+                       break;
+
                default:
                        rad_assert(0 == 1);
                        break;
@@ -1879,13 +1993,14 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                 */
                str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true);
                if (str) {
-                       XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str);
+                       XLAT_DEBUG("EXPAND attr %s", node->da->name);
+                       XLAT_DEBUG("       ---> %s", str);
                }
                break;
 
        case XLAT_VIRTUAL:
                XLAT_DEBUG("xlat_aprint VIRTUAL");
-               str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
+               str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_typed_asprintf */
                rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024);
                if (rcode < 0) {
                        talloc_free(str);
@@ -1899,9 +2014,10 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                        return NULL;
                }
 
-               XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child);
+               XLAT_DEBUG("%.*sEXPAND mod %s %s", lvl, xlat_spaces, node->fmt, node->child->fmt);
+               XLAT_DEBUG("%.*s      ---> %s", lvl, xlat_spaces, child);
 
-               str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */
+               str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_typed_asprintf */
                *str = '\0';    /* Be sure the string is NULL terminated, we now only free on error */
 
                rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024);
@@ -1919,7 +2035,7 @@ static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * c
                                               REQUEST_DATA_REGEX | node->num);
                if (!child) return NULL;
 
-               str = talloc_strdup(ctx, child);
+               str = talloc_typed_strdup(ctx, child);
                break;
 #endif
 
@@ -2041,20 +2157,57 @@ static size_t xlat_process(char **out, REQUEST *request, xlat_exp_t const * cons
  * @param[out] out Where to write pointer to output buffer.
  * @param[in] outlen Size of out.
  * @param[in] request current request.
- * @param[in] fmt string to expand.
+ * @param[in] node the xlat structure to expand
  * @param[in] escape function to escape final value e.g. SQL quoting.
  * @param[in] escape_ctx pointer to pass to escape function.
  * @return length of string written @bug should really have -1 for failure
  */
-static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
-                          RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+static ssize_t xlat_expand_struct(char **out, size_t outlen, REQUEST *request, xlat_exp_t const *node,
+                                 RADIUS_ESCAPE_STRING escape, void *escape_ctx)
 {
        char *buff;
        ssize_t len;
-       xlat_exp_t *node;
 
-       rad_assert(fmt);
-       rad_assert(request);
+       rad_assert(node != NULL);
+
+       len = xlat_process(&buff, request, node, escape, escape_ctx);
+
+       if ((len < 0) || !buff) {
+               rad_assert(buff == NULL);
+               if (*out) *out[0] = '\0';
+               return len;
+       }
+
+       if (!*out) {
+               *out = buff;
+       } else {
+               strlcpy(*out, buff, outlen);
+               talloc_free(buff);
+       }
+
+       return strlen(*out);
+}
+
+static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
+                          RADIUS_ESCAPE_STRING escape, void *escape_ctx) CC_HINT(nonnull (1, 3, 4));
+
+/** Replace %whatever in a string.
+ *
+ * See 'doc/variables.txt' for more information.
+ *
+ * @param[out] out Where to write pointer to output buffer.
+ * @param[in] outlen Size of out.
+ * @param[in] request current request.
+ * @param[in] fmt string to expand.
+ * @param[in] escape function to escape final value e.g. SQL quoting.
+ * @param[in] escape_ctx pointer to pass to escape function.
+ * @return length of string written @bug should really have -1 for failure
+ */
+static ssize_t CC_HINT(nonnull (1, 3, 4)) xlat_expand(char **out, size_t outlen, REQUEST *request, char const *fmt,
+                                                     RADIUS_ESCAPE_STRING escape, void *escape_ctx)
+{
+       ssize_t len;
+       xlat_exp_t *node;
 
        /*
         *      Give better errors than the old code.
@@ -2074,24 +2227,42 @@ static ssize_t xlat_expand(char **out, size_t outlen, REQUEST *request, char con
                return -1;
        }
 
-       len = xlat_process(&buff, request, node, escape, escape_ctx);
+       len = xlat_expand_struct(out, outlen, request, node, escape, escape_ctx);
        talloc_free(node);
 
-       if ((len < 0) || !buff) {
-               rad_assert(buff == NULL);
-               if (*out) *out[0] = '\0';
-               return len;
-       }
+       RDEBUG2("EXPAND %s", fmt);
+       RDEBUG2("   --> %s", *out);
 
-       RDEBUG2("\texpand: \"%s\" -> '%s'", fmt, buff);
+       return len;
+}
 
-       if (!*out) {
-               *out = buff;
-       } else {
-               strlcpy(*out, buff, outlen);
-       }
+/*
+ *     Try to convert an xlat to a tmpl for efficiency
+ */
+value_pair_tmpl_t *radius_xlat2tmpl(TALLOC_CTX *ctx, xlat_exp_t *xlat)
+{
+       value_pair_tmpl_t *vpt;
 
-       return strlen(*out);
+       if (xlat->next || (xlat->type != XLAT_ATTRIBUTE)) return NULL;
+
+       /*
+        * @todo it should be possible to emulate the concat and count operations in the
+        * map code.
+        */
+       if ((xlat->num == NUM_COUNT) || (xlat->num == NUM_JOIN)) return NULL;
+
+       vpt = talloc(ctx, value_pair_tmpl_t);
+       if (!vpt) return NULL;
+
+       vpt->type = VPT_TYPE_ATTR;
+       vpt->name = talloc_strdup(vpt, xlat->fmt);
+       vpt->vpt_request = xlat->ref;
+       vpt->vpt_list = xlat->list;
+       vpt->vpt_da = xlat->da;
+       vpt->vpt_num = xlat->num;
+       vpt->vpt_tag = xlat->tag;
+
+       return vpt;
 }
 
 ssize_t radius_xlat(char *out, size_t outlen, REQUEST *request, char const *fmt, RADIUS_ESCAPE_STRING escape, void *ctx)
@@ -2103,3 +2274,8 @@ ssize_t radius_axlat(char **out, REQUEST *request, char const *fmt, RADIUS_ESCAP
 {
        return xlat_expand(out, 0, request, fmt, escape, ctx);
 }
+
+ssize_t radius_axlat_struct(char **out, REQUEST *request, xlat_exp_t const *xlat, RADIUS_ESCAPE_STRING escape, void *ctx)
+{
+       return xlat_expand_struct(out, 0, request, xlat, escape, ctx);
+}
index 6b3b5fc..bd1c963 100755 (executable)
@@ -22,13 +22,13 @@ do
      esac
 
      if test ! -d "$pathcomp"; then
-        echo "mkdir $pathcomp"
+       echo "mkdir $pathcomp"
 
-        mkdir "$pathcomp" || lasterr=$?
+       mkdir "$pathcomp" || lasterr=$?
 
-        if test ! -d "$pathcomp"; then
-         errstatus=$lasterr
-        fi
+       if test ! -d "$pathcomp"; then
+         errstatus=$lasterr
+       fi
      fi
 
      pathcomp="$pathcomp/"
index bfbef8d..1406c94 100644 (file)
@@ -54,7 +54,6 @@ RCSID("$Id$")
 
 /* @todo: this is a hack */
 #  define DEBUG                        if (fr_debug_flag && fr_log_fp) fr_printf_log
-void fr_strerror_printf(char const *fmt, ...);
 #  define debug_pair(vp)       do { if (fr_debug_flag && fr_log_fp) { \
                                        vp_print(fr_log_fp, vp); \
                                     } \
@@ -222,7 +221,7 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        socklen_t               sizeof_src;
        socklen_t               sizeof_dst;
        RADIUS_PACKET           *packet;
-       int                     port;
+       uint16_t                port;
        uint8_t                 *code;
        ssize_t                 data_len;
 
@@ -252,7 +251,7 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
 #endif
 
        if (data_len <= 0) {
-               fr_strerror_printf("Failed reading DHCP socket: %s", strerror(errno));
+               fr_strerror_printf("Failed reading DHCP socket: %s", fr_syserror(errno));
                rad_free(&packet);
                return NULL;
        }
@@ -260,14 +259,14 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
        packet->data_len = data_len;
        if (packet->data_len < MIN_PACKET_SIZE) {
                fr_strerror_printf("DHCP packet is too small (%zu < %d)",
-                                  packet->data_len, MIN_PACKET_SIZE);
+                                  packet->data_len, MIN_PACKET_SIZE);
                rad_free(&packet);
                return NULL;
        }
 
        if (packet->data_len > MAX_PACKET_SIZE) {
                fr_strerror_printf("DHCP packet is too large (%zx > %d)",
-                                  packet->data_len, MAX_PACKET_SIZE);
+                                  packet->data_len, MAX_PACKET_SIZE);
                rad_free(&packet);
                return NULL;
        }
@@ -351,7 +350,7 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
         *      This should never fail...
         */
        if (getsockname(sockfd, (struct sockaddr *) &dst, &sizeof_dst) < 0) {
-               fr_strerror_printf("getsockname failed: %s", strerror(errno));
+               fr_strerror_printf("getsockname failed: %s", fr_syserror(errno));
                rad_free(&packet);
                return NULL;
        }
@@ -376,7 +375,7 @@ RADIUS_PACKET *fr_dhcp_recv(int sockfd)
                                 packet->code - PW_DHCP_OFFSET);
                }
 
-               DEBUG("Received %s of id %08x from %s:%d to %s:%d\n",
+               DEBUG("Received %s of Id %08x from %s:%d to %s:%d\n",
                       name, (unsigned int) packet->id,
                       inet_ntop(packet->src_ipaddr.af,
                                 &packet->src_ipaddr.ipaddr,
@@ -433,9 +432,9 @@ int fr_dhcp_send(RADIUS_PACKET *packet)
 
                DEBUG(
 #ifdef WITH_UDPFROMTO
-               "Sending %s of id %08x from %s:%d to %s:%d\n",
+               "Sending %s Id %08x from %s:%d to %s:%d\n",
 #else
-               "Sending %s of id %08x to %s:%d\n",
+               "Sending %s Id %08x to %s:%d\n",
 #endif
                   name, (unsigned int) packet->id,
 #ifdef WITH_UDPFROMTO
@@ -483,7 +482,7 @@ static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *dat
         *      Got here... must be well formed.
         */
        head = NULL;
-       paircursor(&cursor, &head);
+       fr_cursor_init(&cursor, &head);
 
        p = data;
        while (p < (data + data_len)) {
@@ -498,18 +497,36 @@ static int decode_tlv(RADIUS_PACKET *packet, VALUE_PAIR *tlv, uint8_t const *dat
                        goto make_tlv;
                }
 
-               pairinsert(&cursor, vp);
+               fr_cursor_insert(&cursor, vp);
                p += 2 + p[1];
        }
 
        /*
         *      The caller allocated TLV, so we need to copy the FIRST
         *      attribute over top of that.
+        *
+        *      This is a pretty awful hack, but we should be able to
+        *      clean it up when we get nested VPs so lets leave it for
+        *      now.
         */
        if (head) {
+               /* Cleanup any old TLV data */
+               talloc_free(tlv->vp_tlv);
+
+               /* @fixme fragile */
                memcpy(tlv, head, sizeof(*tlv));
-               head->next = NULL;
-               pairfree(&head);
+
+               /* If the VP has a talloced value we need to reparent it to the original TLV attribute */
+               switch (head->da->type) {
+                       case PW_TYPE_STRING:
+                       case PW_TYPE_OCTETS:
+                       case PW_TYPE_TLV:
+                               (void) talloc_steal(tlv, head->data.ptr);
+                       default:
+                               break;
+               }
+               tlv->next = head->next;
+               talloc_free(head);
        }
 
        return 0;
@@ -537,13 +554,13 @@ static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const
        switch (vp->da->type) {
        case PW_TYPE_BYTE:
                if (alen != 1) goto raw;
-               vp->vp_integer = p[0];
+               vp->vp_byte = p[0];
                break;
 
        case PW_TYPE_SHORT:
                if (alen != 2) goto raw;
-               memcpy(&vp->vp_integer, p, 2);
-               vp->vp_integer = ntohs(vp->vp_integer);
+               memcpy(&vp->vp_short, p, 2);
+               vp->vp_short = ntohs(vp->vp_short);
                break;
 
        case PW_TYPE_INTEGER:
@@ -552,7 +569,7 @@ static int fr_dhcp_attr2vp(RADIUS_PACKET *packet, VALUE_PAIR *vp, uint8_t const
                vp->vp_integer = ntohl(vp->vp_integer);
                break;
 
-       case PW_TYPE_IPADDR:
+       case PW_TYPE_IPV4_ADDR:
                if (alen != 4) goto raw;
                /*
                 *      Keep value in Network Order!
@@ -607,7 +624,7 @@ ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
        next = data;
 
        *head = NULL;
-       paircursor(&cursor, head);
+       fr_cursor_init(&cursor, head);
 
        /*
         *      FIXME: This should also check sname && file fields.
@@ -658,7 +675,7 @@ ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
                                alen = 2;
                                break;
 
-                       case PW_TYPE_IPADDR:
+                       case PW_TYPE_IPV4_ADDR:
                        case PW_TYPE_INTEGER:
                        case PW_TYPE_DATE: /* ignore any trailing data */
                                num_entries = alen >> 2;
@@ -697,11 +714,11 @@ ssize_t fr_dhcp_decode_options(RADIUS_PACKET *packet,
                                return -1;
                        }
 
-                       pairinsert(&cursor, vp);
+                       fr_cursor_insert(&cursor, vp);
 
-                       for (vp = paircurrent(&cursor);
+                       for (vp = fr_cursor_current(&cursor);
                             vp;
-                            vp = pairnext(&cursor)) {
+                            vp = fr_cursor_next(&cursor)) {
                                debug_pair(vp);
                        }
                        p += alen;
@@ -720,7 +737,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        VALUE_PAIR *head = NULL, *vp;
        VALUE_PAIR *maxms, *mtu;
 
-       paircursor(&cursor, &head);
+       fr_cursor_init(&cursor, &head);
        p = packet->data;
 
        if ((fr_debug_flag > 2) && fr_log_fp) {
@@ -767,12 +784,12 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
 
                switch (vp->da->type) {
                case PW_TYPE_BYTE:
-                       vp->vp_integer = p[0];
+                       vp->vp_byte = p[0];
                        vp->length = 1;
                        break;
 
                case PW_TYPE_SHORT:
-                       vp->vp_integer = (p[0] << 8) | p[1];
+                       vp->vp_short = (p[0] << 8) | p[1];
                        vp->length = 2;
                        break;
 
@@ -782,7 +799,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                        vp->length = 4;
                        break;
 
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        memcpy(&vp->vp_ipaddr, p, 4);
                        vp->length = 4;
                        break;
@@ -817,7 +834,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                if (!vp) continue;
 
                debug_pair(vp);
-               pairinsert(&cursor, vp);
+               fr_cursor_insert(&cursor, vp);
        }
 
        /*
@@ -838,7 +855,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
                }
 
                if (options) {
-                       pairinsert(&cursor, options);
+                       fr_cursor_insert(&cursor, options);
                }
        }
 
@@ -887,7 +904,7 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
        mtu = pairfind(packet->vps, 26, DHCP_MAGIC_VENDOR, TAG_ANY);
 
        if (mtu && (mtu->vp_integer < DEFAULT_PACKET_SIZE)) {
-               fr_strerror_printf("DHCP Fatal: Client says MTU is smaller than minimum permitted by the specification.");
+               fr_strerror_printf("DHCP Fatal: Client says MTU is smaller than minimum permitted by the specification");
                return -1;
        }
 
@@ -907,170 +924,278 @@ int fr_dhcp_decode(RADIUS_PACKET *packet)
 }
 
 
-static int attr_cmp(void const *one, void const *two)
+int8_t fr_dhcp_attr_cmp(void const *a, void const *b)
 {
-       VALUE_PAIR const * const *a = one;
-       VALUE_PAIR const * const *b = two;
+       VALUE_PAIR const *my_a = a;
+       VALUE_PAIR const *my_b = b;
+
+       VERIFY_VP(my_a);
+       VERIFY_VP(my_b);
 
        /*
         *      DHCP-Message-Type is first, for simplicity.
         */
-       if (((*a)->da->attr == 53) &&
-           (*b)->da->attr != 53) return -1;
+       if ((my_a->da->attr == 53) && (my_b->da->attr != 53)) return -1;
 
        /*
         *      Relay-Agent is last
         */
-       if (((*a)->da->attr == 82) &&
-           (*b)->da->attr != 82) return 1;
+       if ((my_a->da->attr == 82) && (my_b->da->attr != 82)) return 1;
+
+       if (my_a->da->attr < my_b->da->attr) return -1;
+       if (my_a->da->attr > my_b->da->attr) return 1;
 
-       return ((*a)->da->attr - (*b)->da->attr);
+       return 0;
 }
 
-/*
- * @todo Check room!
+/** Write DHCP option value into buffer
+ *
+ * Does not include DHCP option length or number.
+ *
+ * @param out where to write the DHCP option.
+ * @param outlen length of output buffer.
+ * @param vp option to encode.
+ * @return the length of data writen, -1 if out of buffer, -2 if unsupported type.
  */
-static size_t fr_dhcp_vp2attr(VALUE_PAIR *vp, uint8_t *p, UNUSED size_t room)
+static ssize_t fr_dhcp_vp2attr(uint8_t *out, size_t outlen, VALUE_PAIR *vp)
 {
-       size_t length;
        uint32_t lvalue;
+       uint8_t *p = out;
+
+       if (outlen < vp->length) {
+               return -1;
+       }
 
-       /*
-        *      Search for all attributes of the same
-        *      type, and pack them into the same
-        *      attribute.
-        */
        switch (vp->da->type) {
        case PW_TYPE_BYTE:
-               length = 1;
-               *p = vp->vp_integer & 0xff;
+               *p = vp->vp_byte;
                break;
 
        case PW_TYPE_SHORT:
-               length = 2;
-               p[0] = (vp->vp_integer >> 8) & 0xff;
-               p[1] = vp->vp_integer & 0xff;
+               p[0] = (vp->vp_short >> 8) & 0xff;
+               p[1] = vp->vp_short & 0xff;
                break;
 
        case PW_TYPE_INTEGER:
-               length = 4;
                lvalue = htonl(vp->vp_integer);
                memcpy(p, &lvalue, 4);
                break;
 
-       case PW_TYPE_IPADDR:
-               length = 4;
+       case PW_TYPE_IPV4_ADDR:
                memcpy(p, &vp->vp_ipaddr, 4);
                break;
 
        case PW_TYPE_ETHERNET:
-               length = 6;
                memcpy(p, &vp->vp_ether, 6);
                break;
 
        case PW_TYPE_STRING:
                memcpy(p, vp->vp_strvalue, vp->length);
-               length = vp->length;
                break;
 
        case PW_TYPE_TLV:       /* FIXME: split it on 255? */
                memcpy(p, vp->vp_tlv, vp->length);
-               length = vp->length;
                break;
 
        case PW_TYPE_OCTETS:
                memcpy(p, vp->vp_octets, vp->length);
-               length = vp->length;
                break;
 
        default:
-               fr_strerror_printf("BAD TYPE2 %d", vp->da->type);
-               length = 0;
-               break;
+               fr_strerror_printf("Unsupported option type %d", vp->da->type);
+               return -2;
        }
 
-       return length;
+       return vp->length;
 }
 
-static VALUE_PAIR *fr_dhcp_vp2suboption(RADIUS_PACKET *packet, VALUE_PAIR *vps)
+/** Create a new TLV attribute from multiple sub options
+ *
+ * @param[in,out] ctx to allocate new attribute in.
+ * @param[in,out] cursor should be set to the start of the list of TLV attributes.
+ *   Will be advanced to the first non-TLV attribute.
+ * @return attribute holding the concatenation of the values of the sub options.
+ */
+static VALUE_PAIR *fr_dhcp_vp2suboption(TALLOC_CTX *ctx, vp_cursor_t *cursor)
 {
-       int length;
-       unsigned int attribute;
-       uint8_t *ptr;
-       vp_cursor_t cursor;
+       ssize_t length;
+       unsigned int parent;    /* Parent attribute of suboption */
+       uint8_t attr = 0;
+       uint8_t *p, *opt_len = NULL;
+       vp_cursor_t to_pack;
        VALUE_PAIR *vp, *tlv;
 
-       attribute = vps->da->attr & 0xffff00ff;
+#define SUBOPTION_PARENT(_x) (_x & 0xffff00ff)
+#define SUBOPTION_ATTR(_x) ((_x & 0xff00) >> 8)
+
+       vp = fr_cursor_current(cursor);
+       if (!vp) return NULL;
 
-       tlv = paircreate(packet, attribute, DHCP_MAGIC_VENDOR);
+       parent = SUBOPTION_PARENT(vp->da->attr);
+       tlv = paircreate(ctx, parent, DHCP_MAGIC_VENDOR);
        if (!tlv) return NULL;
 
-       tlv->length = 0;
-       for (vp = paircursor(&cursor, &vps);
-            vp;
-            vp = pairnext(&cursor)) {
+       fr_cursor_copy(&to_pack, cursor);
+
+       /*
+        *  Loop over TLVs to determine how much memory we need to allocate
+        *
+        *  We advanced the cursor we were passed, so if we fail encoding,
+        *  the cursor is at the right position for the next potentially
+        *  encodable attr.
+        */
+       for (vp = fr_cursor_current(cursor);
+            vp && vp->da->flags.is_tlv && !vp->da->flags.extended && (SUBOPTION_PARENT(vp->da->attr) == parent);
+            vp = fr_cursor_next(cursor)) {
                /*
-                *      Group the attributes ONLY until we see a
-                *      non-TLV attribute.
+                *  If it's not an array type or is an array type, but is not the same
+                *  as the previous attribute, we add 2 for the additional sub-option
+                *  header bytes.
                 */
-               if (!vp->da->flags.is_tlv ||
-                   vp->da->flags.extended ||
-                   ((vp->da->attr & 0xffff00ff) != attribute)) {
-                       break;
+               if (!vp->da->flags.array || (SUBOPTION_ATTR(vp->da->attr) != attr)) {
+                       attr = SUBOPTION_ATTR(vp->da->attr);
+                       tlv->length += 2;
                }
-
-               tlv->length += vp->length + 2;
-       }
-
-       if (!tlv->length) {
-               pairfree(&tlv);
-               return NULL;
+               tlv->length += vp->length;
        }
 
        tlv->vp_tlv = talloc_array(tlv, uint8_t, tlv->length);
        if (!tlv->vp_tlv) {
-               pairfree(&tlv);
+               talloc_free(tlv);
                return NULL;
        }
+       p = tlv->vp_tlv;
+
+       attr = 0;
+       for (vp = fr_cursor_current(&to_pack);
+            vp && vp->da->flags.is_tlv && !vp->da->flags.extended && (SUBOPTION_PARENT(vp->da->attr) == parent);
+            vp = fr_cursor_next(&to_pack)) {
+               if (SUBOPTION_ATTR(vp->da->attr) == 0) {
+                       fr_strerror_printf("Invalid attribute number 0");
+                       return NULL;
+               }
 
-       ptr = tlv->vp_tlv;
-       for (vp = paircursor(&cursor, &vps);
-            vp;
-            vp = pairnext(&cursor)) {
-               if (!vp->da->flags.is_tlv ||
-                   vp->da->flags.extended ||
-                   ((vp->da->attr & 0xffff00ff) != attribute)) {
-                       break;
+               /* Don't write out the header, were packing array options */
+               if (!vp->da->flags.array || (attr != SUBOPTION_ATTR(vp->da->attr))) {
+                       attr = SUBOPTION_ATTR(vp->da->attr);
+                       *p++ = attr;
+                       opt_len = p++;
                }
 
-               length = fr_dhcp_vp2attr(vp, ptr + 2,
-                                        tlv->vp_tlv + tlv->length - ptr);
-               if (length > 255) {
-                       pairfree(&tlv);
+               length = fr_dhcp_vp2attr(p, (tlv->vp_tlv + tlv->length) - p, vp);
+               if ((length < 0) || (length > 255)) {
+                       talloc_free(tlv);
                        return NULL;
                }
 
+               fr_assert(opt_len);
+               *opt_len += length;
+               p += length;
+       };
+
+       return tlv;
+}
+
+/** Encode a DHCP option and any sub-options.
+ *
+ * @param out Where to write encoded DHCP attributes.
+ * @param outlen Length of out buffer.
+ * @param ctx to use for any allocated memory.
+ * @param cursor with current VP set to the option to be encoded. Will be advanced to the next option to encode.
+ * @return > 0 length of data written, < 0 error, 0 not valid option (skipping).
+ */
+ssize_t fr_dhcp_encode_option(uint8_t *out, size_t outlen, TALLOC_CTX *ctx, vp_cursor_t *cursor)
+{
+       VALUE_PAIR *vp;
+       DICT_ATTR const *previous;
+       uint8_t *opt_len, *p = out;
+       size_t freespace = outlen;
+       ssize_t len;
+
+       vp = fr_cursor_current(cursor);
+       if (!vp) return -1;
+
+       if (vp->da->vendor != DHCP_MAGIC_VENDOR) goto next; /* not a DHCP option */
+       if (vp->da->attr == 53) goto next; /* already done */
+       if ((vp->da->attr > 255) && (DHCP_BASE_ATTR(vp->da->attr) != PW_DHCP_OPTION_82)) goto next;
+
+       if (vp->da->flags.extended) {
+       next:
+               fr_strerror_printf("Attribute \"%s\" is not a DHCP option", vp->da->name);
+               fr_cursor_next(cursor);
+               return 0;
+       }
+
+       /* Write out the option number */
+       *(p++) = vp->da->attr & 0xff;
+
+       /* Pointer to the length field of the option */
+       opt_len = p++;
+
+       /* Zero out the option's length field */
+       *opt_len = 0;
+
+       /* We just consumed two bytes for the header */
+       freespace -= 2;
+
+       /* DHCP options with the same number get coalesced into a single option */
+       do {
+               VALUE_PAIR *tlv = NULL;
+
+               /* Sub option */
+               if (vp->da->flags.is_tlv) {
+                       /*
+                        *  Coalesce TLVs into one sub-option.
+                        *  Cursor will be advanced to next non-TLV attribute.
+                        */
+                       tlv = vp = fr_dhcp_vp2suboption(ctx, cursor);
+
+                       /*
+                        *  Skip if there's an issue coalescing the sub-options.
+                        *  Cursor will still have been advanced to next non-TLV attribute.
+                        */
+                       if (!tlv) return 0;
                /*
-                *      Pack the attribute.
+                *  If not calling fr_dhcp_vp2suboption() advance the cursor, so fr_cursor_current()
+                *  returns the next attribute.
                 */
-               ptr[0] = (vp->da->attr & 0xff00) >> 8;
-               ptr[1] = length;
+               } else {
+                       fr_cursor_next(cursor);
+               }
 
-               ptr += length + 2;
-       }
+               if ((*opt_len + vp->length) > 255) {
+                       fr_strerror_printf("Skipping \"%s\": Option splitting not supported "
+                                          "(option > 255 bytes)", vp->da->name);
+                       talloc_free(tlv);
+                       return 0;
+               }
 
-       return tlv;
-}
+               len = fr_dhcp_vp2attr(p, freespace, vp);
+               talloc_free(tlv);
+               if (len < 0) {
+                       /* Failed encoding option */
+                       return len;
+               }
 
+               p += len;
+               *opt_len += len;
+               freespace -= len;
+
+               previous = vp->da;
+       } while ((vp = fr_cursor_current(cursor)) && (previous == vp->da) && vp->da->flags.array);
+
+       return p - out;
+}
 
 int fr_dhcp_encode(RADIUS_PACKET *packet)
 {
-       unsigned int i, num_vps;
+       unsigned int i;
        uint8_t *p;
        vp_cursor_t cursor;
        VALUE_PAIR *vp;
        uint32_t lvalue;
-       size_t dhcp_size, length;
+       size_t dhcp_size;
+       ssize_t len;
 #ifndef NDEBUG
        char const *name;
 #  ifdef WITH_UDPFROMTO
@@ -1096,18 +1221,18 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
        }
 
        DEBUG(
-#ifdef WITH_UDPFROMTO
+#  ifdef WITH_UDPFROMTO
              "Encoding %s of id %08x from %s:%d to %s:%d\n",
-#else
+#  else
              "Encoding %s of id %08x to %s:%d\n",
-#endif
+#  endif
              name, (unsigned int) packet->id,
-#ifdef WITH_UDPFROMTO
+#  ifdef WITH_UDPFROMTO
              inet_ntop(packet->src_ipaddr.af,
                        &packet->src_ipaddr.ipaddr,
                        src_ip_buf, sizeof(src_ip_buf)),
              packet->src_port,
-#endif
+#  endif
              inet_ntop(packet->dst_ipaddr.af,
                        &packet->dst_ipaddr.ipaddr,
                     dst_ip_buf, sizeof(dst_ip_buf)),
@@ -1254,7 +1379,8 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
         */
 
        /* DHCP-Boot-Filename */
-       if ((vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY))) {
+       vp = pairfind(packet->vps, 269, DHCP_MAGIC_VENDOR, TAG_ANY);
+       if (vp) {
                if (vp->length > DHCP_FILE_LEN) {
                        memcpy(p, vp->vp_strvalue, DHCP_FILE_LEN);
                } else {
@@ -1290,24 +1416,20 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
 
                        switch (vp->da->type) {
                        case PW_TYPE_BYTE:
-                               vp->vp_integer = p[0];
-                               vp->length = 1;
+                               vp->vp_byte = p[0];
                                break;
 
                        case PW_TYPE_SHORT:
-                               vp->vp_integer = (p[0] << 8) | p[1];
-                               vp->length = 2;
+                               vp->vp_short = (p[0] << 8) | p[1];
                                break;
 
                        case PW_TYPE_INTEGER:
                                memcpy(&vp->vp_integer, p, 4);
                                vp->vp_integer = ntohl(vp->vp_integer);
-                               vp->length = 4;
                                break;
 
-                       case PW_TYPE_IPADDR:
+                       case PW_TYPE_IPV4_ADDR:
                                memcpy(&vp->vp_ipaddr, p, 4);
-                               vp->length = 4;
                                break;
 
                        case PW_TYPE_STRING:
@@ -1324,7 +1446,6 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
 
                        case PW_TYPE_ETHERNET: /* only for Client HW Address */
                                memcpy(vp->vp_ether, p, sizeof(vp->vp_ether));
-                               vp->length = sizeof(vp->vp_ether);
                                break;
 
                        default:
@@ -1345,144 +1466,29 @@ int fr_dhcp_encode(RADIUS_PACKET *packet)
                p = pp;
        }
 
-       /*
-        *      Before packing the attributes, re-order them so that
-        *      the array ones are all contiguous.  This simplifies
-        *      the later code.
-        */
-       num_vps = 0;
-       for (vp = paircursor(&cursor, &packet->vps);
-            vp;
-            vp = pairnext(&cursor)) {
-               num_vps++;
-       }
-       if (num_vps > 1) {
-               VALUE_PAIR **array;
-
-               array = talloc_array(packet, VALUE_PAIR*, num_vps);
-
-               i = 0;
-               for (vp = paircursor(&cursor, &packet->vps);
-                    vp;
-                    vp = pairnext(&cursor)) {
-                       array[i++] = vp;
-               }
-
-               /*
-                *      Sort the attributes.
-                */
-               qsort(array, (size_t) num_vps, sizeof(VALUE_PAIR *), attr_cmp);
-
-               packet->vps = NULL;
-               paircursor(&cursor, &packet->vps);
-               for (i = 0; i < num_vps; i++) {
-                       array[i]->next = NULL;
-                       pairinsert(&cursor, array[i]);
-               }
-               talloc_free(array);
-       }
-
        p[0] = 0x35;            /* DHCP-Message-Type */
        p[1] = 1;
        p[2] = packet->code - PW_DHCP_OFFSET;
        p += 3;
 
+
        /*
-        *      Pack in the attributes.
+        *  Pre-sort attributes into contiguous blocks so that fr_dhcp_encode_option
+        *  operates correctly. This changes the order of the list, but never mind...
         */
-       vp = packet->vps;
-       while (vp) {
-               unsigned int num_entries = 1;
-               VALUE_PAIR *same;
-               uint8_t *plength;
-
-               if (vp->da->vendor != DHCP_MAGIC_VENDOR) goto next;
-               if (vp->da->attr == 53) goto next; /* already done */
-               if ((vp->da->attr > 255) &&
-                   (DHCP_BASE_ATTR(vp->da->attr) != PW_DHCP_OPTION_82)) goto next;
-
-               debug_pair(vp);
-               if (vp->da->flags.extended) goto next;
-
-               for (same = paircursor(&cursor, &vp->next);
-                    same;
-                    same = pairnext(&cursor)) {
-                       if (same->da->attr != vp->da->attr) break;
-                       num_entries++;
-               }
-
-               /*
-                *      For client-identifier
-                * @todo What's this meant to be doing?!
-                */
-#if 0
-               if ((vp->da->type == PW_TYPE_ETHERNET) &&
-                   (vp->length == 6) &&
-                   (num_entries == 1)) {
-                       vp->da->type = PW_TYPE_OCTETS;
-                       memmove(vp->vp_octets + 1, vp->vp_octets, 6);
-                       vp->vp_octets[0] = 1;
-               }
-#endif
-               *(p++) = vp->da->attr & 0xff;
-               plength = p;
-               *(p++) = 0;     /* header isn't included in attr length */
-
-               for (i = 0; i < num_entries; i++) {
-                       if (i != 0) debug_pair(vp);
-
-                       if (vp->da->flags.is_tlv) {
-                               VALUE_PAIR *tlv;
-
-                               /*
-                                *      Should NOT have been encoded yet!
-                                */
-                               tlv = fr_dhcp_vp2suboption(packet, vp);
-
-                               /*
-                                *      Ignore it if there's an issue
-                                *      encoding it.
-                                */
-                               if (!tlv) goto next;
-
-                               tlv->next = vp->next;
-                               vp->next = tlv;
-                               vp = tlv;
-                       }
-
-                       length = fr_dhcp_vp2attr(vp, p, 0);
+       pairsort(&packet->vps, fr_dhcp_attr_cmp);
+       fr_cursor_init(&cursor, &packet->vps);
 
-                       /*
-                        *      This will never happen due to FreeRADIUS
-                        *      limitations: sizeof(vp->vp_octets) < 255
-                        */
-                       if (length > 255) {
-                               fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->da->name);
-                               break;
-                       }
-
-                       /*
-                        *      More than one attribute of the same type
-                        *      in a row: they are packed together
-                        *      into the same TLV.  If we overflow,
-                        *      go bananas!
-                        */
-                       if ((*plength + length) > 255) {
-                               fr_strerror_printf("WARNING Ignoring too long attribute %s!", vp->da->name);
-                               break;
-                       }
-
-                       *plength += length;
-                       p += length;
-
-                       if (vp->next &&
-                           (vp->next->da->attr == vp->da->attr))
-                               vp = vp->next;
-               } /* loop over num_entries */
-
-       next:
-               vp = vp->next;
-       }
+       /*
+        *  Each call to fr_dhcp_encode_option will encode one complete DHCP option,
+        *  and sub options.
+        */
+       while ((vp = fr_cursor_current(&cursor))) {
+               len = fr_dhcp_encode_option(p, packet->data_len - (p - packet->data), packet, &cursor);
+               if (len < 0) break;
+               if (len > 0) debug_pair(vp);
+               p += len;
+       };
 
        p[0] = 0xff;            /* end of option option */
        p[1] = 0x00;
@@ -1537,8 +1543,8 @@ int fr_dhcp_add_arp_entry(int fd, char const *interface,
 
        if (!fr_assert(macaddr) ||
            !fr_assert((macaddr->da->type == PW_TYPE_ETHERNET) || (macaddr->da->type == PW_TYPE_OCTETS))) {
-               fr_strerror_printf("Wrong VP type (%s) for chaddr",
-                                  fr_int2str(dict_attr_types, macaddr->da->type, "<invalid>"));
+               fr_strerror_printf("Wrong VP type (%s) for chaddr",
+                                  fr_int2str(dict_attr_types, macaddr->da->type, "<invalid>"));
                return -1;
        }
 
@@ -1563,7 +1569,7 @@ int fr_dhcp_add_arp_entry(int fd, char const *interface,
 
        req.arp_flags = ATF_COM;
        if (ioctl(fd, SIOCSARP, &req) < 0) {
-               fr_strerror_printf("Failed to add entry in ARP cache: %s (%d)", strerror(errno), errno);
+               fr_strerror_printf("Failed to add entry in ARP cache: %s (%d)", fr_syserror(errno), errno);
                return -1;
        }
 
index 5c8b43e..052fb22 100644 (file)
@@ -43,12 +43,12 @@ static int success = 0;
 static int retries = 3;
 static float timeout = 5;
 
-static int server_port = 0;
+static uint16_t server_port = 0;
 static int packet_code = 0;
 static fr_ipaddr_t server_ipaddr;
 
 static fr_ipaddr_t client_ipaddr;
-static int client_port = 0;
+static uint16_t client_port = 0;
 
 static int sockfd;
 
@@ -63,7 +63,7 @@ static RADIUS_PACKET *reply = NULL;
 
 char const *dhcpclient_version = "dhcpclient version " RADIUSD_VERSION_STRING
 #ifdef RADIUSD_VERSION_COMMIT
-" (git #" RADIUSD_VERSION_COMMIT ")"
+" (git #" STRINGIFY(RADIUSD_VERSION_COMMIT) ")"
 #endif
 ", built on " __DATE__ " at " __TIME__;
 
@@ -91,7 +91,7 @@ static int request_init(char const *filename)
        FILE *fp;
        vp_cursor_t cursor;
        VALUE_PAIR *vp;
-       int filedone = 0;
+       bool filedone = false;
 
        /*
         *      Determine where to read the VP's from.
@@ -100,7 +100,7 @@ static int request_init(char const *filename)
                fp = fopen(filename, "r");
                if (!fp) {
                        fprintf(stderr, "dhcpclient: Error opening %s: %s\n",
-                               filename, strerror(errno));
+                               filename, fr_syserror(errno));
                        return 0;
                }
        } else {
@@ -112,8 +112,8 @@ static int request_init(char const *filename)
        /*
         *      Read the VP's.
         */
-       request->vps = readvp2(NULL, fp, &filedone, "dhcpclient:");
-       if (!request->vps) {
+       if (readvp2(&request->vps, NULL, fp, &filedone) < 0) {
+               fr_perror("dhcpclient");
                rad_free(&request);
                if (fp != stdin) fclose(fp);
                return 1;
@@ -122,7 +122,7 @@ static int request_init(char const *filename)
        /*
         *      Fix / set various options
         */
-       for (vp = paircursor(&cursor, &request->vps); vp; vp = pairnext(&cursor)) {
+       for (vp = fr_cursor_init(&cursor, &request->vps); vp; vp = fr_cursor_next(&cursor)) {
                switch (vp->da->attr) {
                default:
                        break;
@@ -340,8 +340,8 @@ int main(int argc, char **argv)
                        portname = NULL;
                }
 
-               if (ip_hton(hostname, AF_INET, &server_ipaddr) < 0) {
-                       fprintf(stderr, "dhcpclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
+               if (ip_hton(&server_ipaddr, AF_INET, hostname, false) < 0) {
+                       fprintf(stderr, "dhcpclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno));
                        exit(1);
                }
 
@@ -425,7 +425,7 @@ int main(int argc, char **argv)
 
        if (fr_dhcp_send(request) < 0) {
                fprintf(stderr, "dhcpclient: failed sending: %s\n",
-                       strerror(errno));
+                       fr_syserror(errno));
                exit(1);
        }
 
index c87a337..7fca72c 100644 (file)
@@ -77,21 +77,21 @@ typedef struct dhcp_socket_t {
 static int dhcprelay_process_client_request(REQUEST *request)
 {
        uint8_t maxhops = 16;
-       VALUE_PAIR *vp, *giaddrvp;
+       VALUE_PAIR *vp, *giaddr;
        dhcp_socket_t *sock;
 
        rad_assert(request->packet->data[0] == 1);
 
        /*
-        * Do the forward by ourselves, do not rely on dhcp_socket_send()
+        *      Do the forward by ourselves, do not rely on dhcp_socket_send()
         */
        request->reply->code = 0;
 
        /*
         * It's invalid to have giaddr=0 AND a relay option
         */
-       giaddrvp = vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
-       if (vp && (vp->vp_ipaddr == htonl(INADDR_ANY)) &&
+       giaddr = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
+       if (giaddr && (giaddr->vp_ipaddr == htonl(INADDR_ANY)) &&
            pairfind(request->packet->vps, 82, DHCP_MAGIC_VENDOR, TAG_ANY)) { /* DHCP-Relay-Agent-Information */
                DEBUG("DHCP: Received packet with giaddr = 0 and containing relay option: Discarding packet\n");
                return 1;
@@ -123,7 +123,7 @@ static int dhcprelay_process_client_request(REQUEST *request)
         */
        /* set SRC ipaddr/port to the listener ipaddr/port */
        request->packet->src_ipaddr.af = AF_INET;
-       request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = giaddrvp->vp_ipaddr;
+       request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = sock->lsock.my_ipaddr.ipaddr.ip4addr.s_addr;
        request->packet->src_port = sock->lsock.my_port;
 
        vp = pairfind(request->config_items, 270, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-To-IP-Address */
@@ -132,6 +132,7 @@ static int dhcprelay_process_client_request(REQUEST *request)
        /* set DEST ipaddr/port to the next server ipaddr/port */
        request->packet->dst_ipaddr.af = AF_INET;
        request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+       request->packet->dst_port = sock->lsock.my_port;
 
        if (fr_dhcp_encode(request->packet) < 0) {
                DEBUG("dhcprelay_process_client_request: ERROR in fr_dhcp_encode\n");
@@ -141,9 +142,14 @@ static int dhcprelay_process_client_request(REQUEST *request)
        return fr_dhcp_send(request->packet);
 }
 
+
+/*
+ *     We've seen a reply from a server.
+ *     i.e. we're a relay.
+ */
 static int dhcprelay_process_server_reply(REQUEST *request)
 {
-       VALUE_PAIR *vp, *giaddrvp;
+       VALUE_PAIR *vp, *giaddr;
        dhcp_socket_t *sock;
 
        rad_assert(request->packet->data[0] == 2);
@@ -158,11 +164,10 @@ static int dhcprelay_process_server_reply(REQUEST *request)
        /*
         * Check that packet is for us.
         */
-       giaddrvp = vp = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
-       rad_assert(vp != NULL);
+       giaddr = pairfind(request->packet->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
 
        /* --with-udpfromto is needed just for the following test */
-       if (!vp || vp->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) {
+       if (!giaddr || giaddr->vp_ipaddr != request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr) {
                DEBUG("DHCP: Packet received from server was not for us (was for 0x%x). Discarding packet",
                    ntohl(request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr));
                return 1;
@@ -170,12 +175,16 @@ static int dhcprelay_process_server_reply(REQUEST *request)
 
        /* set SRC ipaddr/port to the listener ipaddr/port */
        request->packet->src_ipaddr.af = AF_INET;
-       request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = giaddrvp->vp_ipaddr;
        request->packet->src_port = sock->lsock.my_port;
 
        /* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */
        request->packet->dst_ipaddr.af = AF_INET;
-       request->packet->dst_port = request->packet->dst_port + 1; /* Port 68 */
+
+       /*
+        *      We're a relay, and send the reply to giaddr.
+        */
+       request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = giaddr->vp_ipaddr;
+       request->reply->dst_port = request->packet->dst_port;           /* server port */
 
        if ((request->packet->code == PW_DHCP_NAK) ||
            !sock->src_interface ||
@@ -207,7 +216,7 @@ static int dhcprelay_process_server_reply(REQUEST *request)
                } else {
                        vp = pairfind(request->packet->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
                        if (!vp) {
-                               DEBUG("DHCP: Failed to find IP Address for request.");
+                               DEBUG("DHCP: Failed to find IP Address for request");
                                return -1;
                        }
 
@@ -245,13 +254,13 @@ static int dhcprelay_process_server_reply(REQUEST *request)
 #else  /* WITH_UDPFROMTO */
 static int dhcprelay_process_server_reply(UNUSED REQUEST *request)
 {
-       WDEBUG("DHCP Relaying requires the server to be configured with UDPFROMTO");
+       WARN("DHCP Relaying requires the server to be configured with UDPFROMTO");
        return -1;
 }
 
 static int dhcprelay_process_client_request(UNUSED REQUEST *request)
 {
-       WDEBUG("DHCP Relaying requires the server to be configured with UDPFROMTO");
+       WARN("DHCP Relaying requires the server to be configured with UDPFROMTO");
        return -1;
 }
 
@@ -287,7 +296,7 @@ static int dhcp_process(REQUEST *request)
                VALUE_PAIR *relay;
 
                /* DHCP-Relay-IP-Address */
-               relay = radius_paircreate(request, &request->reply->vps,
+               relay = radius_paircreate(request->reply, &request->reply->vps,
                                          272, DHCP_MAGIC_VENDOR);
                if (relay) relay->vp_ipaddr = vp->vp_ipaddr;
        }
@@ -410,12 +419,39 @@ static int dhcp_process(REQUEST *request)
        vp->vp_integer = 2; /* BOOTREPLY */
 
        /*
-        * Prepare the reply packet for sending through dhcp_socket_send()
+        *      Allow NAKs to be delayed for a short period of time.
+        */
+       if (request->reply->code == PW_DHCP_NAK) {
+               vp = pairfind(request->reply->vps, PW_FREERADIUS_RESPONSE_DELAY, 0, TAG_ANY);
+               if (vp) {
+                       if (vp->vp_integer <= 10) {
+                               request->response_delay = vp->vp_integer;
+                       } else {
+                               request->response_delay = 10;
+                       }
+               }
+       }
+
+       /*
+        *      Prepare the reply packet for sending through dhcp_socket_send()
         */
        request->reply->dst_ipaddr.af = AF_INET;
        request->reply->src_ipaddr.af = AF_INET;
        request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = sock->src_ipaddr.ipaddr.ip4addr.s_addr;
 
+       /*
+        *      They didn't set a proper src_ipaddr, but we want to
+        *      send the packet with a source IP.  If there's a server
+        *      identifier, use it.
+        */
+       if (request->reply->src_ipaddr.ipaddr.ip4addr.s_addr == INADDR_ANY) {
+               vp = pairfind(request->reply->vps, 265, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Server-IP-Address */
+               if (!vp) vp = pairfind(request->reply->vps, 54, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-DHCP-Server-Identifier */
+               if (vp) {
+                       request->reply->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+               }
+       }
+
        request->reply->dst_port = request->packet->src_port;
        request->reply->src_port = request->packet->dst_port;
 
@@ -427,7 +463,7 @@ static int dhcp_process(REQUEST *request)
         *      public IP, but the gateway a private one.
         */
        vp = pairfind(request->reply->vps, 272, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Relay-IP-Address */
-       if (vp) {
+       if (vp && (vp->vp_ipaddr != ntohl(INADDR_ANY))) {
                RDEBUG("DHCP: Reply will be unicast to giaddr from original packet");
                request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
                return 1;
@@ -437,11 +473,16 @@ static int dhcp_process(REQUEST *request)
         *      Answer to client's nearest DHCP gateway.  In this
         *      case, the client can reach the gateway, as can the
         *      server.
+        *
+        *      We also use *our* source port as the destination port.
+        *      Gateways are servers, and listen on the server port,
+        *      not the client port.
         */
        vp = pairfind(request->reply->vps, 266, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Gateway-IP-Address */
        if (vp && (vp->vp_ipaddr != htonl(INADDR_ANY))) {
                RDEBUG("DHCP: Reply will be unicast to giaddr");
                request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
+               request->reply->dst_port = request->packet->dst_port;
                return 1;
        }
 
@@ -481,7 +522,12 @@ static int dhcp_process(REQUEST *request)
 
        vp = pairfind(request->reply->vps, 264, DHCP_MAGIC_VENDOR, TAG_ANY); /* DHCP-Your-IP-Address */
        if (!vp) {
-               DEBUG("DHCP: Failed to find DHCP-Your-IP-Address for request.");
+               RDEBUG("DHCP: Failed to find DHCP-Client-IP-Address or DHCP-Your-IP-Address for request; "
+                      "not responding");
+               /*
+                *      There is nowhere to send the response to, so don't bother.
+                */
+               request->reply->code = 0;
                return -1;
        }
 
@@ -526,8 +572,13 @@ static int dhcp_process(REQUEST *request)
                }
        }
 #else
-       RDEBUG("DHCP: Reply will be broadcast as this system does not support ARP updates");
-       request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
+       if (request->packet->src_ipaddr.ipaddr.ip4addr.s_addr != ntohl(INADDR_NONE)) {
+               RDEBUG("DHCP: Request will be unicast to the unicast source IP address");
+               request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+       } else {
+               RDEBUG("DHCP: Reply will be broadcast as this system does not support ARP updates");
+               request->reply->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_BROADCAST);
+       }
 #endif
 
        return 1;
@@ -549,7 +600,7 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        sock = this->data;
 
        if (!sock->lsock.interface) {
-               WDEBUG("No \"interface\" setting is defined.  Only unicast DHCP will work.");
+               WARN("No \"interface\" setting is defined.  Only unicast DHCP will work");
        }
 
        /*
@@ -566,14 +617,14 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        if (broadcast) {
                if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
                        ERROR("Can't set broadcast option: %s\n",
-                              strerror(errno));
+                              fr_syserror(errno));
                        return -1;
                }
        }
 
        if (setsockopt(this->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
                ERROR("Can't set re-use addres option: %s\n",
-                      strerror(errno));
+                      fr_syserror(errno));
                return -1;
        }
 
@@ -584,28 +635,25 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        sock->suppress_responses = false;
        cp = cf_pair_find(cs, "suppress_responses");
        if (cp) {
-               cf_item_parse(cs, "suppress_responses", PW_TYPE_BOOLEAN,
-                             &sock->suppress_responses, NULL);
+               cf_item_parse(cs, "suppress_responses", FR_ITEM_POINTER(PW_TYPE_BOOLEAN, &sock->suppress_responses), NULL);
        }
 
        cp = cf_pair_find(cs, "src_interface");
        if (cp) {
-               cf_item_parse(cs, "src_interface", PW_TYPE_STRING_PTR,
-                             &sock->src_interface, NULL);
+               cf_item_parse(cs, "src_interface", FR_ITEM_POINTER(PW_TYPE_STRING, &sock->src_interface), NULL);
        } else {
                sock->src_interface = sock->lsock.interface;
        }
 
        if (!sock->src_interface && sock->lsock.interface) {
-               sock->src_interface = talloc_strdup(sock, sock->lsock.interface);
+               sock->src_interface = talloc_typed_strdup(sock, sock->lsock.interface);
        }
 
        cp = cf_pair_find(cs, "src_ipaddr");
        if (cp) {
                memset(&sock->src_ipaddr, 0, sizeof(sock->src_ipaddr));
                sock->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_NONE);
-               rcode = cf_item_parse(cs, "src_ipaddr", PW_TYPE_IPADDR,
-                                     &sock->src_ipaddr.ipaddr.ip4addr, NULL);
+               rcode = cf_item_parse(cs, "src_ipaddr", FR_ITEM_POINTER(PW_TYPE_IPV4_ADDR, &sock->src_ipaddr), NULL);
                if (rcode < 0) return -1;
 
                sock->src_ipaddr.af = AF_INET;
@@ -619,11 +667,11 @@ static int dhcp_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
        client = &sock->dhcp_client;
        memset(client, 0, sizeof(*client));
        client->ipaddr.af = AF_INET;
-       client->ipaddr.ipaddr.ip4addr.s_addr = INADDR_NONE;
-       client->prefix = 0;
+       client->ipaddr.ipaddr.ip4addr.s_addr = ntohl(INADDR_NONE);
+       client->ipaddr.prefix = 0;
        client->longname = client->shortname = "dhcp";
        client->secret = client->shortname;
-       client->nas_type = talloc_strdup(sock, "none");
+       client->nas_type = talloc_typed_strdup(sock, "none");
 
        return 0;
 }
index 24de684..57fc650 100644 (file)
@@ -46,7 +46,7 @@ typedef struct rlm_dhcp_t {
  *     Allow single attribute values to be retrieved from the dhcp.
  */
 static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
-                                char const *fmt, char *out, size_t freespace)
+                                char const *fmt, char *out, size_t freespace)
 {
        vp_cursor_t cursor;
        VALUE_PAIR *vp, *head = NULL;
@@ -54,7 +54,6 @@ static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
 
        while (isspace((int) *fmt)) fmt++;
 
-
        if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
                 *out = '\0';
                 return 0;
@@ -62,15 +61,15 @@ static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
 
        if ((fr_dhcp_decode_options(request->packet,
                                    vp->vp_octets, vp->length, &head) < 0) || (!head)) {
-               RWDEBUG("DHCP option decoding failed");
+               RWDEBUG("DHCP option decoding failed: %s", fr_strerror());
                *out = '\0';
                return -1;
        }
 
 
-       for (vp = paircursor(&cursor, &head);
+       for (vp = fr_cursor_init(&cursor, &head);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                decoded++;
        }
 
@@ -84,6 +83,38 @@ static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
        return strlen(out);
 }
 
+static ssize_t dhcp_xlat(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
+{
+       vp_cursor_t cursor;
+       VALUE_PAIR *vp;
+       uint8_t binbuf[255];
+       ssize_t len;
+
+       while (isspace((int) *fmt)) fmt++;
+
+       if ((radius_copy_vp(&vp, request, fmt) < 0) || !vp) {
+                *out = '\0';
+                return 0;
+       }
+       fr_cursor_init(&cursor, &vp);
+
+       len = fr_dhcp_encode_option(binbuf, sizeof(binbuf), request, &cursor);
+       talloc_free(vp);
+       if (len <= 0) {
+               REDEBUG("DHCP option encoding failed: %s", fr_strerror());
+
+               return -1;
+       }
+
+       if ((size_t)((len * 2) + 1) > freespace) {
+               REDEBUG("DHCP option encoding failed: Output buffer exhausted, needed %zd bytes, have %zd bytes",
+                       (len * 2) + 1, freespace);
+
+               return -1;
+       }
+
+       return fr_bin2hex(out, binbuf, len);
+}
 
 /*
  *     Only free memory we allocated.  The strings allocated via
@@ -92,6 +123,7 @@ static ssize_t dhcp_options_xlat(UNUSED void *instance, REQUEST *request,
 static int mod_detach(void *instance)
 {
        xlat_unregister("dhcp_options", dhcp_options_xlat, instance);
+       xlat_unregister("dhcp", dhcp_xlat, instance);
        return 0;
 }
 
@@ -104,6 +136,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
        rlm_dhcp_t *inst = instance;
 
        xlat_register("dhcp_options", dhcp_options_xlat, NULL, inst);
+       xlat_register("dhcp", dhcp_xlat, NULL, inst);
 
        return 0;
 }
index 17f33d9..1b34ce2 100644 (file)
@@ -37,7 +37,7 @@ static int vmps_process(REQUEST *request)
        process_post_auth(0, request);
        DEBUG2("Done VMPS");
 
-       request->reply->code = PW_AUTHENTICATION_ACK;
+       request->reply->code = PW_CODE_AUTHENTICATION_ACK;
 
        return 0;
 }
index ae59126..64cecab 100644 (file)
@@ -31,7 +31,6 @@ RCSID("$Id$");
 
 /* @todo: this is a hack */
 #  define DEBUG                        if (fr_debug_flag && fr_log_fp) fr_printf_log
-void fr_strerror_printf(char const *fmt, ...);
 #  define debug_pair(vp)       do { if (fr_debug_flag && fr_log_fp) { \
                                        vp_print(fr_log_fp, vp); \
                                     } \
@@ -87,7 +86,7 @@ static int vqp_sendto(int sockfd, void *data, size_t data_len, int flags,
                      UNUSED fr_ipaddr_t *src_ipaddr,
 #endif
                      fr_ipaddr_t *dst_ipaddr,
-                     int dst_port)
+                     uint16_t dst_port)
 {
        struct sockaddr_storage dst;
        socklen_t               sizeof_dst;
@@ -144,7 +143,7 @@ static ssize_t vqp_recvfrom(int sockfd, RADIUS_PACKET *packet, int flags,
        ssize_t                 data_len;
        uint8_t                 header[4];
        size_t                  len;
-       int                     port;
+       uint16_t                port;
 
        memset(&src, 0, sizeof_src);
        memset(&dst, 0, sizeof_dst);
@@ -293,7 +292,7 @@ RADIUS_PACKET *vqp_recv(int sockfd)
         *      Check for socket errors.
         */
        if (length < 0) {
-               fr_strerror_printf("Error receiving packet: %s", strerror(errno));
+               fr_strerror_printf("Error receiving packet: %s", fr_syserror(errno));
                /* packet->data is NULL */
                rad_free(&packet);
                return NULL;
@@ -385,7 +384,7 @@ RADIUS_PACKET *vqp_recv(int sockfd)
        /*
         *      This is more than a bit of a hack.
         */
-       packet->code = PW_AUTHENTICATION_REQUEST;
+       packet->code = PW_CODE_AUTHENTICATION_REQUEST;
 
        memcpy(&id, packet->data + 4, 4);
        packet->id = ntohl(id);
@@ -433,7 +432,7 @@ int vqp_decode(RADIUS_PACKET *packet)
 
        if (packet->data_len < VQP_HDR_LEN) return -1;
 
-       paircursor(&cursor, &packet->vps);
+       fr_cursor_init(&cursor, &packet->vps);
        vp = paircreate(packet, PW_VQP_PACKET_TYPE, 0);
        if (!vp) {
                fr_strerror_printf("No memory");
@@ -441,7 +440,7 @@ int vqp_decode(RADIUS_PACKET *packet)
        }
        vp->vp_integer = packet->data[1];
        debug_pair(vp);
-       pairinsert(&cursor, vp);
+       fr_cursor_insert(&cursor, vp);
 
        vp = paircreate(packet, PW_VQP_ERROR_CODE, 0);
        if (!vp) {
@@ -450,7 +449,7 @@ int vqp_decode(RADIUS_PACKET *packet)
        }
        vp->vp_integer = packet->data[2];
        debug_pair(vp);
-       pairinsert(&cursor, vp);
+       fr_cursor_insert(&cursor, vp);
 
        vp = paircreate(packet, PW_VQP_SEQUENCE_NUMBER, 0);
        if (!vp) {
@@ -459,7 +458,7 @@ int vqp_decode(RADIUS_PACKET *packet)
        }
        vp->vp_integer = packet->id; /* already set by vqp_recv */
        debug_pair(vp);
-       pairinsert(&cursor, vp);
+       fr_cursor_insert(&cursor, vp);
 
        ptr = packet->data + VQP_HDR_LEN;
        end = packet->data + packet->data_len;
@@ -489,7 +488,7 @@ int vqp_decode(RADIUS_PACKET *packet)
                }
 
                switch (vp->da->type) {
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        if (length == 4) {
                                memcpy(&vp->vp_ipaddr, ptr, 4);
                                vp->length = 4;
@@ -507,20 +506,32 @@ int vqp_decode(RADIUS_PACKET *packet)
 
                default:
                case PW_TYPE_OCTETS:
-                       pairmemcpy(vp, ptr, length);
+                       if (length < 1024) {
+                               pairmemcpy(vp, ptr, length);
+                       } else {
+                               pairmemcpy(vp, ptr, 1024);
+                       }
                        break;
 
                case PW_TYPE_STRING:
-                       vp->length = length;
-                       vp->vp_strvalue = p = talloc_array(vp, char, vp->length + 1);
-                       vp->type = VT_DATA;
-                       memcpy(p, ptr, vp->length);
-                       p[vp->length] = '\0';
+                       if (length < 1024) {
+                               vp->length = length;
+                               vp->vp_strvalue = p = talloc_array(vp, char, vp->length + 1);
+                               vp->type = VT_DATA;
+                               memcpy(p, ptr, vp->length);
+                               p[vp->length] = '\0';
+                       } else {
+                               vp->length = 1024;
+                               vp->vp_strvalue = p = talloc_array(vp, char, 1025);
+                               vp->type = VT_DATA;
+                               memcpy(p, ptr, vp->length);
+                               p[vp->length] = '\0';
+                       }
                        break;
                }
                ptr += length;
                debug_pair(vp);
-               pairinsert(&cursor, vp);
+               fr_cursor_insert(&cursor, vp);
        }
 
        /*
@@ -680,7 +691,7 @@ int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original)
 
                /* Data */
                switch (vp->da->type) {
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        memcpy(ptr, &vp->vp_ipaddr, 4);
                        break;
 
index 979dcd5..7f5a26b 100644 (file)
@@ -32,9 +32,9 @@ RCSID("$Id$")
  *     going to return.
  */
 typedef struct rlm_always_t {
-       char            *rcode_str;
+       char const      *rcode_str;
        rlm_rcode_t     rcode;
-       int             simulcount;
+       uint32_t        simulcount;
        bool            mpp;
 } rlm_always_t;
 
@@ -42,12 +42,9 @@ typedef struct rlm_always_t {
  *     A mapping of configuration file names to internal variables.
  */
 static const CONF_PARSER module_config[] = {
-  { "rcode",      PW_TYPE_STRING_PTR, offsetof(rlm_always_t,rcode_str),
-    NULL, "fail" },
-  { "simulcount", PW_TYPE_INTEGER,    offsetof(rlm_always_t,simulcount),
-    NULL, "0" },
-  { "mpp",     PW_TYPE_BOOLEAN,    offsetof(rlm_always_t,mpp),
-    NULL, "no" },
+  { "rcode", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_always_t, rcode_str), "fail" },
+  { "simulcount", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_always_t, simulcount), "0" },
+  { "mpp", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_always_t, mpp), "no" },
 
   { NULL, -1, 0, NULL, NULL }          /* end the list */
 };
@@ -97,7 +94,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
  *     Just return the rcode ... this function is autz, auth, acct, and
  *     preacct!
  */
-static rlm_rcode_t always_return(void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_always_return(void *instance, UNUSED REQUEST *request)
 {
        return ((struct rlm_always_t *)instance)->rcode;
 }
@@ -106,7 +103,7 @@ static rlm_rcode_t always_return(void *instance, UNUSED REQUEST *request)
 /*
  *     checksimul fakes some other variables besides the rcode...
  */
-static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST *request)
 {
        struct rlm_always_t *inst = instance;
 
@@ -122,28 +119,28 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
 module_t rlm_always = {
        RLM_MODULE_INIT,
        "always",
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
        sizeof(rlm_always_t),           /* config size */
        module_config,                  /* configuration */
        mod_instantiate,                /* instantiation */
        NULL,                           /* detach */
        {
-               always_return,          /* authentication */
-               always_return,          /* authorization */
-               always_return,          /* preaccounting */
-               always_return,          /* accounting */
+               mod_always_return,              /* authentication */
+               mod_always_return,              /* authorization */
+               mod_always_return,              /* preaccounting */
+               mod_always_return,              /* accounting */
 #ifdef WITH_SESSION_MGMT
                mod_checksimul, /* checksimul */
 #else
                NULL,
 #endif
-               always_return,          /* pre-proxy */
-               always_return,          /* post-proxy */
-               always_return           /* post-auth */
+               mod_always_return,              /* pre-proxy */
+               mod_always_return,              /* post-proxy */
+               mod_always_return               /* post-auth */
 #ifdef WITH_COA
                ,
-               always_return,          /* recv-coa */
-               always_return           /* send-coa */
+               mod_always_return,              /* recv-coa */
+               mod_always_return               /* send-coa */
 #endif
        },
 };
index e153cd1..f910741 100644 (file)
@@ -31,50 +31,54 @@ RCSID("$Id$")
 
 #include       <ctype.h>
 #include       <fcntl.h>
-#include       <limits.h>
-
 
 /*
  *     Define a structure with the module configuration, so it can
  *     be used as the instance handle.
  */
 typedef struct rlm_attr_filter {
-       char            *filename;
-       char            *key;
+       char const      *filename;
+       char const      *key;
        bool            relaxed;
        PAIR_LIST       *attrs;
 } rlm_attr_filter_t;
 
 static const CONF_PARSER module_config[] = {
-       { "attrsfile",     PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED,
-         offsetof(rlm_attr_filter_t, filename), NULL, NULL},
-       { "filename",     PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED,
-         offsetof(rlm_attr_filter_t, filename), NULL, NULL},
-       { "key",     PW_TYPE_STRING_PTR,
-         offsetof(rlm_attr_filter_t, key), NULL, "%{Realm}" },
-       { "relaxed",    PW_TYPE_BOOLEAN,
-         offsetof(rlm_attr_filter_t, relaxed), NULL, "no" },
+       { "attrsfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_attr_filter_t, filename), NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, rlm_attr_filter_t, filename), NULL },
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_attr_filter_t, key), "%{Realm}" },
+       { "relaxed", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_attr_filter_t, relaxed), "no" },
        { NULL, -1, 0, NULL, NULL }
 };
 
-static void check_pair(VALUE_PAIR *check_item, VALUE_PAIR *reply_item,
-                     int *pass, int *fail)
+static void check_pair(REQUEST *request, VALUE_PAIR *check_item, VALUE_PAIR *reply_item, int *pass, int *fail)
 {
        int compare;
 
        if (check_item->op == T_OP_SET) return;
 
        compare = paircmp(check_item, reply_item);
+       if (compare < 0) {
+               REDEBUG("Comparison failed: %s", fr_strerror());
+       }
+
        if (compare == 1) {
                ++*(pass);
        } else {
                ++*(fail);
        }
 
+       if (RDEBUG_ENABLED3) {
+               char rule[1024], pair[1024];
+
+               vp_prints(rule, sizeof(rule), check_item);
+               vp_prints(pair, sizeof(pair), reply_item);
+               RDEBUG3("%s %s %s", pair, compare == 1 ? "allowed by" : "disallowed by", rule);
+       }
+
        return;
 }
 
-
 static int attr_filter_getfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list)
 {
        vp_cursor_t cursor;
@@ -97,9 +101,9 @@ static int attr_filter_getfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST
                entry->check = entry->reply;
                entry->reply = NULL;
 
-               for (vp = paircursor(&cursor, &entry->check);
+               for (vp = fr_cursor_init(&cursor, &entry->check);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                    /*
                     * If it's NOT a vendor attribute,
                     * and it's NOT a wire protocol
@@ -107,9 +111,8 @@ static int attr_filter_getfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST
                     * then bitch about it, giving a good warning message.
                     */
                     if ((vp->da->vendor == 0) &&
-                        (vp->da->attr > 0xff) &&
                         (vp->da->attr > 1000)) {
-                       WDEBUG("[%s]:%d Check item \"%s\"\n\tfound in filter list for realm \"%s\".\n",
+                       WARN("[%s]:%d Check item \"%s\"\n\tfound in filter list for realm \"%s\".\n",
                               filename, entry->lineno, vp->da->name, entry->name);
                    }
                }
@@ -144,7 +147,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
 /*
  *     Common attr_filter checks
  */
-static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_PACKET *packet)
+static rlm_rcode_t CC_HINT(nonnull(1,2)) attr_filter_common(void *instance, REQUEST *request, RADIUS_PACKET *packet)
 {
        rlm_attr_filter_t *inst = instance;
        VALUE_PAIR      *vp;
@@ -158,8 +161,6 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
 
        if (!packet) return RLM_MODULE_NOOP;
 
-       rad_assert(request != NULL);
-
        if (!inst->key) {
                VALUE_PAIR      *namepair;
 
@@ -185,7 +186,7 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
         *      Head of the output list
         */
        output = NULL;
-       paircursor(&out, &output);
+       fr_cursor_init(&out, &output);
 
        /*
         *      Find the attr_filter profile entry for the entry.
@@ -207,17 +208,16 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
                RDEBUG2("Matched entry %s at line %d", pl->name, pl->lineno);
                found = 1;
 
-               for (check_item = paircursor(&check, &pl->check);
+               for (check_item = fr_cursor_init(&check, &pl->check);
                     check_item;
-                    check_item = pairnext(&check)) {
+                    check_item = fr_cursor_next(&check)) {
                        if (!check_item->da->vendor &&
                            (check_item->da->attr == PW_FALL_THROUGH) &&
                                (check_item->vp_integer == 1)) {
                                fall_through = 1;
                                continue;
                        }
-                       else if (!check_item->da->vendor &&
-                                check_item->da->attr == PW_RELAX_FILTER) {
+                       else if (!check_item->da->vendor && check_item->da->attr == PW_RELAX_FILTER) {
                                relax_filter = check_item->vp_integer;
                                continue;
                        }
@@ -232,7 +232,7 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
                                        goto error;
                                }
                                radius_xlat_do(request, vp);
-                               pairinsert(&out, vp);
+                               fr_cursor_insert(&out, vp);
                        }
                }
 
@@ -244,23 +244,20 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
                 *      only if it matches all rules that describe an
                 *      Idle-Timeout.
                 */
-               for (input_item = paircursor(&input, &packet->vps);
+               for (input_item = fr_cursor_init(&input, &packet->vps);
                     input_item;
-                    input_item = pairnext(&input)) {
-                       /* reset the pass,fail vars for each reply item */
-                       pass = fail = 0;
+                    input_item = fr_cursor_next(&input)) {
+                       pass = fail = 0; /* reset the pass,fail vars for each reply item */
 
                        /*
-                        *      reset the check_item pointer to
-                        *      beginning of the list
+                        *  Reset the check_item pointer to beginning of the list
                         */
-                       for (check_item = pairfirst(&check);
+                       for (check_item = fr_cursor_first(&check);
                             check_item;
-                            check_item = pairnext(&check)) {
+                            check_item = fr_cursor_next(&check)) {
                                /*
-                                *      Vendor-Specific is special, and
-                                *      matches any VSA if the comparison
-                                *      is always true.
+                                *  Vendor-Specific is special, and matches any VSA if the
+                                *  comparison is always true.
                                 */
                                if ((check_item->da->attr == PW_VENDOR_SPECIFIC) && (input_item->da->vendor != 0) &&
                                    (check_item->op == T_OP_CMP_TRUE)) {
@@ -268,25 +265,26 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
                                        continue;
                                }
 
-                               if (input_item->da->attr == check_item->da->attr) {
-                                       check_pair(check_item, input_item, &pass, &fail);
+                               if (input_item->da == check_item->da) {
+                                       check_pair(request, check_item, input_item, &pass, &fail);
                                }
                        }
 
+                       RDEBUG3("Attribute \"%s\" allowed by %i rules, disallowed by %i rules",
+                               input_item->da->name, pass, fail);
                        /*
-                        *  Only move attribute if it passed all rules,
-                        *  or if the config says we should copy unmatched
-                        *  attributes ('relaxed' mode).
+                        *  Only move attribute if it passed all rules, or if the config says we
+                        *  should copy unmatched attributes ('relaxed' mode).
                         */
                        if (fail == 0 && (pass > 0 || relax_filter)) {
                                if (!pass) {
-                                       RDEBUG3("Attribute (%s) allowed by relaxed mode", input_item->da->name);
+                                       RDEBUG3("Attribute \"%s\" allowed by relaxed mode", input_item->da->name);
                                }
                                vp = paircopyvp(packet, input_item);
                                if (!vp) {
                                        goto error;
                                }
-                               pairinsert(&out, vp);
+                               fr_cursor_insert(&out, vp);
                        }
                }
 
@@ -310,10 +308,11 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
        pairfree(&packet->vps);
        packet->vps = output;
 
-       if (request->packet->code == PW_AUTHENTICATION_REQUEST) {
+       if (request->packet->code == PW_CODE_AUTHENTICATION_REQUEST) {
                request->username = pairfind(request->packet->vps, PW_STRIPPED_USER_NAME, 0, TAG_ANY);
-               if (!request->username)
+               if (!request->username) {
                        request->username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+               }
                request->password = pairfind(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
        }
 
@@ -324,7 +323,7 @@ static rlm_rcode_t attr_filter_common(void *instance, REQUEST *request, RADIUS_P
        return RLM_MODULE_FAIL;
 }
 
-#define RLM_AF_FUNC(_x, _y) static rlm_rcode_t mod_##_x(void *instance, REQUEST *request) \
+#define RLM_AF_FUNC(_x, _y) static rlm_rcode_t CC_HINT(nonnull) mod_##_x(void *instance, REQUEST *request) \
                        { \
                                return attr_filter_common(instance, request, request->_y); \
                        }
@@ -349,17 +348,17 @@ RLM_AF_FUNC(send_coa, reply)
 module_t rlm_attr_filter = {
        RLM_MODULE_INIT,
        "attr_filter",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(rlm_attr_filter_t),
        module_config,
        mod_instantiate,        /* instantiation */
        NULL,                   /* detach */
        {
-               NULL,                   /* authentication */
+               NULL,           /* authentication */
                mod_authorize,  /* authorization */
                mod_preacct,    /* pre-acct */
                mod_accounting, /* accounting */
-               NULL,                   /* checksimul */
+               NULL,           /* checksimul */
 #ifdef WITH_PROXY
                mod_pre_proxy,  /* pre-proxy */
                mod_post_proxy, /* post-proxy */
index 127c080..cbe2437 100644 (file)
@@ -31,6 +31,7 @@ RCSID("$Id$")
 #define PW_CACHE_STATUS_ONLY   1141
 #define PW_CACHE_MERGE         1142
 #define PW_CACHE_ENTRY_HITS    1143
+#define PW_CACHE_READ_ONLY     1144
 
 /*
  *     Define a structure for our module configuration.
@@ -41,10 +42,10 @@ RCSID("$Id$")
  */
 typedef struct rlm_cache_t {
        char const              *xlat_name;
-       char                    *key;
-       int                     ttl;
-       int                     max_entries;
-       int                     epoch;
+       char const              *key;
+       uint32_t                ttl;
+       uint32_t                max_entries;
+       int32_t                 epoch;
        bool                    stats;
        CONF_SECTION            *cs;
        rbtree_t                *cache;
@@ -79,6 +80,28 @@ typedef struct rlm_cache_entry_t {
 #define MAX_ATTRMAP    128
 
 /*
+ *     A mapping of configuration file names to internal variables.
+ *
+ *     Note that the string is dynamically allocated, so it MUST
+ *     be freed.  When the configuration file parse re-reads the string,
+ *     it free's the old one, and strdup's the new one, placing the pointer
+ *     to the strdup'd string into 'config.string'.  This gets around
+ *     buffer over-flows.
+ */
+static const CONF_PARSER module_config[] = {
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_cache_t, key), NULL },
+       { "ttl", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_cache_t, ttl), "500" },
+       { "max_entries", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_cache_t, max_entries), "16384" },
+
+       /* Should be a type which matches time_t, @fixme before 2038 */
+       { "epoch", FR_CONF_OFFSET(PW_TYPE_SIGNED, rlm_cache_t, epoch), "0" },
+       { "add_stats", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_cache_t, stats), "no" },
+
+       { NULL, -1, 0, NULL, NULL }             /* end the list */
+};
+
+
+/*
  *     Compare two entries by key.  There may only be one entry with
  *     the same key.
  */
@@ -119,14 +142,10 @@ static int cache_heap_cmp(void const *one, void const *two)
 /*
  *     Merge a cached entry into a REQUEST.
  */
-static void cache_merge(rlm_cache_t *inst, REQUEST *request,
-                       rlm_cache_entry_t *c)
+static void CC_HINT(nonnull) cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c)
 {
        VALUE_PAIR *vp;
 
-       rad_assert(request != NULL);
-       rad_assert(c != NULL);
-
        vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
        if (vp && (vp->vp_integer == 0)) {
                RDEBUG2("Told not to merge entry into request");
@@ -221,12 +240,13 @@ static rlm_cache_entry_t *cache_find(rlm_cache_t *inst, REQUEST *request,
        /*
         *      Update the expiry time based on the TTL.
         *      A TTL of 0 means "delete from the cache".
+        *      A TTL < 0 means "delete from the cache and recreate the entry".
         */
        vp = pairfind(request->config_items, PW_CACHE_TTL, 0, TAG_ANY);
        if (vp) {
-               if (vp->vp_integer == 0) goto delete;
+               if (vp->vp_signed <= 0) goto delete;
 
-               ttl = vp->vp_integer;
+               ttl = vp->vp_signed;
                c->expires = request->timestamp + ttl;
                RDEBUG("Adding %d to the TTL", ttl);
        }
@@ -236,23 +256,36 @@ static rlm_cache_entry_t *cache_find(rlm_cache_t *inst, REQUEST *request,
 }
 
 
+/** Callback for radius_map2request
+ *
+ * Simplifies merging VALUE_PAIRs into the current request.
+ */
+static int _cache_add(VALUE_PAIR **out, REQUEST *request, UNUSED value_pair_map_t const *map, void *ctx)
+{
+       VALUE_PAIR *vp;
+
+       vp = talloc_get_type_abort(ctx, VALUE_PAIR);
+       /* radius_map2request will reparent */
+       *out = paircopy(request, vp);
+
+       if (!*out) return -1;
+       return 0;
+}
+
 /*
  *     Add an entry to the cache.
  */
-static rlm_cache_entry_t *cache_add(rlm_cache_t *inst, REQUEST *request,
-                                   char const *key)
+static rlm_cache_entry_t *cache_add(rlm_cache_t *inst, REQUEST *request, char const *key)
 {
        int ttl;
-       VALUE_PAIR *vp, *found, **to_req, **to_cache, **from;
-       DICT_ATTR const *da;
+       VALUE_PAIR *vp, *to_cache;
+       vp_cursor_t src_list, cached_request, cached_reply, cached_control;
 
-       int merge = true;
-       REQUEST *context;
+       bool merge = true;
 
        value_pair_map_t const *map;
 
        rlm_cache_entry_t *c;
-       char buffer[1024];
 
        if (rbtree_num_elements(inst->cache) >= inst->max_entries) {
                RDEBUG("Cache is full: %d entries", inst->max_entries);
@@ -263,20 +296,16 @@ static rlm_cache_entry_t *cache_add(rlm_cache_t *inst, REQUEST *request,
         *      TTL of 0 means "don't cache this entry"
         */
        vp = pairfind(request->config_items, PW_CACHE_TTL, 0, TAG_ANY);
-       if (vp && (vp->vp_integer == 0)) return NULL;
+       if (vp && (vp->vp_signed == 0)) return NULL;
 
        c = talloc_zero(inst, rlm_cache_entry_t);
-       c->key = talloc_strdup(c, key);
+       c->key = talloc_typed_strdup(c, key);
        c->created = c->expires = request->timestamp;
 
        /*
-        *      Use per-entry TTL, or globally defined one.
+        *      Use per-entry TTL if > 0, or globally defined one.
         */
-       if (vp) {
-               ttl = vp->vp_integer;
-       } else {
-               ttl = inst->ttl;
-       }
+       ttl = vp && (vp->vp_signed > 0) ? vp->vp_integer : inst->ttl;
        c->expires += ttl;
 
        RDEBUG("Creating entry for \"%s\"", key);
@@ -290,248 +319,100 @@ static rlm_cache_entry_t *cache_add(rlm_cache_t *inst, REQUEST *request,
                RDEBUG2("Told not to merge new entry into request");
        }
 
-       for (map = inst->maps; map != NULL; map = map->next) {
-               rad_assert(map->dst && map->src);
+       fr_cursor_init(&cached_request, &c->packet);
+       fr_cursor_init(&cached_reply, &c->reply);
+       fr_cursor_init(&cached_control, &c->control);
 
-               /*
-                *      Specifying inner/outer request doesn't work here
-                *      but there's no easy fix...
-                */
-               switch (map->dst->list) {
-               case PAIR_LIST_REQUEST:
-                       to_cache = &c->packet;
-                       break;
-
-               case PAIR_LIST_REPLY:
-                       to_cache = &c->reply;
-                       break;
+       for (map = inst->maps; map != NULL; map = map->next) {
+               bool do_merge = merge;
 
-               case PAIR_LIST_CONTROL:
-                       to_cache = &c->control;
-                       break;
+               rad_assert(map->dst && map->src);
 
-               default:
-                       rad_assert(0);
-                       return NULL;
+               if (radius_map2vp(&to_cache, request, map, NULL) < 0) {
+                       RDEBUG("Skipping %s", map->src->name);
+                       continue;
                }
 
                /*
-                *      Resolve the destination in the current request.
-                *      We need to add the to_cache there too if any of these
-                *      are.
-                *      true :
+                *      Merge attributes into the current request if:
                 *        - Map specifies an xlat'd string.
                 *        - Map specifies a literal string.
+                *        - Map specifies an exec.
                 *        - Map src and dst lists differ.
                 *        - Map src and dst attributes differ
+                *
+                *       Unless Cache-Merge = no
                 */
-               to_req = NULL;
-               if (merge && ( !map->src->da ||
-                   (map->src->list != map->dst->list) ||
-                   (map->src->da != map->dst->da))) {
-                       context = request;
-                       /*
-                        *      It's ok if the list isn't valid here...
-                        *      It might be valid later when we merge
-                        *      the cache entry.
-                        */
-                       if (radius_request(&context, map->dst->request) == 0) {
-                               to_req = radius_list(context, map->dst->list);
-                       }
-               }
-
-               /*
-                *      We infer that src was an attribute ref from the fact
-                *      it contains a da.
-                */
-               RDEBUG4(":: dst is \"%s\" src is \"%s\"",
-                       fr_int2str(vpt_types, map->dst->type, "<INVALID>"),
-                       fr_int2str(vpt_types, map->src->type, "<INVALID>"));
-
-               switch (map->src->type) {
-               case VPT_TYPE_ATTR:
-                       {
-                               vp_cursor_t cursor;
-
-                               from = NULL;
-                               da = map->src->da;
-                               context = request;
-                               if (radius_request(&context, map->src->request) == 0) {
-                                       from = radius_list(context, map->src->list);
-                               }
-
-                               /*
-                                *      Can't add the attribute if the list isn't
-                                *      valid.
-                                */
-                               if (!from) continue;
-
-                               paircursor(&cursor, from);
-                               found = pairfindnext(&cursor, da->attr, da->vendor, TAG_ANY);
-                               if (!found) {
-                                       RWDEBUG("\"%s\" not found, skipping",
-                                              map->src->name);
-                                       continue;
-                               }
+               if (do_merge) switch (map->src->type) {
+               case VPT_TYPE_LITERAL:
+               case VPT_TYPE_XLAT:
+               case VPT_TYPE_EXEC:
+                       break;
 
-                               RDEBUG("\t%s %s %s", map->dst->name,
-                                      fr_int2str(fr_tokens, map->op, "<INVALID>"),
-                                      map->src->name);
-
-                               switch (map->op) {
-                               case T_OP_SET:
-                               case T_OP_EQ:
-                               case T_OP_SUB:
-                                       vp = map->dst->type == VPT_TYPE_LIST ?
-                                               paircopyvp(c, found) :
-                                               paircopyvpdata(c, map->dst->da, found);
-
-                                       if (!vp) continue;
-
-                                       pairadd(to_cache, vp);
-
-                                       if (to_req) {
-                                               vp = paircopyvp(request, vp);
-                                               radius_pairmove(request, to_req, vp);
-                                       }
-
-                                       break;
-                               case T_OP_ADD:
-                                       do {
-                                               vp = map->dst->type == VPT_TYPE_LIST ?
-                                                       paircopyvp(c, found) :
-                                                       paircopyvpdata(c, map->dst->da, found);
-                                               if (!vp) continue;
-
-                                               vp->op = map->op;
-                                               pairadd(to_cache, vp);
-
-                                               if (to_req) {
-                                                       vp = paircopyvp(request, vp);
-                                                       radius_pairmove(request, to_req, vp);
-
-                                               }
-                                       } while ((found = pairfindnext(&cursor, da->attr, da->vendor, TAG_ANY)));
-                                       break;
-
-                               default:
-                                       rad_assert(0);
-                                       return NULL;
-                               }
-                               break;
-                       }
                case VPT_TYPE_LIST:
-                       {
-                               vp_cursor_t in, out;
-                               VALUE_PAIR *i;
-
-                               rad_assert(map->src->type == VPT_TYPE_LIST);
+                       if (map->src->vpt_list == map->dst->vpt_list) do_merge = false;
+                       break;
 
-                               from = NULL;
-                               context = request;
-                               if (radius_request(&context, map->src->request) == 0) {
-                                       from = radius_list(context, map->src->list);
-                               }
-                               if (!from) continue;
-
-                               found = NULL;
-                               paircursor(&out, &found);
-                               for (i = paircursor(&in, from);
-                                    i != NULL;
-                                    i = pairnext(&in)) {
-                                       /*
-                                        *      Prevent cache control attributes being added to the cache.
-                                        */
-                                       switch (i->da->attr) {
-                                       case PW_CACHE_TTL:
-                                       case PW_CACHE_STATUS_ONLY:
-                                       case PW_CACHE_MERGE:
-                                       case PW_CACHE_ENTRY_HITS:
-                                               RDEBUG("\tskipping %s", i->da->name);
-                                               continue;
-                                       default:
-                                               break;
-                                       }
-
-                                       vp = paircopyvp(c, i);
-                                       if (!vp) {
-                                               pairfree(&found);
-                                               return NULL;
-                                       }
-                                       RDEBUG("\t%s %s %s (%s)", map->dst->name,
-                                              fr_int2str(fr_tokens, map->op, "<INVALID>"),
-                                              map->src->name, vp->da->name);
-                                       vp->op = map->op;
-                                       pairinsert(&out, vp);
-                               }
+               case VPT_TYPE_ATTR:
+                       if (map->src->vpt_da == map->dst->vpt_da) do_merge = false;
+                       break;
 
-                               pairadd(to_cache, found);
-                               if (to_req) {
-                                       vp = paircopy(request, found);
-                                       radius_pairmove(request, to_req, vp);
-                               }
+               default:
+                       do_merge = false;
+               }
 
-                               break;
-                       }
                /*
-                *      It was most likely a double quoted string that now
-                *      needs to be expanded.
+                *      Reparent the VPs radius_map2vp may return multiple.
                 */
-               case VPT_TYPE_XLAT:
-                       if (radius_xlat(buffer, sizeof(buffer), request, map->src->name, NULL, NULL) <= 0) {
+               for (vp = fr_cursor_init(&src_list, &to_cache);
+                    vp;
+                    vp = fr_cursor_next(&src_list)) {
+                       VERIFY_VP(vp);
+
+                       /*
+                        *      Prevent people from accidentally caching
+                        *      cache control attributes.
+                        */
+                       if (map->src->type == VPT_TYPE_LIST) switch (vp->da->attr) {
+                       case PW_CACHE_TTL:
+                       case PW_CACHE_STATUS_ONLY:
+                       case PW_CACHE_READ_ONLY:
+                       case PW_CACHE_MERGE:
+                       case PW_CACHE_ENTRY_HITS:
+                               RDEBUG2("Skipping %s", vp->da->name);
                                continue;
+                       default:
+                               break;
                        }
 
-                       RDEBUG("\t%s %s \"%s\"", map->dst->name,
-                              fr_int2str(fr_tokens, map->op, "<INVALID>"),
-                              buffer);
-
-                       vp = pairalloc(NULL, map->dst->da);
-                       if (!vp) continue;
+                       RDEBUG2("Adding to cache entry:");
+                       if (debug_flag) radius_map_debug(request, map, vp);
+                       (void) talloc_steal(c, vp);
 
                        vp->op = map->op;
-                       if (!pairparsevalue(vp, buffer)) {
-                               pairfree(&vp);
-                               continue;
-                       }
 
-                       pairadd(to_cache, vp);
-
-                       if (to_req) {
-                               vp = paircopyvp(request, vp);
-                               radius_pairmove(request, to_req, vp);
-                       }
+                       switch (map->dst->vpt_list) {
+                       case PAIR_LIST_REQUEST:
+                               fr_cursor_insert(&cached_request, vp);
+                               break;
 
-                       break;
-               /*
-                *      Literal string.
-                */
-               case VPT_TYPE_LITERAL:
-                       RDEBUG("\t%s %s '%s'", map->dst->name,
-                              fr_int2str(fr_tokens, map->op, "<INVALID>"),
-                              map->src->name);
+                       case PAIR_LIST_REPLY:
+                               fr_cursor_insert(&cached_reply, vp);
+                               break;
 
-                       vp = pairalloc(NULL, map->dst->da);
-                       if (!vp) continue;
+                       case PAIR_LIST_CONTROL:
+                               fr_cursor_insert(&cached_control, vp);
+                               break;
 
-                       vp->op = map->op;
-                       if (!pairparsevalue(vp, map->src->name)) {
-                               pairfree(&vp);
-                               continue;
+                       default:
+                               rad_assert(0);  /* should have been caught by validation */
                        }
 
-                       pairadd(to_cache, vp);
-
-                       if (to_req) {
-                               vp = paircopyvp(request, vp);
-                               radius_pairmove(request, to_req, vp);
+                       if (do_merge && radius_map_dst_valid(request, map)) {
+                               /* There's no reason for this to fail (we checked the dst was valid) */
+                               RDEBUG2("Adding to request:");
+                               if (radius_map2request(request, map, _cache_add, vp) < 0) rad_assert(0);
                        }
-
-                       break;
-
-               default:
-                       rad_assert(0);
-                       return NULL;
                }
        }
 
@@ -582,6 +463,22 @@ static int cache_verify(rlm_cache_t *inst, value_pair_map_t **head)
                        return -1;
                }
 
+               /*
+                *      Can't copy an xlat expansion or literal into a list,
+                *      we don't know what type of attribute we'd need
+                *      to create.
+                *
+                *      The only exception is where were using a unary
+                *      operator like !*.
+                */
+               if ((map->dst->type == VPT_TYPE_LIST) &&
+                   (map->op != T_OP_CMP_FALSE) &&
+                   ((map->src->type == VPT_TYPE_XLAT) || (map->src->type == VPT_TYPE_LITERAL))) {
+                       cf_log_err(map->ci, "Can't copy value into list (we don't know which attribute to create)");
+
+                       return -1;
+               }
+
                switch (map->src->type) {
                case VPT_TYPE_EXEC:
                        cf_log_err(map->ci, "Exec values are not allowed");
@@ -599,14 +496,14 @@ static int cache_verify(rlm_cache_t *inst, value_pair_map_t **head)
                         */
                        if (map->dst->type == VPT_TYPE_ATTR) {
                                VALUE_PAIR *vp;
-                               bool ret;
+                               int ret;
 
-                               MEM(vp = pairalloc(NULL, map->dst->da));
+                               MEM(vp = pairalloc(map->dst, map->dst->vpt_da));
                                vp->op = map->op;
 
-                               ret = pairparsevalue(vp, map->src->name);
+                               ret = pairparsevalue(vp, map->src->name, 0);
                                talloc_free(vp);
-                               if (!ret) {
+                               if (ret < 0) {
                                        cf_log_err(map->ci, "%s", fr_strerror());
                                        return -1;
                                }
@@ -644,13 +541,14 @@ static int cache_verify(rlm_cache_t *inst, value_pair_map_t **head)
 static ssize_t cache_xlat(void *instance, REQUEST *request,
                          char const *fmt, char *out, size_t freespace)
 {
-       rlm_cache_entry_t *c;
-       rlm_cache_t *inst = instance;
-       VALUE_PAIR *vp, *vps;
-       pair_lists_t list;
-       DICT_ATTR const *target;
-       char const *p = fmt;
-       int ret = 0;
+       rlm_cache_entry_t       *c;
+       rlm_cache_t             *inst = instance;
+       VALUE_PAIR              *vp, *vps;
+       pair_lists_t            list;
+       DICT_ATTR const         *target;
+       char const              *p = fmt;
+       size_t                  len;
+       int                     ret = 0;
 
        list = radius_list_name(&p, PAIR_LIST_REQUEST);
 
@@ -690,7 +588,7 @@ static ssize_t cache_xlat(void *instance, REQUEST *request,
        default:
                PTHREAD_MUTEX_UNLOCK(&inst->cache_mutex);
                REDEBUG("Unsupported list \"%s\"",
-                       fr_int2str(pair_lists, list, "¿Unknown?"));
+                       fr_int2str(pair_lists, list, "<UNKNOWN>"));
                return -1;
        }
 
@@ -701,7 +599,12 @@ static ssize_t cache_xlat(void *instance, REQUEST *request,
                goto done;
        }
 
-       ret = vp_prints_value(out, freespace, vp, 0);
+       len = vp_prints_value(out, freespace, vp, 0);
+       if (is_truncated(len, freespace)) {
+               PTHREAD_MUTEX_UNLOCK(&inst->cache_mutex);
+               REDEBUG("Insufficient buffer space to write cached value");
+               return -1;
+       }
 done:
        PTHREAD_MUTEX_UNLOCK(&inst->cache_mutex);
 
@@ -709,31 +612,6 @@ done:
 }
 
 /*
- *     A mapping of configuration file names to internal variables.
- *
- *     Note that the string is dynamically allocated, so it MUST
- *     be freed.  When the configuration file parse re-reads the string,
- *     it free's the old one, and strdup's the new one, placing the pointer
- *     to the strdup'd string into 'config.string'.  This gets around
- *     buffer over-flows.
- */
-static const CONF_PARSER module_config[] = {
-       { "key",  PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_cache_t, key), NULL, NULL},
-       { "ttl", PW_TYPE_INTEGER,
-         offsetof(rlm_cache_t, ttl), NULL, "500" },
-       { "max_entries", PW_TYPE_INTEGER,
-         offsetof(rlm_cache_t, max_entries), NULL, "16384" },
-       { "epoch", PW_TYPE_INTEGER,
-         offsetof(rlm_cache_t, epoch), NULL, "0" },
-       { "add_stats", PW_TYPE_BOOLEAN,
-         offsetof(rlm_cache_t, stats), NULL, "no" },
-
-       { NULL, -1, 0, NULL, NULL }             /* end the list */
-};
-
-
-/*
  *     Only free memory we allocated.  The strings allocated via
  *     cf_section_parse() do not need to be freed.
  */
@@ -786,8 +664,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 #ifdef HAVE_PTHREAD_H
        if (pthread_mutex_init(&inst->cache_mutex, NULL) < 0) {
-               EDEBUG("Failed initializing mutex: %s",
-                      strerror(errno));
+               ERROR("Failed initializing mutex: %s",
+                      fr_syserror(errno));
                return -1;
        }
 #endif
@@ -797,7 +675,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
         */
        inst->cache = rbtree_create(cache_entry_cmp, cache_entry_free, 0);
        if (!inst->cache) {
-               EDEBUG("Failed to create cache");
+               ERROR("Failed to create cache");
                return -1;
        }
 
@@ -807,7 +685,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        inst->heap = fr_heap_create(cache_heap_cmp,
                                    offsetof(rlm_cache_entry_t, offset));
        if (!inst->heap) {
-               EDEBUG("Failed to create heap for the cache");
+               ERROR("Failed to create heap for the cache");
                return -1;
        }
 
@@ -828,10 +706,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     If you want to cache something different in different sections,
  *     configure another cache module.
  */
-static rlm_rcode_t cache_it(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *request)
 {
        rlm_cache_entry_t *c;
        rlm_cache_t *inst = instance;
+       vp_cursor_t cursor;
        VALUE_PAIR *vp;
        char buffer[1024];
        rlm_rcode_t rcode;
@@ -860,6 +739,12 @@ static rlm_rcode_t cache_it(void *instance, REQUEST *request)
                goto done;
        }
 
+       vp = pairfind(request->config_items, PW_CACHE_READ_ONLY, 0, TAG_ANY);
+       if (vp && vp->vp_integer) {
+               rcode = RLM_MODULE_NOTFOUND;
+               goto done;
+       }
+
        c = cache_add(inst, request, buffer);
        if (!c) {
                rcode = RLM_MODULE_NOOP;
@@ -870,6 +755,22 @@ static rlm_rcode_t cache_it(void *instance, REQUEST *request)
 
 done:
        PTHREAD_MUTEX_UNLOCK(&inst->cache_mutex);
+
+       /*
+        *      Reset control attributes
+        */
+       for (vp = fr_cursor_init(&cursor, &request->config_items);
+            vp;
+            vp = fr_cursor_next(&cursor)) {
+               if (vp->da->vendor == 0) switch (vp->da->attr) {
+               case PW_CACHE_TTL:
+               case PW_CACHE_READ_ONLY:
+               case PW_CACHE_MERGE:
+                       fr_cursor_remove(&cursor);
+                       break;
+               }
+       }
+
        return rcode;
 }
 
@@ -893,12 +794,12 @@ module_t rlm_cache = {
        mod_detach,                     /* detach */
        {
                NULL,                   /* authentication */
-               cache_it,               /* authorization */
-               cache_it,               /* preaccounting */
-               cache_it,               /* accounting */
+               mod_cache_it,           /* authorization */
+               mod_cache_it,           /* preaccounting */
+               mod_cache_it,           /* accounting */
                NULL,                   /* checksimul */
-               cache_it,               /* pre-proxy */
-               cache_it,               /* post-proxy */
-               cache_it,               /* post-auth */
+               mod_cache_it,           /* pre-proxy */
+               mod_cache_it,           /* post-proxy */
+               mod_cache_it,           /* post-auth */
        },
 };
index 12d67a2..efb0956 100644 (file)
@@ -26,7 +26,7 @@ RCSID("$Id$")
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
 
-static rlm_rcode_t mod_authorize(UNUSED void *instance,
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance,
                                  UNUSED REQUEST *request)
 {
        if (!pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
@@ -51,7 +51,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance,
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authenticate(UNUSED void *instance,
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance,
                                     UNUSED REQUEST *request)
 {
        VALUE_PAIR *passwd_item, *chap;
@@ -88,22 +88,50 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance,
                if (pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
                        REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                        REDEBUG("!!! Please update your configuration so that the \"known !!!");
-                       REDEBUG("!!! good\" clear text password is in Cleartext-Password, !!!");
+                       REDEBUG("!!! good\" cleartext password is in Cleartext-Password,  !!!");
                        REDEBUG("!!! and NOT in User-Password.                            !!!");
                        REDEBUG("!!!                                                      !!!");
                        REDEBUG("!!! Authentication will fail because of this.            !!!");
                        REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                }
 
-               REDEBUG("Clear-Text password is required for authentication");
+               REDEBUG("Cleartext password is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
-       RDEBUG("Using Clear-Text password \"%s\" for user %s authentication.",
-             passwd_item->vp_strvalue, request->username->vp_strvalue);
+       rad_chap_encode(request->packet, pass_str,
+                       chap->vp_octets[0], passwd_item);
 
-       rad_chap_encode(request->packet,pass_str,
-                       chap->vp_octets[0],passwd_item);
+       if (RDEBUG_ENABLED3) {
+               uint8_t const *p;
+               size_t length;
+               VALUE_PAIR *vp;
+               char buffer[CHAP_VALUE_LENGTH * 2 + 1];
+
+               RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\"", passwd_item->vp_strvalue);
+
+               vp = pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
+               if (vp) {
+                       p = vp->vp_octets;
+                       length = vp->length;
+               } else {
+                       p = request->packet->vector;
+                       length = sizeof(request->packet->vector);
+               }
+
+               fr_bin2hex(buffer, p, length);
+               RINDENT();
+               RDEBUG3("CHAP challenge :  %s", buffer);
+
+               fr_bin2hex(buffer, chap->vp_octets + 1, CHAP_VALUE_LENGTH);
+               RDEBUG3("Client sent    : %s", buffer);
+
+               fr_bin2hex(buffer, pass_str + 1, CHAP_VALUE_LENGTH);
+               RDEBUG3("We calculated  : %s", buffer);
+               REXDENT();
+       } else {
+               RDEBUG2("Comparing with \"known good\" Cleartext-Password");
+       }
 
        if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1,
                           CHAP_VALUE_LENGTH) != 0) {
@@ -129,7 +157,7 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance,
 module_t rlm_chap = {
         RLM_MODULE_INIT,
        "CHAP",
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
         0,
         NULL,                          /* CONF_PARSER */
        NULL,                           /* instantiation */
diff --git a/src/modules/rlm_couchbase/.gitignore b/src/modules/rlm_couchbase/.gitignore
new file mode 100644 (file)
index 0000000..01a5daa
--- /dev/null
@@ -0,0 +1 @@
+all.mk
diff --git a/src/modules/rlm_couchbase/README.md b/src/modules/rlm_couchbase/README.md
new file mode 100644 (file)
index 0000000..8056a3f
--- /dev/null
@@ -0,0 +1,240 @@
+rlm_couchbase
+=============
+
+General
+-------
+
+This module allows you to write accounting data directly to Couchbase as JSON documents and authorize users from JSON documents stored within Couchbase.  It was tested to handle thousands of radius requests per second from several thousand Aerohive access points using a FreeRADIUS installation with this module for accounting and authorization.  You should list the ```couchbase``` module in both the ```accounting``` and ```authorization``` sections of your site configuration if you are planning to use it for both purposes.  You should also have ```pap``` enabled for authenticating users based on cleartext or hashed password attributes.  As always YMMV.
+
+Accounting
+----------
+
+You can use any radius attribute available in the accounting request to build the key for storing the accounting documents. The default configuration will try to use 'Acct-Unique-Session-Id' and fallback to 'Acct-Session-Id' if 'Acct-Unique-Session-Id' is not present.  You will need to have the ```acct_unique``` policy in the ```preacct``` section of your configuration to generate the unique id attribute.   Different status types (start/stop/update) are merged into a single document to facilitate querying and reporting via views.  When everything is configured correctly you will see accounting requests recorded as JSON documents in your Couchbaase cluster.  You have full control over what attributes are recorded and how those attributes are mapped to JSON element names via the configuration descibed later in this document.
+
+This exmaple is from an Aerohive wireless access point.
+
+```
+{
+       "docType": "radacct",
+       "startTimestamp": "Jul 15 2013 13:22:07 CDT",
+       "stopTimestamp": "null",
+       "sessionId": "51D241D3-0000047A",
+       "lastStatus": 3,
+       "authentic": 1,
+       "userName": "mruser@blargs.com",
+       "nasIpAddress": "172.28.4.150",
+       "nasIdentifier": "air4.corp.blargs.com",
+       "nasPort": 0,
+       "calledStationId": "40-18-b1-01-3c-54",
+       "framedIpAddress": "172.27.2.87",
+       "callingStationId": "8C-2D-AA-72-36-BA",
+       "nasPortType": 19,
+       "connectInfo": "11ng",
+       "sessionTime": 5821,
+       "inputPackets": 5591,
+       "inputOctets": 681742,
+       "inputGigawords": 0,
+       "outputOctets": 536306,
+       "outputGigawords": 0,
+       "outputPackets": 1087,
+       "lastUpdated": "Jul 15 2013 14:59:08 CDT",
+       "uniqueId": "029d975fc48ecb41444da52a65e62a55",
+       "calledStationSSID": "BLARGS-WIFI",
+       "strippedUserName": "mruser",
+       "strippedUserDomain": "blargs.com"
+}
+```
+
+To generate the 'calledStationSSID' fields you will need to use the ```rewrite_called_station_id``` policy in the ```preacct``` section of your config.  Similarly to get the 'Stripped-User-Name' and 'Stripped-User-Domain' attributes you can create a file in ```raddb/policy.d/``` with the following content:
+
+```
+## simple nt domain regex
+simple_nt_regexp = "^([^\\\\\\\\]*)(\\\\\\\\(.*))$"
+
+## simple nai regex
+simple_nai_regexp = "^([^@]*)(@(.*))$"
+
+## split user@domain and domain\user formats
+strip_user_domain {
+       if(User-Name && (User-Name =~ /${policy.simple_nt_regexp}/)){
+               update request {
+                       Stripped-User-Domain = "%{1}"
+                       Stripped-User-Name = "%{3}"
+               }
+       }
+       elsif(User-Name && (User-Name =~ /${policy.simple_nai_regexp}/)){
+               update request {
+                       Stripped-User-Name = "%{1}"
+                       Stripped-User-Domain = "%{3}"
+               }
+       }
+       else {
+               noop
+       }
+}
+```
+
+You can then reference this policy in both the ```preacct``` and ```authorization``` sections of your configuration before calling this module.
+
+Authorization
+-------------
+
+The authorization funcionality relies on the user documents being stored with deterministic keys based on information available in the authorization request.  The format of those keys may be specified in unlang like the example below:
+
+```user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"```
+
+This will create an md5 hash of the lowercase 'Stripped-User-Name' attribute or the 'User-Name' attribute if 'Stripped-User-Name' doesn't exist.  The module will then attempt to fetch the resulting key from the configured couchbase bucket.
+
+The document structure is straight forward and flexible:
+
+```json
+{
+       "docType": "raduser",
+       "userName": "test",
+       "config": {
+               "SHA-Password": {
+                       "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
+                       "op": ":="
+               }
+       },
+       "reply": {
+               "Reply-Message": {
+                       "value": "Hidey Ho!",
+                       "op": "="
+               }
+       }
+}
+```
+
+You may specify any valid combination of attributes and operations in the JSON document.
+
+To Use
+------
+
+Pull freeradius-server master and clone this module under src/modules.  Then enable and compile as usual.
+You will also need the following libraries:
+
+* [libcouchbase](https://github.com/couchbase/libcouchbase) >= 2.0.0 with a valid libio module
+* [json-c](https://github.com/json-c/json-c) >= 0.9 (0.10+ HIGHLY encouraged)
+
+Configuration
+-------------
+
+```
+couchbase {
+       #
+       # List of Couchbase hosts (hosts may be space, tab, comma or semi-colon separated).
+       # Ports are optional if servers are listening on the standard port.
+       # Complete pool urls are preferred.
+       #
+       server = "http://cb01.blargs.com:8091/pools/ http://cb04.blargs.com:8091/pools/"
+
+       # Couchbase bucket name
+       bucket = "radius"
+
+       # Couchbase bucket password (optional)
+       #password = "password"
+
+       # Couchbase accounting document key (unlang supported)
+       acct_key = "radacct_%{%{Acct-Unique-Session-Id}:-%{Acct-Session-Id}}"
+
+       # Value for the 'docType' element in the json body for accounting documents
+       doctype = "radacct"
+
+       ## Accounting document expire time in seconds (0 = never)
+       expire = 2592000
+
+       #
+       # Map attribute names to json element names for accounting.
+       #
+       # Configuration items are in the format:
+       # <element name> = '<radius attribute>'
+       #
+       # Attribute names should be single quoted.
+       #
+       # Note: Atrributes not in this map will not be recorded.
+       #
+       map {
+               sessionId           = 'Acct-Session-Id'
+               uniqueId            = 'Acct-Unique-Session-Id'
+               lastStatus          = 'Acct-Status-Type'
+               authentic           = 'Acct-Authentic'
+               userName            = 'User-Name'
+               strippedUserName    = 'Stripped-User-Name'
+               strippedUserDomain  = 'Stripped-User-Domain'
+               realm               = 'Realm'
+               nasIpAddress        = 'NAS-IP-Address'
+               nasIdentifier       = 'NAS-Identifier'
+               nasPort             = 'NAS-Port'
+               calledStationId     = 'Called-Station-Id'
+               calledStationSSID   = 'Called-Station-SSID'
+               callingStationId    = 'Calling-Station-Id'
+               framedIpAddress     = 'Framed-IP-Address'
+               nasPortType         = 'NAS-Port-Type'
+               connectInfo         = 'Connect-Info'
+               sessionTime         = 'Acct-Session-Time'
+               inputPackets        = 'Acct-Input-Packets'
+               outputPackets       = 'Acct-Output-Packets'
+               inputOctets         = 'Acct-Input-Octets'
+               outputOctets        = 'Acct-Output-Octets'
+               inputGigawords      = 'Acct-Input-Gigawords'
+               outputGigawords     = 'Acct-Output-Gigawords'
+               lastUpdated         = 'Event-Timestamp'
+       }
+
+       # Couchbase document key for user documents (unlang supported)
+       user_key = "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}"
+
+       #
+       #  The connection pool is new for 3.0, and will be used in many
+       #  modules, for all kinds of connection-related activity.
+       #
+       pool {
+               # Number of connections to start
+               start = 5
+
+               # Minimum number of connections to keep open
+               min = 5
+
+               # Maximum number of connections
+               #
+               # If these connections are all in use and a new one
+               # is requested, the request will NOT get a connection.
+               #
+               # NOTE: This should be greater than or equal to "min" above.
+               max = 20
+
+               # Spare connections to be left idle
+               #
+               # NOTE: Idle connections WILL be closed if "idle_timeout"
+               # is set.  This should be less than or equal to "max" above.
+               spare = 15
+
+               # Number of uses before the connection is closed
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
+               uses = 0
+
+               # The lifetime (in seconds) of the connection
+               #
+               # NOTE: A setting of 0 means infinite (no limit).
+               lifetime = 0
+
+               # The idle timeout (in seconds).  A connection which is
+               # unused for this length of time will be closed.
+               #
+               # NOTE: A setting of 0 means infinite (no timeout).
+               idle_timeout = 1200
+
+               # NOTE: All configuration settings are enforced.  If a
+               # connection is closed because of "idle_timeout",
+               # "uses", or "lifetime", then the total number of
+               # connections MAY fall below "min".  When that
+               # happens, it will open a new connection.  It will
+               # also log a WARNING message.
+               #
+               # The solution is to either lower the "min" connections,
+               # or increase lifetime/idle_timeout.
+       }
+}
+```
diff --git a/src/modules/rlm_couchbase/all.mk.in b/src/modules/rlm_couchbase/all.mk.in
new file mode 100644 (file)
index 0000000..1b52bcf
--- /dev/null
@@ -0,0 +1,13 @@
+TARGETNAME     := @targetname@
+
+ifneq "$(TARGETNAME)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := $(TARGETNAME).c mod.c jsonc_missing.c couchbase.c
+
+SRC_CFLAGS     := @mod_cflags@
+TGT_LDLIBS     := @mod_ldflags@
+
+# TODO: create man page
+#MAN           := rlm_couchbase.8
diff --git a/src/modules/rlm_couchbase/config.h.in b/src/modules/rlm_couchbase/config.h.in
new file mode 100644 (file)
index 0000000..168a02e
--- /dev/null
@@ -0,0 +1,40 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to 1 if you have the `json_c_version' function. */
+#undef HAVE_JSON_C_VERSION
+
+/* Define to 1 if you have the `json_object_get_string_len' function. */
+#undef HAVE_JSON_OBJECT_GET_STRING_LEN
+
+/* Define to 1 if you have the `json_object_new_int64' function. */
+#undef HAVE_JSON_OBJECT_NEW_INT64
+
+/* Define to 1 if you have the `json_object_object_get_ex' function. */
+#undef HAVE_JSON_OBJECT_OBJECT_GET_EX
+
+/* Define to 1 if you have the `json_tokener_error_desc' function. */
+#undef HAVE_JSON_TOKENER_ERROR_DESC
+
+/* Define to 1 if you have the `json_tokener_get_error' function. */
+#undef HAVE_JSON_TOKENER_GET_ERROR
+
+/* Define to 1 if you have the `json_tokener_parse_verbose' function. */
+#undef HAVE_JSON_TOKENER_PARSE_VERBOSE
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
diff --git a/src/modules/rlm_couchbase/configure b/src/modules/rlm_couchbase/configure
new file mode 100755 (executable)
index 0000000..6369068
--- /dev/null
@@ -0,0 +1,5131 @@
+#! /bin/sh
+# From configure.ac Revision.
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+         { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+            # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+PACKAGE_URL=
+
+ac_unique_file="rlm_couchbase.c"
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+targetname
+mod_ldflags
+mod_cflags
+CPP
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+with_jsonc_include_dir
+with_jsonc_lib_dir
+with_jsonc_dir
+with_libcouchbase_include_dir
+with_libcouchbase_lib_dir
+with_libcouchbase_dir
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                         [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                         [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/PACKAGE]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Optional Packages:
+  --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
+  --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
+  --with-jsonc-include-dir=DIR
+                         Directory where the json-c includes may be found
+  --with-jsonc-lib-dir=DIR
+                         Directory where the json-c libraries may be found
+  --with-jsonc-dir=DIR    Base directory where json-c is installed
+  --with-libcouchbase-include-dir=DIR
+                         Directory where the libcouchbase includes may be
+                         found
+  --with-libcouchbase-lib-dir=DIR
+                         Directory where the libcouchbase libraries may be
+                         found
+  --with-libcouchbase-dir=DIR
+                         Base directory where libcouchbase is installed
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+             nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+             you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+if test x$with_rlm_couchbase != xno; then
+
+       ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+         if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+        10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+       ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+               jsonc_include_dir=
+
+# Check whether --with-jsonc-include-dir was given.
+if test "${with_jsonc_include_dir+set}" = set; then :
+  withval=$with_jsonc_include_dir; case "$withval" in
+                       no)
+                               as_fn_error $? "Need jsonc-include-dir" "$LINENO" 5
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               jsonc_include_dir="$withval"
+                       ;;
+               esac
+fi
+
+
+               jsonc_lib_dir=
+
+# Check whether --with-jsonc-lib-dir was given.
+if test "${with_jsonc_lib_dir+set}" = set; then :
+  withval=$with_jsonc_lib_dir; case "$withval" in
+                       no)
+                               as_fn_error $? "Need jsonc-lib-dir" "$LINENO" 5
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               jsonc_lib_dir="$withval"
+                       ;;
+               esac
+fi
+
+
+
+# Check whether --with-jsonc-dir was given.
+if test "${with_jsonc_dir+set}" = set; then :
+  withval=$with_jsonc_dir; case "$withval" in
+                       no)
+                               as_fn_error $? "Need json-c-dir" "$LINENO" 5
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               jsonc_lib_dir="$withval/lib"
+                               jsonc_include_dir="$withval/include"
+                       ;;
+               esac
+fi
+
+
+
+       have_json="yes"
+       smart_try_dir="$jsonc_include_dir"
+
+
+
+ac_safe=`echo "json/json.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json/json.h in $try" >&5
+$as_echo_n "checking for json/json.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <json/json.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json/json.h" >&5
+$as_echo_n "checking for json/json.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <json/json.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=json/json.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json/json.h in $try" >&5
+$as_echo_n "checking for json/json.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <json/json.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
+fi
+
+       if test "x$ac_cv_header_json_json_h" != "xyes"; then
+               have_json="no"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: json-c headers not found. Use --with-jsonc-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: json-c headers not found. Use --with-jsonc-include-dir=<path>." >&2;}
+       fi
+
+
+       smart_try_dir="$jsonc_lib_dir"
+
+
+sm_lib_safe=`echo "json-c" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "json_c_version" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_c_version in -ljson-c in $try" >&5
+$as_echo_n "checking for json_c_version in -ljson-c in $try... " >&6; }
+    LIBS="-ljson-c $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_c_version();
+int
+main ()
+{
+json_c_version()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-ljson-c"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_c_version in -ljson-c" >&5
+$as_echo_n "checking for json_c_version in -ljson-c... " >&6; }
+  LIBS="-ljson-c $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_c_version();
+int
+main ()
+{
+json_c_version()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-ljson-c"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libjson-c${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libjson-c.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_c_version in -ljson-c in $try" >&5
+$as_echo_n "checking for json_c_version in -ljson-c in $try... " >&6; }
+    LIBS="-ljson-c $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_c_version();
+int
+main ()
+{
+json_c_version()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-ljson-c"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+       if test "x$ac_cv_lib_json_c_json_c_version" != "xyes"
+       then
+
+
+sm_lib_safe=`echo "json" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "json_tokener_new" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_tokener_new in -ljson in $try" >&5
+$as_echo_n "checking for json_tokener_new in -ljson in $try... " >&6; }
+    LIBS="-ljson $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_tokener_new();
+int
+main ()
+{
+json_tokener_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-ljson"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_tokener_new in -ljson" >&5
+$as_echo_n "checking for json_tokener_new in -ljson... " >&6; }
+  LIBS="-ljson $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_tokener_new();
+int
+main ()
+{
+json_tokener_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-ljson"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libjson${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libjson.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_tokener_new in -ljson in $try" >&5
+$as_echo_n "checking for json_tokener_new in -ljson in $try... " >&6; }
+    LIBS="-ljson $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_tokener_new();
+int
+main ()
+{
+json_tokener_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-ljson"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+               if test "x$ac_cv_lib_json_json_tokener_new" != "xyes"
+               then
+                       have_json="no"
+               fi
+       fi
+
+       if test "x$have_json" = "xyes"; then
+                               LDFLAGS="$SMART_LIBS"
+
+                               for ac_func in \
+                       json_c_version \
+                       json_object_get_string_len \
+                       json_object_object_get_ex \
+                       json_object_new_int64 \
+                       json_tokener_parse_verbose \
+                       json_tokener_error_desc \
+                       json_tokener_get_error
+
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+       else
+               fail="$fail json-c"
+       fi
+
+
+               libcouchbase_include_dir=
+
+# Check whether --with-libcouchbase-include-dir was given.
+if test "${with_libcouchbase_include_dir+set}" = set; then :
+  withval=$with_libcouchbase_include_dir; case "$withval" in
+                       no)
+                               as_fn_error $? "Need libcouchbase-include-dir" "$LINENO" 5
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               libcouchbase_include_dir="$withval"
+                       ;;
+               esac
+fi
+
+
+               libcouchbase_lib_dir=
+
+# Check whether --with-libcouchbase-lib-dir was given.
+if test "${with_libcouchbase_lib_dir+set}" = set; then :
+  withval=$with_libcouchbase_lib_dir; case "$withval" in
+                       no)
+                               as_fn_error $? "Need libcouchbase-lib-dir" "$LINENO" 5
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               libcouchbase_lib_dir="$withval"
+                       ;;
+               esac
+fi
+
+
+
+# Check whether --with-libcouchbase-dir was given.
+if test "${with_libcouchbase_dir+set}" = set; then :
+  withval=$with_libcouchbase_dir; case "$withval" in
+                       no)
+                               as_fn_error $? "Need libcouchbase-dir" "$LINENO" 5
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               libcouchbase_lib_dir="$withval/lib"
+                               libcouchbase_include_dir="$withval/include"
+                       ;;
+               esac
+fi
+
+
+
+       have_couchbase="yes"
+       smart_try_dir="$libcouchbase_include_dir"
+
+
+ac_safe=`echo "libcouchbase/couchbase.h" | sed 'y%./+-%__pm%'`
+old_CPPFLAGS="$CPPFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcouchbase/couchbase.h in $try" >&5
+$as_echo_n "checking for libcouchbase/couchbase.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <libcouchbase/couchbase.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcouchbase/couchbase.h" >&5
+$as_echo_n "checking for libcouchbase/couchbase.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <libcouchbase/couchbase.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libcouchbase/couchbase.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcouchbase/couchbase.h in $try" >&5
+$as_echo_n "checking for libcouchbase/couchbase.h in $try... " >&6; }
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <libcouchbase/couchbase.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
+fi
+
+       if test "x$ac_cv_header_libcouchbase_couchbase_h" != "xyes"; then
+               have_couchbase="no"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libcouchbase headers not found. Use --with-libcouchbase-include-dir=<path>." >&5
+$as_echo "$as_me: WARNING: libcouchbase headers not found. Use --with-libcouchbase-include-dir=<path>." >&2;}
+       fi
+
+
+       smart_try_dir="$libcouchbase_lib_dir"
+
+
+sm_lib_safe=`echo "couchbase" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "lcb_get_version" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
+smart_lib=
+smart_ldflags=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcb_get_version in -lcouchbase in $try" >&5
+$as_echo_n "checking for lcb_get_version in -lcouchbase in $try... " >&6; }
+    LIBS="-lcouchbase $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcb_get_version();
+int
+main ()
+{
+lcb_get_version()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-lcouchbase"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcb_get_version in -lcouchbase" >&5
+$as_echo_n "checking for lcb_get_version in -lcouchbase... " >&6; }
+  LIBS="-lcouchbase $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcb_get_version();
+int
+main ()
+{
+lcb_get_version()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lcouchbase"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libcouchbase${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libcouchbase.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for lcb_get_version in -lcouchbase in $try" >&5
+$as_echo_n "checking for lcb_get_version in -lcouchbase in $try... " >&6; }
+    LIBS="-lcouchbase $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char lcb_get_version();
+int
+main ()
+{
+lcb_get_version()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-lcouchbase"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
+fi
+
+       if test "x$ac_cv_lib_couchbase_lcb_get_version" != "xyes"
+       then
+               have_couchbase="no"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libcouchbase libraries not found. Use --with-libcouchbase-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: libcouchbase libraries not found. Use --with-libcouchbase-lib-dir=<path>." >&2;}
+       fi
+
+       if test "x$have_couchbase" != "xyes"; then
+               fail="$fail json-c"
+       fi
+
+       if test "x$have_couchbase" = "xno"; then
+               fail="$fail libcouchbase"
+       fi
+
+
+       targetname=rlm_couchbase
+else
+       targetname=
+       echo \*\*\* module rlm_couchbase is disabled.
+fi
+
+if test x"$fail" != x""; then
+       if test x"${enable_strict_dependencies}" = x"yes"; then
+               as_fn_error $? "set --without-rlm_couchbase to disable it explicitly." "$LINENO" 5
+       else
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building rlm_couchbase." >&5
+$as_echo "$as_me: WARNING: silently not building rlm_couchbase." >&2;}
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: rlm_couchbase requires: $fail." >&5
+$as_echo "$as_me: WARNING: FAILURE: rlm_couchbase requires: $fail." >&2;};
+               targetname=""
+       fi
+fi
+
+mod_ldflags="${SMART_LIBS}"
+mod_cflags="${SMART_CFLAGS}"
+
+
+
+
+
+
+  unset ac_cv_env_LIBS_set
+  unset ac_cv_env_LIBS_value
+
+  ac_config_files="$ac_config_files all.mk"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+       case $cache_file in #(
+       */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+       *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                  do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                  instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                  instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
diff --git a/src/modules/rlm_couchbase/configure.ac b/src/modules/rlm_couchbase/configure.ac
new file mode 100644 (file)
index 0000000..5f20f3d
--- /dev/null
@@ -0,0 +1,229 @@
+AC_PREREQ([2.53])
+AC_INIT(rlm_couchbase.c)
+AC_REVISION($Revision$)
+AC_DEFUN(modname,[rlm_couchbase])
+AC_CONFIG_HEADER(config.h)
+
+if test x$with_[]modname != xno; then
+
+       AC_PROG_CC
+       AC_PROG_CPP
+
+       dnl put configuration checks here.
+       dnl set $fail to what's missing, on fatal errors.
+       dnl use AC_MSG_WARN() on important messages.
+
+       dnl ############################################################
+       dnl # Check for json-c
+       dnl ############################################################
+
+       dnl extra argument: --with-jsonc-include-dir=DIR
+       jsonc_include_dir=
+       AC_ARG_WITH(jsonc-include-dir,
+               [AS_HELP_STRING([--with-jsonc-include-dir=DIR],
+               [Directory where the json-c includes may be found])],
+               [case "$withval" in
+                       no)
+                               AC_MSG_ERROR(Need jsonc-include-dir)
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               jsonc_include_dir="$withval"
+                       ;;
+               esac])
+
+       dnl extra argument: --with-jsonc-lib-dir=DIR
+       jsonc_lib_dir=
+       AC_ARG_WITH(jsonc-lib-dir,
+       [AS_HELP_STRING([--with-jsonc-lib-dir=DIR],
+               [Directory where the json-c libraries may be found])],
+               [case "$withval" in
+                       no)
+                               AC_MSG_ERROR(Need jsonc-lib-dir)
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               jsonc_lib_dir="$withval"
+                       ;;
+               esac])
+
+       dnl extra argument: --with-jsonc-dir=DIR
+       AC_ARG_WITH(jsonc-dir,
+       [AS_HELP_STRING([--with-jsonc-dir=DIR],
+               [Base directory where json-c is installed])],
+               [case "$withval" in
+                       no)
+                               AC_MSG_ERROR(Need json-c-dir)
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               jsonc_lib_dir="$withval/lib"
+                               jsonc_include_dir="$withval/include"
+                       ;;
+               esac])
+
+       dnl ############################################################
+       dnl # Check for json-c header files
+       dnl ############################################################
+
+       have_json="yes"
+       smart_try_dir="$jsonc_include_dir"
+       FR_SMART_CHECK_INCLUDE([json/json.h])
+       if test "x$ac_cv_header_json_json_h" != "xyes"; then
+               have_json="no"
+               AC_MSG_WARN([json-c headers not found. Use --with-jsonc-include-dir=<path>.])
+       fi
+
+       dnl ############################################################
+       dnl # Check for json-c libraries
+       dnl ############################################################
+
+       smart_try_dir="$jsonc_lib_dir"
+       dnl # Use a json-c specific function which is only
+       dnl # available in newer versions.
+       FR_SMART_CHECK_LIB([json-c], [json_c_version])
+       if test "x$ac_cv_lib_json_c_json_c_version" != "xyes"
+       then
+               dnl # Use a function which is included in legacy versions
+               dnl # but which may be available in other json libraries
+               FR_SMART_CHECK_LIB([json], [json_tokener_new])
+               if test "x$ac_cv_lib_json_json_tokener_new" != "xyes"
+               then
+                       have_json="no"
+               fi
+       fi
+
+       if test "x$have_json" = "xyes"; then
+               dnl # Ensure we use the library we just found the rest of the checks
+               LDFLAGS="$SMART_LIBS"
+
+               dnl # Add any optional functions here
+               AC_CHECK_FUNCS(\
+                       json_c_version \
+                       json_object_get_string_len \
+                       json_object_object_get_ex \
+                       json_object_new_int64 \
+                       json_tokener_parse_verbose \
+                       json_tokener_error_desc \
+                       json_tokener_get_error
+               )
+       else
+               fail="$fail json-c"
+       fi
+
+       dnl ############################################################
+       dnl # Check for libcouchbase
+       dnl ############################################################
+
+       dnl extra argument: --with-libcouchbase-include-dir=DIR
+       libcouchbase_include_dir=
+       AC_ARG_WITH(libcouchbase-include-dir,
+               [AS_HELP_STRING([--with-libcouchbase-include-dir=DIR],
+               [Directory where the libcouchbase includes may be found])],
+               [case "$withval" in
+                       no)
+                               AC_MSG_ERROR(Need libcouchbase-include-dir)
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               libcouchbase_include_dir="$withval"
+                       ;;
+               esac])
+
+       dnl extra argument: --with-libcouchbase-lib-dir=DIR
+       libcouchbase_lib_dir=
+       AC_ARG_WITH(libcouchbase-lib-dir,
+       [AS_HELP_STRING([--with-libcouchbase-lib-dir=DIR],
+               [Directory where the libcouchbase libraries may be found])],
+               [case "$withval" in
+                       no)
+                               AC_MSG_ERROR(Need libcouchbase-lib-dir)
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               libcouchbase_lib_dir="$withval"
+                       ;;
+               esac])
+
+       dnl extra argument: --with-libcouchbase-dir=DIR
+       AC_ARG_WITH(libcouchbase-dir,
+       [AS_HELP_STRING([--with-libcouchbase-dir=DIR],
+               [Base directory where libcouchbase is installed])],
+               [case "$withval" in
+                       no)
+                               AC_MSG_ERROR(Need libcouchbase-dir)
+                       ;;
+                       yes)
+                       ;;
+                       *)
+                               libcouchbase_lib_dir="$withval/lib"
+                               libcouchbase_include_dir="$withval/include"
+                       ;;
+               esac])
+
+       dnl ############################################################
+       dnl # Check for libcouchbase header files
+       dnl ############################################################
+
+       have_couchbase="yes"
+       smart_try_dir="$libcouchbase_include_dir"
+       FR_SMART_CHECK_INCLUDE([libcouchbase/couchbase.h])
+       if test "x$ac_cv_header_libcouchbase_couchbase_h" != "xyes"; then
+               have_couchbase="no"
+               AC_MSG_WARN([libcouchbase headers not found. Use --with-libcouchbase-include-dir=<path>.])
+       fi
+
+       dnl ############################################################
+       dnl # Check for libcouchbase libraries
+       dnl ############################################################
+
+       smart_try_dir="$libcouchbase_lib_dir"
+       FR_SMART_CHECK_LIB([couchbase], [lcb_get_version])
+       if test "x$ac_cv_lib_couchbase_lcb_get_version" != "xyes"
+       then
+               have_couchbase="no"
+               AC_MSG_WARN([libcouchbase libraries not found. Use --with-libcouchbase-lib-dir=<path>.])
+       fi
+
+       if test "x$have_couchbase" != "xyes"; then
+               fail="$fail json-c"
+       fi
+
+       if test "x$have_couchbase" = "xno"; then
+               fail="$fail libcouchbase"
+       fi
+
+       dnl ############################################################
+       dnl # Checks done - set targetname
+       dnl ############################################################
+
+       targetname=modname
+else
+       targetname=
+       echo \*\*\* module modname is disabled.
+fi
+
+dnl  Don't change this section.
+if test x"$fail" != x""; then
+       if test x"${enable_strict_dependencies}" = x"yes"; then
+               AC_MSG_ERROR([set --without-]modname[ to disable it explicitly.])
+       else
+               AC_MSG_WARN([silently not building ]modname[.])
+               AC_MSG_WARN([FAILURE: ]modname[ requires: $fail.]);
+               targetname=""
+       fi
+fi
+
+mod_ldflags="${SMART_LIBS}"
+mod_cflags="${SMART_CFLAGS}"
+
+AC_SUBST(mod_cflags)
+AC_SUBST(mod_ldflags)
+
+AC_SUBST(targetname)
+AC_OUTPUT(all.mk)
diff --git a/src/modules/rlm_couchbase/couchbase.c b/src/modules/rlm_couchbase/couchbase.c
new file mode 100644 (file)
index 0000000..843f8ad
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Wrapper functions around the libcouchbase Couchbase client driver.
+ * @file couchbase.c
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+RCSID("$Id$");
+
+#include <freeradius-devel/radiusd.h>
+
+#include <libcouchbase/couchbase.h>
+#include <json/json.h>
+
+#include "couchbase.h"
+#include "jsonc_missing.h"
+
+/* general couchbase error callback */
+void couchbase_error_callback(lcb_t instance, lcb_error_t error, const char *errinfo) {
+       /* log error */
+       ERROR("rlm_couchbase: (error_callback) %s (0x%x), %s", lcb_strerror(instance, error), error, errinfo);
+}
+
+/* couchbase value store callback */
+void couchbase_store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation, lcb_error_t error, const lcb_store_resp_t *resp) {
+       if (error != LCB_SUCCESS) {
+               /* log error */
+               ERROR("rlm_couchbase: (store_callback) %s (0x%x)", lcb_strerror(instance, error), error);
+       }
+       /* silent compiler */
+       (void)cookie;
+       (void)operation;
+       (void)resp;
+}
+
+/* couchbase value get callback */
+void couchbase_get_callback(lcb_t instance, const void *cookie, lcb_error_t error, const lcb_get_resp_t *resp) {
+       cookie_u cu;                            /* union of const and non const pointers */
+       cu.cdata = cookie;                      /* set const union member to cookie passed from couchbase */
+       cookie_t *c = (cookie_t *) cu.data;     /* set our cookie struct using non-const member */
+       const char *bytes = resp->v.v0.bytes;   /* the payload of this chunk */
+       lcb_size_t nbytes = resp->v.v0.nbytes;  /* length of this data chunk */
+
+       /* check error */
+       switch (error) {
+               case LCB_SUCCESS:
+                       /* check for valid bytes */
+                       if (bytes && nbytes > 1) {
+                               /* debug */
+                               DEBUG("rlm_couchbase: (get_callback) got %zu bytes", nbytes);
+                               /* build json object */
+                               c->jobj = json_tokener_parse_verbose(bytes, &c->jerr);
+                               /* switch on current error status */
+                               switch (c->jerr) {
+                                       case json_tokener_success:
+                                               /* do nothing */
+                                       break;
+                                       default:
+                                               /* log error */
+                                               ERROR("rlm_couchbase: (get_callback) JSON Tokener error: %s", json_tokener_error_desc(c->jerr));
+                                       break;
+                               }
+                       }
+               break;
+               case LCB_KEY_ENOENT:
+                       /* ignored */
+                       DEBUG("rlm_couchbase: (get_callback) key does not exist");
+               break;
+               default:
+                       /* log error */
+                       ERROR("rlm_couchbase: (get_callback) %s (0x%x)", lcb_strerror(instance, error), error);
+               break;
+       }
+}
+
+/* connect to couchbase */
+lcb_t couchbase_init_connection(const char *host, const char *bucket, const char *pass) {
+       lcb_t instance;                         /* couchbase instance */
+       lcb_error_t error;                      /* couchbase command return */
+       struct lcb_create_st options;           /* init create struct */
+
+       /* init options */
+       memset(&options, 0, sizeof(options));
+
+       /* assign couchbase create options */
+       options.v.v0.host = host;
+       options.v.v0.bucket = bucket;
+
+       /* assign user and password if they were both passed */
+       if (bucket != NULL && pass != NULL) {
+               options.v.v0.user = bucket;
+               options.v.v0.passwd = pass;
+       }
+
+       /* create couchbase connection instance */
+       if ((error = lcb_create(&instance, &options)) != LCB_SUCCESS) {
+               /* log error and return */
+               ERROR("rlm_couchbase: failed to create couchbase instance: %s (0x%x)", lcb_strerror(NULL, error), error);
+               /* return instance */
+               return instance;
+       }
+
+       /* initiate connection */
+       if ((error = lcb_connect(instance)) == LCB_SUCCESS) {
+               /* set general method callbacks */
+               lcb_set_error_callback(instance, couchbase_error_callback);
+               lcb_set_get_callback(instance, couchbase_get_callback);
+               lcb_set_store_callback(instance, couchbase_store_callback);
+               /* wait on connection */
+               lcb_wait(instance);
+       } else {
+               /* log error */
+               ERROR("rlm_couchbase: Failed to initiate couchbase connection: %s (0x%x)", lcb_strerror(NULL, error), error);
+       }
+
+       /* return instance */
+       return instance;
+}
+
+/* store document/key in couchbase */
+lcb_error_t couchbase_set_key(lcb_t instance, const char *key, const char *document, int expire) {
+       lcb_error_t error;                  /* couchbase command return */
+       lcb_store_cmd_t cmd;                /* store command stuct */
+       const lcb_store_cmd_t *commands[1]; /* store commands array */
+
+       /* init commands */
+       commands[0] = &cmd;
+       memset(&cmd, 0, sizeof(cmd));
+
+       /* populate command struct */
+       cmd.v.v0.key = key;
+       cmd.v.v0.nkey = strlen(cmd.v.v0.key);
+       cmd.v.v0.bytes = document;
+       cmd.v.v0.nbytes = strlen(cmd.v.v0.bytes);
+       cmd.v.v0.exptime = expire;
+       cmd.v.v0.operation = LCB_SET;
+
+       /* store key/document in couchbase */
+       if ((error = lcb_store(instance, NULL, 1, commands)) == LCB_SUCCESS) {
+               /* enter event loop on success */
+               lcb_wait(instance);
+       }
+
+       /* return error */
+       return error;
+}
+
+/* pull document from couchbase by key */
+lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key) {
+       lcb_error_t error;                  /* couchbase command return */
+       lcb_get_cmd_t cmd;                  /* get command struct */
+       const lcb_get_cmd_t *commands[1];   /* get commands array */
+
+       /* init commands */
+       commands[0] = &cmd;
+       memset(&cmd, 0, sizeof(cmd));
+
+       /* populate command struct */
+       cmd.v.v0.key = key;
+       cmd.v.v0.nkey = strlen(cmd.v.v0.key);
+
+       /* get document */
+       if ((error = lcb_get(instance, cookie, 1, commands)) == LCB_SUCCESS) {
+               /* enter event loop on success */
+               lcb_wait(instance);
+       }
+
+       /* return error */
+       return error;
+}
diff --git a/src/modules/rlm_couchbase/couchbase.h b/src/modules/rlm_couchbase/couchbase.h
new file mode 100644 (file)
index 0000000..1cb29c7
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Couchbase wrapper function prototypes and datatypes.
+ * @file couchbase.h
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+#ifndef _couchbase_h_
+#define _couchbase_h_
+
+RCSIDH(couchbase_h, "$Id$");
+
+#include <libcouchbase/couchbase.h>
+#include <json/json.h>
+
+/* struct to hold cookie data for couchbase callbacks */
+typedef struct cookie_t {
+       json_object *jobj;              /* json object */
+       json_tokener *jtok;             /* json tokener */
+       enum json_tokener_error jerr;   /* tokener error */
+} cookie_t;
+
+/* union of const and non const pointers */
+typedef union cookie_u {
+       const void *cdata;
+       void *data;
+} cookie_u;
+
+/* general error callback */
+void couchbase_error_callback(lcb_t instance, lcb_error_t error, const char *errinfo);
+
+/* store a key/document in couchbase */
+void couchbase_store_callback(lcb_t instance, const void *cookie, lcb_storage_t operation,
+       lcb_error_t error, const lcb_store_resp_t *item);
+
+/* get a document by key from couchbase */
+void couchbase_get_callback(lcb_t instance, const void *cookie, lcb_error_t error,
+       const lcb_get_resp_t *item);
+
+/* create a couchbase instance and connect to the cluster */
+lcb_t couchbase_init_connection(const char *host, const char *bucket, const char *pass);
+
+/* store document/key in couchbase */
+lcb_error_t couchbase_set_key(lcb_t instance, const char *key, const char *document, int expire);
+
+/* pull document from couchbase by key */
+lcb_error_t couchbase_get_key(lcb_t instance, const void *cookie, const char *key);
+
+#endif /* _couchbase_h_ */
diff --git a/src/modules/rlm_couchbase/jsonc_missing.c b/src/modules/rlm_couchbase/jsonc_missing.c
new file mode 100644 (file)
index 0000000..f27c2f0
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Workarounds for missing functions in older json-c libraries.
+ * @file json_missing.c
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+RCSID("$Id$");
+
+#include <string.h>
+
+#include "jsonc_missing.h"
+
+#ifndef HAVE_JSON_OBJECT_GET_STRING_LEN
+int json_object_get_string_len(json_object *obj) {
+       if (json_object_get_type(obj) != json_type_string)
+               return 0;
+       return (int)strlen(json_object_to_json_string(obj));
+}
+#endif
+
+#ifndef HAVE_JSON_OBJECT_OBJECT_GET_EX
+int json_object_object_get_ex(struct json_object *jso, const char *key, struct json_object **value) {
+       struct json_object *jobj;
+
+       if ((jso == NULL) || (key == NULL)) return 0;
+       if (value != NULL) *value = NULL;
+
+       switch (json_object_get_type(jso)) {
+       case json_type_object:
+               jobj = json_object_object_get(jso, key);
+               if (jobj == NULL) return 0;
+
+               if (value != NULL) *value = jobj;
+               return 1;
+
+       default:
+               if (value != NULL) *value = NULL;
+               return 0;
+       }
+}
+#endif
+
+#ifndef HAVE_JSON_TOKENER_PARSE_VERBOSE
+struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error) {
+       struct json_tokener* tok;
+       struct json_object* obj;
+
+       tok = json_tokener_new();
+       if (!tok)
+               return NULL;
+       obj = json_tokener_parse_ex(tok, str, -1);
+       *error = tok->err;
+       if(tok->err != json_tokener_success) {
+               if (obj != NULL)
+                       json_object_put(obj);
+               obj = NULL;
+       }
+
+       json_tokener_free(tok);
+       return obj;
+}
+#endif
+
+#ifndef HAVE_JSON_TOKENER_GET_ERROR
+enum json_tokener_error json_tokener_get_error(json_tokener *tok) {
+       return tok->err;
+}
+#endif
+
+#ifndef HAVE_JSON_TOKENER_ERROR_DESC
+const char *json_tokener_error_desc(enum json_tokener_error jerr) {
+       int jerr_int = (int)jerr;
+       if (json_tokener_errors[jerr_int] == NULL)
+               return "Unknown error, invalid json_tokener_error value passed to json_tokener_error_desc()";
+       return json_tokener_errors[jerr_int];
+}
+#endif
diff --git a/src/modules/rlm_couchbase/jsonc_missing.h b/src/modules/rlm_couchbase/jsonc_missing.h
new file mode 100644 (file)
index 0000000..be76cc8
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Function prototypes for missing functions in older json-c libraries.
+ * @file json_missing.h
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+#ifndef _jsonc_missing_h_
+#define _jsonc_missing_h_
+
+RCSIDH(jsonc_missing_h, "$Id$");
+
+#include <json/json.h>
+
+#include "config.h"
+
+#ifndef HAVE_JSON_OBJECT_OBJECT_GET_EX
+       #include <json/json_object_private.h>
+#endif
+
+#ifndef HAVE_JSON_OBJECT_GET_STRING_LEN
+       int json_object_get_string_len(struct json_object *obj);
+#endif
+
+#ifndef HAVE_JSON_OBJECT_OBJECT_GET_EX
+       int json_object_object_get_ex(struct json_object* jso, const char *key, struct json_object **value);
+#endif
+
+#ifndef HAVE_JSON_TOKENER_PARSE_VERBOSE
+       struct json_object* json_tokener_parse_verbose(const char *str, enum json_tokener_error *error);
+#endif
+
+#ifndef HAVE_JSON_TOKENER_ERROR_DESC
+       const char *json_tokener_error_desc(enum json_tokener_error jerr);
+#endif
+
+#ifndef HAVE_JSON_TOKENER_GET_ERROR
+       enum json_tokener_error json_tokener_get_error(json_tokener *tok);
+#endif
+
+/* correct poor const handling within json-c library */
+#ifdef json_object_object_foreach
+       #undef json_object_object_foreach
+#endif
+
+/* redefine with correct handling of const pointers */
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+       #define json_object_object_foreach(obj, key, val) \
+       char *key; struct json_object *val; \
+       union ctn_u {const void *cdata; void *data; } ctn; \
+       for (struct lh_entry *entry = json_object_get_object(obj)->head; \
+               ({ if (entry) { key = (char *)entry->k; ctn.cdata = entry->v; \
+               val = (struct json_object *)ctn.data; }; entry; }); \
+               entry = entry->next)
+#else /* ANSI C or MSC */
+       #define json_object_object_foreach(obj,key,val) \
+       char *key; struct json_object *val; struct lh_entry *entry; \
+       union ctn_u {const void *cdata; void *data; } ctn; \
+       for (entry = json_object_get_object(obj)->head; \
+               (entry ? (key = (char *)entry->k, ctn.cdata = entry->v, \
+               val = (struct json_object *)ctn.data, entry) : 0); entry = entry->next)
+#endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) */
+
+
+/* correct poor const handling within json-c library */
+#ifdef json_object_object_foreachC
+       #undef json_object_object_foreachC
+#endif
+
+/* redefine with correct const handling */
+#define json_object_object_foreachC(obj,iter) \
+       union ctn_u {const void *cdata; void *data; } ctn; \
+       for (iter.entry = json_object_get_object(obj)->head; \
+               (iter.entry ? (iter.key = (char *)iter.entry->k, \
+               ctn.cdata = iter.entry->v, iter.val = (struct json_object*) ctn.data, iter.entry) : 0); \
+               iter.entry = iter.entry->next)
+
+#endif  /* _jsonc_missing_h_ */
diff --git a/src/modules/rlm_couchbase/mod.c b/src/modules/rlm_couchbase/mod.c
new file mode 100644 (file)
index 0000000..e95f94e
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Utillity functions used in the module.
+ * @file mod.c
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+RCSID("$Id$");
+
+#include <freeradius-devel/radiusd.h>
+
+#include <libcouchbase/couchbase.h>
+#include <json/json.h>
+
+#include "mod.h"
+#include "couchbase.h"
+#include "jsonc_missing.h"
+
+/* create new connection pool handle */
+void *mod_conn_create(void *instance) {
+       rlm_couchbase_t *inst = instance;           /* module instance pointer */
+       rlm_couchbase_handle_t *chandle = NULL;     /* connection handle pointer */
+       cookie_t *cookie = NULL;                    /* couchbase cookie */
+       lcb_t cb_inst;                              /* couchbase connection instance */
+       lcb_error_t cb_error = LCB_SUCCESS;         /* couchbase error status */
+
+       /* create instance */
+       cb_inst = couchbase_init_connection(inst->server, inst->bucket, inst->password);
+
+       /* check couchbase instance status */
+       if ((cb_error = lcb_get_last_error(cb_inst)) != LCB_SUCCESS) {
+               ERROR("rlm_couchbase: failed to initiate couchbase connection: %s (0x%x)", lcb_strerror(NULL, cb_error), cb_error);
+               /* destroy/free couchbase instance */
+               lcb_destroy(cb_inst);
+               /* fail */
+               return NULL;
+       }
+
+       /* allocate memory for couchbase connection instance abstraction */
+       chandle = talloc_zero(inst, rlm_couchbase_handle_t);
+       cookie = talloc_zero(chandle, cookie_t);
+
+       /* initialize cookie error holder */
+       cookie->jerr = json_tokener_success;
+
+       /* populate handle with allocated structs */
+       chandle->cookie = cookie;
+       chandle->handle = cb_inst;
+
+       /* return handle struct */
+       return chandle;
+}
+
+/* verify valid couchbase connection handle */
+int mod_conn_alive(UNUSED void *instance, void *handle) {
+       rlm_couchbase_handle_t *chandle = handle;   /* connection handle pointer */
+       lcb_t cb_inst = chandle->handle;            /* couchbase instance */
+       lcb_error_t cb_error = LCB_SUCCESS;         /* couchbase error status */
+
+       /* attempt to get server list */
+       const char *const *servers = lcb_get_server_list(cb_inst);
+
+       /* check error state and server list return */
+       if (((cb_error = lcb_get_last_error(cb_inst)) != LCB_SUCCESS) || (servers == NULL)) {
+               /* log error */
+               ERROR("rlm_couchbase: failed to get couchbase server topology: %s (0x%x)", lcb_strerror(NULL, cb_error), cb_error);
+               /* return false */
+               return false;
+       }
+       return true;
+}
+
+/* free couchbase instance handle and any additional context memory */
+int mod_conn_delete(UNUSED void *instance, void *handle) {
+       rlm_couchbase_handle_t *chandle = handle;       /* connection instance handle */
+       lcb_t cb_inst = chandle->handle;                /* couchbase instance */
+
+       /* destroy/free couchbase instance */
+       lcb_destroy(cb_inst);
+
+       /* free handle */
+       talloc_free(chandle);
+
+       /* return */
+       return true;
+}
+
+/* build json object for mapping radius attributes to json elements */
+int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance) {
+       rlm_couchbase_t *inst = instance;   /* our module instance */
+       CONF_SECTION *cs;                   /* module config section */
+       CONF_ITEM *ci;                      /* config item */
+       CONF_PAIR *cp;                      /* conig pair */
+       const char *attribute, *element;    /* attribute and element names */
+
+       /* find map section */
+       cs = cf_section_sub_find(conf, "map");
+
+       /* check section */
+       if (!cs) {
+               ERROR("rlm_couchbase: failed to find 'map' section in config");
+               /* fail */
+               return -1;
+       }
+
+       /* create attribute map object */
+       inst->map = json_object_new_object();
+
+       /* parse update section */
+       for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
+               /* validate item */
+               if (!cf_item_is_pair(ci)) {
+                       ERROR("rlm_couchbase: failed to parse invalid item in 'map' section");
+                       /* free map */
+                       if (inst->map) {
+                               json_object_put(inst->map);
+                       }
+                       /* fail */
+                       return -1;
+               }
+
+               /* get value pair from item */
+               cp = cf_itemtopair(ci);
+
+               /* get pair name (element name) */
+               element = cf_pair_attr(cp);
+
+               /* get pair value (attribute name) */
+               attribute = cf_pair_value(cp);
+
+               /* add pair name and value */
+               json_object_object_add(inst->map, attribute, json_object_new_string(element));
+
+               /* debugging */
+               DEBUG("rlm_couchbase: added attribute '%s' to element '%s' map to object", attribute, element);
+       }
+
+       /* debugging */
+       DEBUG("rlm_couchbase: built attribute to element map %s", json_object_to_json_string(inst->map));
+
+       /* return */
+       return 0;
+}
+
+/* map free radius attribute to user defined json element name */
+int mod_attribute_to_element(const char *name, json_object *map, void *buf) {
+       json_object *jval;  /* json object values */
+
+       /* clear buffer */
+       memset((char *) buf, 0, MAX_KEY_SIZE);
+
+       /* attempt to map attribute */
+       if (json_object_object_get_ex(map, name, &jval)) {
+               int length;     /* json value length */
+               /* get value length */
+               length = json_object_get_string_len(jval);
+               /* check buffer size */
+               if (length > MAX_KEY_SIZE -1) {
+                       /* oops ... this value is bigger than our buffer ... error out */
+                       ERROR("rlm_couchbase: json map value larger than MAX_KEY_SIZE - %d", MAX_KEY_SIZE);
+                       /* return fail */
+                       return -1;
+               } else {
+                       /* copy string value to buffer */
+                       strncpy(buf, json_object_get_string(jval), length);
+                       /* return good */
+                       return 0;
+               }
+       }
+
+       /* debugging */
+       DEBUG("rlm_couchbase: skipping attribute with no map entry - %s", name);
+
+       /* default return */
+       return -1;
+}
+
+/* inject value pairs into given request
+ * that are defined in the passed json object
+ */
+void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request) {
+       json_object *jobj, *jval, *jop;     /* json object pointers */
+       TALLOC_CTX *ctx;                    /* talloc context for pairmake */
+       VALUE_PAIR *vp, **ptr;              /* value pair and value pair pointer for pairmake */
+
+       /* assign ctx and vps for pairmake based on section */
+       if (strcmp(section, "config") == 0) {
+               ctx = request;
+               ptr = &(request->config_items);
+       } else if (strcmp(section, "reply") == 0) {
+               ctx = request->reply;
+               ptr = &(request->reply->vps);
+       } else {
+               /* log error - this shouldn't happen */
+               RERROR("invalid section passed for pairmake");
+               /* return */
+               return NULL;
+       }
+
+       /* get config payload */
+       if (json_object_object_get_ex(json, section, &jobj)) {
+               /* make sure we have the correct type */
+               if (!json_object_is_type(jobj, json_type_object)) {
+                       /* log error */
+                       RERROR("invalid json type for '%s' section - sections must be json objects", section);
+                       /* reuturn */
+                       return NULL;
+               }
+               /* loop through object */
+               json_object_object_foreach(jobj, attribute, json_vp) {
+                       /* check for appropriate type in value and op */
+                       if (!json_object_is_type(json_vp, json_type_object)) {
+                               /* log error */
+                               RERROR("invalid json type for '%s' attribute - attributes must be json objects", attribute);
+                               /* return */
+                               return NULL;
+                       }
+                       /* debugging */
+                       RDEBUG("parsing '%s' attribute: %s => %s", section, attribute, json_object_to_json_string(json_vp));
+                       /* create pair from json object */
+                       if (json_object_object_get_ex(json_vp, "value", &jval) &&
+                               json_object_object_get_ex(json_vp, "op", &jop)) {
+                               /* make correct pairs based on json object type */
+                               switch (json_object_get_type(jval)) {
+                                       case json_type_double:
+                                       case json_type_int:
+                                       case json_type_string:
+                                               /* debugging */
+                                               RDEBUG("adding '%s' attribute to '%s' section", attribute, section);
+                                               /* add pair */
+                                               vp = pairmake(ctx, ptr, attribute, json_object_get_string(jval),
+                                                       fr_str2int(fr_tokens, json_object_get_string(jop), 0));
+                                               /* check pair */
+                                               if (!vp) {
+                                                       RERROR("could not build value pair for '%s' attribute (%s)", attribute, fr_strerror());
+                                                       /* return */
+                                                       return NULL;
+                                               }
+                                       break;
+                                       case json_type_object:
+                                       case json_type_array:
+                                               /* log error - we want to handle these eventually */
+                                               RERROR("skipping unhandled nested json object or array value pair object");
+                                       break;
+                                       default:
+                                               /* log error - this shouldn't ever happen */
+                                               RERROR("skipping unhandled json type in value pair object");
+                                       break;
+                               }
+                       } else {
+                               /* log error */
+                               RERROR("failed to get 'value' or 'op' element for '%s' attribute", attribute);
+                       }
+               }
+               /* return NULL */
+               return NULL;
+       }
+
+       /* debugging */
+       RDEBUG("couldn't find '%s' section in json object - not adding value pairs for this section", section);
+
+       /* return NULL */
+       return NULL;
+}
+
+/* convert freeradius value/pair to json object
+ * basic structure taken from freeradius function
+ * vp_prints_value_json in src/lib/print.c */
+json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp) {
+       char value[255];    /* radius attribute value */
+
+       /* add this attribute/value pair to our json output */
+       if (!vp->da->flags.has_tag) {
+               switch (vp->da->type) {
+                       case PW_TYPE_INTEGER:
+                       case PW_TYPE_BYTE:
+                       case PW_TYPE_SHORT:
+                               /* skip if we have flags */
+                               if (vp->da->flags.has_value) break;
+#ifdef HAVE_JSON_OBJECT_NEW_INT64
+                               /* debug */
+                               RDEBUG3("creating new int64 for unsigned 32 bit int/byte/short '%s'", vp->da->name);
+                               /* return as 64 bit int - JSON spec does not support unsigned ints */
+                               return json_object_new_int64(vp->vp_integer);
+#else
+                               /* debug */
+                               RDEBUG3("creating new int for unsigned 32 bit int/byte/short '%s'", vp->da->name);
+                               /* return as 64 bit int - JSON spec does not support unsigned ints */
+                               return json_object_new_int(vp->vp_integer);
+#endif
+                       break;
+                       case PW_TYPE_SIGNED:
+#ifdef HAVE_JSON_OBJECT_NEW_INT64
+                               /* debug */
+                               RDEBUG3("creating new int64 for signed 32 bit integer '%s'", vp->da->name);
+                               /* return as 64 bit int - json-c represents all ints as 64 bits internally */
+                               return json_object_new_int64(vp->vp_signed);
+#else
+                               RDEBUG3("creating new int for signed 32 bit integer '%s'", vp->da->name);
+                               /* return as signed int */
+                               return json_object_new_int(vp->vp_signed);
+#endif
+                       break;
+                       case PW_TYPE_INTEGER64:
+#ifdef HAVE_JSON_OBJECT_NEW_INT64
+                               /* debug */
+                               RDEBUG3("creating new int64 for 64 bit integer '%s'", vp->da->name);
+                               /* return as 64 bit int - because it is a 64 bit int */
+                               return json_object_new_int64(vp->vp_integer64);
+#else
+                               /* warning */
+                               RWARN("skipping 64 bit integer attribute '%s' - please upgrade json-c to 0.10+", vp->da->name);
+#endif
+                       break;
+                       default:
+                               /* silence warnings - do nothing */
+                       break;
+               }
+       }
+
+       /* keep going if not set above */
+       switch (vp->da->type) {
+               case PW_TYPE_STRING:
+                       /* debug */
+                       RDEBUG3("assigning string '%s' as string", vp->da->name);
+                       /* return string value */
+                       return json_object_new_string(vp->vp_strvalue);
+               default:
+                       /* debug */
+                       RDEBUG3("assigning unhandled '%s' as string", vp->da->name);
+                       /* get standard value */
+                       vp_prints_value(value, sizeof(value), vp, 0);
+                       /* return string value from above */
+                       return json_object_new_string(value);
+               break;
+       }
+}
+
+/* check current value of start timestamp in json body and update if needed */
+int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps) {
+       json_object *jval;      /* json object value */
+       struct tm tm;           /* struct to hold event time */
+       time_t ts = 0;          /* values to hold time in seconds */
+       VALUE_PAIR *vp;         /* values to hold value pairs */
+       char value[255];        /* store radius attribute values and our timestamp */
+
+       /* get our current start timestamp from our json body */
+       if (json_object_object_get_ex(json, "startTimestamp", &jval) == 0) {
+               /* debugging ... this shouldn't ever happen */
+               DEBUG("rlm_couchbase: failed to find start timestamp in current json body");
+               /* return */
+               return -1;
+       }
+
+       /* check the value */
+       if (strcmp(json_object_get_string(jval), "null") != 0) {
+               /* debugging */
+               DEBUG("rlm_couchbase: start timestamp looks good - nothing to do");
+               /* already set - nothing else to do */
+               return 0;
+       }
+
+       /* get current event timestamp */
+       if ((vp = pairfind(vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
+               /* get seconds value from attribute */
+               ts = vp->vp_date;
+       } else {
+               /* debugging */
+               DEBUG("rlm_couchbase: failed to find event timestamp in current request");
+               /* return */
+               return -1;
+       }
+
+       /* clear value */
+       memset(value, 0, sizeof(value));
+
+       /* get elapsed session time */
+       if ((vp = pairfind(vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL) {
+               /* calculate diff */
+               ts = (ts - vp->vp_integer);
+               /* calculate start time */
+               size_t length = strftime(value, sizeof(value), "%b %e %Y %H:%M:%S %Z", localtime_r(&ts, &tm));
+               /* check length */
+               if (length > 0) {
+                       /* debugging */
+                       DEBUG("rlm_couchbase: calculated start timestamp: %s", value);
+                       /* store new value in json body */
+                       json_object_object_add(json, "startTimestamp", json_object_new_string(value));
+               } else {
+                       /* debugging */
+                       DEBUG("rlm_couchbase: failed to format calculated timestamp");
+                       /* return */
+                       return -1;
+               }
+       }
+
+       /* default return */
+       return 0;
+}
diff --git a/src/modules/rlm_couchbase/mod.h b/src/modules/rlm_couchbase/mod.h
new file mode 100644 (file)
index 0000000..36711d8
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Function prototypes and datatypes used in the module.
+ * @file mod.h
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+#ifndef _mod_h_
+#define _mod_h_
+
+RCSIDH(mod_h, "$Id$");
+
+#include <libcouchbase/couchbase.h>
+#include <json/json.h>
+
+#include "jsonc_missing.h"
+
+/* maximum size of a stored value */
+#define MAX_VALUE_SIZE 20480
+
+/* maximum length of a document key */
+#define MAX_KEY_SIZE 250
+
+/* configuration struct */
+typedef struct rlm_couchbase_t {
+       char const *acct_key;           //!< Accounting document key.
+       char const *doctype;            //!< Value of 'docType' element name.
+       char const *server_raw;         //!< Raw server string before parsing.
+       char const *server;             //!< Couchbase server list.
+       char const *bucket;             //!< Couchbase bucket.
+       char const *password;           //!< Couchbase bucket password.
+       uint32_t expire;                //!< Document expire time in seconds.
+       const char *user_key;           //!< User document key.
+       json_object *map;               //!< Json object to hold user defined attribute map.
+       fr_connection_pool_t *pool;     //!< Connection pool.
+} rlm_couchbase_t;
+
+/* connection pool handle struct */
+typedef struct rlm_couchbase_handle_t {
+       void *handle;                   //!< Real couchbase instance.
+       void *cookie;                   //!< Couchbase cookie.
+} rlm_couchbase_handle_t;
+
+/* define functions */
+void *mod_conn_create(void *instance);
+
+int mod_conn_alive(UNUSED void *instance, void *handle);
+
+int mod_conn_delete(UNUSED void *instance, void *handle);
+
+int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance);
+
+int mod_attribute_to_element(const char *name, json_object *map, void *buf);
+
+void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request);
+
+json_object *mod_value_pair_to_json_object(REQUEST *request, VALUE_PAIR *vp);
+
+int mod_ensure_start_timestamp(json_object *json, VALUE_PAIR *vps);
+
+#endif /* _mod_h_ */
diff --git a/src/modules/rlm_couchbase/rlm_couchbase.c b/src/modules/rlm_couchbase/rlm_couchbase.c
new file mode 100644 (file)
index 0000000..8ebcd9c
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ *   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
+ */
+
+/*
+ * $Id$
+ *
+ * @brief Integrate FreeRADIUS with the Couchbase document database.
+ * @file rlm_couchbase.c
+ *
+ * @copyright 2013-2014 Aaron Hurt <ahurt@anbcs.com>
+ */
+
+RCSID("$Id$");
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/libradius.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <libcouchbase/couchbase.h>
+#include <json/json.h>
+
+#include "mod.h"
+#include "couchbase.h"
+#include "jsonc_missing.h"
+
+/**
+ * Module Configuration
+ */
+static const CONF_PARSER module_config[] = {
+       { "acct_key", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_couchbase_t, acct_key), "radacct_%{%{Acct-Unique-Session-Id}:-%{Acct-Session-Id}}" },
+       { "doctype", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_couchbase_t, doctype), "radacct" },
+       { "server", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_couchbase_t, server), NULL },
+       { "bucket", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_couchbase_t, bucket), NULL },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_couchbase_t, password), NULL },
+       { "expire", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_couchbase_t, expire), 0 },
+       { "user_key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_couchbase_t, user_key), "raduser_%{md5:%{tolower:%{%{Stripped-User-Name}:-%{User-Name}}}}" },
+       {NULL, -1, 0, NULL, NULL}     /* end the list */
+};
+
+/* initialize couchbase connection */
+static int mod_instantiate(CONF_SECTION *conf, void *instance) {
+       rlm_couchbase_t *inst = instance;   /* our module instance */
+
+       {
+               char *server, *p;
+               size_t len, i;
+               bool sep = false;
+
+               len = talloc_array_length(inst->server_raw);
+               server = p = talloc_array(inst, char, len);
+               for (i = 0; i < len; i++) {
+                       switch (inst->server_raw[i]) {
+                       case '\t':
+                       case ' ':
+                       case ',':
+                               /* Consume multiple separators occurring in sequence */
+                               if (sep == true) continue;
+
+                               sep = true;
+                               *p++ = ';';
+                               break;
+
+                       default:
+                               sep = false;
+                               *p++ = inst->server_raw[i];
+                               break;
+                       }
+               }
+
+               *p = '\0';
+               inst->server = server;
+       }
+
+       /* setup item map */
+       if (mod_build_attribute_element_map(conf, inst) != 0) {
+               /* fail */
+               return -1;
+       }
+
+       /* initiate connection pool */
+       inst->pool = fr_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, mod_conn_delete, NULL);
+
+       /* check connection pool */
+       if (!inst->pool) {
+               ERROR("rlm_couchbase: failed to initiate connection pool");
+               /* fail */
+               return -1;
+       }
+
+       /* return okay */
+       return 0;
+}
+
+/* authorize users via couchbase */
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) {
+       rlm_couchbase_t *inst = instance;       /* our module instance */
+       void *handle = NULL;                    /* connection pool handle */
+       char dockey[MAX_KEY_SIZE];              /* our document key */
+       lcb_error_t cb_error = LCB_SUCCESS;     /* couchbase error holder */
+
+       /* assert packet as not null */
+       rad_assert(request->packet != NULL);
+
+       /* attempt to build document key */
+       if (radius_xlat(dockey, sizeof(dockey), request, inst->user_key, NULL, NULL) < 0) {
+               /* log error */
+               RERROR("could not find user key attribute (%s) in packet", inst->user_key);
+               /* return */
+               return RLM_MODULE_FAIL;
+       }
+
+       /* get handle */
+       handle = fr_connection_get(inst->pool);
+
+       /* check handle */
+       if (!handle) return RLM_MODULE_FAIL;
+
+       /* set handle pointer */
+       rlm_couchbase_handle_t *handle_t = handle;
+
+       /* set couchbase instance */
+       lcb_t cb_inst = handle_t->handle;
+
+       /* set cookie */
+       cookie_t *cookie = handle_t->cookie;
+
+       /* check cookie */
+       if (cookie) {
+               /* clear cookie */
+               memset(cookie, 0, sizeof(cookie_t));
+       } else {
+               /* log error */
+               RERROR("cookie not usable - possibly not allocated");
+               /* free connection */
+               if (handle) {
+                       fr_connection_release(inst->pool, handle);
+               }
+               /* return */
+               return RLM_MODULE_FAIL;
+       }
+
+       /* reset  cookie error status */
+       cookie->jerr = json_tokener_success;
+
+       /* fetch document */
+       cb_error = couchbase_get_key(cb_inst, cookie, dockey);
+
+       /* check error */
+       if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || cookie->jobj == NULL) {
+               /* log error */
+               RERROR("failed to fetch document or parse return");
+               /* free json object */
+               if (cookie->jobj) {
+                       json_object_put(cookie->jobj);
+               }
+               /* release handle */
+               if (handle) {
+                       fr_connection_release(inst->pool, handle);
+               }
+               /* return */
+               return RLM_MODULE_FAIL;
+       }
+
+       /* debugging */
+       RDEBUG("parsed user document == %s", json_object_to_json_string(cookie->jobj));
+
+       /* inject config value pairs defined in this json oblect */
+       mod_json_object_to_value_pairs(cookie->jobj, "config", request);
+
+       /* inject reply value pairs defined in this json oblect */
+       mod_json_object_to_value_pairs(cookie->jobj, "reply", request);
+
+       /* free json object */
+       if (cookie->jobj) {
+               json_object_put(cookie->jobj);
+       }
+
+       /* release handle */
+       if (handle) {
+               fr_connection_release(inst->pool, handle);
+       }
+
+       /* return okay */
+       return RLM_MODULE_OK;
+}
+
+/* write accounting data to couchbase */
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request) {
+       rlm_couchbase_t *inst = instance;   /* our module instance */
+       void *handle = NULL;                /* connection pool handle */
+       VALUE_PAIR *vp;                     /* radius value pair linked list */
+       char dockey[MAX_KEY_SIZE];          /* our document key */
+       char document[MAX_VALUE_SIZE];      /* our document body */
+       char element[MAX_KEY_SIZE];         /* mapped radius attribute to element name */
+       int status = 0;                     /* account status type */
+       int docfound = 0;                   /* document found toggle */
+       lcb_error_t cb_error = LCB_SUCCESS; /* couchbase error holder */
+
+       /* assert packet as not null */
+       rad_assert(request->packet != NULL);
+
+       /* sanity check */
+       if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
+               /* log debug */
+               RDEBUG("could not find status type in packet");
+               /* return */
+               return RLM_MODULE_NOOP;
+       }
+
+       /* set status */
+       status = vp->vp_integer;
+
+       /* acknowledge the request but take no action */
+       if (status == PW_STATUS_ACCOUNTING_ON || status == PW_STATUS_ACCOUNTING_OFF) {
+               /* log debug */
+               RDEBUG("handling accounting on/off request without action");
+               /* return */
+               return RLM_MODULE_OK;
+       }
+
+       /* get handle */
+       handle = fr_connection_get(inst->pool);
+
+       /* check handle */
+       if (!handle) return RLM_MODULE_FAIL;
+
+       /* set handle pointer */
+       rlm_couchbase_handle_t *handle_t = handle;
+
+       /* set couchbase instance */
+       lcb_t cb_inst = handle_t->handle;
+
+       /* set cookie */
+       cookie_t *cookie = handle_t->cookie;
+
+       /* check cookie */
+       if (cookie) {
+               /* clear cookie */
+               memset(cookie, 0, sizeof(cookie_t));
+       } else {
+               /* log error */
+               RERROR("cookie not usable - possibly not allocated");
+               /* free connection */
+               if (handle) {
+                       fr_connection_release(inst->pool, handle);
+               }
+               /* return */
+               return RLM_MODULE_FAIL;
+       }
+
+       /* attempt to build document key */
+       if (radius_xlat(dockey, sizeof(dockey), request, inst->acct_key, NULL, NULL) < 0) {
+               /* log error */
+               RERROR("could not find accounting key attribute (%s) in packet", inst->acct_key);
+               /* release handle */
+               if (handle) {
+                       fr_connection_release(inst->pool, handle);
+               }
+               /* return */
+               return RLM_MODULE_NOOP;
+       }
+
+       /* init cookie error status */
+       cookie->jerr = json_tokener_success;
+
+       /* attempt to fetch document */
+       cb_error = couchbase_get_key(cb_inst, cookie, dockey);
+
+       /* check error */
+       if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success) {
+               /* log error */
+               RERROR("failed to execute get request or parse returned json object");
+               /* free json object */
+               if (cookie->jobj) {
+                       json_object_put(cookie->jobj);
+               }
+       } else {
+               /* check cookie json object */
+               if (cookie->jobj != NULL) {
+                       /* set doc found */
+                       docfound = 1;
+                       /* debugging */
+                       RDEBUG("parsed json body from couchbase: %s", json_object_to_json_string(cookie->jobj));
+               }
+       }
+
+       /* start json document if needed */
+       if (docfound != 1) {
+               /* debugging */
+               RDEBUG("document not found - creating new json document");
+               /* create new json object */
+               cookie->jobj = json_object_new_object();
+               /* set 'docType' element for new document */
+               json_object_object_add(cookie->jobj, "docType", json_object_new_string(inst->doctype));
+               /* set start and stop times ... ensure we always have these elements */
+               json_object_object_add(cookie->jobj, "startTimestamp", json_object_new_string("null"));
+               json_object_object_add(cookie->jobj, "stopTimestamp", json_object_new_string("null"));
+       }
+
+       /* status specific replacements for start/stop time */
+       switch (status) {
+               case PW_STATUS_START:
+                       /* add start time */
+                       if ((vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
+                               /* add to json object */
+                               json_object_object_add(cookie->jobj, "startTimestamp", mod_value_pair_to_json_object(request, vp));
+                       }
+               break;
+               case PW_STATUS_STOP:
+                       /* add stop time */
+                       if ((vp = pairfind(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
+                               /* add to json object */
+                               json_object_object_add(cookie->jobj, "stopTimestamp", mod_value_pair_to_json_object(request, vp));
+                       }
+                       /* check start timestamp and adjust if needed */
+                       mod_ensure_start_timestamp(cookie->jobj, request->packet->vps);
+               break;
+               case PW_STATUS_ALIVE:
+                       /* check start timestamp and adjust if needed */
+                       mod_ensure_start_timestamp(cookie->jobj, request->packet->vps);
+               break;
+               default:
+                       /* we shouldn't get here - free json object */
+                       if (cookie->jobj) {
+                               json_object_put(cookie->jobj);
+                       }
+                       /* release our connection handle */
+                       if (handle) {
+                               fr_connection_release(inst->pool, handle);
+                       }
+                       /* return without doing anything */
+                       return RLM_MODULE_NOOP;
+       }
+
+       /* loop through pairs and add to json document */
+       for (vp = request->packet->vps; vp; vp = vp->next) {
+               /* map attribute to element */
+               if (mod_attribute_to_element(vp->da->name, inst->map, &element) == 0) {
+                       /* debug */
+                       RDEBUG("mapped attribute %s => %s", vp->da->name, element);
+                       /* add to json object with mapped name */
+                       json_object_object_add(cookie->jobj, element, mod_value_pair_to_json_object(request, vp));
+               }
+       }
+
+       /* make sure we have enough room in our document buffer */
+       if ((unsigned int) json_object_get_string_len(cookie->jobj) > sizeof(document) - 1) {
+               /* this isn't good */
+               RERROR("could not write json document - insufficient buffer space");
+               /* free json output */
+               if (cookie->jobj) {
+                       json_object_put(cookie->jobj);
+               }
+               /* release handle */
+               if (handle) {
+                       fr_connection_release(inst->pool, handle);
+               }
+               /* return */
+               return RLM_MODULE_FAIL;
+       } else {
+               /* copy json string to document */
+               strlcpy(document, json_object_to_json_string(cookie->jobj), sizeof(document));
+               /* free json output */
+               if (cookie->jobj) {
+                       json_object_put(cookie->jobj);
+               }
+       }
+
+       /* debugging */
+       RDEBUG("setting '%s' => '%s'", dockey, document);
+
+       /* store document/key in couchbase */
+       cb_error = couchbase_set_key(cb_inst, dockey, document, inst->expire);
+
+       /* check return */
+       if (cb_error != LCB_SUCCESS) {
+               RERROR("failed to store document (%s): %s (0x%x)", dockey, lcb_strerror(NULL, cb_error), cb_error);
+       }
+
+       /* release handle */
+       if (handle) {
+               fr_connection_release(inst->pool, handle);
+       }
+
+       /* return */
+       return RLM_MODULE_OK;
+}
+
+/* free any memory we allocated */
+static int mod_detach(void *instance) {
+       rlm_couchbase_t *inst = instance;  /* instance struct */
+
+       /* free json object attribute map */
+       if (inst->map) {
+               json_object_put(inst->map);
+       }
+
+       /* destroy connection pool */
+       if (inst->pool) {
+               fr_connection_pool_delete(inst->pool);
+       }
+
+       /* return okay */
+       return 0;
+}
+
+/* hook the module into freeradius */
+module_t rlm_couchbase = {
+       RLM_MODULE_INIT,
+       "rlm_couchbase",
+       RLM_TYPE_THREAD_SAFE,       /* type */
+       sizeof(rlm_couchbase_t),
+       module_config,
+       mod_instantiate,            /* instantiation */
+       mod_detach,                 /* detach */
+       {
+               NULL,                   /* authentication */
+               mod_authorize,          /* authorization */
+               NULL,                   /* preaccounting */
+               mod_accounting,         /* accounting */
+               NULL,                   /* checksimul */
+               NULL,                   /* pre-proxy */
+               NULL,                   /* post-proxy */
+               NULL                    /* post-auth */
+       },
+};
index 2eb3f6d..34b8092 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1212,9 +1212,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1257,10 +1257,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1985,7 +1985,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2207,7 +2207,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2894,22 +2894,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=gdbm.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3018,8 +3018,8 @@ gdbm_open()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lgdbm"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lgdbm"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3035,22 +3035,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libgdbm${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3062,22 +3062,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libgdbm.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3410,11 +3410,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3888,13 +3888,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index b0f9ab8..be0a33a 100755 (executable)
@@ -39,8 +39,8 @@ sub show_help {
 GetOptions ('user=s'  => \$user,
            'match=s' => \$match,
            'file=s'  => \$filename,
-            'reset=i' => \$reset,
-            'help'    => \$help,
+           'reset=i' => \$reset,
+           'help'    => \$help,
            'hours'   => sub { $divisor = 3600 },
            'minutes' => sub { $divisor = 60 },
            'seconds' => sub { $divisor = 1 } );
@@ -61,14 +61,14 @@ if ($reset){
 #
 if ($user ne '') {
     if (defined($hash{$user})){
-        print $user, "\t\t", int ( unpack('L',$hash{$user}) / $divisor), "\n";
+       print $user, "\t\t", int ( unpack('L',$hash{$user}) / $divisor), "\n";
        if ($reset){
-            my $uniqueid = (unpack('L A32',$hash{$user}))[1];
-            $hash{$user} = pack('L A32',$reset * $divisor,$uniqueid);
-            print $user, "\t\t", "Counter reset to ", $reset * $divisor, "\n";
-        }
+           my $uniqueid = (unpack('L A32',$hash{$user}))[1];
+           $hash{$user} = pack('L A32',$reset * $divisor,$uniqueid);
+           print $user, "\t\t", "Counter reset to ", $reset * $divisor, "\n";
+       }
     }else{
-        print $user, "\t\t", "Not found\n";
+       print $user, "\t\t", "Not found\n";
     }
 
     undef $db;
@@ -95,9 +95,9 @@ foreach $key (sort keys %hash) {
     #  Print out the names...
     print $key, "\t\t", int ( unpack('L',$hash{$key}) / $divisor), "\n";
     if ($reset){
-        my $uniqueid = (unpack('L A32',$hash{$key}))[1];
-        $hash{$key} = pack('L A32',$reset * $divisor,$uniqueid);
-        print $key, "\t\t", "Counter reset to ", $reset * $divisor, "\n";
+       my $uniqueid = (unpack('L A32',$hash{$key}))[1];
+       $hash{$key} = pack('L A32',$reset * $divisor,$uniqueid);
+       print $key, "\t\t", "Counter reset to ", $reset * $divisor, "\n";
     }
 }
 undef $db;
index ff946f1..c59ceff 100644 (file)
@@ -69,14 +69,14 @@ typedef struct rlm_counter_t {
        char const *reply_name;         /* Session-Timeout */
        char const *service_type;       /* Service-Type to search for */
 
-       int cache_size;
+       uint32_t cache_size;
        uint32_t service_val;
 
-       int key_attr;
-       int count_attr;
-       int check_attr;
-       int reply_attr;
-       int dict_attr;          /* attribute number for the counter. */
+       DICT_ATTR const *key_attr;
+       DICT_ATTR const *count_attr;
+       DICT_ATTR const *check_attr;
+       DICT_ATTR const *reply_attr;
+       DICT_ATTR const *dict_attr;             /* attribute number for the counter. */
 
        time_t reset_time;      /* The time of the next reset. */
        time_t last_reset;      /* The time of the last reset. */
@@ -113,42 +113,27 @@ typedef struct rad_counter {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "filename", PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED,
-         offsetof(rlm_counter_t,filename), NULL, NULL },
-       { "key", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,
-         offsetof(rlm_counter_t,key_name), NULL, NULL },
-       { "reset", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_counter_t,reset), NULL, NULL },
-
-       { "count-attribute", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_counter_t,count_attribute), NULL, NULL },
-       { "count_attribute", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,
-         offsetof(rlm_counter_t,count_attribute), NULL, NULL },
-
-       { "counter-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_counter_t,counter_name), NULL,  NULL },
-       { "counter_name", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_counter_t,counter_name), NULL,  NULL },
-
-       { "check-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_counter_t,check_name), NULL, NULL },
-       { "check_name", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_counter_t,check_name), NULL, NULL },
-
-       { "reply-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_counter_t,reply_name), NULL, NULL },
-       { "reply_name", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,
-         offsetof(rlm_counter_t,reply_name), NULL, NULL},
-
-       { "allowed-servicetype", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_counter_t,service_type), NULL, NULL },
-       { "allowed_service_type", PW_TYPE_STRING_PTR,
-         offsetof(rlm_counter_t,service_type), NULL, NULL },
-
-       { "cache-size", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED,
-         offsetof(rlm_counter_t,cache_size), NULL, NULL },
-       { "cache_size", PW_TYPE_INTEGER,
-         offsetof(rlm_counter_t,cache_size), NULL, "1000" },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_counter_t, filename), NULL },
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_ATTRIBUTE, rlm_counter_t, key_name), NULL },
+       { "reset", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_counter_t, reset), NULL },
+
+       { "count-attribute", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_counter_t, count_attribute), NULL },
+       { "count_attribute", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_ATTRIBUTE, rlm_counter_t, count_attribute), NULL },
+
+       { "counter-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_counter_t, counter_name), NULL },
+       { "counter_name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_counter_t, counter_name), NULL },
+
+       { "check-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_counter_t, check_name), NULL },
+       { "check_name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_counter_t, check_name), NULL },
+
+       { "reply-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_counter_t, reply_name), NULL },
+       { "reply_name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_ATTRIBUTE, rlm_counter_t, reply_name), NULL },
+
+       { "allowed-servicetype", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_counter_t, service_type), NULL },
+       { "allowed_service_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_counter_t, service_type), NULL },
+
+       { "cache-size", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_counter_t, cache_size), NULL },
+       { "cache_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_counter_t, cache_size), "1000" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -175,7 +160,7 @@ static int counter_cmp(void *instance, UNUSED REQUEST *req, VALUE_PAIR *request,
        /*
         *      Find the key attribute.
         */
-       key_vp = pairfind(request, inst->key_attr, 0, TAG_ANY);
+       key_vp = pairfind_da(request, inst->key_attr, TAG_ANY);
        if (!key_vp) {
                return RLM_MODULE_NOOP;
        }
@@ -209,9 +194,8 @@ static rlm_rcode_t add_defaults(rlm_counter_t *inst)
        time_datum.dptr = (char *) &inst->reset_time;
        time_datum.dsize = sizeof(time_t);
 
-       if (gdbm_store(inst->gdbm, key_datum, time_datum, GDBM_REPLACE) < 0){
-               ERROR("rlm_counter: Failed storing data to %s: %s",
-                               inst->filename, gdbm_strerror(gdbm_errno));
+       if (gdbm_store(inst->gdbm, key_datum, time_datum, GDBM_REPLACE) < 0) {
+               ERROR("rlm_counter: Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
                return RLM_MODULE_FAIL;
        }
        DEBUG2("rlm_counter: DEFAULT1 set to %u", (unsigned int) inst->reset_time);
@@ -222,9 +206,8 @@ static rlm_rcode_t add_defaults(rlm_counter_t *inst)
        time_datum.dptr = (char *) &inst->last_reset;
        time_datum.dsize = sizeof(time_t);
 
-       if (gdbm_store(inst->gdbm, key_datum, time_datum, GDBM_REPLACE) < 0){
-               ERROR("rlm_counter: Failed storing data to %s: %s",
-                               inst->filename, gdbm_strerror(gdbm_errno));
+       if (gdbm_store(inst->gdbm, key_datum, time_datum, GDBM_REPLACE) < 0) {
+               ERROR("rlm_counter: Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
                return RLM_MODULE_FAIL;
        }
        DEBUG2("rlm_counter: DEFAULT2 set to %u", (unsigned int) inst->last_reset);
@@ -251,12 +234,10 @@ static rlm_rcode_t reset_db(rlm_counter_t *inst)
                inst->gdbm = gdbm_open(filename, sizeof(int), GDBM_NEWDB | GDBM_COUNTER_OPTS, 0600, NULL);
        }
        if (!inst->gdbm) {
-               ERROR("rlm_counter: Failed to open file %s: %s",
-                               inst->filename, strerror(errno));
+               ERROR("rlm_counter: Failed to open file %s: %s", inst->filename, fr_syserror(errno));
                return RLM_MODULE_FAIL;
        }
-       if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size,
-                       sizeof(cache_size)) == -1) {
+       if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(cache_size)) == -1) {
                ERROR("rlm_counter: Failed to set cache size");
        }
 
@@ -290,7 +271,7 @@ static int find_next_reset(rlm_counter_t *inst, time_t timeval)
 
        if (!inst->reset)
                return -1;
-       if (isdigit((int) inst->reset[0])){
+       if (isdigit((int) inst->reset[0])) {
                len = strlen(inst->reset);
                if (len == 0)
                        return -1;
@@ -355,7 +336,7 @@ static int find_next_reset(rlm_counter_t *inst, time_t timeval)
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_counter_t *inst = instance;
-       DICT_ATTR const *dattr;
+       DICT_ATTR const *da;
        DICT_VALUE *dval;
        ATTR_FLAGS flags;
        time_t now;
@@ -368,41 +349,33 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        cache_size = inst->cache_size;
 
-       dattr = dict_attrbyname(inst->key_name);
-       rad_assert(dattr != NULL);
-       if (dattr->vendor != 0) {
-               cf_log_err_cs(conf, "Configuration item 'key' cannot be a VSA");
-               return -1;
-       }
-       inst->key_attr = dattr->attr;
+       da = dict_attrbyname(inst->key_name);
+       rad_assert(da != NULL);
+       inst->key_attr = da;
 
        /*
         *      Discover the attribute number of the counter.
         */
-       dattr = dict_attrbyname(inst->count_attribute);
-       rad_assert(dattr != NULL);
-       if (dattr->vendor != 0) {
-               cf_log_err_cs(conf, "Configuration item 'count_attribute' cannot be a VSA");
-               return -1;
-       }
-       inst->count_attr = dattr->attr;
+       da = dict_attrbyname(inst->count_attribute);
+       rad_assert(da != NULL);
+       inst->count_attr = da;
 
        /*
         * Discover the attribute number of the reply attribute.
         */
        if (inst->reply_name != NULL) {
-               dattr = dict_attrbyname(inst->reply_name);
-               if (!dattr) {
-                       cf_log_err_cs(conf, "No such attribute %s",
-                                     inst->reply_name);
+               da = dict_attrbyname(inst->reply_name);
+               if (!da) {
+                       cf_log_err_cs(conf, "No such attribute %s", inst->reply_name);
                        return -1;
                }
-               if (dattr->type != PW_TYPE_INTEGER) {
-                       cf_log_err_cs(conf, "Reply attribute' %s' is not of type integer",
-                                     inst->reply_name);
+               if (da->type != PW_TYPE_INTEGER) {
+                       cf_log_err_cs(conf, "Reply attribute' %s' is not of type integer", inst->reply_name);
                        return -1;
                }
-               inst->reply_attr = dattr->attr;
+               inst->reply_attr = da;
+       } else {
+               inst->reply_attr = NULL;
        }
 
        /*
@@ -411,46 +384,40 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        rad_assert(inst->counter_name && *inst->counter_name);
        memset(&flags, 0, sizeof(flags));
        if (dict_addattr(inst->counter_name, -1, 0, PW_TYPE_INTEGER, flags) < 0) {
-               ERROR("rlm_counter: Failed to create counter attribute %s: %s",
-                      inst->counter_name, fr_strerror());
+               ERROR("rlm_counter: Failed to create counter attribute %s: %s", inst->counter_name, fr_strerror());
                return -1;
        }
 
-       dattr = dict_attrbyname(inst->counter_name);
-       if (!dattr) {
-               cf_log_err_cs(conf, "Failed to find counter attribute %s",
-                             inst->counter_name);
+       da = dict_attrbyname(inst->counter_name);
+       if (!da) {
+               cf_log_err_cs(conf, "Failed to find counter attribute %s", inst->counter_name);
                return -1;
        }
-       inst->dict_attr = dattr->attr;
-       DEBUG2("rlm_counter: Counter attribute %s is number %d",
-                       inst->counter_name, inst->dict_attr);
+       inst->dict_attr = da;
+       DEBUG2("rlm_counter: Counter attribute %s is number %d", inst->counter_name, inst->dict_attr->attr);
 
        /*
         * Create a new attribute for the check item.
         */
        rad_assert(inst->check_name && *inst->check_name);
        if (dict_addattr(inst->check_name, -1, 0, PW_TYPE_INTEGER, flags) < 0) {
-               ERROR("rlm_counter: Failed to create check attribute %s: %s",
-                      inst->counter_name, fr_strerror());
+               ERROR("rlm_counter: Failed to create check attribute %s: %s", inst->counter_name, fr_strerror());
                return -1;
 
        }
-       dattr = dict_attrbyname(inst->check_name);
-       if (!dattr) {
-               ERROR("rlm_counter: Failed to find check attribute %s",
-                               inst->counter_name);
+       da = dict_attrbyname(inst->check_name);
+       if (!da) {
+               ERROR("rlm_counter: Failed to find check attribute %s", inst->counter_name);
                return -1;
        }
-       inst->check_attr = dattr->attr;
+       inst->check_attr = da;
 
        /*
         * Find the attribute for the allowed protocol
         */
        if (inst->service_type != NULL) {
                if ((dval = dict_valbyname(PW_SERVICE_TYPE, 0, inst->service_type)) == NULL) {
-                       ERROR("rlm_counter: Failed to find attribute number for %s",
-                                       inst->service_type);
+                       ERROR("rlm_counter: Failed to find attribute number for %s", inst->service_type);
                        return -1;
                }
                inst->service_val = dval->value;
@@ -464,8 +431,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        inst->reset_time = 0;
        inst->last_reset = now;
 
-       if (find_next_reset(inst,now) == -1){
-               ERROR("rlm_counter: find_next_reset() returned -1. Exiting.");
+       if (find_next_reset(inst,now) == -1) {
+               ERROR("rlm_counter: find_next_reset() returned -1. Exiting");
                return -1;
        }
 
@@ -476,12 +443,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                inst->gdbm = gdbm_open(filename, sizeof(int), GDBM_NEWDB | GDBM_COUNTER_OPTS, 0600, NULL);
        }
        if (!inst->gdbm) {
-               ERROR("rlm_counter: Failed to open file %s: %s",
-                               inst->filename, strerror(errno));
+               ERROR("rlm_counter: Failed to open file %s: %s", inst->filename, fr_syserror(errno));
                return -1;
        }
-       if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size,
-                       sizeof(cache_size)) == -1) {
+       if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(cache_size)) == -1) {
                ERROR("rlm_counter: Failed to set cache size");
        }
 
@@ -502,17 +467,17 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        key_datum.dsize = strlen(key_datum.dptr);
 
        time_datum = gdbm_fetch(inst->gdbm, key_datum);
-       if (time_datum.dptr != NULL){
+       if (time_datum.dptr != NULL) {
                time_t next_reset = 0;
 
                memcpy(&next_reset, time_datum.dptr, sizeof(time_t));
                free(time_datum.dptr);
                time_datum.dptr = NULL;
-               if (next_reset && next_reset <= now){
+               if (next_reset && next_reset <= now) {
 
                        inst->last_reset = now;
                        ret = reset_db(inst);
-                       if (ret != RLM_MODULE_OK){
+                       if (ret != RLM_MODULE_OK) {
                                ERROR("rlm_counter: reset_db() failed");
                                return -1;
                        }
@@ -524,14 +489,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                key_datum.dsize = strlen(key_datum.dptr);
 
                time_datum = gdbm_fetch(inst->gdbm, key_datum);
-               if (time_datum.dptr != NULL){
+               if (time_datum.dptr != NULL) {
                        memcpy(&inst->last_reset, time_datum.dptr, sizeof(time_t));
                        free(time_datum.dptr);
                }
-       }
-       else{
+       } else {
                ret = add_defaults(inst);
-               if (ret != RLM_MODULE_OK){
+               if (ret != RLM_MODULE_OK) {
                        ERROR("rlm_counter: add_defaults() failed");
                        return -1;
                }
@@ -542,7 +506,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
         *      Register the counter comparison operation.
         * FIXME: move all attributes to DA
         */
-       paircompare_register(dict_attrbyvalue(inst->dict_attr, 0), NULL, true, counter_cmp, inst);
+       paircompare_register(inst->dict_attr, NULL, true, counter_cmp, inst);
 
        /*
         * Init the mutex
@@ -555,7 +519,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 /*
  *     Write accounting information to this modules database.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        rlm_counter_t *inst = instance;
        datum key_datum;
@@ -570,11 +534,11 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        if ((key_vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL)
                acctstatustype = key_vp->vp_integer;
        else {
-               DEBUG("rlm_counter: Could not find account status type in packet.");
+               DEBUG("rlm_counter: Could not find account status type in packet");
                return RLM_MODULE_NOOP;
        }
-       if (acctstatustype != PW_STATUS_STOP){
-               DEBUG("rlm_counter: We only run on Accounting-Stop packets.");
+       if (acctstatustype != PW_STATUS_STOP) {
+               DEBUG("rlm_counter: We only run on Accounting-Stop packets");
                return RLM_MODULE_NOOP;
        }
        uniqueid_vp = pairfind(request->packet->vps, PW_ACCT_UNIQUE_SESSION_ID, 0, TAG_ANY);
@@ -586,7 +550,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         *      the counters.
         */
        if (inst->reset_time && (inst->reset_time <= request->timestamp)) {
-               DEBUG("rlm_counter: Time to reset the database.");
+               DEBUG("rlm_counter: Time to reset the database");
                inst->last_reset = inst->reset_time;
                find_next_reset(inst,request->timestamp);
                pthread_mutex_lock(&inst->mutex);
@@ -599,12 +563,12 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         * Check if we need to watch out for a specific service-type. If yes then check it
         */
        if (inst->service_type != NULL) {
-               if ((proto_vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY)) == NULL){
-                       DEBUG("rlm_counter: Could not find Service-Type attribute in the request. Returning NOOP.");
+               if ((proto_vp = pairfind(request->packet->vps, PW_SERVICE_TYPE, 0, TAG_ANY)) == NULL) {
+                       DEBUG("rlm_counter: Could not find Service-Type attribute in the request. Returning NOOP");
                        return RLM_MODULE_NOOP;
                }
-               if ((unsigned)proto_vp->vp_integer != inst->service_val){
-                       DEBUG("rlm_counter: This Service-Type is not allowed. Returning NOOP.");
+               if ((unsigned)proto_vp->vp_integer != inst->service_val) {
+                       DEBUG("rlm_counter: This Service-Type is not allowed. Returning NOOP");
                        return RLM_MODULE_NOOP;
                }
        }
@@ -613,10 +577,9 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         * If yes reject the packet since it is very old
         */
        key_vp = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
-       if (key_vp != NULL){
-               if (key_vp->vp_integer != 0 &&
-                   (request->timestamp - key_vp->vp_integer) < inst->last_reset){
-                       DEBUG("rlm_counter: This packet is too old. Returning NOOP.");
+       if (key_vp != NULL) {
+               if ((key_vp->vp_integer != 0) && (request->timestamp - (time_t) key_vp->vp_integer) < inst->last_reset) {
+                       DEBUG("rlm_counter: This packet is too old. Returning NOOP");
                        return RLM_MODULE_NOOP;
                }
        }
@@ -627,18 +590,19 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         *      Look for the key.  User-Name is special.  It means
         *      The REAL username, after stripping.
         */
-       key_vp = (inst->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, inst->key_attr, 0, TAG_ANY);
-       if (!key_vp){
-               DEBUG("rlm_counter: Could not find the key-attribute in the request. Returning NOOP.");
+       key_vp = (inst->key_attr->attr == PW_USER_NAME) ? request->username :
+                                       pairfind_da(request->packet->vps, inst->key_attr, TAG_ANY);
+       if (!key_vp) {
+               DEBUG("rlm_counter: Could not find the key-attribute in the request. Returning NOOP");
                return RLM_MODULE_NOOP;
        }
 
        /*
         *      Look for the attribute to use as a counter.
         */
-       count_vp = pairfind(request->packet->vps, inst->count_attr, 0, TAG_ANY);
-       if (!count_vp){
-               DEBUG("rlm_counter: Could not find the count_attribute in the request.");
+       count_vp = pairfind_da(request->packet->vps, inst->count_attr, TAG_ANY);
+       if (!count_vp) {
+               DEBUG("rlm_counter: Could not find the count_attribute in the request");
                return RLM_MODULE_NOOP;
        }
 
@@ -648,33 +612,30 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        DEBUG("rlm_counter: Searching the database for key '%s'",key_vp->vp_strvalue);
        pthread_mutex_lock(&inst->mutex);
        count_datum = gdbm_fetch(inst->gdbm, key_datum);
-       pthread_mutex_unlock(&inst->mutex);
-       if (!count_datum.dptr){
-               DEBUG("rlm_counter: Could not find the requested key in the database.");
+       if (!count_datum.dptr) {
+               DEBUG("rlm_counter: Could not find the requested key in the database");
                counter.user_counter = 0;
                if (uniqueid_vp != NULL)
-                       strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue,
-                               sizeof(counter.uniqueid));
+                       strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue, sizeof(counter.uniqueid));
                else
                        memset((char *)counter.uniqueid,0,UNIQUEID_MAX_LEN);
-       }
-       else{
-               DEBUG("rlm_counter: Key found.");
+       } else {
+               DEBUG("rlm_counter: Key found");
                memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
                free(count_datum.dptr);
                DEBUG("rlm_counter: Counter Unique ID = '%s'",counter.uniqueid);
-               if (uniqueid_vp != NULL){
-                       if (strncmp(uniqueid_vp->vp_strvalue,counter.uniqueid, UNIQUEID_MAX_LEN - 1) == 0){
-                               DEBUG("rlm_counter: Unique IDs for user match. Droping the request.");
+               if (uniqueid_vp != NULL) {
+                       if (strncmp(uniqueid_vp->vp_strvalue,counter.uniqueid, UNIQUEID_MAX_LEN - 1) == 0) {
+                               DEBUG("rlm_counter: Unique IDs for user match. Droping the request");
+                               pthread_mutex_unlock(&inst->mutex);
                                return RLM_MODULE_NOOP;
                        }
-                       strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue,
-                               sizeof(counter.uniqueid));
+                       strlcpy(counter.uniqueid,uniqueid_vp->vp_strvalue, sizeof(counter.uniqueid));
                }
                DEBUG("rlm_counter: User=%s, Counter=%d.",request->username->vp_strvalue,counter.user_counter);
        }
 
-       if (inst->count_attr == PW_ACCT_SESSION_TIME) {
+       if (inst->count_attr->attr == PW_ACCT_SESSION_TIME) {
                /*
                 *      If session time < diff then the user got in after the
                 *      last reset. So add his session time, otherwise add the
@@ -686,7 +647,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                 *      day). That is the right thing
                 */
                diff = request->timestamp - inst->last_reset;
-               counter.user_counter += (count_vp->vp_integer < diff) ? count_vp->vp_integer : diff;
+               counter.user_counter += ((time_t) count_vp->vp_integer < diff) ? count_vp->vp_integer : diff;
 
        } else if (count_vp->da->type == PW_TYPE_INTEGER) {
                /*
@@ -707,16 +668,14 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        count_datum.dptr = (char *) &counter;
        count_datum.dsize = sizeof(rad_counter);
 
-       DEBUG("rlm_counter: Storing new value in database.");
-       pthread_mutex_lock(&inst->mutex);
+       DEBUG("rlm_counter: Storing new value in database");
        ret = gdbm_store(inst->gdbm, key_datum, count_datum, GDBM_REPLACE);
        pthread_mutex_unlock(&inst->mutex);
        if (ret < 0) {
-               ERROR("rlm_counter: Failed storing data to %s: %s",
-                               inst->filename, gdbm_strerror(gdbm_errno));
+               ERROR("rlm_counter: Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
                return RLM_MODULE_FAIL;
        }
-       DEBUG("rlm_counter: New value stored successfully.");
+       DEBUG("rlm_counter: New value stored successfully");
 
        return RLM_MODULE_OK;
 }
@@ -727,14 +686,13 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
 {
        rlm_counter_t *inst = instance;
        rlm_rcode_t rcode = RLM_MODULE_NOOP;
        datum key_datum;
        datum count_datum;
        rad_counter counter;
-       unsigned int res = 0;
        VALUE_PAIR *key_vp, *check_vp;
        VALUE_PAIR *reply_item;
        char msg[128];
@@ -751,8 +709,9 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
                pthread_mutex_lock(&inst->mutex);
                rcode2 = reset_db(inst);
                pthread_mutex_unlock(&inst->mutex);
-               if (rcode2 != RLM_MODULE_OK)
+               if (rcode2 != RLM_MODULE_OK) {
                        return rcode2;
+               }
        }
 
 
@@ -761,7 +720,8 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
         *      The REAL username, after stripping.
         */
        DEBUG2("rlm_counter: Entering module authorize code");
-       key_vp = (inst->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, inst->key_attr, 0, TAG_ANY);
+       key_vp = (inst->key_attr->attr == PW_USER_NAME) ? request->username :
+                pairfind_da(request->packet->vps, inst->key_attr, TAG_ANY);
        if (!key_vp) {
                DEBUG2("rlm_counter: Could not find Key value pair");
                return rcode;
@@ -770,7 +730,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
        /*
         *      Look for the check item
         */
-       if ((check_vp= pairfind(request->config_items, inst->check_attr, 0, TAG_ANY)) == NULL) {
+       if ((check_vp = pairfind_da(request->config_items, inst->check_attr, TAG_ANY)) == NULL) {
                DEBUG2("rlm_counter: Could not find Check item value pair");
                return rcode;
        }
@@ -789,66 +749,72 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
        pthread_mutex_lock(&inst->mutex);
        count_datum = gdbm_fetch(inst->gdbm, key_datum);
        pthread_mutex_unlock(&inst->mutex);
-       if (count_datum.dptr != NULL){
-               DEBUG("rlm_counter: Key Found.");
+       if (count_datum.dptr != NULL) {
+               DEBUG("rlm_counter: Key Found");
                memcpy(&counter, count_datum.dptr, sizeof(rad_counter));
                free(count_datum.dptr);
        }
        else
-               DEBUG("rlm_counter: Could not find the requested key in the database.");
+               DEBUG("rlm_counter: Could not find the requested key in the database");
 
        /*
         * Check if check item > counter
         */
        DEBUG("rlm_counter: Check item = %d, Count = %d",check_vp->vp_integer,counter.user_counter);
-       res=check_vp->vp_integer - counter.user_counter;
-       if (res > 0) {
+       if (check_vp->vp_integer > counter.user_counter) {
+               unsigned int res;
+
+               res = check_vp->vp_integer - counter.user_counter;
+
                DEBUG("rlm_counter: res is greater than zero");
-               if (inst->count_attr == PW_ACCT_SESSION_TIME) {
+               if (inst->count_attr->attr == PW_ACCT_SESSION_TIME) {
                        /*
                         * Do the following only if the count attribute is
                         * AcctSessionTime
                         */
 
                        /*
-                       *       We are assuming that simultaneous-use=1. But
-                       *       even if that does not happen then our user
-                       *       could login at max for 2*max-usage-time Is
-                       *       that acceptable?
-                       */
+                       *       We are assuming that simultaneous-use=1. But
+                       *       even if that does not happen then our user
+                       *       could login at max for 2*max-usage-time Is
+                       *       that acceptable?
+                       */
 
                        /*
-                       *       User is allowed, but set Session-Timeout.
-                       *       Stolen from main/auth.c
-                       */
+                       *       User is allowed, but set Session-Timeout.
+                       *       Stolen from main/auth.c
+                       */
 
                        /*
-                       *       If we are near a reset then add the next
-                       *       limit, so that the user will not need to
-                       *       login again
+                       *       If we are near a reset then add the next
+                       *       limit, so that the user will not need to
+                       *       login again
                        *       Before that set the return value to the time
                        *       remaining to next reset
-                       */
-                       if (inst->reset_time && (
-                               res >= (inst->reset_time - request->timestamp))) {
+                       */
+                       if (inst->reset_time && (res >= (inst->reset_time - request->timestamp))) {
                                res = inst->reset_time - request->timestamp;
                                res += check_vp->vp_integer;
                        }
 
                        reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
-                       if (reply_item && (reply_item->vp_integer > res)) {
-                               reply_item->vp_integer = res;
+                       if (reply_item) {
+                               if (reply_item->vp_integer > res) {
+                                       reply_item->vp_integer = res;
+                               }
                        } else {
-                               reply_item = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+                               reply_item = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
                                reply_item->vp_integer = res;
                        }
-               }
-               else if (inst->reply_attr) {
-                       reply_item = pairfind(request->reply->vps, inst->reply_attr, 0, TAG_ANY);
-                       if (reply_item && (reply_item->vp_integer > res)) {
-                               reply_item->vp_integer = res;
+               } else if (inst->reply_attr) {
+                       reply_item = pairfind_da(request->reply->vps, inst->reply_attr, TAG_ANY);
+                       if (reply_item) {
+                               if (reply_item->vp_integer > res) {
+                                       reply_item->vp_integer = res;
+                               }
                        } else {
-                               reply_item = radius_paircreate(request, &request->reply->vps, inst->reply_attr, 0);
+                               reply_item = radius_paircreate(request->reply, &request->reply->vps, inst->reply_attr->attr,
+                                                              inst->reply_attr->vendor);
                                reply_item->vp_integer = res;
                        }
                }
@@ -858,18 +824,15 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
                DEBUG2("rlm_counter: (Check item - counter) is greater than zero");
                DEBUG2("rlm_counter: Authorized user %s, check_item=%d, counter=%d",
                                key_vp->vp_strvalue,check_vp->vp_integer,counter.user_counter);
-               DEBUG2("rlm_counter: Sent Reply-Item for user %s, Type=Session-Timeout, value=%d",
-                               key_vp->vp_strvalue,res);
+               DEBUG2("rlm_counter: Sent Reply-Item for user %s, Type=Session-Timeout, value=%d", key_vp->vp_strvalue,res);
        } else {
                /*
                 * User is denied access, send back a reply message
                */
-               sprintf(msg, "Your maximum %s usage time has been reached",
-                       inst->reset);
+               sprintf(msg, "Your maximum %s usage time has been reached", inst->reset);
                pairmake_reply("Reply-Message", msg, T_OP_EQ);
 
-               REDEBUG("Maximum %s usage time reached",
-                                  inst->reset);
+               REDEBUG("Maximum %s usage time reached", inst->reset);
                rcode = RLM_MODULE_REJECT;
 
                DEBUG2("rlm_counter: Rejected user %s, check_item=%d, counter=%d",
@@ -911,9 +874,9 @@ module_t rlm_counter = {
        mod_detach,                     /* detach */
        {
                NULL,                   /* authentication */
-               mod_authorize,  /* authorization */
+               mod_authorize,          /* authorization */
                NULL,                   /* preaccounting */
-               mod_accounting, /* accounting */
+               mod_accounting,         /* accounting */
                NULL,                   /* checksimul */
                NULL,                   /* pre-proxy */
                NULL,                   /* post-proxy */
index a88ac91..5926344 100644 (file)
@@ -94,10 +94,10 @@ KRB4 - (reserved) Kerberos V4 authentication
 KRB5 - (reserved) Kerberos V5 authentication
 
 NTLM  -  Microsoft  NTLM  v1  authentication.  SHOULD  be implemented as
-         MS-CHAP v1 (RFC2433/RFC2458)
+        MS-CHAP v1 (RFC2433/RFC2458)
 
 NTLM2   -   (reserved)  Microsoft  NTLM  v2  authentication.  SHOULD  be
-        implemented as MS-CHAP v2 (RFC2759/RFC2458)
+       implemented as MS-CHAP v2 (RFC2759/RFC2458)
 
 CRAM-MD4 - MD4 digest authentication
 
index e389bfb..1d6ea91 100644 (file)
@@ -120,14 +120,14 @@ static void calc_sha1_digest(uint8_t *buffer, uint8_t const *challenge,
 }
 
 
-static rlm_rcode_t mod_authenticate(UNUSED void * instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void * instance, REQUEST *request)
 {
        VALUE_PAIR *authtype, *challenge, *response, *password;
        uint8_t buffer[64];
 
        password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        if(!password) {
-               AUTH("rlm_cram: Cleartext-Password is required for authentication.");
+               AUTH("rlm_cram: Cleartext-Password is required for authentication");
                return RLM_MODULE_INVALID;
        }
        authtype = pairfind(request->packet->vps, SM_AUTHTYPE, VENDORPEC_SM, TAG_ANY);
index c155a31..31af474 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
+#include <ctype.h>
 #include <time.h>
 
 typedef struct rlm_date_t {
@@ -33,7 +34,7 @@ typedef struct rlm_date_t {
 } rlm_date_t;
 
 static const CONF_PARSER module_config[] = {
-       {"format", PW_TYPE_STRING_PTR, offsetof(rlm_date_t, fmt), NULL, "%b %e %Y %H:%M:%S %Z"},
+       { "format", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_date_t, fmt), "%b %e %Y %H:%M:%S %Z" },
        {NULL, -1, 0, NULL, NULL}
 };
 
@@ -116,7 +117,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 module_t rlm_date = {
        RLM_MODULE_INIT,
        "date",                         /* Name */
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
        sizeof(rlm_date_t),
        module_config,
        mod_instantiate,                /* instantiation */
index d61a341..bd3dd5f 100644 (file)
@@ -51,32 +51,29 @@ RCSID("$Id$")
  */
 typedef struct detail_instance {
        char const      *name;          //!< Instance name.
-       char            *filename;      //!< File/path to write to.
-       int             perm;           //!< Permissions to use for new files.
-       char            *group;         //!< Group to use for new files.
+       char const      *filename;      //!< File/path to write to.
+       uint32_t        perm;           //!< Permissions to use for new files.
+       char const      *group;         //!< Group to use for new files.
 
-       int             dirperm;        //!< Directory permissions to use for new files.
-
-       char            *header;        //!< Header format.
+       char const      *header;        //!< Header format.
        bool            locking;        //!< Whether the file should be locked.
 
        bool            log_srcdst;     //!< Add IP src/dst attributes to entries.
 
+       fr_logfile_t    *lf;            //!< Log file handler
+
        fr_hash_table_t *ht;            //!< Holds suppressed attributes.
 } detail_instance_t;
 
 static const CONF_PARSER module_config[] = {
-       { "detailfile", PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, offsetof(detail_instance_t, filename),
-        NULL, NULL },
-       { "filename", PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, offsetof(detail_instance_t, filename),
-        NULL, "%A/%{Client-IP-Address}/detail" },
-       { "header", PW_TYPE_STRING_PTR, offsetof(detail_instance_t, header), NULL, "%t" },
-       { "detailperm", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, offsetof(detail_instance_t, perm), NULL, NULL },
-       { "permissions", PW_TYPE_INTEGER, offsetof(detail_instance_t, perm), NULL, "0600" },
-       { "group", PW_TYPE_STRING_PTR, offsetof(detail_instance_t, group), NULL,  NULL},
-       { "dir_permissions", PW_TYPE_INTEGER, offsetof(detail_instance_t, dirperm), NULL, "0755" },
-       { "locking", PW_TYPE_BOOLEAN, offsetof(detail_instance_t, locking), NULL, "no" },
-       { "log_packet_header", PW_TYPE_BOOLEAN, offsetof(detail_instance_t, log_srcdst), NULL, "no" },
+       { "detailfile", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, detail_instance_t, filename), NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, detail_instance_t, filename), "%A/%{Client-IP-Address}/detail" },
+       { "header", FR_CONF_OFFSET(PW_TYPE_STRING, detail_instance_t, header), "%t" },
+       { "detailperm", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, detail_instance_t, perm), NULL },
+       { "permissions", FR_CONF_OFFSET(PW_TYPE_INTEGER, detail_instance_t, perm), "0600" },
+       { "group", FR_CONF_OFFSET(PW_TYPE_STRING, detail_instance_t, group), NULL },
+       { "locking", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, detail_instance_t, locking), "no" },
+       { "log_packet_header", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, detail_instance_t, log_srcdst), "no" },
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -120,6 +117,12 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                inst->name = cf_section_name1(conf);
        }
 
+       inst->lf= fr_logfile_init(inst);
+       if (!inst->lf) {
+               cf_log_err_cs(conf, "Failed creating log file context");
+               return -1;
+       }
+
        /*
         *      Suppress certain attributes.
         */
@@ -176,6 +179,22 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        return 0;
 }
 
+/*
+ *     Wrapper for VPs allocated on the stack.
+ */
+static void detail_vp_print(TALLOC_CTX *ctx, FILE *out, VALUE_PAIR const *stacked)
+{
+       VALUE_PAIR *vp;
+
+       vp = talloc(ctx, VALUE_PAIR);
+       if (!vp) return;
+
+       memcpy(vp, stacked, sizeof(*vp));
+       vp_print(out, vp);
+       talloc_free(vp);
+}
+
+
 /** Write a single detail entry to file pointer
  *
  * @param[in] out Where to write entry.
@@ -195,7 +214,7 @@ static int detail_write(FILE *out, detail_instance_t *inst, REQUEST *request, RA
 
 #define WRITE(fmt, ...) do {\
        if (fprintf(out, fmt, ## __VA_ARGS__) < 0) {\
-               RERROR("Failed writing to detail file: %s", strerror(errno));\
+               RERROR("Failed writing to detail file: %s", fr_syserror(errno));\
                return -1;\
        }\
 } while(0)
@@ -210,7 +229,7 @@ static int detail_write(FILE *out, detail_instance_t *inst, REQUEST *request, RA
                 *      Print out names, if they're OK.
                 *      Numbers, if not.
                 */
-               if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+               if (is_radius_code(packet->code)) {
                        WRITE("\tPacket-Type = %s\n", fr_packet_codes[packet->code]);
                } else {
                        WRITE("\tPacket-Type = %d\n", packet->code);
@@ -246,24 +265,24 @@ static int detail_write(FILE *out, detail_instance_t *inst, REQUEST *request, RA
                        break;
                }
 
-               vp_print(out, &src_vp);
-               vp_print(out, &dst_vp);
+               detail_vp_print(request, out, &src_vp);
+               detail_vp_print(request, out, &dst_vp);
 
                src_vp.da = dict_attrbyvalue(PW_PACKET_SRC_PORT, 0);
                src_vp.vp_integer = packet->src_port;
                dst_vp.da = dict_attrbyvalue(PW_PACKET_DST_PORT, 0);
                dst_vp.vp_integer = packet->dst_port;
 
-               vp_print(out, &src_vp);
-               vp_print(out, &dst_vp);
+               detail_vp_print(request, out, &src_vp);
+               detail_vp_print(request, out, &dst_vp);
        }
 
        {
                vp_cursor_t cursor;
                /* Write each attribute/value to the log file */
-               for (vp = paircursor(&cursor, &packet->vps);
+               for (vp = fr_cursor_init(&cursor, &packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        if (inst->ht &&
                            fr_hash_table_finddata(inst->ht, vp->da)) continue;
 
@@ -304,17 +323,11 @@ static int detail_write(FILE *out, detail_instance_t *inst, REQUEST *request, RA
 /*
  *     Do detail, compatible with old accounting
  */
-static rlm_rcode_t detail_do(void *instance, REQUEST *request, RADIUS_PACKET *packet, bool compat)
+static rlm_rcode_t CC_HINT(nonnull) detail_do(void *instance, REQUEST *request, RADIUS_PACKET *packet, bool compat)
 {
        int             outfd;
        char            buffer[DIRLEN];
-       char            *p;
-       struct stat     st;
-       int             locked;
-       int             lock_count;
-       struct timeval  tv;
 
-       off_t           fsize;
        FILE            *outfp;
 
 #ifdef HAVE_GRP_H
@@ -325,15 +338,6 @@ static rlm_rcode_t detail_do(void *instance, REQUEST *request, RADIUS_PACKET *pa
 
        detail_instance_t *inst = instance;
 
-       rad_assert(request != NULL);
-
-       /*
-        *      Nothing to log: don't do anything.
-        */
-       if (!packet) {
-               return RLM_MODULE_NOOP;
-       }
-
        /*
         *      Generate the path for the detail file.  Use the same
         *      format, but truncate at the last /.  Then feed it
@@ -361,90 +365,12 @@ static rlm_rcode_t detail_do(void *instance, REQUEST *request, RADIUS_PACKET *pa
 #endif
 #endif
 
-       /*
-        *      Grab the last directory delimiter.
-        */
-       p = strrchr(buffer,'/');
-
-       /*
-        *      There WAS a directory delimiter there, and the file
-        *      doesn't exist, so we must create it the directories..
-        */
-       if (p) {
-               *p = '\0';
-
-               /*
-                *      Always try to create the directory.  If it
-                *      exists, rad_mkdir() will check via stat(), and
-                *      return immediately.
-                *
-                *      This catches the case where some idiot deleted
-                *      a directory that the server was using.
-                */
-               if (rad_mkdir(buffer, inst->dirperm) < 0) {
-                       RERROR("Failed to create directory %s: %s", buffer, strerror(errno));
-                       return RLM_MODULE_FAIL;
-               }
-
-               *p = '/';
-       } /* else there was no directory delimiter. */
-
-       locked = 0;
-       lock_count = 0;
-       do {
-               /*
-                *      Open & create the file, with the given
-                *      permissions.
-                */
-               if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, inst->perm)) < 0) {
-                       RERROR("Couldn't open file %s: %s", buffer, strerror(errno));
-                       return RLM_MODULE_FAIL;
-               }
-
-               /*
-                *      If we fail to aquire the filelock in 80 tries
-                *      (approximately two seconds) we bail out.
-                */
-               if (inst->locking) {
-                       lseek(outfd, 0L, SEEK_SET);
-                       if (rad_lockfd_nonblock(outfd, 0) < 0) {
-                               close(outfd);
-                               tv.tv_sec = 0;
-                               tv.tv_usec = 25000;
-                               select(0, NULL, NULL, NULL, &tv);
-                               lock_count++;
-                               continue;
-                       }
-
-                       /*
-                        *      The file might have been deleted by
-                        *      radrelay while we tried to acquire
-                        *      the lock (race condition)
-                        */
-                       if (fstat(outfd, &st) != 0) {
-                               RERROR("Couldn't stat file %s: %s", buffer, strerror(errno));
-                               close(outfd);
-                               return RLM_MODULE_FAIL;
-                       }
-                       if (st.st_nlink == 0) {
-                               RDEBUG2("File '%s' removed by another program, retrying", buffer);
-                               close(outfd);
-                               lock_count = 0;
-                               continue;
-                       }
-
-                       RDEBUG2("Acquired filelock, tried %d time(s)", lock_count + 1);
-                       locked = 1;
-               }
-       } while (inst->locking && !locked && lock_count < 80);
-
-       if (inst->locking && !locked) {
-               close(outfd);
-               RERROR("Failed to acquire filelock for '%s', giving up", buffer);
+       outfd = fr_logfile_open(inst->lf, buffer, inst->perm);
+       if (outfd < 0) {
+               RERROR("Couldn't open file %s: %s", buffer, fr_strerror());
                return RLM_MODULE_FAIL;
        }
 
-
 #ifdef HAVE_GRP_H
        if (inst->group != NULL) {
                gid = strtol(inst->group, &endptr, 10);
@@ -465,42 +391,24 @@ static rlm_rcode_t detail_do(void *instance, REQUEST *request, RADIUS_PACKET *pa
 skip_group:
 #endif
 
-       fsize = lseek(outfd, 0L, SEEK_END);
-       if (fsize < 0) {
-               RERROR("Failed to seek to the end of detail file '%s'", buffer);
-               close(outfd);
-               return RLM_MODULE_FAIL;
-       }
-
        /*
         *      Open the output fp for buffering.
         */
        if ((outfp = fdopen(outfd, "a")) == NULL) {
-               RERROR("Couldn't open file %s: %s", buffer, strerror(errno));
-               close(outfd);
+               RERROR("Couldn't open file %s: %s", buffer, fr_syserror(errno));
+       fail:
+               if (outfp) fclose(outfp);
+               fr_logfile_unlock(inst->lf, outfd);
                return RLM_MODULE_FAIL;
        }
 
-       if (detail_write(outfp, inst, request, packet, compat) < 0) {
-               fclose(outfp);
-               return RLM_MODULE_FAIL;
-       }
+       if (detail_write(outfp, inst, request, packet, compat) < 0) goto fail;
 
        /*
-        *      If we can't flush it to disk, truncate the file and return an error.
+        *      Flush everything
         */
-       if (fflush(outfp) != 0) {
-               int ret;
-               ret = ftruncate(outfd, fsize);
-               if (ret) {
-                       REDEBUG4("Failed truncating detail file: %s", strerror(ret));
-               }
-
-               fclose(outfp);
-               return RLM_MODULE_FAIL;
-       }
-
        fclose(outfp);
+       fr_logfile_unlock(inst->lf, outfd); /* do NOT close outfp */
 
        /*
         *      And everything is fine.
@@ -511,13 +419,13 @@ skip_group:
 /*
  *     Accounting - write the detail files.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
 #ifdef WITH_DETAIL
        if (request->listener->type == RAD_LISTEN_DETAIL &&
            strcmp(((detail_instance_t *)instance)->filename,
                   ((listen_detail_t *)request->listener->data)->filename) == 0) {
-               RDEBUG("Suppressing writes to detail file as the request was just read from a detail file.");
+               RDEBUG("Suppressing writes to detail file as the request was just read from a detail file");
                return RLM_MODULE_NOOP;
        }
 #endif
@@ -528,7 +436,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 /*
  *     Incoming Access Request - write the detail files.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        return detail_do(instance, request, request->packet, false);
 }
@@ -536,7 +444,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
 /*
  *     Outgoing Access-Request Reply - write the detail files.
  */
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        return detail_do(instance, request, request->reply, false);
 }
@@ -545,7 +453,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
 /*
  *     Incoming CoA - write the detail files.
  */
-static rlm_rcode_t mod_recv_coa(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_recv_coa(void *instance, REQUEST *request)
 {
        return detail_do(instance, request, request->packet, false);
 }
@@ -553,7 +461,7 @@ static rlm_rcode_t mod_recv_coa(void *instance, REQUEST *request)
 /*
  *     Outgoing CoA - write the detail files.
  */
-static rlm_rcode_t mod_send_coa(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_send_coa(void *instance, REQUEST *request)
 {
        return detail_do(instance, request, request->reply, false);
 }
@@ -563,7 +471,7 @@ static rlm_rcode_t mod_send_coa(void *instance, REQUEST *request)
  *     Outgoing Access-Request to home server - write the detail files.
  */
 #ifdef WITH_PROXY
-static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request)
 {
        if (request->proxy && request->proxy->vps) {
                return detail_do(instance, request, request->proxy, false);
@@ -576,7 +484,7 @@ static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
 /*
  *     Outgoing Access-Request Reply - write the detail files.
  */
-static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *instance, REQUEST *request)
 {
        if (request->proxy_reply && request->proxy_reply->vps) {
                return detail_do(instance, request, request->proxy_reply, false);
@@ -594,7 +502,7 @@ static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
 
                rcode = mod_accounting(instance, request);
                if (rcode == RLM_MODULE_OK) {
-                       request->reply->code = PW_ACCOUNTING_RESPONSE;
+                       request->reply->code = PW_CODE_ACCOUNTING_RESPONSE;
                }
                return rcode;
        }
@@ -607,7 +515,7 @@ static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
 module_t rlm_detail = {
        RLM_MODULE_INIT,
        "detail",
-       RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
+       RLM_TYPE_HUP_SAFE,
        sizeof(detail_instance_t),
        module_config,
        mod_instantiate,                /* instantiation */
index 15f934a..bdf0922 100644 (file)
@@ -56,8 +56,8 @@ static int digest_fix(REQUEST *request)
                return RLM_MODULE_NOOP;
        }
 
-       paircursor(&cursor, &first);
-       while ((i = pairfindnext(&cursor, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY))) {
+       fr_cursor_init(&cursor, &first);
+       while ((i = fr_cursor_next_by_num(&cursor, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY))) {
                int length = i->length;
                int attrlen;
                uint8_t const *p = i->vp_octets;
@@ -100,9 +100,9 @@ static int digest_fix(REQUEST *request)
        /*
         *      Convert them to something sane.
         */
-       RDEBUG("Digest-Attributes look OK.  Converting them to something more useful.");
-       pairfirst(&cursor);
-       while ((i = pairfindnext(&cursor, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY))) {
+       RDEBUG("Digest-Attributes look OK.  Converting them to something more useful");
+       fr_cursor_first(&cursor);
+       while ((i = fr_cursor_next_by_num(&cursor, PW_DIGEST_ATTRIBUTES, 0, TAG_ANY))) {
                int length = i->length;
                int attrlen;
                uint8_t const *p = &i->vp_octets[0];
@@ -145,7 +145,7 @@ static int digest_fix(REQUEST *request)
                         *
                         *      Didn't they know that VSA's exist?
                         */
-                       sub = radius_paircreate(request, &request->packet->vps,
+                       sub = radius_paircreate(request->packet, &request->packet->vps,
                                                PW_DIGEST_REALM - 1 + p[0], 0);
                        sub->length = attrlen - 2;
                        sub->vp_strvalue = q = talloc_array(sub, char, sub->length + 1);
@@ -169,7 +169,7 @@ static int digest_fix(REQUEST *request)
        return RLM_MODULE_OK;
 }
 
-static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
 {
        rlm_rcode_t rcode;
 
@@ -197,7 +197,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
 /*
  *     Perform all of the wondrous variants of digest authentication.
  */
-static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request)
 {
        int i;
        size_t a1_len, a2_len, kd_len;
@@ -215,14 +215,14 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
        passwd = pairfind(request->config_items, PW_DIGEST_HA1, 0, TAG_ANY);
        if (passwd) {
                if (passwd->length != 32) {
-                       RAUTH("Digest-HA1 has invalid length, authentication failed.");
+                       RAUTH("Digest-HA1 has invalid length, authentication failed");
                        return RLM_MODULE_INVALID;
                }
        } else {
                passwd = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        }
        if (!passwd) {
-               RAUTH("Cleartext-Password or Digest-HA1 is required for authentication.");
+               RAUTH("Cleartext-Password or Digest-HA1 is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
@@ -596,7 +596,7 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
 module_t rlm_digest = {
        RLM_MODULE_INIT,
        "digest",
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
        0,
        NULL,                           /* CONF_PARSER */
        NULL,                           /* instantiation */
index ef84ad4..eece16e 100644 (file)
@@ -30,7 +30,7 @@ RCSID("$Id$")
 /*
  *     Find the client definition.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance,
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance,
                                 REQUEST *request)
 {
        size_t length;
@@ -62,7 +62,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance,
 
        value = cf_pair_value(cp);
        if (!value) {
-               RDEBUG("No value given for the directory entry in the client.");
+               RDEBUG("No value given for the directory entry in the client");
                return RLM_MODULE_NOOP;
        }
 
@@ -91,9 +91,9 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance,
        return RLM_MODULE_OK;
 }
 #else
-static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
 {
-       RDEBUG("Dynamic clients are unsupported in this build.");
+       RDEBUG("Dynamic clients are unsupported in this build");
        return RLM_MODULE_FAIL;
 }
 #endif
index 6af01f2..8070171 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,10 +1255,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1835,7 +1835,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2057,7 +2057,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2683,11 +2683,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3189,7 +3189,7 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
 
@@ -3451,7 +3451,7 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
       ac_sub_configure=$ac_srcdir/configure.gnu
     elif test -f "$ac_srcdir/configure"; then
       ac_sub_configure=$ac_srcdir/configure
-    elif test -f "$ac_srcdir/configure.ac"; then
+    elif test -f "$ac_srcdir/configure.in"; then
       # This should be Cygnus configure.
       ac_sub_configure=$ac_aux_dir/configure
     else
index 4822094..321cb0a 100644 (file)
@@ -87,7 +87,7 @@ static int eap_module_free(void *ctx)
         *      debugging.  This removes the symbols needed by
         *      valgrind.
         */
-       if (!mainconfig.debug_memory)
+       if (!main_config.debug_memory)
 #endif
        if (inst->handle) dlclose(inst->handle);
 
@@ -115,7 +115,7 @@ int eap_module_load(rlm_eap_t *inst, eap_module_t **m_inst, eap_type_t num, CONF
        /*
         *      The name of the module were trying to load
         */
-       mod_name = talloc_asprintf(method, "rlm_eap_%s", method->name);
+       mod_name = talloc_typed_asprintf(method, "rlm_eap_%s", method->name);
 
        /*
         *      dlopen is case sensitive
@@ -236,7 +236,7 @@ static int eap_module_call(eap_module_t *module, eap_handler_t *handler)
  */
 static eap_type_t eap_process_nak(rlm_eap_t *inst, REQUEST *request,
                                    eap_type_t type,
-                                   eap_type_data_t *nak)
+                                   eap_type_data_t *nak)
 {
        unsigned int i;
        VALUE_PAIR *vp;
@@ -374,7 +374,7 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
         *      Multiple levels of nesting are invalid.
         */
        if (handler->request->parent && handler->request->parent->parent) {
-               RDEBUG2("Multiple levels of TLS nesting is invalid.");
+               RDEBUG2("Multiple levels of TLS nesting is invalid");
 
                return EAP_INVALID;
        }
@@ -474,7 +474,7 @@ eap_rcode_t eap_method_select(rlm_eap_t *inst, eap_handler_t *handler)
                                            handler) == 0) {
                                REDEBUG2("Failed continuing EAP %s (%d) session. "
                                         "EAP sub-module failed",
-                                        eap_type2name(type->num),
+                                        eap_type2name(type->num),
                                         type->num);
 
                                return EAP_INVALID;
@@ -548,14 +548,14 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
                         */
                case PW_EAP_SUCCESS:
                case PW_EAP_FAILURE:
-                       break;
+                       break;
 
                        /*
                         *      We've sent a response to their
                         *      request, the Id is incremented.
                         */
                default:
-                       ++reply->id;
+                       ++reply->id;
                }
        } else {
                RDEBUG2("Underlying EAP-Type set EAP ID to %d",
@@ -584,7 +584,7 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
        }
        eap_packet = (eap_packet_raw_t *)reply->packet;
 
-       vp = radius_paircreate(request, &request->reply->vps, PW_EAP_MESSAGE, 0);
+       vp = radius_paircreate(request->reply, &request->reply->vps, PW_EAP_MESSAGE, 0);
        if (!vp) return RLM_MODULE_INVALID;
 
        vp->length = eap_packet->length[0] * 256 + eap_packet->length[1];
@@ -610,19 +610,19 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
        rcode = RLM_MODULE_OK;
        if (!request->reply->code) switch(reply->code) {
        case PW_EAP_RESPONSE:
-               request->reply->code = PW_AUTHENTICATION_ACK;
+               request->reply->code = PW_CODE_AUTHENTICATION_ACK;
                rcode = RLM_MODULE_HANDLED; /* leap weirdness */
                break;
        case PW_EAP_SUCCESS:
-               request->reply->code = PW_AUTHENTICATION_ACK;
+               request->reply->code = PW_CODE_AUTHENTICATION_ACK;
                rcode = RLM_MODULE_OK;
                break;
        case PW_EAP_FAILURE:
-               request->reply->code = PW_AUTHENTICATION_REJECT;
+               request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                rcode = RLM_MODULE_REJECT;
                break;
        case PW_EAP_REQUEST:
-               request->reply->code = PW_ACCESS_CHALLENGE;
+               request->reply->code = PW_CODE_ACCESS_CHALLENGE;
                rcode = RLM_MODULE_HANDLED;
                break;
        default:
@@ -631,13 +631,13 @@ rlm_rcode_t eap_compose(eap_handler_t *handler)
                 *      we do so WITHOUT setting a reply code, as the
                 *      request is being proxied.
                 */
-               if (request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
+               if (request->log.lvl & RAD_REQUEST_OPTION_PROXY_EAP) {
                        return RLM_MODULE_HANDLED;
                }
 
                /* Should never enter here */
                ERROR("rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
-               request->reply->code = PW_AUTHENTICATION_REJECT;
+               request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                reply->code = PW_EAP_FAILURE;
                rcode = RLM_MODULE_REJECT;
                break;
@@ -667,7 +667,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         */
        vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
        if (vp && vp->vp_integer == 0) {
-               RDEBUG2("Found EAP-Message, but EAP-Type = None, so we're not doing EAP.");
+               RDEBUG2("Found EAP-Message, but EAP-Type = None, so we're not doing EAP");
                return EAP_NOOP;
        }
 
@@ -747,7 +747,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
        if (eap_msg->length < (EAP_HEADER_LEN + 1)) {
                if (proxy) goto do_proxy;
 
-               RDEBUG2("Ignoring EAP-Message which is too short to be meaningful.");
+               RDEBUG2("Ignoring EAP-Message which is too short to be meaningful");
                return EAP_FAIL;
        }
 
@@ -783,7 +783,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
            (eap_msg->vp_octets[0] >= PW_EAP_MAX_CODES)) {
                RDEBUG2("Unknown EAP packet");
        } else {
-               RDEBUG2("EAP packet type %s id %d length %d",
+               RDEBUG2("EAP packet type %s id %d length %zu",
                       eap_codes[eap_msg->vp_octets[0]],
                       eap_msg->vp_octets[1],
                       eap_msg->length);
@@ -797,7 +797,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
         */
        if ((eap_msg->vp_octets[0] != PW_EAP_REQUEST) &&
            (eap_msg->vp_octets[0] != PW_EAP_RESPONSE)) {
-               RDEBUG2("Ignoring EAP packet which we don't know how to handle.");
+               RDEBUG2("Ignoring EAP packet which we don't know how to handle");
                return EAP_FAIL;
        }
 
@@ -814,7 +814,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
            ((eap_msg->vp_octets[4] == 0) ||
             (eap_msg->vp_octets[4] >= PW_EAP_MAX_TYPES) ||
             (!inst->methods[eap_msg->vp_octets[4]]))) {
-               RDEBUG2(" Ignoring Unknown EAP type");
+               RDEBUG2("Ignoring Unknown EAP type");
                return EAP_NOOP;
        }
 
@@ -845,11 +845,11 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
 
        if ((eap_msg->vp_octets[4] == PW_EAP_TTLS) ||
            (eap_msg->vp_octets[4] == PW_EAP_PEAP)) {
-               RDEBUG2("Continuing tunnel setup.");
+               RDEBUG2("Continuing tunnel setup");
                return EAP_OK;
        }
        /*
-        * We return ok in response to EAP identity and MSCHAP success/fail
+        * We return ok in response to EAP identity
         * This means we can write:
         *
         * eap {
@@ -864,16 +864,6 @@ int eap_start(rlm_eap_t *inst, REQUEST *request)
                RDEBUG2("EAP-Identity reply, returning 'ok' so we can short-circuit the rest of authorize");
                return EAP_OK;
        }
-       if ((eap_msg->vp_octets[4] == PW_EAP_MSCHAPV2) && (eap_msg->length >= 6)) {
-               switch (eap_msg->vp_octets[5]) {
-                       case 3:
-                               RDEBUG2("EAP-MSCHAPV2 success, returning short-circuit ok");
-                               return EAP_OK;
-                       case 4:
-                               RDEBUG2("EAP-MSCHAPV2 failure, returning short-circuit ok");
-                               return EAP_OK;
-               }
-       }
 
        /*
         *      Later EAP messages are longer than the 'start'
@@ -927,8 +917,8 @@ static int eap_validation(REQUEST *request, eap_packet_raw_t *eap_packet)
         *      High level EAP packet checks
         */
        if ((len <= EAP_HEADER_LEN) ||
-           ((eap_packet->code != PW_EAP_RESPONSE) &&
-            (eap_packet->code != PW_EAP_REQUEST)) ||
+           ((eap_packet->code != PW_EAP_RESPONSE) &&
+            (eap_packet->code != PW_EAP_REQUEST)) ||
            (eap_packet->data[0] <= 0) ||
            (eap_packet->data[0] >= PW_EAP_MAX_TYPES)) {
 
@@ -1114,7 +1104,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                        */
                       if (strncmp(handler->identity, vp->vp_strvalue,
                                   MAX_STRING_LEN) != 0) {
-                              RDEBUG("Identity does not match User-Name.  Authentication failed.");
+                              RDEBUG("Identity does not match User-Name.  Authentication failed");
                               goto error;
                       }
               }
@@ -1158,7 +1148,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p,
                        */
                       if (strncmp(handler->identity, vp->vp_strvalue,
                                   MAX_STRING_LEN) != 0) {
-                              RDEBUG("Identity does not match User-Name, setting from EAP Identity.");
+                              RDEBUG("Identity does not match User-Name, setting from EAP Identity");
                               goto error2;
                       }
               }
index bb11101..dd068ea 100644 (file)
@@ -275,8 +275,8 @@ void comp128v1(uint8_t *sres, uint8_t *kc, uint8_t const *ki, uint8_t const *ran
 
        for (i = 0; i < 12; i += 2) {
                kc[i>>1] = (x[i + 18] << 6) |
-                          (x[i + 19] << 2) |
-                          (x[i + 20] >> 2);
+                          (x[i + 19] << 2) |
+                          (x[i + 20] >> 2);
        }
 
        kc[6] = (x[30] << 6) | (x[31] << 2);
index 06b1121..61b63d4 100644 (file)
@@ -64,7 +64,7 @@ void chbind_free(CHBIND_REQ *chbind)
 
 int chbind_process(REQUEST *req, CHBIND_REQ *chbind_req)
 {
-  int rcode = PW_AUTHENTICATION_REJECT;
+  int rcode = PW_CODE_AUTHENTICATION_REJECT;
   REQUEST *fake = NULL;
   VALUE_PAIR *vp = NULL;
   uint8_t *attr_data;
@@ -75,7 +75,7 @@ int chbind_process(REQUEST *req, CHBIND_REQ *chbind_req)
             (chbind_req != NULL) &&
             (chbind_req->chbind_req_pkt != NULL));
   if (chbind_req->chbind_req_len < 4)
-    return PW_AUTHENTICATION_REJECT;  /* Is this the right response? */
+    return PW_CODE_AUTHENTICATION_REJECT;  /* Is this the right response? */
 
   /* Set-up NULL response for cases where channel bindings can't be processed */
   chbind_req->chbind_resp = NULL;
@@ -116,7 +116,7 @@ int chbind_process(REQUEST *req, CHBIND_REQ *chbind_req)
                          /* If radaddr2vp fails, return NULL string for 
                             channel binding response */
                          request_free(&fake);
-                         return PW_AUTHENTICATION_ACK;
+                         return PW_CODE_AUTHENTICATION_ACK;
                  }
                  /* TODO: need to account for the possibility of rad_attr2vp generating 
                     multiple vps */
@@ -140,21 +140,21 @@ int chbind_process(REQUEST *req, CHBIND_REQ *chbind_req)
          fprintf(fr_log_fp, "server %s {\n",
            (fake->server == NULL) ? "" : fake->server);
   }
-  rcode = rad_authenticate(fake);
+  rcode = rad_virtual_server(fake);
 
   switch(rcode) {
     /* If rad_authenticate succeeded, build a reply */
   case RLM_MODULE_OK:
   case RLM_MODULE_HANDLED:
     if ((chbind_req->chbind_resp = chbind_build_response(fake, &chbind_req->chbind_resp_len)) != NULL)
-      rcode = PW_AUTHENTICATION_ACK;
+      rcode = PW_CODE_AUTHENTICATION_ACK;
     else
-      rcode = PW_AUTHENTICATION_REJECT;
+      rcode = PW_CODE_AUTHENTICATION_REJECT;
     break;
   
   /* If we got any other response from rad_authenticate, it maps to a reject */
   default:
-    rcode = PW_AUTHENTICATION_REJECT;
+    rcode = PW_CODE_AUTHENTICATION_REJECT;
     break;
   }
 
index 68e2cb8..5d0fae0 100644 (file)
@@ -52,6 +52,9 @@ RCSIDH(eap_sim_h, "$Id$")
 #define ATTRIBUTE_EAP_SIM_KC2     1213
 #define ATTRIBUTE_EAP_SIM_KC3     1214
 
+#define ATTRIBUTE_EAP_SIM_KI           1215
+#define ATTRIBUTE_EAP_SIM_ALGO_VERSION 1216
+
 enum eapsim_subtype {
   eapsim_start       = 10,
   eapsim_challenge   = 11,
@@ -79,25 +82,6 @@ enum eapsim_serverstates {
   eapsim_server_maxstates
 };
 
-#define PW_EAP_SIM_RAND                 1
-#define PW_EAP_SIM_PADDING           6
-#define PW_EAP_SIM_NONCE_MT         7
-#define PW_EAP_SIM_PERMANENT_ID_REQ    10
-#define PW_EAP_SIM_MAC          11
-#define PW_EAP_SIM_NOTIFICATION        12
-#define PW_EAP_SIM_ANY_ID_REQ    13
-#define PW_EAP_SIM_IDENTITY        14
-#define PW_EAP_SIM_VERSION_LIST        15
-#define PW_EAP_SIM_SELECTED_VERSION    16
-#define PW_EAP_SIM_FULLAUTH_ID_REQ     17
-#define PW_EAP_SIM_COUNTER          19
-#define PW_EAP_SIM_COUNTER_TOO_SMALL   20
-#define PW_EAP_SIM_NONCE_S          21
-#define PW_EAP_SIM_IV           129
-#define PW_EAP_SIM_ENCR_DATA     130
-#define PW_EAP_SIM_NEXT_PSEUDONUM     132
-#define PW_EAP_SIM_NEXT_REAUTH_ID     133
-#define PW_EAP_SIM_CHECKCODE     134
 
 /*
  * interfaces in eapsimlib.c
@@ -120,7 +104,7 @@ extern int unmap_eapsim_basictypes(RADIUS_PACKET *r,
 
 #define EAPSIM_SRES_SIZE 4
 #define EAPSIM_RAND_SIZE 16
-#define EAPSIM_Kc_SIZE   8
+#define EAPSIM_KC_SIZE   8
 #define EAPSIM_CALCMAC_SIZE 20
 #define EAPSIM_NONCEMT_SIZE 16
 #define EAPSIM_AUTH_SIZE    16
@@ -132,7 +116,7 @@ struct eapsim_keys {
   unsigned char nonce_mt[EAPSIM_NONCEMT_SIZE];
   unsigned char rand[3][EAPSIM_RAND_SIZE];
   unsigned char sres[3][EAPSIM_SRES_SIZE];
-  unsigned char Kc[3][EAPSIM_Kc_SIZE];
+  unsigned char Kc[3][EAPSIM_KC_SIZE];
   unsigned char versionlist[MAX_STRING_LEN];
   unsigned char versionlistlen;
   unsigned char versionselect[2];
index 1e31d8f..8b1ade7 100644 (file)
@@ -110,6 +110,7 @@ tls_session_t *eaptls_session(fr_tls_server_conf_t *tls_conf, eap_handler_t *han
        SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_STORE, (void *)tls_conf->ocsp_store);
 #endif
        SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_SSN, (void *)ssn);
+       SSL_set_ex_data(ssn->ssl, FR_TLS_EX_INDEX_TALLOC, (void *)tls_conf);
 
        return talloc_steal(handler, ssn); /* ssn */
 }
@@ -519,7 +520,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
                memcpy(&data_len, &eap_ds->response->type.data[1], 4);
                data_len = ntohl(data_len);
                if (data_len > MAX_RECORD_SIZE) {
-                       RDEBUG("The EAP-TLS packet will contain more data than we can process.");
+                       RDEBUG("The EAP-TLS packet will contain more data than we can process");
                        talloc_free(tlspacket);
                        return NULL;
                }
@@ -528,7 +529,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st
                DEBUG2(" TLS: %d %d\n", data_len, tlspacket->length);
 
                if (data_len < tlspacket->length) {
-                       RDEBUG("EAP-TLS packet claims to be smaller than the encapsulating EAP packet.");
+                       RDEBUG("EAP-TLS packet claims to be smaller than the encapsulating EAP packet");
                        talloc_free(tlspacket);
                        return NULL;
                }
@@ -656,7 +657,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status,
         */
        if (!tls_handshake_recv(handler->request, tls_session)) {
                DEBUG2("TLS receive handshake failed during operation");
-               eaptls_fail(handler, tls_session->peap_flag);
+               tls_fail(tls_session);
                return FR_TLS_FAIL;
        }
 
@@ -827,7 +828,7 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler)
                 */
                if ((status == FR_TLS_MORE_FRAGMENTS) ||
                    (status == FR_TLS_MORE_FRAGMENTS_WITH_LENGTH) ||
-                   (status == FR_TLS_FIRST_FRAGMENT)) {
+                   (status == FR_TLS_FIRST_FRAGMENT)) {
                        /*
                         *      Send the ACK.
                         */
@@ -974,7 +975,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr)
         *      The EAP RFC's say 1020, but we're less picky.
         */
        if (tls_conf->fragment_size < 100) {
-               ERROR("Fragment size is too small.");
+               ERROR("Fragment size is too small");
                return NULL;
        }
 
@@ -985,7 +986,7 @@ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr)
         *      that can be devoted *solely* to EAP.
         */
        if (tls_conf->fragment_size > 4000) {
-               ERROR("Fragment size is too large.");
+               ERROR("Fragment size is too large");
                return NULL;
        }
 
index 93a4953..4dbd69e 100644 (file)
@@ -43,10 +43,6 @@ USES_APPLE_DEPRECATED_API    /* OpenSSL API has been deprecated by Apple */
 #include <sys/time.h>
 #include <arpa/inet.h>
 
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
@@ -61,9 +57,9 @@ USES_APPLE_DEPRECATED_API     /* OpenSSL API has been deprecated by Apple */
  */
 fr_tls_status_t eaptls_process(eap_handler_t *handler);
 
-int            eaptls_success(eap_handler_t *handler, int peap_flag);
-int            eaptls_fail(eap_handler_t *handler, int peap_flag);
-int            eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn);
+int            eaptls_success(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull);
+int            eaptls_fail(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull);
+int            eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) CC_HINT(nonnull);
 
 
 /* MPPE key generation */
index 201ad07..507df40 100644 (file)
@@ -226,21 +226,21 @@ int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply)
        if (!packet->code) switch(reply->code) {
        case PW_EAP_RESPONSE:
        case PW_EAP_SUCCESS:
-               packet->code = PW_AUTHENTICATION_ACK;
+               packet->code = PW_CODE_AUTHENTICATION_ACK;
                rcode = RLM_MODULE_HANDLED;
                break;
        case PW_EAP_FAILURE:
-               packet->code = PW_AUTHENTICATION_REJECT;
+               packet->code = PW_CODE_AUTHENTICATION_REJECT;
                rcode = RLM_MODULE_REJECT;
                break;
        case PW_EAP_REQUEST:
-               packet->code = PW_ACCESS_CHALLENGE;
+               packet->code = PW_CODE_ACCESS_CHALLENGE;
                rcode = RLM_MODULE_HANDLED;
                break;
        default:
                /* Should never enter here */
                ERROR("rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
-               packet->code = PW_AUTHENTICATION_REJECT;
+               packet->code = PW_CODE_AUTHENTICATION_REJECT;
                break;
        }
 
@@ -265,7 +265,7 @@ VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
 
        ptr = (uint8_t const *) eap;
 
-       paircursor(&out, &head);
+       fr_cursor_init(&out, &head);
        do {
                size = total;
                if (size > 253) size = 253;
@@ -277,7 +277,7 @@ VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
                }
                pairmemcpy(vp, ptr, size);
 
-               pairinsert(&out, vp);
+               fr_cursor_insert(&out, vp);
 
                ptr += size;
                total -= size;
@@ -316,7 +316,7 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
         *      Sanity check the length before doing anything.
         */
        if (first->length < 4) {
-               DEBUG("rlm_eap: EAP packet is too short.");
+               DEBUG("rlm_eap: EAP packet is too short");
                return NULL;
        }
 
@@ -331,7 +331,7 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
         *      Take out even more weird things.
         */
        if (len < 4) {
-               DEBUG("rlm_eap: EAP packet has invalid length.");
+               DEBUG("rlm_eap: EAP packet has invalid length");
                return NULL;
        }
 
@@ -339,8 +339,8 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
         *      Sanity check the length, BEFORE allocating  memory.
         */
        total_len = 0;
-       paircursor(&cursor, &first);
-       while ((i = pairfindnext(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) {
+       fr_cursor_init(&cursor, &first);
+       while ((i = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) {
                total_len += i->length;
 
                if (total_len > len) {
@@ -371,8 +371,8 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
        ptr = (unsigned char *)eap_packet;
 
        /* RADIUS ensures order of attrs, so just concatenate all */
-       pairfirst(&cursor);
-       while ((i = pairfindnext(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) {
+       fr_cursor_first(&cursor);
+       while ((i = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) {
                memcpy(ptr, i->vp_strvalue, i->length);
                ptr += i->length;
        }
index 7388271..3430fb5 100644 (file)
@@ -44,9 +44,9 @@ void eapsim_calculate_keys(struct eapsim_keys *ek)
 
        p = buf;
        memcpy(p, ek->identity, ek->identitylen);   p = p+ek->identitylen;
-       memcpy(p, ek->Kc[0], EAPSIM_Kc_SIZE);       p = p+EAPSIM_Kc_SIZE;
-       memcpy(p, ek->Kc[1], EAPSIM_Kc_SIZE);       p = p+EAPSIM_Kc_SIZE;
-       memcpy(p, ek->Kc[2], EAPSIM_Kc_SIZE);       p = p+EAPSIM_Kc_SIZE;
+       memcpy(p, ek->Kc[0], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
+       memcpy(p, ek->Kc[1], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
+       memcpy(p, ek->Kc[2], EAPSIM_KC_SIZE);       p = p+EAPSIM_KC_SIZE;
        memcpy(p, ek->nonce_mt, sizeof(ek->nonce_mt)); p=p+sizeof(ek->nonce_mt);
        memcpy(p, ek->versionlist, ek->versionlistlen);p=p+ek->versionlistlen;
        memcpy(p, ek->versionselect, sizeof(ek->versionselect)); p=p+sizeof(ek->versionselect);
@@ -104,8 +104,6 @@ void eapsim_dump_mk(struct eapsim_keys *ek)
 {
        unsigned int i, j, k;
 
-       j=0; k=0;
-
        printf("Input was: \n");
        printf("   identity: (len=%d)", ek->identitylen);
        for (i = 0; i < ek->identitylen; i++) {
@@ -133,7 +131,7 @@ void eapsim_dump_mk(struct eapsim_keys *ek)
 
        for (k = 0; k<3; k++) {
                printf("\n   Kc%d: ", k);
-               for (i = 0; i < EAPSIM_Kc_SIZE; i++) {
+               for (i = 0; i < EAPSIM_KC_SIZE; i++) {
                        printf("%02x", ek->Kc[k][i]);
                }
        }
@@ -150,7 +148,7 @@ void eapsim_dump_mk(struct eapsim_keys *ek)
        printf("\n\nOutput\n");
 
        printf("mk:      ");
-       j=0; k=0;
+       j=0;
        for (i = 0; i < sizeof(ek->master_key); i++) {
                if(j==4) {
                        printf("_");
@@ -162,7 +160,7 @@ void eapsim_dump_mk(struct eapsim_keys *ek)
        }
 
        printf("\nK_aut:      ");
-       j=0; k=0;
+       j=0;
        for (i = 0; i < sizeof(ek->K_aut); i++) {
                if(j==4) {
                        printf("_");
@@ -174,7 +172,7 @@ void eapsim_dump_mk(struct eapsim_keys *ek)
        }
 
        printf("\nK_encr:     ");
-       j=0; k=0;
+       j=0;
        for (i = 0; i < sizeof(ek->K_encr); i++) {
                if(j==4) {
                        printf("_");
index bafb329..c2975ed 100644 (file)
@@ -95,9 +95,9 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
         * that we need to encode all of this.
         */
        encoded_size = 0;
-       for (vp = paircursor(&cursor, &r->vps);
+       for (vp = fr_cursor_init(&cursor, &r->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                int roundedlen;
                int vplen;
 
@@ -112,7 +112,7 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
                 * attribute, we pull the contents out, save it for later
                 * processing, set the size to 16 bytes (plus 2 bytes padding).
                 *
-                * At this point, we only care about the size.
+                * At this point, we only care about the size.
                 */
                if(vp->da->attr == ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_MAC) {
                        vplen = 18;
@@ -179,7 +179,7 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
         */
        attr = encodedmsg+3;
 
-       for (vp = pairfirst(&cursor); vp; vp = pairnext(&cursor)) {
+       for (vp = fr_cursor_first(&cursor); vp; vp = fr_cursor_next(&cursor)) {
                int roundedlen;
 
                if(vp->da->attr < ATTRIBUTE_EAP_SIM_BASE ||
@@ -192,7 +192,7 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
                 * attribute, we pull the contents out, save it for later
                 * processing, set the size to 16 bytes (plus 2 bytes padding).
                 *
-                * At this point, we put in zeros, and remember where the
+                * At this point, we put in zeros, and remember where the
                 * sixteen bytes go.
                 */
                if(vp->da->attr == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) {
@@ -255,7 +255,7 @@ int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
                fr_hmac_sha1(buffer, hmaclen, vp->vp_octets, vp->length, sha1digest);
 
                /* done with the buffer, free it */
-               free(buffer);
+               talloc_free(buffer);
 
                /* now copy the digest to where it belongs in the AT_MAC */
                /* note that it is truncated to 128-bits */
@@ -408,7 +408,7 @@ int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_S
                 */
                attr = buffer+8;
                while(attr < (buffer+elen)) {
-                       if(attr[0] == PW_EAP_SIM_MAC) {
+                       if (attr[0] == (PW_EAP_SIM_MAC - PW_EAP_SIM_BASE)) {
                                /* zero the data portion, after making sure
                                 * the size is >=5. Maybe future versions.
                                 * will use more bytes, so be liberal.
index 4d098be..62d8797 100644 (file)
@@ -133,7 +133,7 @@ void fips186_2prf(uint8_t mk[20], uint8_t finalkey[160])
                /*   b. w_0 = SHA1(XVAL)  */
                fr_SHA1Init(&context);
 
-               memset(zeros, 0, sizeof(zeros));
+               memset(zeros + 20, 0, sizeof(zeros) - 20);
                memcpy(zeros, xval.p, 20);
 #ifndef WITH_OPENSSL_SHA1
                fr_SHA1Transform(context.state, zeros);
@@ -152,7 +152,7 @@ void fips186_2prf(uint8_t mk[20], uint8_t finalkey[160])
                /*   e. w_1 = SHA1(XVAL)  */
                fr_SHA1Init(&context);
 
-               memset(zeros, 0, sizeof(zeros));
+               memset(zeros + 20, 0, sizeof(zeros) - 20);
                memcpy(zeros, xval.p, 20);
 #ifndef WITH_OPENSSL_SHA1
                fr_SHA1Transform(context.state, zeros);
index 067d053..c8c38ff 100644 (file)
@@ -113,7 +113,7 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s,
        size_t prf_size;
 
        if (!s->s3) {
-               EDEBUG("No SSLv3 information");
+               ERROR("No SSLv3 information");
                return;
        }
 
@@ -157,7 +157,7 @@ void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size)
        uint8_t *p = seed;
 
        if (!s->s3) {
-               EDEBUG("No SSLv3 information");
+               ERROR("No SSLv3 information");
                return;
        }
 
@@ -183,7 +183,7 @@ void eaptls_gen_eap_key(RADIUS_PACKET *packet, SSL *s, uint32_t header)
        uint8_t *p;
 
        if (!s->s3) {
-               EDEBUG("No SSLv3 information");
+               ERROR("No SSLv3 information");
                return;
        }
 
index 94af4ca..ccafa74 100644 (file)
@@ -132,7 +132,7 @@ typedef struct check_handler_t {
 
 static int check_opaque_free(check_handler_t *check)
 {
-       int do_warning = false;
+       bool do_warning = false;
        uint8_t state[8];
 
        if (!check->inst || !check->handler) {
@@ -176,15 +176,15 @@ done:
        PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex));
 
        if (do_warning) {
-               WDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
-               WDEBUG("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!  !!",
+               WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+               WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!  !!",
                      state[0], state[1],
                      state[2], state[3],
                      state[4], state[5],
                      state[6], state[7]);
 
-               WDEBUG("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
-               WDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+               WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
+               WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
        }
 
        return 0;
@@ -194,7 +194,7 @@ void eaplist_free(rlm_eap_t *inst)
 {
        eap_handler_t *node, *next;
 
-               for (node = inst->session_head; node != NULL; node = next) {
+       for (node = inst->session_head; node != NULL; node = next) {
                next = node->next;
                talloc_free(node);
        }
@@ -286,7 +286,7 @@ static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
                 *      Expire entries from the start of the list.
                 *      They should be the oldest ones.
                 */
-               if ((timestamp - handler->timestamp) > inst->timer_limit) {
+               if ((timestamp - handler->timestamp) > (int)inst->timer_limit) {
                        rbnode_t *node;
                        node = rbtree_find(inst->session_tree, handler);
                        rad_assert(node != NULL);
@@ -321,9 +321,6 @@ int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
        VALUE_PAIR      *state;
        REQUEST         *request = handler->request;
 
-       rad_assert(handler != NULL);
-       rad_assert(request != NULL);
-
        /*
         *      Generate State, since we've been asked to add it to
         *      the list.
index 7761c01..6eeb7cb 100644 (file)
@@ -46,14 +46,18 @@ static float timeout = 3;
 static char const *secret = NULL;
 static int do_output = 1;
 static int do_summary = 0;
-static int filedone = 0;
+static bool filedone = false;
 static int totalapp = 0;
 static int totaldeny = 0;
 static char filesecret[256];
 char const *radius_dir = NULL;
+char const *dict_dir = NULL;
 char const *progname = "radeapclient";
 /* fr_randctx randctx; */
 
+struct main_config_t main_config;
+char const *radiusd_version = "";
+
 #ifdef WITH_TLS
 #include <freeradius-devel/tls.h>
 #endif
@@ -80,7 +84,6 @@ static void NEVER_RETURNS usage(void)
        fprintf(stderr, "Usage: radeapclient [options] server[:port] <command> [<secret>]\n");
 
        fprintf(stderr, "  <command>    One of auth, acct, status, or disconnect.\n");
-       fprintf(stderr, "  -c count    Send each packet 'count' times.\n");
        fprintf(stderr, "  -d raddb    Set dictionary directory.\n");
        fprintf(stderr, "  -f file     Read packets from file, not stdin.\n");
        fprintf(stderr, "  -r retries  If timeout, retry sending the packet 'retries' times.\n");
@@ -98,13 +101,11 @@ static void NEVER_RETURNS usage(void)
        exit(1);
 }
 
-int radlog(log_type_t lvl, char const *fmt, ...)
+int radlog(UNUSED log_type_t lvl, char const *fmt, ...)
 {
        va_list ap;
        int r;
 
-       r = lvl; /* shut up compiler */
-
        va_start(ap, fmt);
        r = vfprintf(stderr, fmt, ap);
        va_end(ap);
@@ -113,18 +114,14 @@ int radlog(log_type_t lvl, char const *fmt, ...)
        return r;
 }
 
-void radlog_request(UNUSED log_type_t lvl, UNUSED log_debug_t priority,
-                   UNUSED REQUEST *request, char const *msg, ...)
+void vradlog_request(UNUSED log_type_t lvl, UNUSED log_debug_t priority,
+                    UNUSED REQUEST *request, char const *msg, va_list ap)
 {
-       va_list ap;
-
-       va_start(ap, msg);
        vfprintf(stderr, msg, ap);
-       va_end(ap);
        fputc('\n', stderr);
 }
 
-static int getport(char const *name)
+static uint16_t getport(char const *name)
 {
        struct  servent         *svp;
 
@@ -144,7 +141,7 @@ static void debug_packet(RADIUS_PACKET *packet, int direction)
        char buffer[1024];
        char const *received, *from;
        fr_ipaddr_t const *ip;
-       int port;
+       uint16_t port;
 
        if (!packet) return;
 
@@ -167,7 +164,7 @@ static void debug_packet(RADIUS_PACKET *packet, int direction)
         *
         *      This really belongs in a utility library
         */
-       if ((packet->code > 0) && (packet->code < FR_MAX_PACKET_CODE)) {
+       if (is_radius_code(packet->code)) {
                printf("%s %s packet %s host %s port %d, id=%d, length=%d\n",
                       received, fr_packet_codes[packet->code], from,
                       inet_ntop(ip->af, &ip->ipaddr, buffer, sizeof(buffer)),
@@ -193,12 +190,17 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
        int i;
        struct timeval  tv;
 
+       if (!req || !rep || !*rep) return -1;
+
        for (i = 0; i < retries; i++) {
                fd_set          rdfdesc;
 
                debug_packet(req, R_SENT);
 
-               rad_send(req, NULL, secret);
+               if (rad_send(req, NULL, secret) < 0) {
+                       fr_perror("radeapclient");
+                       exit(1);
+               }
 
                /* And wait for reply, timing out as necessary */
                FD_ZERO(&rdfdesc);
@@ -237,7 +239,7 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
                        }
                        break;
                } else {        /* NULL: couldn't receive the packet */
-                       fr_perror("radclient:");
+                       fr_perror("radclient");
                        exit(1);
                }
        }
@@ -253,7 +255,7 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
         *
         *      Hmm... we should really be using eapol_test, which does
         *      a lot more than radeapclient.
-                */
+        */
        if (rad_verify(*rep, req, secret) != 0) {
                fr_perror("rad_verify");
                exit(1);
@@ -268,9 +270,9 @@ static int send_packet(RADIUS_PACKET *req, RADIUS_PACKET **rep)
        if (!fr_debug_flag && do_output) {
                debug_packet(*rep, R_RECV);
        }
-       if((*rep)->code == PW_AUTHENTICATION_ACK) {
+       if((*rep)->code == PW_CODE_AUTHENTICATION_ACK) {
                totalapp++;
-       } else if ((*rep)->code == PW_AUTHENTICATION_REJECT) {
+       } else if ((*rep)->code == PW_CODE_AUTHENTICATION_REJECT) {
                totaldeny++;
        }
 
@@ -405,32 +407,38 @@ static int process_eap_start(RADIUS_PACKET *req,
        pairreplace(&(rep->vps), newvp);
 
        /* insert selected version into response. */
-       newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION, 0);
-       versions = (uint16_t const *)newvp->vp_strvalue;
-       versions[0] = htons(selectedversion);
-       newvp->length = 2;
-       pairreplace(&(rep->vps), newvp);
+       {
+               uint16_t no_versions;
+
+               no_versions = htons(selectedversion);
+
+               newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_SELECTED_VERSION, 0);
+               pairmemcpy(newvp, (uint8_t *) &no_versions, 2);
+               pairreplace(&(rep->vps), newvp);
 
-       /* record the selected version */
-       memcpy(eapsim_mk.versionselect, (unsigned char const *)versions, 2);
+               /* record the selected version */
+               memcpy(eapsim_mk.versionselect, &no_versions, 2);
+       }
 
        vp = newvp = NULL;
 
        {
                uint32_t nonce[4];
+               uint8_t *p;
                /*
                 * insert a nonce_mt that we make up.
                 */
-               newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, 0);
-               newvp->vp_octets[0]=0;
-               newvp->vp_octets[1]=0;
-               newvp->length = 18;  /* 16 bytes of nonce + padding */
-
                nonce[0]=fr_rand();
                nonce[1]=fr_rand();
                nonce[2]=fr_rand();
                nonce[3]=fr_rand();
-               memcpy(&newvp->vp_octets[2], nonce, 16);
+
+               newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, 0);
+
+               p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */
+               memcpy(&p[2], nonce, 16);
+               pairmemsteal(newvp, p);
+
                pairreplace(&(rep->vps), newvp);
 
                /* also keep a copy of the nonce! */
@@ -438,7 +446,9 @@ static int process_eap_start(RADIUS_PACKET *req,
        }
 
        {
-               uint16_t *pidlen, idlen;
+               uint16_t idlen;
+               uint8_t *p;
+               uint16_t no_idlen;
 
                /*
                 * insert the identity here.
@@ -450,12 +460,14 @@ static int process_eap_start(RADIUS_PACKET *req,
                        return 0;
                }
                newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY, 0);
+
                idlen = strlen(vp->vp_strvalue);
-               pidlen = (uint16_t *)newvp->vp_strvalue;
-               *pidlen = htons(idlen);
-               newvp->length = idlen + 2;
+               p = talloc_zero_array(newvp, uint8_t, idlen + 2);
+               no_idlen = htons(idlen);
+               memcpy(p, &no_idlen, 2);
+               memcpy(p + 2, vp->vp_strvalue, idlen);
+               pairmemsteal(newvp, p);
 
-               memcpy(&newvp->vp_strvalue[2], vp->vp_strvalue, idlen);
                pairreplace(&(rep->vps), newvp);
 
                /* record it */
@@ -499,7 +511,7 @@ static int process_eap_challenge(RADIUS_PACKET *req,
         */
        {
          VALUE_PAIR *randcfgvp[3];
-         uint8_t *randcfg[3];
+         uint8_t const *randcfg[3];
 
          randcfg[0] = &randvp->vp_octets[2];
          randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
@@ -623,20 +635,26 @@ static int process_eap_challenge(RADIUS_PACKET *req,
        newvp->vp_integer = eapsim_challenge;
        pairreplace(&(rep->vps), newvp);
 
-       /*
-        * fill the SIM_MAC with a field that will in fact get appended
-        * to the packet before the MAC is calculated
-        */
-       newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0);
-       memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
-       memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
-       memcpy(newvp->vp_strvalue+EAPSIM_SRES_SIZE*2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
-       newvp->length = EAPSIM_SRES_SIZE*3;
-       pairreplace(&(rep->vps), newvp);
+       {
+               uint8_t *p;
+               /*
+                * fill the SIM_MAC with a field that will in fact get appended
+                * to the packet before the MAC is calculated
+                */
+               newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0);
+
+               p = talloc_zero_array(newvp, uint8_t, EAPSIM_SRES_SIZE*3);
+               memcpy(p+EAPSIM_SRES_SIZE * 0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
+               memcpy(p+EAPSIM_SRES_SIZE * 1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
+               memcpy(p+EAPSIM_SRES_SIZE * 2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
+               pairmemsteal(newvp, p);
+
+               pairreplace(&(rep->vps), newvp);
+       }
 
        newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_KEY, 0);
-       memcpy(newvp->vp_strvalue,    eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
-       newvp->length = EAPSIM_AUTH_SIZE;
+       pairmemcpy(newvp, eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);
+
        pairreplace(&(rep->vps), newvp);
 
        return 1;
@@ -756,10 +774,9 @@ static int respond_eap_md5(RADIUS_PACKET *req,
                           RADIUS_PACKET *rep)
 {
        VALUE_PAIR *vp, *id, *state;
-       size_t valuesize, namesize;
+       size_t valuesize;
        uint8_t identifier;
        uint8_t const *value;
-       uint8_t const *name;
        FR_MD5_CTX      context;
        uint8_t    response[16];
 
@@ -787,8 +804,6 @@ static int respond_eap_md5(RADIUS_PACKET *req,
        /* got the details of the MD5 challenge */
        valuesize = vp->vp_octets[0];
        value = &vp->vp_octets[1];
-       name  = &vp->vp_octets[valuesize+1];
-       namesize = vp->length - (valuesize + 1);
 
        /* sanitize items */
        if(valuesize > vp->length)
@@ -808,11 +823,19 @@ static int respond_eap_md5(RADIUS_PACKET *req,
        fr_MD5Update(&context, value, valuesize);
        fr_MD5Final(response, &context);
 
-       vp = paircreate(rep, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0);
-       vp->vp_octets[0]=16;
-       memcpy(&vp->vp_strvalue[1], response, 16);
-       vp->length = 17;
+       {
+               uint8_t *p;
+               uint8_t lg_response;
+
+               vp = paircreate(rep, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0);
+               vp->length = 17;
 
+               p = talloc_zero_array(vp, uint8_t, 17);
+               lg_response = 16;
+               memcpy(p, &lg_response, 1);
+               memcpy(p + 1, response, 16);
+               pairmemsteal(vp, p);
+       }
        pairreplace(&(rep->vps), vp);
 
        pairreplace(&(rep->vps), id);
@@ -831,11 +854,8 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
        VALUE_PAIR *vp, *vpnext;
        int tried_eap_md5 = 0;
 
-       if (vp->length > sizeof(password)) {
-               fprintf(stderr, "radeapclient: Password buffer too small have %zu bytes need %zu bytes\n",
-                       sizeof(password), vp->length);
-               return 0;
-       }
+       if (!rep) return -1;
+
        /*
         *      Keep a copy of the the User-Password attribute.
         */
@@ -881,11 +901,37 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
                case PW_DIGEST_NONCE_COUNT:
                case PW_DIGEST_USER_NAME:
                        /* overlapping! */
-                       memmove(&vp->vp_strvalue[2], &vp->vp_octets[0], vp->length);
-                       vp->vp_octets[0] = vp->da->attr - PW_DIGEST_REALM + 1;
-                       vp->length += 2;
-                       vp->vp_octets[1] = vp->length;
-                       vp->da->attr = PW_DIGEST_ATTRIBUTES;
+                       {
+                               DICT_ATTR const *da;
+                               uint8_t *p, *q;
+
+                               p = talloc_array(vp, uint8_t, vp->length + 2);
+
+                               memcpy(p + 2, vp->vp_octets, vp->length);
+                               p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
+                               vp->length += 2;
+                               p[1] = vp->length;
+
+                               da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
+                               vp->da = da;
+
+                               /*
+                                *      Re-do pairmemsteal ourselves,
+                                *      because we play games with
+                                *      vp->da, and pairmemsteal goes
+                                *      to GREAT lengths to sanitize
+                                *      and fix and change and
+                                *      double-check the various
+                                *      fields.
+                                */
+                               memcpy(&q, &vp->vp_octets, sizeof(q));
+                               talloc_free(q);
+
+                               vp->vp_octets = talloc_steal(vp, p);
+                               vp->type = VT_DATA;
+
+                               VERIFY_VP(vp);
+                       }
                        break;
                }
        }
@@ -913,14 +959,18 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
                } else if ((vp = pairfind(rep->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) != NULL) {
                        pairstrcpy(vp, password);
 
-                       rad_chap_encode(rep, vp->vp_octets, rep->id, vp);
-                       vp->length = 17;
+                       uint8_t *p;
+                       p = talloc_zero_array(vp, uint8_t, 17);
+                       rad_chap_encode(rep, p, rep->id, vp);
+                       pairmemsteal(vp, p);
                }
        } /* there WAS a password */
 
        /* send the response, wait for the next request */
        send_packet(rep, &req);
 
+       if (!req) return -1;
+
        /* okay got back the packet, go and decode the EAP-Message. */
        unmap_eap_methods(req);
 
@@ -955,24 +1005,45 @@ static int sendrecv_eap(RADIUS_PACKET *rep)
 }
 
 
+void set_radius_dir(TALLOC_CTX *ctx, char const *path)
+{
+       if (radius_dir) {
+               char *p;
+
+               memcpy(&p, &radius_dir, sizeof(p));
+               talloc_free(p);
+               radius_dir = NULL;
+       }
+       if (path) radius_dir = talloc_strdup(ctx, path);
+}
+
+
 int main(int argc, char **argv)
 {
        RADIUS_PACKET *req;
        char *p;
        int c;
-       int port = 0;
+       uint16_t port = 0;
        char *filename = NULL;
        FILE *fp;
-       int count = 1;
        int id;
        int force_af = AF_UNSPEC;
 
+       /*
+        *      We probably don't want to free the talloc autofree context
+        *      directly, so we'll allocate a new context beneath it, and
+        *      free that before any leak reports.
+        */
+       TALLOC_CTX *autofree = talloc_init("main");
+
        id = ((int)getpid() & 0xff);
        fr_debug_flag = 0;
 
        radlog_dest = L_DST_STDERR;
 
-       while ((c = getopt(argc, argv, "46c:d:f:hi:qst:r:S:xXv")) != EOF)
+       set_radius_dir(autofree, RADIUS_DIR);
+
+       while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF)
        {
                switch(c) {
                case '4':
@@ -981,13 +1052,11 @@ int main(int argc, char **argv)
                case '6':
                        force_af = AF_INET6;
                        break;
-               case 'c':
-                       if (!isdigit((int) *optarg))
-                               usage();
-                       count = atoi(optarg);
-                       break;
                case 'd':
-                       radius_dir = strdup(optarg);
+                       set_radius_dir(autofree, optarg);
+                       break;
+               case 'D':
+                       main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg);
                        break;
                case 'f':
                        filename = optarg;
@@ -1030,19 +1099,19 @@ int main(int argc, char **argv)
                        timeout = atof(optarg);
                        break;
                case 'v':
-                       printf("radclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
+                       printf("radeapclient: $Id$ built on " __DATE__ " at " __TIME__ "\n");
                        exit(0);
                        break;
               case 'S':
                       fp = fopen(optarg, "r");
                       if (!fp) {
                               fprintf(stderr, "radclient: Error opening %s: %s\n",
-                                      optarg, strerror(errno));
+                                      optarg, fr_syserror(errno));
                               exit(1);
                       }
                       if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
                               fprintf(stderr, "radclient: Error reading %s: %s\n",
-                                      optarg, strerror(errno));
+                                      optarg, fr_syserror(errno));
                               exit(1);
                       }
                       fclose(fp);
@@ -1075,11 +1144,38 @@ int main(int argc, char **argv)
                usage();
        }
 
-       if (!radius_dir) radius_dir = strdup(RADDBDIR);
+       if (!main_config.dictionary_dir) {
+               main_config.dictionary_dir = DICTDIR;
+       }
 
-       if (dict_init(radius_dir, RADIUS_DICTIONARY) < 0) {
-               fr_perror("radclient");
-               return 1;
+       /*
+        *      Read the distribution dictionaries first, then
+        *      the ones in raddb.
+        */
+       DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY);
+       if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) {
+               ERROR("Errors reading dictionary: %s",
+                     fr_strerror());
+               exit(1);
+       }
+
+       /*
+        *      It's OK if this one doesn't exist.
+        */
+       int rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
+       if (rcode == -1) {
+               ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY,
+                     fr_strerror());
+               exit(1);
+       }
+
+       /*
+        *      We print this after reading it.  That way if
+        *      it doesn't exist, it's OK, and we don't print
+        *      anything.
+        */
+       if (rcode == 0) {
+               DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY);
        }
 
        req = rad_alloc(NULL, 1);
@@ -1136,8 +1232,8 @@ int main(int argc, char **argv)
                        portname = NULL;
                }
 
-               if (ip_hton(hostname, force_af, &req->dst_ipaddr) < 0) {
-                       fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, strerror(errno));
+               if (ip_hton(&req->dst_ipaddr, force_af, hostname, false) < 0) {
+                       fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno));
                        exit(1);
                }
 
@@ -1153,22 +1249,22 @@ int main(int argc, char **argv)
        if (strcmp(argv[2], "auth") == 0) {
                if (port == 0) port = getport("radius");
                if (port == 0) port = PW_AUTH_UDP_PORT;
-               req->code = PW_AUTHENTICATION_REQUEST;
+               req->code = PW_CODE_AUTHENTICATION_REQUEST;
 
        } else if (strcmp(argv[2], "acct") == 0) {
                if (port == 0) port = getport("radacct");
                if (port == 0) port = PW_ACCT_UDP_PORT;
-               req->code = PW_ACCOUNTING_REQUEST;
+               req->code = PW_CODE_ACCOUNTING_REQUEST;
                do_summary = 0;
 
        } else if (strcmp(argv[2], "status") == 0) {
                if (port == 0) port = getport("radius");
                if (port == 0) port = PW_AUTH_UDP_PORT;
-               req->code = PW_STATUS_SERVER;
+               req->code = PW_CODE_STATUS_SERVER;
 
        } else if (strcmp(argv[2], "disconnect") == 0) {
                if (port == 0) port = PW_POD_UDP_PORT;
-               req->code = PW_DISCONNECT_REQUEST;
+               req->code = PW_CODE_DISCONNECT_REQUEST;
 
        } else if (isdigit((int) argv[2][0])) {
                if (port == 0) port = getport("radius");
@@ -1193,7 +1289,7 @@ int main(int argc, char **argv)
                fp = fopen(filename, "r");
                if (!fp) {
                        fprintf(stderr, "radclient: Error opening %s: %s\n",
-                               filename, strerror(errno));
+                               filename, fr_syserror(errno));
                        exit(1);
                }
        } else {
@@ -1209,21 +1305,22 @@ int main(int argc, char **argv)
        }
 
        while(!filedone) {
-               if(req->vps) pairfree(&req->vps);
-
-               if ((req->vps = readvp2(NULL, fp, &filedone, "radeapclient:"))
-                   == NULL) {
+               if (req->vps) pairfree(&req->vps);
+               if (readvp2(&req->vps, NULL, fp, &filedone) < 0) {
+                       fr_perror("radeapclient");
                        break;
                }
 
                sendrecv_eap(req);
        }
 
-       free(radius_dir);
        if(do_summary) {
                printf("\n\t   Total approved auths:  %d\n", totalapp);
                printf("\t     Total denied auths:  %d\n", totaldeny);
        }
+
+       talloc_free(autofree);
+
        return 0;
 }
 
@@ -1243,9 +1340,10 @@ static void map_eap_methods(RADIUS_PACKET *req)
 {
        VALUE_PAIR *vp, *vpnext;
        int id, eapcode;
-       eap_packet_t ep;
        int eap_method;
 
+       eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t);
+
        vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY);
        if(!vp) {
                id = ((int)getpid() & 0xff);
@@ -1260,7 +1358,6 @@ static void map_eap_methods(RADIUS_PACKET *req)
                eapcode = vp->vp_integer;
        }
 
-
        for(vp = req->vps; vp != NULL; vp = vpnext) {
                /* save it in case it changes! */
                vpnext = vp->next;
@@ -1297,13 +1394,15 @@ static void map_eap_methods(RADIUS_PACKET *req)
                /* nuke any existing EAP-Messages */
                pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY);
 
-               memset(&ep, 0, sizeof(ep));
-               ep.code = eapcode;
-               ep.id   = id;
-               ep.type.num = eap_method;
-               ep.type.length = vp->length;
-               ep.type.data = vp->vp_octets; /* no need for copy */
-               eap_basic_compose(req, &ep);
+               pt_ep->code = eapcode;
+               pt_ep->id = id;
+               pt_ep->type.num = eap_method;
+               pt_ep->type.length = vp->length;
+
+               pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->length);
+               talloc_set_type(pt_ep->type.data, uint8_t);
+
+               eap_basic_compose(req, pt_ep);
        }
 }
 
@@ -1318,6 +1417,8 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
        int len;
        int type;
 
+       if (!rep) return;
+
        /* find eap message */
        e = eap_vp2packet(NULL, rep->vps);
 
@@ -1363,13 +1464,13 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
                type += ATTRIBUTE_EAP_BASE;
                len -= 5;
 
-               if(len > MAX_STRING_LEN) {
+               if (len > MAX_STRING_LEN) {
                        len = MAX_STRING_LEN;
                }
 
                eap1 = paircreate(rep, type, 0);
-               memcpy(eap1->vp_strvalue, &e->data[1], len);
-               eap1->length = len;
+               pairmemcpy(eap1, e->data + 1, len);
+
                pairadd(&(rep->vps), eap1);
                break;
        }
@@ -1380,15 +1481,17 @@ static void unmap_eap_methods(RADIUS_PACKET *rep)
 
 static int map_eapsim_types(RADIUS_PACKET *r)
 {
-       eap_packet_t ep;
        int ret;
 
-       memset(&ep, 0, sizeof(ep));
-       ret = map_eapsim_basictypes(r, &ep);
+       eap_packet_t *pt_ep = talloc_zero(r, eap_packet_t);
+
+       ret = map_eapsim_basictypes(r, pt_ep);
+
        if(ret != 1) {
                return ret;
        }
-       eap_basic_compose(r, &ep);
+
+       eap_basic_compose(r, pt_ep);
 
        return 1;
 }
@@ -1396,6 +1499,8 @@ static int map_eapsim_types(RADIUS_PACKET *r)
 static int unmap_eapsim_types(RADIUS_PACKET *r)
 {
        VALUE_PAIR           *esvp;
+       uint8_t *eap_data;
+       int rcode_unmap;
 
        esvp = pairfind(r->vps, ATTRIBUTE_EAP_BASE+PW_EAP_SIM, 0, TAG_ANY);
        if (!esvp) {
@@ -1403,7 +1508,13 @@ static int unmap_eapsim_types(RADIUS_PACKET *r)
                return 0;
        }
 
-       return unmap_eapsim_basictypes(r, esvp->vp_octets, esvp->length);
+       eap_data = talloc_memdup(esvp, esvp->vp_octets, esvp->length);
+       talloc_set_type(eap_data, uint8_t);
+
+       rcode_unmap = unmap_eapsim_basictypes(r, eap_data, esvp->length);
+
+       talloc_free(eap_data);
+       return rcode_unmap;
 }
 
 #ifdef TEST_CASE
@@ -1458,10 +1569,11 @@ main(int argc, char *argv[])
        }
 
        while(!filedone) {
-               if(req->vps) pairfree(&req->vps);
-               if(req2->vps) pairfree(&req2->vps);
+               if (req->vps) pairfree(&req->vps);
+               if (req2->vps) pairfree(&req2->vps);
 
-               if ((req->vps = readvp2(NULL, stdin, &filedone, "eapsimlib:")) == NULL) {
+               if (readvp2(&req->vps, NULL, stdin, &filedone) < 0) {
+                       fr_perror("radeapclient");
                        break;
                }
 
index 9448efc..fe86f7d 100644 (file)
@@ -1,11 +1,24 @@
-TARGET   :=
+TARGET   := radeapclient
 SOURCES := radeapclient.c
 
+SOURCES += ${top_srcdir}/src/main/files.c \
+          ${top_srcdir}/src/main/threads.c \
+          ${top_srcdir}/src/main/version.c
+
+TGT_PREREQS := libfreeradius-radius.a libfreeradius-server.a
+TGT_LDLIBS  := $(LIBS)
+
+#
+#  For future work, if we want radeapclient to become radclient
+#
+ifneq "$(filter libfreeradius-eap%,${ALL_TGTS})" ""
+TGT_PREREQS += libfreeradius-eap.a
+
 ifneq ($(OPENSSL_LIBS),)
 SOURCES += ${top_srcdir}/src/main/cb.c ${top_srcdir}/src/main/tls.c
+TGT_LDLIBS  += $(OPENSSL_LIBS)
 endif
 
-TGT_PREREQS := libfreeradius-radius.a libfreeradius-eap.a
-TGT_LDLIBS  := $(LIBS)
-
-SRC_INCDIRS  := libeap
+SRC_CFLAGS += -DWITH_EAPCLIENT
+SRC_INCDIRS  := ${top_srcdir}/src/modules/rlm_eap/libeap
+endif
index dd5e81b..4e920cf 100644 (file)
@@ -30,18 +30,13 @@ RCSID("$Id$")
 #include "rlm_eap.h"
 
 static const CONF_PARSER module_config[] = {
-       { "default_eap_type", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_t, default_method_name), NULL, "md5" },
-       { "timer_expire", PW_TYPE_INTEGER,
-         offsetof(rlm_eap_t, timer_limit), NULL, "60"},
-       { "ignore_unknown_eap_types", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_t, ignore_unknown_types), NULL, "no" },
-       { "mod_accounting_username_bug", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_t, mod_accounting_username_bug), NULL, "no" },
-       { "max_sessions", PW_TYPE_INTEGER,
-         offsetof(rlm_eap_t, max_sessions), NULL, "2048"},
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_t, default_method_name), "md5" },
+       { "timer_expire", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, timer_limit), "60" },
+       { "ignore_unknown_eap_types", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, ignore_unknown_types), "no" },
+       { "mod_accounting_username_bug", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, mod_accounting_username_bug), "no" },
+       { "max_sessions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, max_sessions), "2048" },
+
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 /*
@@ -59,7 +54,14 @@ static int mod_detach(void *instance)
 #endif
 
        rbtree_free(inst->session_tree);
-       if (inst->handler_tree) rbtree_free(inst->handler_tree);
+       if (inst->handler_tree) {
+               rbtree_free(inst->handler_tree);
+               /*
+                *  Must be NULL else when nodes are freed they try to
+                *  delete themselves from the tree.
+                */
+               inst->handler_tree = NULL;
+       }
        inst->session_tree = NULL;
        eaplist_free(inst);
 
@@ -89,7 +91,7 @@ static int eap_handler_cmp(void const *a, void const *b)
         *      EAP work.
         */
        if (fr_ipaddr_cmp(&one->src_ipaddr, &two->src_ipaddr) != 0) {
-               WDEBUG("EAP packets are arriving from two different upstream "
+               WARN("EAP packets are arriving from two different upstream "
                       "servers.  Has there been a proxy fail-over?");
        }
 
@@ -239,7 +241,7 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
 
 #ifdef HAVE_PTHREAD_H
                if (pthread_mutex_init(&(inst->handler_mutex), NULL) < 0) {
-                       ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, strerror(errno));
+                       ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, fr_syserror(errno));
                        return -1;
                }
 #endif
@@ -247,7 +249,7 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
 
 #ifdef HAVE_PTHREAD_H
        if (pthread_mutex_init(&(inst->session_mutex), NULL) < 0) {
-               ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, strerror(errno));
+               ERROR("rlm_eap (%s): Failed initializing mutex: %s", inst->xlat_name, fr_syserror(errno));
                return -1;
        }
 #endif
@@ -259,7 +261,7 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance)
 /*
  *     For backwards compatibility.
  */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_eap_t               *inst;
        eap_handler_t           *handler;
@@ -315,8 +317,8 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
        /*
         *      If we're doing horrible tunneling work, remember it.
         */
-       if ((request->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
-               RDEBUG2("  Not-EAP proxy set.  Not composing EAP");
+       if ((request->log.lvl & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
+               RDEBUG2("No EAP proxy set.  Not composing EAP");
                /*
                 *      Add the handle to the proxied list, so that we
                 *      can retrieve it in the post-proxy stage, and
@@ -373,7 +375,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                 */
                pairdelete(&request->proxy->vps, PW_FREERADIUS_PROXIED_TO, VENDORPEC_FREERADIUS, TAG_ANY);
 
-               RDEBUG2("  Tunneled session will be proxied.  Not doing EAP.");
+               RDEBUG2("Tunneled session will be proxied.  Not doing EAP");
                return RLM_MODULE_HANDLED;
        }
 #endif
@@ -432,7 +434,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         *      says that we MUST include a User-Name attribute in the
         *      Access-Accept.
         */
-       if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
+       if ((request->reply->code == PW_CODE_AUTHENTICATION_ACK) &&
            request->username) {
                VALUE_PAIR *vp;
 
@@ -450,14 +452,14 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                 *      terminated string in Access-Accept.
                 */
                if (inst->mod_accounting_username_bug) {
-                       char const *old = vp->vp_strvalue;
-                       char *new = talloc_zero_array(vp, char, vp->length + 1);
+                       char const *old = vp->vp_strvalue;
+                       char *new = talloc_zero_array(vp, char, vp->length + 1);
 
-                       memcpy(new, old, vp->length);
-                       vp->vp_strvalue = new;
-                       vp->length++;
+                       memcpy(new, old, vp->length);
+                       vp->vp_strvalue = new;
+                       vp->length++;
 
-                       rad_const_free(old);
+                       rad_const_free(old);
                }
        }
 
@@ -469,7 +471,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
  * to check for user existence & get their configured values.
  * It Handles EAP-START Messages, User-Name initilization.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_eap_t       *inst;
        int             status;
@@ -541,7 +543,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
  *     If we're proxying EAP, then there may be magic we need
  *     to do.
  */
-static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *inst, REQUEST *request)
 {
        size_t          i;
        size_t          len;
@@ -618,7 +620,7 @@ static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
                 *      says that we MUST include a User-Name attribute in the
                 *      Access-Accept.
                 */
-               if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
+               if ((request->reply->code == PW_CODE_AUTHENTICATION_ACK) &&
                    request->username) {
                        /*
                         *      Doesn't exist, add it in.
@@ -637,10 +639,15 @@ static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
        }
 
        /*
+        *      This is allowed.
+        */
+       if (!request->proxy_reply) return RLM_MODULE_NOOP;
+
+       /*
         *      There may be more than one Cisco-AVPair.
         *      Ensure we find the one with the LEAP attribute.
         */
-       paircursor(&cursor, &request->proxy_reply->vps);
+       fr_cursor_init(&cursor, &request->proxy_reply->vps);
        for (;;) {
                /*
                 *      Hmm... there's got to be a better way to
@@ -649,7 +656,7 @@ static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
                 *      This is vendor Cisco (9), Cisco-AVPair
                 *      attribute (1)
                 */
-               vp = pairfindnext(&cursor, 1, 9, TAG_ANY);
+               vp = fr_cursor_next_by_num(&cursor, 1, 9, TAG_ANY);
                if (!vp) {
                        return RLM_MODULE_NOOP;
                }
@@ -668,7 +675,7 @@ static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
         *      The format is very specific.
         */
        if (vp->length != (17 + 34)) {
-               RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %d: Expected %d",
+               RDEBUG2("Cisco-AVPair with leap:session-key has incorrect length %zu: Expected %d",
                       vp->length, 17 + 34);
                return RLM_MODULE_NOOP;
        }
@@ -681,9 +688,8 @@ static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
         */
        i = 34;
        p = talloc_memdup(vp, vp->vp_octets, vp->length);
-       len = rad_tunnel_pwdecode((uint8_t *)p + 17, &i,
-                                 request->home_server->secret,
-                                 request->proxy->vector);
+       talloc_set_type(p, uint8_t);
+       len = rad_tunnel_pwdecode((uint8_t *)p + 17, &i, request->home_server->secret, request->proxy->vector);
 
        /*
         *      FIXME: Assert that i == 16.
@@ -701,7 +707,7 @@ static rlm_rcode_t mod_post_proxy(void *inst, REQUEST *request)
 }
 #endif
 
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        rlm_eap_t       *inst = instance;
        VALUE_PAIR      *vp;
@@ -760,7 +766,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
 module_t rlm_eap = {
        RLM_MODULE_INIT,
        "eap",
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
        sizeof(rlm_eap_t),
        module_config,
        mod_instantiate,                /* instantiation */
index 6f856fd..409f104 100644 (file)
@@ -56,7 +56,7 @@ typedef struct rlm_eap {
        /*
         *      Configuration items.
         */
-       int             timer_limit;
+       uint32_t        timer_limit;
 
        char const      *default_method_name;
        eap_type_t      default_method;
@@ -64,7 +64,7 @@ typedef struct rlm_eap {
        bool            ignore_unknown_types;
        bool            mod_accounting_username_bug;
 
-       int             max_sessions;
+       uint32_t        max_sessions;
 
 #ifdef HAVE_PTHREAD_H
        pthread_mutex_t session_mutex;
@@ -90,24 +90,22 @@ typedef struct rlm_eap {
 
 /* function definitions */
 /* EAP-Type */
-int            eap_module_load(rlm_eap_t *inst, eap_module_t **method, eap_type_t num,
-                               CONF_SECTION *cs);
+int            eap_module_load(rlm_eap_t *inst, eap_module_t **method, eap_type_t num, CONF_SECTION *cs);
 eap_rcode_t    eap_method_select(rlm_eap_t *inst, eap_handler_t *handler);
 
 /* EAP */
-int            eap_start(rlm_eap_t *inst, REQUEST *request);
-void           eap_fail(eap_handler_t *handler);
-void           eap_success(eap_handler_t *handler);
-rlm_rcode_t    eap_compose(eap_handler_t *handler);
-eap_handler_t  *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_msg, REQUEST *request);
+int            eap_start(rlm_eap_t *inst, REQUEST *request) CC_HINT(nonnull);
+void           eap_fail(eap_handler_t *handler) CC_HINT(nonnull);
+void           eap_success(eap_handler_t *handler) CC_HINT(nonnull);
+rlm_rcode_t    eap_compose(eap_handler_t *handler) CC_HINT(nonnull);
+eap_handler_t  *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_msg, REQUEST *request) CC_HINT(nonnull);
 
 /* Memory Management */
 EAP_DS         *eap_ds_alloc(eap_handler_t *handler);
 eap_handler_t  *eap_handler_alloc(rlm_eap_t *inst);
 void           eap_ds_free(EAP_DS **eap_ds);
-int            eaplist_add(rlm_eap_t *inst, eap_handler_t *handler);
-eap_handler_t  *eaplist_find(rlm_eap_t *inst, REQUEST *request,
-                             eap_packet_raw_t *eap_packet);
+int            eaplist_add(rlm_eap_t *inst, eap_handler_t *handler) CC_HINT(nonnull);
+eap_handler_t  *eaplist_find(rlm_eap_t *inst, REQUEST *request, eap_packet_raw_t *eap_packet);
 void           eaplist_free(rlm_eap_t *inst);
 
 /* State */
index c3d943a..5c11c21 100644 (file)
@@ -41,13 +41,11 @@ typedef struct rlm_eap_gtc_t {
 } rlm_eap_gtc_t;
 
 static CONF_PARSER module_config[] = {
-       { "challenge", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_gtc_t, challenge), NULL, "Password: " },
+       { "challenge", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_gtc_t, challenge), "Password: " },
 
-       { "auth_type", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_gtc_t, auth_type_name), NULL, "PAP" },
+       { "auth_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_gtc_t, auth_type_name), "PAP" },
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 
@@ -70,15 +68,18 @@ static int gtc_attach(CONF_SECTION *cs, void **instance)
                return -1;
        }
 
-       dval = dict_valbyname(PW_AUTH_TYPE, 0, inst->auth_type_name);
-       if (!dval) {
-               ERROR("rlm_eap_gtc: Unknown Auth-Type %s",
-                      inst->auth_type_name);
-               return -1;
-       }
-
-       inst->auth_type = dval->value;
+       if (inst->auth_type_name && *inst->auth_type_name) {
+               dval = dict_valbyname(PW_AUTH_TYPE, 0, inst->auth_type_name);
+               if (!dval) {
+                       ERROR("rlm_eap_gtc: Unknown Auth-Type %s",
+                             inst->auth_type_name);
+                       return -1;
+               }
 
+               inst->auth_type = dval->value;
+       } else {
+               inst->auth_type = PW_AUTHTYPE_LOCAL;
+       }
        return 0;
 }
 
@@ -128,7 +129,7 @@ static int gtc_initiate(void *instance, eap_handler_t *handler)
 /*
  *     Authenticate a previously sent challenge.
  */
-static int mod_authenticate(void *instance, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_authenticate(void *instance, eap_handler_t *handler)
 {
        VALUE_PAIR *vp;
        EAP_DS *eap_ds = handler->eap_ds;
@@ -138,7 +139,6 @@ static int mod_authenticate(void *instance, eap_handler_t *handler)
        /*
         *      Get the Cleartext-Password for this user.
         */
-       rad_assert(request != NULL);
        rad_assert(handler->stage == AUTHENTICATE);
 
        /*
@@ -170,11 +170,11 @@ static int mod_authenticate(void *instance, eap_handler_t *handler)
         */
        if (inst->auth_type == PW_AUTHTYPE_LOCAL) {
                /*
-                *      For now, do clear-text password authentication.
+                *      For now, do cleartext password authentication.
                 */
                vp = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
                if (!vp) {
-                       REDEBUG2("Cleartext-Password is required for authentication.");
+                       REDEBUG2("Cleartext-Password is required for authentication");
                        eap_ds->request->code = PW_EAP_FAILURE;
                        return 0;
                }
index 154d36e..e56796f 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,20 +1254,20 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-eap-ikev2-include-dir=DIR
-                          Directory where the eap-ikev2 includes may be found
+                         Directory where the eap-ikev2 includes may be found
   --with-eap-ikev2-lib-dir=DIR
-                          Directory where the eap-ikev2 libraries may be found
+                         Directory where the eap-ikev2 libraries may be found
   --with-eap-ikev2-dir=DIR
-                          Base directory where eap-ikev2 is installed
+                         Base directory where eap-ikev2 is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1796,10 +1796,10 @@ if test "${with_eap_ikev2_include_dir+set}" = set; then :
                    no)
                        as_fn_error $? "Need eap-ikev2-include-dir" "$LINENO" 5
                    ;;
-                       yes)
+                       yes)
                    ;;
                    *)
-                       eap_ikev2_include_dir="$withval"
+                       eap_ikev2_include_dir="$withval"
                    ;;
                esac
 fi
@@ -1950,7 +1950,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2172,7 +2172,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2721,22 +2721,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=EAPIKEv2/connector.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2852,8 +2852,8 @@ ikev2_set_log_callback()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-leap-ikev2"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-leap-ikev2"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2869,22 +2869,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libeap-ikev2${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2896,22 +2896,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libeap-ikev2.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3064,11 +3064,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3574,11 +3574,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 46a5392..4ce79e7 100644 (file)
@@ -12,10 +12,10 @@ if test x$with_[]modname != xno; then
                    no)
                        AC_MSG_ERROR(Need eap-ikev2-include-dir)
                    ;;
-                       yes)
+                       yes)
                    ;;
                    *)
-                       eap_ikev2_include_dir="$withval"
+                       eap_ikev2_include_dir="$withval"
                    ;;
                esac])
 
index ad51754..5033040 100644 (file)
@@ -89,7 +89,7 @@ int getusersfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list, c
                (strcmp(compat_mode_str, "cistron") == 0)) {
                PAIR_LIST *entry;
                VALUE_PAIR *vp;
-               int compat_mode = false;
+               bool compat_mode = false;
 
                if (strcmp(compat_mode_str, "cistron") == 0) {
                        compat_mode = true;
@@ -127,7 +127,7 @@ int getusersfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list, c
                                if ((vp->da->vendor!= 0) ||
                                                (vp->da->attr < 0x100)) {
                                        if (!compat_mode) {
-                                               WDEBUG("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
+                                               WARN("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
                                                                filename, entry->lineno,
                                                                vp->da->name, vp->da->name,
                                                                entry->name);
@@ -187,9 +187,8 @@ int getusersfile(TALLOC_CTX *ctx, char const *filename, PAIR_LIST **pair_list, c
                                 *      good warning message.
                                 */
                                 if ((vp->da->vendor == 0) &&
-                                       (vp->da->attr > 0xff) &&
-                                       (vp->da->attr > 1000)) {
-                                       WDEBUG("[%s]:%d Check item \"%s\"\n"
+                                    (vp->da->attr > 1000)) {
+                                       WARN("[%s]:%d Check item \"%s\"\n"
                                               "\tfound in reply item list for user \"%s\".\n"
                                               "\tThis attribute MUST go on the first line"
                                               " with the other check items",
index d2b3269..394b1de 100644 (file)
@@ -146,41 +146,25 @@ static int ikev2_attach(CONF_SECTION *conf, void **instance)
     char *server_idtype=NULL;
 
     CONF_PARSER module_config[] = {
-       {  "ca_file", PW_TYPE_STRING_PTR,
-           offsetof(ikev2_ctx,trusted),NULL,NULL },
-       {  "private_key_file",PW_TYPE_STRING_PTR,
-           offsetof(ikev2_ctx,pkfile),NULL,NULL },
-       {  "private_key_password",PW_TYPE_STRING_PTR,
-           offsetof(ikev2_ctx,pkfile_pwd),NULL,NULL },
-       {  "certificate_file", PW_TYPE_STRING_PTR,
-           offsetof(ikev2_ctx,certificate_file),NULL,NULL },
-       {  "crl_file", PW_TYPE_STRING_PTR,
-           offsetof(ikev2_ctx,crl_file),NULL,NULL },
-       {   "id", PW_TYPE_STRING_PTR,
-           offsetof(ikev2_ctx,id),NULL,NULL },
-       {  "fragment_size",PW_TYPE_INTEGER,
-           offsetof(ikev2_ctx,max_fragment_size),NULL,IKEv2_DEFAULT_MAX_FRAGMENT_SIZE_STR},
-       {  "dh_counter_max", PW_TYPE_INTEGER,
-           offsetof(ikev2_ctx,DHCounterMax),NULL,IKEv2_DEFAULT_dh_counter_max_STR},
-       {  "default_authtype",PW_TYPE_STRING_PTR,
-           0,&default_authtype,"both" },
-       {  "usersfile",PW_TYPE_FILE_INPUT,
-           0,&usersfilename,"${confdir}/users" },
-       {  "server_authtype",PW_TYPE_STRING_PTR,
-           0,&server_authtype,"secret" },
-       {  "idtype",PW_TYPE_STRING_PTR,
-           0,&server_idtype,IKEv2_DEFAULT_IDTYPE_STR},
-       {  "certreq",PW_TYPE_BOOLEAN,
-           offsetof(ikev2_ctx,sendCertReq),NULL,"no"},
-       {  "fast_dh_exchange",PW_TYPE_BOOLEAN,
-           offsetof(ikev2_ctx,enableFastDHEx),NULL,"no"},
-       {  "fast_timer_expire",PW_TYPE_INTEGER,
-           offsetof(ikev2_ctx,fastExpire),NULL,"900"},
-       {   "enable_fast_reauth",PW_TYPE_BOOLEAN,
-           offsetof(ikev2_ctx,enableFastReconnect),NULL,"yes"},
-
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_STRING, ikev2_ctx, trusted), NULL  },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_STRING, ikev2_ctx, pkfile), NULL  },
+       { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING, ikev2_ctx, pkfile_pwd), NULL  },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_STRING, ikev2_ctx, certificate_file), NULL  },
+       { "crl_file", FR_CONF_OFFSET(PW_TYPE_STRING, ikev2_ctx, crl_file), NULL  },
+       { "id", FR_CONF_OFFSET(PW_TYPE_STRING, ikev2_ctx, id), NULL  },
+       { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, ikev2_ctx, max_fragment_size), IKEv2_DEFAULT_MAX_FRAGMENT_SIZE_STR },
+       { "dh_counter_max", FR_CONF_OFFSET(PW_TYPE_INTEGER, ikev2_ctx, DHCounterMax), IKEv2_DEFAULT_dh_counter_max_STR },
+       { "default_authtype", FR_CONF_POINTER(PW_TYPE_STRING, &default_authtype), "both" },
+       { "usersfile", FR_CONF_POINTER(PW_TYPE_FILE_INPUT, &usersfilename),"${confdir}/users" },
+       { "server_authtype", FR_CONF_POINTER(PW_TYPE_STRING, &server_authtype), "secret" },
+       { "idtype", FR_CONF_POINTER(PW_TYPE_STRING, &server_idtype), IKEv2_DEFAULT_IDTYPE_STR },
+       { "certreq", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ikev2_ctx, sendCertReq), "no" },
+       { "fast_dh_exchange", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ikev2_ctx, enableFastDHEx), "no" },
+       { "fast_timer_expire", FR_CONF_OFFSET(PW_TYPE_INTEGER, ikev2_ctx, fastExpire), "900" },
+       { "enable_fast_reauth", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ikev2_ctx, enableFastReconnect), "yes" },
+
+
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
      };
 
     ikev2_set_log_callback(vxlogf);
@@ -275,8 +259,8 @@ static int ikev2_initiate(void *instance, eap_handler_t *handler)
 {
     INFO(IKEv2_LOG_PREFIX "Initiate connection!");
 // This is the way for silent discarding behavior
-//    handler->request->options|=RAD_REQUEST_OPTION_FAKE_REQUEST;
-//    handler->request->options|=RAD_REQUEST_OPTION_DONT_CACHE;
+//    handler->request->log.lvl|=RAD_REQUEST_OPTION_FAKE_REQUEST;
+//    handler->request->log.lvl|=RAD_REQUEST_OPTION_DONT_CACHE;
 //    handler->request->reply->code=0;
 //    return 0;
 
@@ -292,7 +276,7 @@ static int ikev2_initiate(void *instance, eap_handler_t *handler)
     session = FindSessionByFastid(i2, (char const *)eap_username);
     if(!session) {
        if(IKEv2BeginSession( i2, &session, IKEv2_STY_INITIATOR ) != IKEv2_RET_OK) {
-           ERROR(IKEv2_LOG_PREFIX "Can't initialize IKEv2 session.");
+           ERROR(IKEv2_LOG_PREFIX "Can't initialize IKEv2 session");
            return 1;
        }
     } else {
@@ -416,7 +400,7 @@ static int ikev2_authenticate(void *instance, eap_handler_t *handler)
                if(ComposeRadMsg(out,olen,handler->eap_ds)){
                                free(out);
                                return 0;
-               }
+               }
                free(out);
                return 1;
        }
index e4033bd..2a78567 100644 (file)
@@ -67,7 +67,6 @@ int           eapleap_stage4(REQUEST *request, leap_packet_t *packet, VALUE_PAIR* passwor
 leap_packet_t  *eapleap_stage6(REQUEST *request, leap_packet_t *packet, VALUE_PAIR *user_name, VALUE_PAIR* password,
                                leap_session_t *session);
 
-void eapleap_lmpwdhash(unsigned char const *password,unsigned char *lmhash);
 void eapleap_mschap(unsigned char const *win_password, unsigned char const *challenge, unsigned char *response);
 
 #endif /*_EAP_LEAP_H*/
index d390930..e46edfc 100644 (file)
@@ -37,7 +37,7 @@ RCSID("$Id$")
  * len = header + type + leap_methoddata
  * leap_methoddata = value_size + value
  */
-static int leap_initiate(UNUSED void *instance, eap_handler_t *handler)
+static int CC_HINT(nonnull) leap_initiate(UNUSED void *instance, eap_handler_t *handler)
 {
        REQUEST         *request = handler->request;
        leap_session_t  *session;
@@ -87,7 +87,7 @@ static int leap_initiate(UNUSED void *instance, eap_handler_t *handler)
        return 1;
 }
 
-static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
 {
        int             rcode;
        REQUEST         *request = handler->request;
@@ -96,8 +96,6 @@ static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
        leap_packet_t   *reply;
        VALUE_PAIR      *password;
 
-       rad_assert(request);
-
        if (!handler->opaque) {
                REDEBUG("Cannot authenticate without LEAP history");
                return 0;
@@ -161,7 +159,7 @@ static int mod_authenticate(UNUSED void *instance, eap_handler_t *handler)
                 *      by eap_compose() in eap.c, when the EAP reply code
                 *      is EAP_SUCCESS.
                 */
-               handler->request->reply->code = PW_ACCESS_CHALLENGE;
+               handler->request->reply->code = PW_CODE_ACCESS_CHALLENGE;
                talloc_free(packet);
                return 1;
 
index c6e5e4a..f289564 100644 (file)
@@ -310,25 +310,6 @@ static void smbhash(unsigned char *out, unsigned char const *in, unsigned char *
 }
 
 /*
- *     Converts the password to uppercase, and creates the LM
- *     password hash.
- */
-void eapleap_lmpwdhash(unsigned char const *password, unsigned char *lmhash)
-{
-       int i;
-       unsigned char p14[14];
-       static unsigned char sp8[8] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
-
-       memset(p14, 0, sizeof(p14));
-       for (i = 0; i < 14 && password[i]; i++) {
-               p14[i] = toupper((int) password[i]);
-       }
-
-       smbhash(lmhash, sp8, p14);
-       smbhash(lmhash+8, sp8, p14+7);
-}
-
-/*
  *     Take the NT or LM password, and return the MSCHAP response
  *
  *     The win_password MUST be exactly 16 bytes long.
index f2bbf26..e001452 100644 (file)
@@ -37,6 +37,7 @@ static int md5_initiate(UNUSED void *instance, eap_handler_t *handler)
 {
        int             i;
        MD5_PACKET      *reply;
+       REQUEST         *request = handler->request;
 
        /*
         *      Allocate an EAP-MD5 packet.
@@ -68,7 +69,7 @@ static int md5_initiate(UNUSED void *instance, eap_handler_t *handler)
        for (i = 0; i < reply->value_size; i++) {
                reply->value[i] = fr_rand();
        }
-       DEBUG2("rlm_eap_md5: Issuing Challenge");
+       RDEBUG2("Issuing MD5 Challenge");
 
        /*
         *      Keep track of the challenge.
@@ -105,6 +106,7 @@ static int md5_authenticate(UNUSED void *arg, eap_handler_t *handler)
        MD5_PACKET      *packet;
        MD5_PACKET      *reply;
        VALUE_PAIR      *password;
+       REQUEST         *request = handler->request;
 
        /*
         *      Get the Cleartext-Password for this user.
@@ -114,7 +116,7 @@ static int md5_authenticate(UNUSED void *arg, eap_handler_t *handler)
 
        password = pairfind(handler->request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
        if (!password) {
-               DEBUG2("rlm_eap_md5: Cleartext-Password is required for EAP-MD5 authentication");
+               RDEBUG2("Cleartext-Password is required for EAP-MD5 authentication");
                return 0;
        }
 
index e1f1ff4..26f874e 100644 (file)
@@ -35,11 +35,9 @@ typedef struct rlm_eap_mschapv2_t {
 } rlm_eap_mschapv2_t;
 
 static CONF_PARSER module_config[] = {
-       { "with_ntdomain_hack",     PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_mschapv2_t,with_ntdomain_hack), NULL, "no" },
+       { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_mschapv2_t, with_ntdomain_hack), "no" },
 
-       { "send_error",     PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_mschapv2_t,send_error), NULL, "no" },
+       { "send_error", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_mschapv2_t, send_error), "no" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -103,23 +101,22 @@ static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
        switch (reply->da->attr) {
        case PW_MSCHAP_CHALLENGE:
                /*
-                *   0             1               2               3
+                *   0                   1                   2                   3
                 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-                *  |     Code      |   Identifier  |       Length           |
+                *  |     Code      |   Identifier  |            Length             |
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 *  |     Type      |   OpCode      |  MS-CHAPv2-ID |  MS-Length...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 *  |   MS-Length   |  Value-Size   |  Challenge...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-                *  |                        Challenge...
+                *  |                             Challenge...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-                *  |                        Name...
+                *  |                             Name...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 */
                length = MSCHAPV2_HEADER_LEN + MSCHAPV2_CHALLENGE_LEN + strlen(handler->identity);
-               eap_ds->request->type.data = talloc_array(eap_ds->request,
-                                                         uint8_t, length);
+               eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, length);
                /*
                 *      Allocate room for the EAP-MS-CHAPv2 data.
                 */
@@ -142,20 +139,20 @@ static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
                /*
                 *      Copy the Challenge, success, or error over.
                 */
-               memcpy(ptr, reply->vp_strvalue, reply->length);
+               memcpy(ptr, reply->vp_octets, reply->length);
                memcpy((ptr + reply->length), handler->identity, strlen(handler->identity));
                break;
 
        case PW_MSCHAP2_SUCCESS:
                /*
-                *   0             1               2               3
+                *   0                   1                   2                   3
                 *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-                *  |     Code      |   Identifier  |       Length           |
+                *  |     Code      |   Identifier  |            Length             |
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 *  |     Type      |   OpCode      |  MS-CHAPv2-ID |  MS-Length...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-                *  |   MS-Length   |               Message...
+                *  |   MS-Length   |                    Message...
                 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                 */
                DEBUG2("MSCHAP Success\n");
@@ -181,8 +178,7 @@ static int eapmschapv2_compose(eap_handler_t *handler, VALUE_PAIR *reply)
        case PW_MSCHAP_ERROR:
                DEBUG2("MSCHAP Failure\n");
                length = 4 + reply->length - 1;
-               eap_ds->request->type.data = talloc_array(eap_ds->request,
-                                                         uint8_t, length);
+               eap_ds->request->type.data = talloc_array(eap_ds->request, uint8_t, length);
 
                /*
                 *      Allocate room for the EAP-MS-CHAPv2 data.
@@ -248,7 +244,7 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
         *      We're at the stage where we're challenging the user.
         */
        data->code = PW_EAP_MSCHAPV2_CHALLENGE;
-       memcpy(data->challenge, challenge->vp_strvalue, MSCHAPV2_CHALLENGE_LEN);
+       memcpy(data->challenge, challenge->vp_octets, MSCHAPV2_CHALLENGE_LEN);
        data->mppe_keys = NULL;
        data->reply = NULL;
 
@@ -267,7 +263,7 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
         *      The EAP session doesn't have enough information to
         *      proxy the "inside EAP" protocol.  Disable EAP proxying.
         */
-       handler->request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+       handler->request->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
 #endif
 
        /*
@@ -290,14 +286,13 @@ static int mschapv2_initiate(UNUSED void *instance, eap_handler_t *handler)
  *
  *     Called from rlm_eap.c, eap_postproxy().
  */
-static int mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
+static int CC_HINT(nonnull) mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
 {
        VALUE_PAIR *response = NULL;
        mschapv2_opaque_t *data;
        REQUEST *request = handler->request;
 
        data = (mschapv2_opaque_t *) handler->opaque;
-       rad_assert(data != NULL);
        rad_assert(request != NULL);
 
        RDEBUG2("Passing reply from proxy back into the tunnel %d.",
@@ -307,21 +302,19 @@ static int mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
         *      There is only a limited number of possibilities.
         */
        switch (request->reply->code) {
-       case PW_AUTHENTICATION_ACK:
-               RDEBUG2("Proxied authentication succeeded.");
+       case PW_CODE_AUTHENTICATION_ACK:
+               RDEBUG2("Proxied authentication succeeded");
 
                /*
                 *      Move the attribute, so it doesn't go into
                 *      the reply.
                 */
-               pairfilter(data, &response,
-                         &request->reply->vps,
-                         PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
+               pairfilter(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
                break;
 
        default:
-       case PW_AUTHENTICATION_REJECT:
-               RDEBUG("Proxied authentication did not succeed.");
+       case PW_CODE_AUTHENTICATION_REJECT:
+               RDEBUG("Proxied authentication did not succeed");
                return 0;
        }
 
@@ -336,7 +329,7 @@ static int mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
        /*
         *      Done doing EAP proxy stuff.
         */
-       request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+       request->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
        eapmschapv2_compose(handler, response);
        data->code = PW_EAP_MSCHAPV2_SUCCESS;
 
@@ -358,7 +351,7 @@ static int mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
         *      And we need to challenge the user, not ack/reject them,
         *      so we re-write the ACK to a challenge.  Yuck.
         */
-       request->reply->code = PW_ACCESS_CHALLENGE;
+       request->reply->code = PW_CODE_ACCESS_CHALLENGE;
        pairfree(&response);
 
        return 1;
@@ -368,17 +361,18 @@ static int mschap_postproxy(eap_handler_t *handler, UNUSED void *tunnel_data)
 /*
  *     Authenticate a previously sent challenge.
  */
-static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
+static int CC_HINT(nonnull) mschapv2_authenticate(void *arg, eap_handler_t *handler)
 {
        int rcode, ccode;
        uint8_t *p;
+       size_t length;
+       char *q;
        mschapv2_opaque_t *data;
        EAP_DS *eap_ds = handler->eap_ds;
        VALUE_PAIR *challenge, *response, *name;
        rlm_eap_mschapv2_t *inst = (rlm_eap_mschapv2_t *) arg;
        REQUEST *request = handler->request;
 
-       rad_assert(request != NULL);
        rad_assert(handler->stage == AUTHENTICATE);
 
        data = (mschapv2_opaque_t *) handler->opaque;
@@ -469,7 +463,7 @@ static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
                        }
 
        failure:
-                       request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+                       request->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
                        eap_ds->request->code = PW_EAP_FAILURE;
                        return 1;
 
@@ -494,7 +488,7 @@ static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
                                        /*
                                         *      It's a success.  Don't proxy it.
                                         */
-                                       request->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+                                       request->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
 #endif
                                        pairfilter(request->reply,
                                                  &request->reply->vps,
@@ -552,11 +546,10 @@ static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
         *      The MS-Length field is 5 + value_size + length
         *      of name, which is put after the response.
         */
-       if (((eap_ds->response->type.data[2] << 8) |
-            eap_ds->response->type.data[3]) < (5 + 49)) {
-               REDEBUG("Response contains contradictory length %d %d",
-                       (eap_ds->response->type.data[2] << 8) |
-                      eap_ds->response->type.data[3], 5 + 49);
+       length = (eap_ds->response->type.data[2] << 8) | eap_ds->response->type.data[3];
+       if ((length < (5 + 49)) || (length > (256 + 5 + 49))) {
+               REDEBUG("Response contains contradictory length %zu %d",
+                       length, 5 + 49);
                return 0;
        }
 
@@ -579,14 +572,12 @@ static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
        if (!response) {
                return 0;
        }
-
        response->length = MSCHAPV2_RESPONSE_LEN;
        response->vp_octets = p = talloc_array(response, uint8_t, response->length);
 
        p[0] = eap_ds->response->type.data[1];
        p[1] = eap_ds->response->type.data[5 + MSCHAPV2_RESPONSE_LEN];
-       memcpy(p + 2, &eap_ds->response->type.data[5],
-              MSCHAPV2_RESPONSE_LEN - 2);
+       memcpy(p + 2, &eap_ds->response->type.data[5], MSCHAPV2_RESPONSE_LEN - 2);
 
        name = pairmake_packet("MS-CHAP-User-Name", NULL, T_OP_EQ);
        if (!name) {
@@ -596,14 +587,12 @@ static int mschapv2_authenticate(void *arg, eap_handler_t *handler)
        /*
         *      MS-Length - MS-Value - 5.
         */
-       name->length = (((eap_ds->response->type.data[2] << 8) |
-                        eap_ds->response->type.data[3]) -
-                       eap_ds->response->type.data[4] - 5);
-       name->vp_octets = p = talloc_array(name, uint8_t, name->length + 1);
-       memcpy(p,
+       name->length = length - 49 - 5;
+       name->vp_strvalue = q = talloc_array(name, char, name->length + 1);
+       memcpy(q,
               &eap_ds->response->type.data[4 + MSCHAPV2_RESPONSE_LEN],
               name->length);
-       p[name->length] = '\0';
+       q[name->length] = '\0';
 
 packet_ready:
 
@@ -618,7 +607,7 @@ packet_ready:
         *      EAP attributes, and proxy the MS-CHAP attributes to a
         *      home server.
         */
-       if (request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
+       if (request->log.lvl & RAD_REQUEST_OPTION_PROXY_EAP) {
                char *username = NULL;
                eap_tunnel_data_t *tunnel;
 
@@ -697,17 +686,16 @@ packet_ready:
         */
        response = NULL;
        if (rcode == RLM_MODULE_OK) {
-               pairfilter(data, &response, &request->reply->vps,
-                        PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
+               pairfilter(data, &response, &request->reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
                data->code = PW_EAP_MSCHAPV2_SUCCESS;
-
        } else if (inst->send_error) {
-               pairfilter(data, &response, &request->reply->vps,
-                         PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY);
+               pairfilter(data, &response, &request->reply->vps, PW_MSCHAP_ERROR, VENDORPEC_MICROSOFT, TAG_ANY);
                if (response) {
                        int n,err,retry;
                        char buf[34];
 
+                       VERIFY_VP(response);
+
                        RDEBUG2("MSCHAP-Error: %s", response->vp_strvalue);
 
                        /*
@@ -734,7 +722,7 @@ packet_ready:
         *      No response, die.
         */
        if (!response) {
-               REDEBUG("No MS-CHAP-Success or MS-CHAP-Error was found.");
+               REDEBUG("No MS-CHAP-Success or MS-CHAP-Error was found");
                return 0;
        }
 
index 9c74445..d08dee0 100644 (file)
@@ -67,5 +67,5 @@ typedef struct peap_tunnel_t {
 /*
  *     Process the PEAP portion of an EAP-PEAP request.
  */
-rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session);
+rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session) CC_HINT(nonnull);
 #endif /* _EAP_PEAP_H */
index 743693d..009b305 100644 (file)
@@ -239,10 +239,10 @@ static int eapmessage_verify(REQUEST *request,
                 */
        case PW_EAP_RESPONSE:
                if (eap_packet->data[0] == PW_EAP_TLV) {
-                       RDEBUG2("Received EAP-TLV response.");
+                       RDEBUG2("Received EAP-TLV response");
                        return 1;
                }
-               RDEBUG2("Got something weird.");
+               RDEBUG2("Got something weird");
                break;
 
 
@@ -297,8 +297,8 @@ static VALUE_PAIR *eap2vp(REQUEST *request, RADIUS_PACKET *packet,
 
        memcpy(p + EAP_HEADER_LEN, data, total);
 
-       paircursor(&cursor, &head);
-       pairinsert(&cursor, vp);
+       fr_cursor_init(&cursor, &head);
+       fr_cursor_insert(&cursor, vp);
        while (total < data_len) {
                vp = paircreate(packet, PW_EAP_MESSAGE, 0);
                if (!vp) {
@@ -311,7 +311,7 @@ static VALUE_PAIR *eap2vp(REQUEST *request, RADIUS_PACKET *packet,
 
                total += vp->length;
 
-               pairinsert(&cursor, vp);
+               fr_cursor_insert(&cursor, vp);
        }
 
        return head;
@@ -337,7 +337,7 @@ static int vp2eap(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR *vp)
                size_t i, total, start = EAP_HEADER_LEN;
                total = 0;
 
-               for (this = paircursor(&cursor, &vp); this; this = pairnext(&cursor)) {
+               for (this = fr_cursor_init(&cursor, &vp); this; this = fr_cursor_next(&cursor)) {
                        for (i = start; i < vp->length; i++) {
                                if ((total & 0x0f) == 0) {
                                        fprintf(fr_log_fp, "  PEAP tunnel data out %04x: ", (int) total);
@@ -361,16 +361,18 @@ static int vp2eap(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR *vp)
 #endif
 
        /*
-        *      Send the EAP data, WITHOUT the header.
+        *      Send the EAP data in the first attribute, WITHOUT the
+        *      header.
         */
        (tls_session->record_plus)(&tls_session->clean_in, vp->vp_octets + EAP_HEADER_LEN, vp->length - EAP_HEADER_LEN);
 
        /*
-        *      Send the rest of the EAP data.
+        *      Send the rest of the EAP data, but skipping the first VP.
         */
-       for (this = paircursor(&cursor, &vp);
+       fr_cursor_init(&cursor, &vp);
+       for (this = fr_cursor_next(&cursor);
             this;
-            this = pairnext(&cursor)) {
+            this = fr_cursor_next(&cursor)) {
                (tls_session->record_plus)(&tls_session->clean_in, this->vp_octets, this->length);
        }
 
@@ -400,7 +402,7 @@ static int eappeap_check_tlv(REQUEST *request, uint8_t const *data,
                }
 
                if (data[10] == EAP_TLV_FAILURE) {
-                       RDEBUG2("Client rejected our response.  The password is probably incorrect.");
+                       RDEBUG2("Client rejected our response.  The password is probably incorrect");
                        return 0;
                }
        }
@@ -414,8 +416,8 @@ static int eappeap_check_tlv(REQUEST *request, uint8_t const *data,
 /*
  *     Use a reply packet to determine what to do.
  */
-static int process_reply(eap_handler_t *handler, tls_session_t *tls_session,
-                        REQUEST *request, RADIUS_PACKET *reply)
+static int CC_HINT(nonnull) process_reply(eap_handler_t *handler, tls_session_t *tls_session,
+                                         REQUEST *request, RADIUS_PACKET *reply)
 {
        int rcode = RLM_MODULE_REJECT;
        VALUE_PAIR *vp;
@@ -429,8 +431,8 @@ static int process_reply(eap_handler_t *handler, tls_session_t *tls_session,
        }
 
        switch (reply->code) {
-       case PW_AUTHENTICATION_ACK:
-               RDEBUG2("Tunneled authentication was successful.");
+       case PW_CODE_AUTHENTICATION_ACK:
+               RDEBUG2("Tunneled authentication was successful");
                t->status = PEAP_STATUS_SENT_TLV_SUCCESS;
                eappeap_success(handler, tls_session);
                rcode = RLM_MODULE_HANDLED;
@@ -467,14 +469,14 @@ static int process_reply(eap_handler_t *handler, tls_session_t *tls_session,
                }
                break;
 
-       case PW_AUTHENTICATION_REJECT:
-               RDEBUG2("Tunneled authentication was rejected.");
+       case PW_CODE_AUTHENTICATION_REJECT:
+               RDEBUG2("Tunneled authentication was rejected");
                t->status = PEAP_STATUS_SENT_TLV_FAILURE;
                eappeap_failure(handler, tls_session);
                rcode = RLM_MODULE_HANDLED;
                break;
 
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_CHALLENGE:
                RDEBUG2("Got tunneled Access-Challenge");
 
                /*
@@ -540,14 +542,13 @@ static int process_reply(eap_handler_t *handler, tls_session_t *tls_session,
 /*
  *     Do post-proxy processing,
  */
-static int eappeap_postproxy(eap_handler_t *handler, void *data)
+static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data)
 {
        int rcode;
        tls_session_t *tls_session = (tls_session_t *) data;
        REQUEST *fake, *request = handler->request;
 
-       rad_assert(request != NULL);
-       RDEBUG2("Passing reply from proxy back into the tunnel.");
+       RDEBUG2("Passing reply from proxy back into the tunnel");
 
        /*
         *      If there was a fake request associated with the proxied
@@ -560,7 +561,7 @@ static int eappeap_postproxy(eap_handler_t *handler, void *data)
        /*
         *      Do the callback, if it exists, and if it was a success.
         */
-       if (fake && (handler->request->proxy_reply->code == PW_AUTHENTICATION_ACK)) {
+       if (fake && (handler->request->proxy_reply->code == PW_CODE_AUTHENTICATION_ACK)) {
                peap_tunnel_t *t = tls_session->opaque;
 
                t->home_access_accept = true;
@@ -585,7 +586,7 @@ static int eappeap_postproxy(eap_handler_t *handler, void *data)
                 *      Perform a post-auth stage, which will get the EAP
                 *      handler, too...
                 */
-               fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+               fake->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
                RDEBUG2("Passing reply back for EAP-MS-CHAP-V2");
                process_post_proxy(0, fake);
 
@@ -621,7 +622,6 @@ static int eappeap_postproxy(eap_handler_t *handler, void *data)
                        request_free(&fake);
                        eaptls_fail(handler, 0);
                        return 0;
-                       break;
 
                default:  /* Don't Do Anything */
                        RDEBUG2("Got reply %d", request->proxy_reply->code);
@@ -670,7 +670,7 @@ static int eappeap_postproxy(eap_handler_t *handler, void *data)
                return eaptls_success(handler, 0);
 
        default:
-               RDEBUG2("Reply was unknown.");
+               RDEBUG2("Reply was unknown");
                break;
        }
 
@@ -736,8 +736,6 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
        REQUEST *request = handler->request;
        EAP_DS *eap_ds = handler->eap_ds;
 
-       rad_assert(request != NULL);
-
        /*
         *      Just look at the buffer directly, without doing
         *      record_minus.  This lets us avoid another data copy.
@@ -750,7 +748,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
 
        if ((t->status != PEAP_STATUS_TUNNEL_ESTABLISHED) &&
            !eapmessage_verify(request, data, data_len)) {
-               RDEBUG2("FAILED processing PEAP: Tunneled data is invalid.");
+               RDEBUG2("FAILED processing PEAP: Tunneled data is invalid");
                if (debug_flag > 2) print_tunneled_data(data, data_len);
                return RLM_MODULE_REJECT;
        }
@@ -783,7 +781,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
        case PEAP_STATUS_INNER_IDENTITY_REQ_SENT:
                /* we're expecting an identity response */
                if (data[0] != PW_EAP_IDENTITY) {
-                       RDEBUG("Expected EAP-Identity, got something else.");
+                       RDEBUG("Expected EAP-Identity, got something else");
                        return RLM_MODULE_REJECT;
                }
 
@@ -824,7 +822,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                RDEBUG("Got SoH reply");
                debug_pair_list(fake->reply->vps);
 
-               if (fake->reply->code != PW_AUTHENTICATION_ACK) {
+               if (fake->reply->code != PW_CODE_AUTHENTICATION_ACK) {
                        RDEBUG2("SoH was rejected");
                        request_free(&fake);
                        t->status = PEAP_STATUS_SENT_TLV_FAILURE;
@@ -879,7 +877,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                        return RLM_MODULE_HANDLED;
                }
 
-               RDEBUG2("We sent a success, but received something weird in return.");
+               RDEBUG2("We sent a success, but the client did not agree");
                return RLM_MODULE_REJECT;
 
        /*
@@ -887,11 +885,14 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
         *      packets after we told them to f*ck off.
         */
        case PEAP_STATUS_SENT_TLV_FAILURE:
-               RDEBUG(" The users session was previously rejected: returning reject (again.)");
-               RDEBUG(" *** This means you need to read the PREVIOUS messages in the debug output");
-               RDEBUG(" *** to find out the reason why the user was rejected.");
-               RDEBUG(" *** Look for \"reject\" or \"fail\".  Those earlier messages will tell you.");
-               RDEBUG(" *** what went wrong, and how to fix the problem.");
+               RINDENT();
+               RDEBUG("The users session was previously rejected: returning reject (again.)");
+               RDEBUG("*** This means you need to read the PREVIOUS messages in the debug output");
+               RDEBUG("*** to find out the reason why the user was rejected");
+               RDEBUG("*** Look for \"reject\" or \"fail\".  Those earlier messages will tell you");
+               RDEBUG("*** what went wrong, and how to fix the problem");
+               REXDENT();
+
                return RLM_MODULE_REJECT;
 
                case PEAP_STATUS_PHASE2_INIT:
@@ -938,7 +939,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                pairadd(&fake->packet->vps, vp);
 
                if (t->default_method != 0) {
-                       RDEBUG2("Setting default EAP type for tunneled EAP session.");
+                       RDEBUG2("Setting default EAP type for tunneled EAP session");
                        vp = pairmake(fake, &fake->config_items, "EAP-Type", "0", T_OP_EQ);
                        vp->vp_integer = t->default_method;
                }
@@ -992,7 +993,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      set it here.
                         */
                        if (t->default_method != 0) {
-                               DEBUG2("  PEAP: Setting default EAP type for tunneled EAP session.");
+                               DEBUG2("  PEAP: Setting default EAP type for tunneled EAP session");
                                vp = pairmake(fake, &fake->config_items, "EAP-Type", "0", T_OP_EQ);
                                vp->vp_integer = t->default_method;
                        }
@@ -1064,7 +1065,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      done, THEN we proxy it...
                         */
                        if (!t->proxy_tunneled_request_as_eap) {
-                               fake->options |= RAD_REQUEST_OPTION_PROXY_EAP;
+                               fake->log.lvl |= RAD_REQUEST_OPTION_PROXY_EAP;
 
                                /*
                                 *      Hmm... should we check for
@@ -1075,13 +1076,13 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                                /*
                                 *      Run the EAP authentication.
                                 */
-                               DEBUG2("  PEAP: Calling authenticate in order to initiate tunneled EAP session.");
+                               DEBUG2("  PEAP: Calling authenticate in order to initiate tunneled EAP session");
                                rcode = process_authenticate(PW_AUTHTYPE_EAP, fake);
                                if (rcode == RLM_MODULE_OK) {
                                        /*
                                         *      Authentication succeeded! Rah!
                                         */
-                                       fake->reply->code = PW_AUTHENTICATION_ACK;
+                                       fake->reply->code = PW_CODE_AUTHENTICATION_ACK;
                                        goto do_process;
                                }
 
@@ -1095,7 +1096,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                                 *      The module decided it wasn't
                                 *      done.  Handle it like normal.
                                 */
-                               if ((fake->options & RAD_REQUEST_OPTION_PROXY_EAP) == 0) {
+                               if ((fake->log.lvl & RAD_REQUEST_OPTION_PROXY_EAP) == 0) {
                                        DEBUG2("    PEAP: Cancelling proxy to realm %s until the tunneled EAP session has been established", vp->vp_strvalue);
                                        goto do_process;
                                }
@@ -1124,7 +1125,7 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      tunneled request.
                         */
                        rad_assert(!request->proxy);
-                       request->proxy = fake->packet;
+                       request->proxy = talloc_steal(request, fake->packet);
                        memset(&request->proxy->src_ipaddr, 0,
                               sizeof(request->proxy->src_ipaddr));
                        memset(&request->proxy->dst_ipaddr, 0,
@@ -1155,8 +1156,8 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      We're not proxying it as EAP, so we've got
                         *      to do the callback later.
                         */
-                       if ((fake->options & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
-                               DEBUG2("  PEAP: Remembering to do EAP-MS-CHAP-V2 post-proxy.");
+                       if ((fake->log.lvl & RAD_REQUEST_OPTION_PROXY_EAP) != 0) {
+                               DEBUG2("  PEAP: Remembering to do EAP-MS-CHAP-V2 post-proxy");
 
                                /*
                                 *      rlm_eap.c has taken care of associating
@@ -1208,9 +1209,10 @@ rlm_rcode_t eappeap_process(eap_handler_t *handler, tls_session_t *tls_session)
        return rcode;
 }
 
-static int setup_fake_request(REQUEST *request, REQUEST *fake, peap_tunnel_t *t) {
+static int CC_HINT(nonnull) setup_fake_request(REQUEST *request, REQUEST *fake, peap_tunnel_t *t) {
 
        VALUE_PAIR *vp;
+
        /*
         *      Tell the request that it's a fake one.
         */
@@ -1248,9 +1250,9 @@ static int setup_fake_request(REQUEST *request, REQUEST *fake, peap_tunnel_t *t)
                VALUE_PAIR *copy;
                vp_cursor_t cursor;
 
-               for (vp = paircursor(&cursor, &request->packet->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        /*
                         *      The attribute is a server-side thingy,
                         *      don't copy it.
@@ -1290,7 +1292,6 @@ static int setup_fake_request(REQUEST *request, REQUEST *fake, peap_tunnel_t *t)
                        case PW_EAP_MESSAGE:
                        case PW_STATE:
                                continue;
-                               break;
 
                                /*
                                 *      By default, copy it over.
index ada5d5b..4793ee3 100644 (file)
@@ -26,87 +26,49 @@ RCSID("$Id$")
 #include "eap_peap.h"
 
 typedef struct rlm_eap_peap_t {
-       /*
-        *      TLS configuration
-        */
-       char    *tls_conf_name;
+       char const *tls_conf_name;              //!< TLS configuration.
        fr_tls_server_conf_t *tls_conf;
+       char const *default_method_name;        //!< Default tunneled EAP type.
+       int default_method;
+       bool use_tunneled_reply;                //!< Use the reply attributes from the tunneled session in
+                                               //!< the non-tunneled reply to the client.
 
-       /*
-        *      Default tunneled EAP type
-        */
-       char    *default_method_name;
-       int     default_method;
-
-       /*
-        *      Use the reply attributes from the tunneled session in
-        *      the non-tunneled reply to the client.
-        */
-       bool    use_tunneled_reply;
-
-       /*
-        *      Use SOME of the request attributes from outside of the
-        *      tunneled session in the tunneled request
-        */
-       bool    copy_request_to_tunnel;
-
+       bool copy_request_to_tunnel;            //!< Use SOME of the request attributes from outside of the
+                                               //!< tunneled session in the tunneled request.
 #ifdef WITH_PROXY
-       /*
-        *      Proxy tunneled session as EAP, or as de-capsulated
-        *      protocol.
-        */
-       bool    proxy_tunneled_request_as_eap;
+       bool proxy_tunneled_request_as_eap;     //!< Proxy tunneled session as EAP, or as de-capsulated
+                                               //!< protocol.
 #endif
+       char const *virtual_server;             //!< Virtual server for inner tunnel session.
 
-       /*
-        *      Virtual server for inner tunnel session.
-        */
-       char    *virtual_server;
-
-       /*
-        *      Do we do SoH request?
-        */
-       bool    soh;
-       char    *soh_virtual_server;
-
-       /*
-        *      Do we do require a client cert?
-        */
-       bool    req_client_cert;
+       bool soh;                               //!< Do we do SoH request?
+       char const *soh_virtual_server;
+       bool req_client_cert;                   //!< Do we do require a client cert?
 } rlm_eap_peap_t;
 
 
 static CONF_PARSER module_config[] = {
-       { "tls", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, tls_conf_name), NULL, NULL },
+       { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, tls_conf_name), NULL },
 
-       { "default_method", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, default_method_name), NULL, "mschapv2" },
+       { "default_method", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, default_method_name), "mschapv2" },
 
-       { "copy_request_to_tunnel", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, copy_request_to_tunnel), NULL, "no" },
+       { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, copy_request_to_tunnel), "no" },
 
-       { "use_tunneled_reply", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, use_tunneled_reply), NULL, "no" },
+       { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, use_tunneled_reply), "no" },
 
 #ifdef WITH_PROXY
-       { "proxy_tunneled_request_as_eap", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, proxy_tunneled_request_as_eap), NULL, "yes" },
+       { "proxy_tunneled_request_as_eap", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, proxy_tunneled_request_as_eap), "yes" },
 #endif
 
-       { "virtual_server", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, virtual_server), NULL, NULL },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, virtual_server), NULL },
 
-       { "soh", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, soh), NULL, "no" },
+       { "soh", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, soh), "no" },
 
-       { "require_client_cert", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_peap_t, req_client_cert), NULL, "no" },
+       { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_peap_t, req_client_cert), "no" },
 
-       { "soh_virtual_server", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_peap_t, soh_virtual_server), NULL, NULL },
+       { "soh_virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_peap_t, soh_virtual_server), NULL },
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 
@@ -202,7 +164,7 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
        tls_session_t   *ssn;
        rlm_eap_peap_t  *inst;
        VALUE_PAIR      *vp;
-       int             client_cert = false;
+       bool            client_cert;
        REQUEST         *request = handler->request;
 
        inst = type_arg;
@@ -213,7 +175,6 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
        /*
         *      Check if we need a client certificate.
         */
-       client_cert = inst->req_client_cert;
 
        /*
         * EAP-TLS-Require-Client-Cert attribute will override
@@ -222,6 +183,8 @@ static int eappeap_initiate(void *type_arg, eap_handler_t *handler)
        vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
        if (vp) {
                client_cert = vp->vp_integer;
+       } else {
+               client_cert = inst->req_client_cert;
        }
 
        ssn = eaptls_session(inst->tls_conf, handler, client_cert);
@@ -343,7 +306,7 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
         *      Session is established, proceed with decoding
         *      tunneled data.
         */
-       RDEBUG2("Session established.  Decoding tunneled attributes.");
+       RDEBUG2("Session established.  Decoding tunneled attributes");
 
        /*
         *      We may need PEAP data associated with the session, so
index eda7782..0910c4e 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1260,10 +1260,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1971,7 +1971,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2193,7 +2193,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2742,22 +2742,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=openssl/ec.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2892,7 +2892,7 @@ done
 
           if test "x$ac_cv_func_ev_group_free" != "xyes"; then
              fail="EC_GROUP_free"
-           fi
+          fi
        fi
 
 
@@ -3002,11 +3002,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3512,11 +3512,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 18f89a8..a0bea5c 100644 (file)
@@ -64,7 +64,7 @@ if test x$with_[]modname != xno; then
           )
           if test "x$ac_cv_func_ev_group_free" != "xyes"; then
              fail="EC_GROUP_free"
-           fi
+          fi
        fi
        
 
index 0b87830..00c0687 100644 (file)
@@ -108,8 +108,8 @@ eap_pwd_kdf(uint8_t *key, int keylen, char const *label, int labellen,
 int
 compute_password_element (pwd_session_t *sess, uint16_t grp_num,
                          char const *password, int password_len,
-                         char *id_server, int id_server_len,
-                         char *id_peer, int id_peer_len,
+                         char const *id_server, int id_server_len,
+                         char const *id_peer, int id_peer_len,
                          uint32_t *token)
 {
     BIGNUM *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
@@ -191,8 +191,8 @@ compute_password_element (pwd_session_t *sess, uint16_t grp_num,
         */
        H_Init(&ctx);
        H_Update(&ctx, (uint8_t *)token, sizeof(*token));
-       H_Update(&ctx, (uint8_t *)id_peer, id_peer_len);
-       H_Update(&ctx, (uint8_t *)id_server, id_server_len);
+       H_Update(&ctx, (uint8_t const *)id_peer, id_peer_len);
+       H_Update(&ctx, (uint8_t const *)id_server, id_server_len);
        H_Update(&ctx, (uint8_t const *)password, password_len);
        H_Update(&ctx, (uint8_t *)&ctr, sizeof(ctr));
        H_Final(&ctx, pwe_digest);
index 9c9cfe8..2f14393 100644 (file)
@@ -50,7 +50,7 @@ typedef struct _pwd_hdr {
 #define EAP_PWD_EXCH_CONFIRM       3
 //    uint16_t total_length;      /* there if the L-bit is set */
     uint8_t data[0];
-} __attribute__ ((packed)) pwd_hdr;
+} CC_HINT(packed) pwd_hdr;
 
 #define EAP_PWD_GET_LENGTH_BIT(x)       ((x)->lm_exchange & 0x80)
 #define EAP_PWD_SET_LENGTH_BIT(x)       ((x)->lm_exchange |= 0x80)
@@ -71,7 +71,7 @@ typedef struct _pwd_id_packet {
 #define EAP_PWD_PREP_MS                 1
 #define EAP_PWD_PREP_SASL             2
     char identity[0];
-} __attribute__ ((packed)) pwd_id_packet;
+} CC_HINT(packed) pwd_id_packet;
 
 typedef struct _pwd_session_t {
     uint16_t state;
@@ -105,8 +105,8 @@ typedef struct _pwd_session_t {
 
 int compute_password_element(pwd_session_t *sess, uint16_t grp_num,
                             char const *password, int password_len,
-                            char *id_server, int id_server_len,
-                            char *id_peer, int id_peer_len,
+                            char const *id_server, int id_server_len,
+                            char const *id_peer, int id_peer_len,
                             uint32_t *token);
 int compute_scalar_element(pwd_session_t *sess, BN_CTX *bnctx);
 int process_peer_commit (pwd_session_t *sess, uint8_t *commit, BN_CTX *bnctx);
index c65bd37..a895147 100644 (file)
@@ -42,14 +42,10 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 #define MPPE_KEY_LEN    32
 
 static CONF_PARSER pwd_module_config[] = {
-    { "group", PW_TYPE_INTEGER,
-      offsetof(EAP_PWD_CONF, group), NULL, "19"},
-    { "fragment_size", PW_TYPE_INTEGER,
-      offsetof(EAP_PWD_CONF, fragment_size), NULL, "1020"},
-    { "server_id", PW_TYPE_STRING_PTR,
-      offsetof(EAP_PWD_CONF, server_id), NULL, NULL },
-    { "virtual_server", PW_TYPE_STRING_PTR,
-      offsetof(EAP_PWD_CONF, virtual_server), NULL, NULL },
+    { "group", FR_CONF_OFFSET(PW_TYPE_INTEGER, EAP_PWD_CONF, group), "19" },
+    { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, EAP_PWD_CONF, fragment_size), "1020" },
+    { "server_id", FR_CONF_OFFSET(PW_TYPE_STRING, EAP_PWD_CONF, server_id), NULL },
+    { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, EAP_PWD_CONF, virtual_server), NULL },
     { NULL, -1, 0, NULL, NULL }
 };
 
@@ -341,7 +337,7 @@ mod_authenticate (void *arg, eap_handler_t *handler)
     if (EAP_PWD_GET_MORE_BIT(hdr)) {
        rad_assert(pwd_session->in_buf != NULL);
        if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
-           RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
+           RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent");
            return 0;
        }
        memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
@@ -369,7 +365,7 @@ mod_authenticate (void *arg, eap_handler_t *handler)
         * the last fragment...
         */
        if ((pwd_session->in_buf_pos + len) > pwd_session->in_buf_len) {
-           RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent.");
+           RDEBUG2("pwd will not overflow a fragment buffer. Nope, not prudent");
            return 0;
        }
        memcpy(pwd_session->in_buf + pwd_session->in_buf_pos, buf, len);
index 4d9bfc0..b04a57e 100644 (file)
 #include <freeradius-devel/modules.h>
 
 typedef struct eap_pwd_conf {
-    int group;
-    int fragment_size;
-    char *server_id;
-    char *virtual_server;
+    uint32_t   group;
+    uint32_t   fragment_size;
+    char const *server_id;
+    char const *virtual_server;
 } EAP_PWD_CONF;
 
 typedef struct _eap_pwd_t {
index 5240c39..c99bb51 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1195,9 +1195,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1778,11 +1778,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -2288,11 +2288,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index cd8b0d4..e78ff59 100644 (file)
@@ -117,45 +117,137 @@ static int eap_sim_sendstart(eap_handler_t *handler)
 static int eap_sim_get_challenge(eap_handler_t *handler, VALUE_PAIR *vps, int idx, eap_sim_state_t *ess)
 {
        REQUEST *request = handler->request;
-       VALUE_PAIR *vp;
+       VALUE_PAIR *vp, *ki, *algo_version;
 
        rad_assert(idx >= 0 && idx < 3);
 
+       /*
+        *      Generate a new RAND value, and derive Kc and SRES from Ki
+        */
+       ki = pairfind(vps, ATTRIBUTE_EAP_SIM_KI, 0, TAG_ANY);
+       if (ki) {
+               int i;
+
+               /*
+                *      Check to see if have a Ki for the IMSI, this allows us to generate the rest
+                *      of the triplets.
+                */
+               algo_version = pairfind(vps, ATTRIBUTE_EAP_SIM_ALGO_VERSION, 0, TAG_ANY);
+               if (!algo_version) {
+                       REDEBUG("Found Ki, but missing EAP-Sim-Algo-Version");
+                       return 0;
+               }
+
+               for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
+                       ess->keys.rand[idx][i] = fr_rand();
+               }
+
+               switch (algo_version->vp_integer) {
+                       case 1:
+                               comp128v1(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx]);
+                               break;
+
+                       case 2:
+                               comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx],
+                                          true);
+                               break;
+
+                       case 3:
+                               comp128v23(ess->keys.sres[idx], ess->keys.Kc[idx], ki->vp_octets, ess->keys.rand[idx],
+                                          false);
+                               break;
+
+                       case 4:
+                               REDEBUG("Comp128-4 algorithm is not supported as details have not yet been published. "
+                                       "If you have details of this algorithm please contact the FreeRADIUS "
+                                       "maintainers");
+                               return 0;
+
+                       default:
+                               REDEBUG("Unknown/unsupported algorithm Comp128-%i", algo_version->vp_integer);
+               }
+
+               if (RDEBUG_ENABLED2) {
+                       char buffer[33];        /* 32 hexits (16 bytes) + 1 */
+                       char *p;
+
+                       RDEBUG2("Generated following triplets for round %i:", idx);
+
+                       p = buffer;
+                       for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
+                               p += sprintf(p, "%02x", ess->keys.rand[idx][i]);
+                       }
+                       RDEBUG2("\tRAND : 0x%s", buffer);
+
+                       p = buffer;
+                       for (i = 0; i < EAPSIM_SRES_SIZE; i++) {
+                               p += sprintf(p, "%02x", ess->keys.sres[idx][i]);
+                       }
+                       RDEBUG2("\tSRES : 0x%s", buffer);
+
+                       p = buffer;
+                       for (i = 0; i < EAPSIM_KC_SIZE; i++) {
+                               p += sprintf(p, "%02x", ess->keys.Kc[idx][i]);
+                       }
+                       RDEBUG2("\tKc   : 0x%s", buffer);
+               }
+               return 1;
+       }
+
+       /*
+        *      Use known RAND, SRES, and Kc values, these may of been pulled in from an AuC,
+        *      or created by sending challenges to the SIM directly.
+        */
        vp = pairfind(vps, ATTRIBUTE_EAP_SIM_RAND1 + idx, 0, TAG_ANY);
-       if(!vp) {
+       /* Hack for backwards compatibility */
+       if (!vp) {
+               vp = pairfind(request->reply->vps, ATTRIBUTE_EAP_SIM_KC1 + idx, 0, TAG_ANY);
+       }
+       if (!vp) {
                /* bad, we can't find stuff! */
-               REDEBUG("EAP-SIM Challenge[%i] not found", idx);
+               REDEBUG("EAP-SIM-RAND%i not found", idx + 1);
                return 0;
        }
-       if(vp->length != EAPSIM_RAND_SIZE) {
-               REDEBUG("EAP-SIM challenge[%i] is not 8 bytes, got %zu bytes", idx, vp->length);
+       if (vp->length != EAPSIM_RAND_SIZE) {
+               REDEBUG("EAP-SIM-RAND%i is not " STRINGIFY(EAPSIM_RAND_SIZE) " bytes, got %zu bytes",
+                       idx + 1, vp->length);
                return 0;
        }
        memcpy(ess->keys.rand[idx], vp->vp_strvalue, EAPSIM_RAND_SIZE);
 
        vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SRES1 + idx, 0, TAG_ANY);
+       /* Hack for backwards compatibility */
+       if (!vp) {
+               vp = pairfind(request->reply->vps, ATTRIBUTE_EAP_SIM_KC1 + idx, 0, TAG_ANY);
+       }
        if (!vp) {
                /* bad, we can't find stuff! */
-               REDEBUG("EAP-SIM SRES[%i] not found", idx);
+               REDEBUG("EAP-SIM-SRES%i not found", idx + 1);
                return 0;
        }
        if (vp->length != EAPSIM_SRES_SIZE) {
-               REDEBUG("EAP-SIM SRES[%i] is not 16 bytes, got %zu bytes", idx, vp->length);
+               REDEBUG("EAP-SIM-SRES%i is not " STRINGIFY(EAPSIM_SRES_SIZE) " bytes, got %zu bytes",
+                       idx + 1, vp->length);
                return 0;
        }
        memcpy(ess->keys.sres[idx], vp->vp_strvalue, EAPSIM_SRES_SIZE);
 
        vp = pairfind(vps, ATTRIBUTE_EAP_SIM_KC1 + idx, 0, TAG_ANY);
+       /* Hack for backwards compatibility */
+       if (!vp) {
+               vp = pairfind(request->reply->vps, ATTRIBUTE_EAP_SIM_KC1 + idx, 0, TAG_ANY);
+       }
        if (!vp) {
                /* bad, we can't find stuff! */
-               REDEBUG("EAP-SIM Kc[%i] not found", idx);
+               REDEBUG("EAP-SIM-Kc%i not found", idx + 1);
                return 0;
        }
-       if (vp->length != EAPSIM_Kc_SIZE) {
-               REDEBUG("EAP-SIM Kc[%i] is not 16 bytes, got %zu bytes", idx, vp->length);
+       if (vp->length != EAPSIM_KC_SIZE) {
+               REDEBUG("EAP-SIM-Kc%i is not " STRINGIFY(EAPSIM_KC_SIZE) " bytes, got %zu bytes",
+                       idx + 1, vp->length);
                return 0;
        }
-       memcpy(ess->keys.Kc[idx], vp->vp_strvalue, EAPSIM_Kc_SIZE);
+       memcpy(ess->keys.Kc[idx], vp->vp_strvalue, EAPSIM_KC_SIZE);
 
        return 1;
 }
@@ -342,7 +434,7 @@ static void eap_sim_stateenter(eap_handler_t *handler,
         *      Send the EAP Success message
         */
        case eapsim_server_success:
-               eap_sim_sendsuccess(handler);
+               eap_sim_sendsuccess(handler);
                handler->eap_ds->request->code = PW_EAP_SUCCESS;
                break;
        /*
@@ -367,18 +459,8 @@ static int eap_sim_initiate(UNUSED void *instance, eap_handler_t *handler)
 {
        REQUEST *request = handler->request;
        eap_sim_state_t *ess;
-       VALUE_PAIR *vp;
-       VALUE_PAIR *outvps;
        time_t n;
 
-       outvps = handler->request->reply->vps;
-
-       vp = pairfind(outvps, ATTRIBUTE_EAP_SIM_RAND1, 0, TAG_ANY);
-       if (!vp) {
-               RDEBUG2("Can't initiate EAP-SIM, no RAND1 attribute");
-               return 0;
-       }
-
        ess = talloc_zero(handler, eap_sim_state_t);
        if (!ess) {
                RDEBUG2("No space for EAP-SIM state");
@@ -391,10 +473,9 @@ static int eap_sim_initiate(UNUSED void *instance, eap_handler_t *handler)
        /*
         *      Save the keying material, because it could change on a subsequent retrival.
         */
-       if ((eap_sim_get_challenge(handler, outvps, 0, ess) +
-            eap_sim_get_challenge(handler, outvps, 1, ess) +
-            eap_sim_get_challenge(handler, outvps, 2, ess)) != 3) {
-               RDEBUG2("Can't initiate EAP-SIM, missing attributes");
+       if (!eap_sim_get_challenge(handler, request->config_items, 0, ess) ||
+           !eap_sim_get_challenge(handler, request->config_items, 1, ess) ||
+           !eap_sim_get_challenge(handler, request->config_items, 2, ess)) {
                return 0;
        }
 
@@ -451,7 +532,7 @@ static int process_eap_sim_start(eap_handler_t *handler, VALUE_PAIR *vps)
        /*
         *      Record it for later keying
         */
-       memcpy(ess->keys.versionselect, selectedversion_vp->vp_strvalue, sizeof(ess->keys.versionselect));
+       memcpy(ess->keys.versionselect, selectedversion_vp->vp_strvalue, sizeof(ess->keys.versionselect));
 
        /*
         *      Double check the nonce size.
@@ -602,7 +683,7 @@ static int mod_authenticate(UNUSED void *arg, eap_handler_t *handler)
 
        default:
                rad_assert(0 == 1);
-       }
+       }
 
        return 0;
 }
index 0f5563a..be3ba5d 100644 (file)
@@ -41,13 +41,11 @@ USES_APPLE_DEPRECATED_API   /* OpenSSL API has been deprecated by Apple */
 #endif
 
 static CONF_PARSER module_config[] = {
-       { "tls", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_tls_t, tls_conf_name), NULL, NULL },
+       { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_tls_t, tls_conf_name), NULL },
 
-       { "virtual_server", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_tls_t, virtual_server), NULL, NULL },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_tls_t, virtual_server), NULL },
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 
@@ -131,7 +129,7 @@ static int eaptls_initiate(void *type_arg, eap_handler_t *handler)
 /*
  *     Do authentication, by letting EAP-TLS do most of the work.
  */
-static int mod_authenticate(void *type_arg, eap_handler_t *handler)
+static int CC_HINT(nonnull) mod_authenticate(void *type_arg, eap_handler_t *handler)
 {
        fr_tls_status_t status;
        tls_session_t *tls_session = (tls_session_t *) handler->opaque;
@@ -140,8 +138,6 @@ static int mod_authenticate(void *type_arg, eap_handler_t *handler)
 
        inst = type_arg;
 
-       rad_assert(request != NULL);
-
        RDEBUG2("Authenticate");
 
        status = eaptls_process(handler);
@@ -186,7 +182,7 @@ static int mod_authenticate(void *type_arg, eap_handler_t *handler)
                                  &fake->reply->vps, 0, 0, TAG_ANY);
 
                        /* reject if virtual server didn't return accept */
-                       if (fake->reply->code != PW_AUTHENTICATION_ACK) {
+                       if (fake->reply->code != PW_CODE_AUTHENTICATION_ACK) {
                                RDEBUG2("Certificates were rejected by the virtual server");
                                request_free(&fake);
                                eaptls_fail(handler, 0);
@@ -211,7 +207,7 @@ static int mod_authenticate(void *type_arg, eap_handler_t *handler)
                 *      data.
                 */
        case FR_TLS_OK:
-               RDEBUG2("Received unexpected tunneled data after successful handshake.");
+               RDEBUG2("Received unexpected tunneled data after successful handshake");
 #ifndef NDEBUG
                if ((debug_flag > 2) && fr_log_fp) {
                        unsigned int i;
index 97bc8c8..cebcb92 100644 (file)
@@ -35,13 +35,13 @@ typedef struct rlm_eap_tls_t {
        /*
         *      TLS configuration
         */
-       char    *tls_conf_name;
+       char const *tls_conf_name;
        fr_tls_server_conf_t *tls_conf;
 
        /*
         *      Virtual server for checking certificates
         */
-       char    *virtual_server;
+       char const *virtual_server;
 } rlm_eap_tls_t;
 
 #endif /* _RLM_EAP_TLS_H */
index 3c58084..8af89c6 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-eap-tnc-include-dir=DIR
-                          Directory where the libtnc includes may be found
+                         Directory where the libtnc includes may be found
   --with-eap-tnc-lib-dir=DIR
-                          Directory where the libtnc libraries may be found
+                         Directory where the libtnc libraries may be found
   --with-eap-tnc-dir=DIR  Base directory where libtnc is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1796,10 +1796,10 @@ if test "${with_eap_tnc_include_dir+set}" = set; then :
                    no)
                        as_fn_error $? "Need eap-tnc-include-dir" "$LINENO" 5
                    ;;
-                       yes)
+                       yes)
                    ;;
                    *)
-                       eap_tnc_include_dir="$withval"
+                       eap_tnc_include_dir="$withval"
                    ;;
                esac
 fi
@@ -1938,7 +1938,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2160,7 +2160,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2709,22 +2709,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=naaeap/naaeap.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2839,8 +2839,8 @@ processEAPTNCData()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lnaaeap"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lnaaeap"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2856,22 +2856,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libnaaeap${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2883,22 +2883,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libnaaeap.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3053,11 +3053,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3563,11 +3563,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 9548c82..7f94013 100644 (file)
@@ -12,10 +12,10 @@ if test x$with_[]modname != xno; then
                    no)
                        AC_MSG_ERROR(Need eap-tnc-include-dir)
                    ;;
-                       yes)
+                       yes)
                    ;;
                    *)
-                       eap_tnc_include_dir="$withval"
+                       eap_tnc_include_dir="$withval"
                    ;;
                esac])
 
index 286c354..d085675 100644 (file)
 #define SET_START(x)           ((x) | (0x20))
 
 typedef struct rlm_eap_tnc {
-       char    *connection_string;
+       char const      *connection_string;
 } rlm_eap_tnc_t;
 
 static CONF_PARSER module_config[] = {
-       { "connection_string", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_tnc_t, connection_string), NULL,
-         "NAS Port: %{NAS-Port} NAS IP: %{NAS-IP-Address} NAS_PORT_TYPE: %{NAS-Port-Type}"},
+       { "connection_string", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_tnc_t, connection_string), "NAS Port: %{NAS-Port} NAS IP: %{NAS-IP-Address} NAS_PORT_TYPE: %{NAS-Port-Type}" },
 
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 static int tnc_attach(CONF_SECTION *cs, void **instance)
index bfd01fb..6491ded 100644 (file)
@@ -41,6 +41,6 @@ typedef struct ttls_tunnel_t {
 /*
  *     Process the TTLS portion of an EAP-TTLS request.
  */
-int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session);
+int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session) CC_HINT(nonnull);
 
 #endif /* _EAP_TTLS_H */
index 3e6beb4..c51d438 100644 (file)
@@ -30,26 +30,26 @@ typedef struct rlm_eap_ttls_t {
        /*
         *      TLS configuration
         */
-       char    *tls_conf_name;
+       char const *tls_conf_name;
        fr_tls_server_conf_t *tls_conf;
 
        /*
         *      Default tunneled EAP type
         */
-       char    *default_method_name;
-       int     default_method;
+       char const *default_method_name;
+       int default_method;
 
        /*
         *      Use the reply attributes from the tunneled session in
         *      the non-tunneled reply to the client.
         */
-       bool    use_tunneled_reply;
+       bool use_tunneled_reply;
 
        /*
         *      Use SOME of the request attributes from outside of the
         *      tunneled session in the tunneled request
         */
-       bool    copy_request_to_tunnel;
+       bool copy_request_to_tunnel;
 
        /*
         *      RFC 5281 (TTLS) says that the length field MUST NOT be
@@ -60,43 +60,30 @@ typedef struct rlm_eap_ttls_t {
         *      RFC, we add the option here.  If set to "no", it sends
         *      the length field in ONLY the first fragment.
         */
-       bool    include_length;
+       bool include_length;
 
        /*
         *      Virtual server for inner tunnel session.
         */
-       char    *virtual_server;
+       char const *virtual_server;
 
        /*
         *      Do we do require a client cert?
         */
-       bool    req_client_cert;
+       bool req_client_cert;
 } rlm_eap_ttls_t;
 
 
 static CONF_PARSER module_config[] = {
-       { "tls", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_ttls_t, tls_conf_name), NULL, NULL },
-
-       { "default_eap_type", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_ttls_t, default_method_name), NULL, "md5" },
-
-       { "copy_request_to_tunnel", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_ttls_t, copy_request_to_tunnel), NULL, "no" },
-
-       { "use_tunneled_reply", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_ttls_t, use_tunneled_reply), NULL, "no" },
-
-       { "virtual_server", PW_TYPE_STRING_PTR,
-         offsetof(rlm_eap_ttls_t, virtual_server), NULL, NULL },
-
-       { "include_length", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_ttls_t, include_length), NULL, "yes" },
-
-       { "require_client_cert", PW_TYPE_BOOLEAN,
-         offsetof(rlm_eap_ttls_t, req_client_cert), NULL, "no" },
-
-       { NULL, -1, 0, NULL, NULL }        /* end the list */
+       { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_ttls_t, tls_conf_name), NULL },
+       { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_ttls_t, default_method_name), "md5" },
+       { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, copy_request_to_tunnel), "no" },
+       { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, use_tunneled_reply), "no" },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_ttls_t, virtual_server), NULL },
+       { "include_length", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, include_length), "yes" },
+       { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_ttls_t, req_client_cert), "no" },
+
+       { NULL, -1, 0, NULL, NULL }        /* end the list */
 };
 
 
@@ -193,7 +180,7 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
        tls_session_t   *ssn;
        rlm_eap_ttls_t  *inst;
        VALUE_PAIR      *vp;
-       int             client_cert = false;
+       bool            client_cert;
        REQUEST         *request = handler->request;
 
        inst = type_arg;
@@ -204,7 +191,6 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
        /*
         *      Check if we need a client certificate.
         */
-       client_cert = inst->req_client_cert;
 
        /*
         * EAP-TLS-Require-Client-Cert attribute will override
@@ -213,6 +199,8 @@ static int eapttls_initiate(void *type_arg, eap_handler_t *handler)
        vp = pairfind(handler->request->config_items, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY);
        if (vp) {
                client_cert = vp->vp_integer;
+       } else {
+               client_cert = inst->req_client_cert;
        }
 
        ssn = eaptls_session(inst->tls_conf, handler, client_cert);
@@ -324,7 +312,7 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
         *      Session is established, proceed with decoding
         *      tunneled data.
         */
-       RDEBUG2("Session established.  Proceeding to decode tunneled attributes.");
+       RDEBUG2("Session established.  Proceeding to decode tunneled attributes");
 
        /*
         *      We may need TTLS data associated with the session, so
@@ -340,21 +328,21 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
         */
        rcode = eapttls_process(handler, tls_session);
        switch (rcode) {
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_REJECT:
                eaptls_fail(handler, 0);
                return 0;
 
                /*
                 *      Access-Challenge, continue tunneled conversation.
                 */
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_CHALLENGE:
                eaptls_request(handler->eap_ds, tls_session);
                return 1;
 
                /*
                 *      Success: Automatically return MPPE keys.
                 */
-       case PW_AUTHENTICATION_ACK:
+       case PW_CODE_AUTHENTICATION_ACK:
                return eaptls_success(handler, 0);
 
                /*
@@ -363,7 +351,7 @@ static int mod_authenticate(void *arg, eap_handler_t *handler)
                 *      that the request now has a "proxy" packet, and
                 *      will proxy it, rather than returning an EAP packet.
                 */
-       case PW_STATUS_CLIENT:
+       case PW_CODE_STATUS_CLIENT:
 #ifdef WITH_PROXY
                rad_assert(handler->request->proxy != NULL);
 #endif
index 6c1f12f..8eb5892 100644 (file)
@@ -27,14 +27,14 @@ RCSID("$Id$")
 #include "eap_chbind.h"
 
 /*
- *    0                   1               2               3
+ *    0                   1                   2                   3
  *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *   |                    AVP Code                         |
+ *   |                           AVP Code                            |
  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *   |V M r r r r r r|           AVP Length               |
+ *   |V M r r r r r r|                  AVP Length                   |
  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *   |                 Vendor-ID (opt)                 |
+ *   |                        Vendor-ID (opt)                        |
  *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *   |    Data ...
  *   +-+-+-+-+-+-+-+-+
@@ -54,7 +54,7 @@ static int diameter_verify(REQUEST *request, uint8_t const *data, unsigned int d
                hdr_len = 12;
 
                if (remaining < hdr_len) {
-                 RDEBUG2(" Diameter attribute is too small (%u) to contain a Diameter header", remaining);
+                 RDEBUG2("Diameter attribute is too small (%u) to contain a Diameter header", remaining);
                        return 0;
                }
 
@@ -65,7 +65,7 @@ static int diameter_verify(REQUEST *request, uint8_t const *data, unsigned int d
 
                if ((data[4] & 0x80) != 0) {
                        if (remaining < 16) {
-                               RDEBUG2(" Diameter attribute is too small to contain a Diameter header with Vendor-Id");
+                               RDEBUG2("Diameter attribute is too small to contain a Diameter header with Vendor-Id");
                                return 0;
                        }
 
@@ -150,7 +150,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
        RADIUS_PACKET   *packet = fake->packet; /* FIXME: api issues */
        vp_cursor_t     out;
 
-       paircursor(&out, &first);
+       fr_cursor_init(&out, &first);
 
        while (data_left > 0) {
                rad_assert(data_left <= data_len);
@@ -249,7 +249,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                                goto do_octets;
                        }
 
-                       pairinsert(&out, vp);
+                       fr_cursor_insert(&out, vp);
 
                        goto next_attr;
                }
@@ -288,7 +288,8 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                                if (vp) pairfree(&vp);
                                da = dict_attrunknown(attr, vendor, true);
                                if (!da) return NULL;
-                               vp = pairalloc(NULL, da);
+                               vp = pairalloc(packet, da);
+                               if (!vp) return NULL;
                                pairmemcpy(vp, data, size);
                                break;
                        }
@@ -310,7 +311,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                        vp->vp_integer64 = ntohll(vp->vp_integer64);
                        break;
 
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        if (size != vp->length) {
                                RDEBUG2("Invalid length attribute %d",
                                       attr);
@@ -341,12 +342,12 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                        vp->vp_signed = ntohl(vp->vp_signed);
                        break;
 
-               case PW_TYPE_IPV6ADDR:
+               case PW_TYPE_IPV6_ADDR:
                        if (size != vp->length) goto raw;
                        memcpy(&vp->vp_ipv6addr, data, vp->length);
                        break;
 
-               case PW_TYPE_IPV6PREFIX:
+               case PW_TYPE_IPV6_PREFIX:
                        if (size != vp->length) goto raw;
                        memcpy(&vp->vp_ipv6prefix, data, vp->length);
                        break;
@@ -420,7 +421,7 @@ static VALUE_PAIR *diameter2vp(REQUEST *request, REQUEST *fake, SSL *ssl,
                /*
                 *      Update the list.
                 */
-               pairinsert(&out, vp);
+               fr_cursor_insert(&out, vp);
 
        next_attr:
                /*
@@ -475,7 +476,7 @@ static int vp2diameter(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR
        p = buffer;
        total = 0;
 
-       for (vp = paircursor(&cursor, &first); vp; vp = pairnext(&cursor)) {
+       for (vp = fr_cursor_init(&cursor, &first); vp; vp = fr_cursor_next(&cursor)) {
                /*
                 *      Too much data: die.
                 */
@@ -544,7 +545,7 @@ static int vp2diameter(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR
                        length = 8;
                        break;
 
-               case PW_TYPE_IPADDR:
+               case PW_TYPE_IPV4_ADDR:
                        memcpy(p, &vp->vp_ipaddr, 4); /* network order */
                        length = 4;
                        break;
@@ -614,14 +615,13 @@ static int vp2diameter(REQUEST *request, tls_session_t *tls_session, VALUE_PAIR
 /*
  *     Use a reply packet to determine what to do.
  */
-static rlm_rcode_t process_reply(UNUSED eap_handler_t *handler, tls_session_t *tls_session,
-                                REQUEST *request, RADIUS_PACKET *reply)
+static rlm_rcode_t CC_HINT(nonnull) process_reply(UNUSED eap_handler_t *handler, tls_session_t *tls_session,
+                                                 REQUEST *request, RADIUS_PACKET *reply)
 {
        rlm_rcode_t rcode = RLM_MODULE_REJECT;
        VALUE_PAIR *vp;
        ttls_tunnel_t *t = tls_session->opaque;
 
-       rad_assert(request != NULL);
        rad_assert(handler->request == request);
 
        /*
@@ -646,7 +646,7 @@ static rlm_rcode_t process_reply(UNUSED eap_handler_t *handler, tls_session_t *t
         *      NOT 'eap start', so we should check for that....
         */
        switch (reply->code) {
-       case PW_AUTHENTICATION_ACK:
+       case PW_CODE_AUTHENTICATION_ACK:
                RDEBUG("Got tunneled Access-Accept");
 
                rcode = RLM_MODULE_OK;
@@ -662,7 +662,7 @@ static rlm_rcode_t process_reply(UNUSED eap_handler_t *handler, tls_session_t *t
                vp = NULL;
                pairfilter(tls_session, &vp, &reply->vps, PW_MSCHAP2_SUCCESS, VENDORPEC_MICROSOFT, TAG_ANY);
                if (vp) {
-                       RDEBUG("Got MS-CHAP2-Success, tunneling it to the client in a challenge.");
+                       RDEBUG("Got MS-CHAP2-Success, tunneling it to the client in a challenge");
                        rcode = RLM_MODULE_HANDLED;
                        t->authenticated = true;
 
@@ -742,7 +742,7 @@ static rlm_rcode_t process_reply(UNUSED eap_handler_t *handler, tls_session_t *t
                break;
 
 
-       case PW_AUTHENTICATION_REJECT:
+       case PW_CODE_AUTHENTICATION_REJECT:
                RDEBUG("Got tunneled Access-Reject");
                rcode = RLM_MODULE_REJECT;
                break;
@@ -753,7 +753,7 @@ static rlm_rcode_t process_reply(UNUSED eap_handler_t *handler, tls_session_t *t
                 *      an Access-Challenge means that we MUST tunnel
                 *      a Reply-Message to the client.
                 */
-       case PW_ACCESS_CHALLENGE:
+       case PW_CODE_ACCESS_CHALLENGE:
                RDEBUG("Got tunneled Access-Challenge");
 
                /*
@@ -813,14 +813,13 @@ static rlm_rcode_t process_reply(UNUSED eap_handler_t *handler, tls_session_t *t
 /*
  *     Do post-proxy processing,
  */
-static int eapttls_postproxy(eap_handler_t *handler, void *data)
+static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data)
 {
        int rcode;
        tls_session_t *tls_session = (tls_session_t *) data;
        REQUEST *fake, *request = handler->request;
 
-       rad_assert(request != NULL);
-       RDEBUG("Passing reply from proxy back into the tunnel.");
+       RDEBUG("Passing reply from proxy back into the tunnel");
 
        /*
         *      If there was a fake request associated with the proxied
@@ -833,9 +832,7 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
        /*
         *      Do the callback, if it exists, and if it was a success.
         */
-       if (fake &&
-           handler->request->proxy_reply &&
-           (handler->request->proxy_reply->code == PW_AUTHENTICATION_ACK)) {
+       if (fake && (handler->request->proxy_reply->code == PW_CODE_AUTHENTICATION_ACK)) {
                /*
                 *      Terrible hacks.
                 */
@@ -857,7 +854,7 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
                 *      Perform a post-auth stage for the tunneled
                 *      session.
                 */
-               fake->options &= ~RAD_REQUEST_OPTION_PROXY_EAP;
+               fake->log.lvl &= ~RAD_REQUEST_OPTION_PROXY_EAP;
                rcode = rad_postauth(fake);
                RDEBUG2("post-auth returns %d", rcode);
 
@@ -873,9 +870,9 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
                /*
                 *      Terrible hacks.
                 */
-               request->proxy = fake->packet;
+               request->proxy = talloc_steal(request, fake->packet);
                fake->packet = NULL;
-               request->proxy_reply = fake->reply;
+               request->proxy_reply = talloc_steal(request, fake->reply);
                fake->reply = NULL;
 
                /*
@@ -887,7 +884,6 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
                        request_free(&fake);
                        eaptls_fail(handler, 0);
                        return 0;
-                       break;
 
                default:  /* Don't Do Anything */
                        RDEBUG2("Got reply %d",
@@ -900,8 +896,7 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
        /*
         *      Process the reply from the home server.
         */
-       rcode = process_reply(handler, tls_session, handler->request,
-                             handler->request->proxy_reply);
+       rcode = process_reply(handler, tls_session, handler->request, handler->request->proxy_reply);
 
        /*
         *      The proxy code uses the reply from the home server as
@@ -929,7 +924,7 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
                return eaptls_success(handler, 0);
 
        default:
-               RDEBUG("Reply was unknown.");
+               RDEBUG("Reply was unknown");
                break;
        }
 
@@ -944,7 +939,7 @@ static int eapttls_postproxy(eap_handler_t *handler, void *data)
  */
 int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
 {
-       int code = PW_AUTHENTICATION_REJECT;
+       int code = PW_CODE_AUTHENTICATION_REJECT;
        rlm_rcode_t rcode;
        REQUEST *fake;
        VALUE_PAIR *vp;
@@ -955,8 +950,6 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        eap_chbind_packet_t *chbind_packet;
        size_t chbind_len;
 
-       rad_assert(request != NULL);
-
        /*
         *      Just look at the buffer directly, without doing
         *      record_minus.
@@ -973,8 +966,8 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
         */
        if (data_len == 0) {
                if (t->authenticated) {
-                       RDEBUG("Got ACK, and the user was already authenticated.");
-                       return PW_AUTHENTICATION_ACK;
+                       RDEBUG("Got ACK, and the user was already authenticated");
+                       return PW_CODE_AUTHENTICATION_ACK;
                } /* else no session, no data, die. */
 
                /*
@@ -982,7 +975,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                 *      wrong.
                 */
                RDEBUG2("SSL_read Error");
-               return PW_AUTHENTICATION_REJECT;
+               return PW_CODE_AUTHENTICATION_REJECT;
        }
 
 #ifndef NDEBUG
@@ -1001,7 +994,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
 #endif
 
        if (!diameter_verify(request, data, data_len)) {
-               return PW_AUTHENTICATION_REJECT;
+               return PW_CODE_AUTHENTICATION_REJECT;
        }
 
        /*
@@ -1017,7 +1010,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
        fake->packet->vps = diameter2vp(request, fake, tls_session->ssl, data, data_len);
        if (!fake->packet->vps) {
                request_free(&fake);
-               return PW_AUTHENTICATION_REJECT;
+               return PW_CODE_AUTHENTICATION_REJECT;
        }
 
        /*
@@ -1074,7 +1067,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                                 *      set it here.
                                 */
                                if (t->default_method != 0) {
-                                       RDEBUG("Setting default EAP type for tunneled EAP session.");
+                                       RDEBUG("Setting default EAP type for tunneled EAP session");
                                        vp = paircreate(fake, PW_EAP_TYPE, 0);
                                        rad_assert(vp != NULL);
                                        vp->vp_integer = t->default_method;
@@ -1087,7 +1080,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                                 *      as it's permitted to do EAP without
                                 *      user-name.
                                 */
-                               RWDEBUG2("No EAP-Identity found to start EAP conversation.");
+                               RWDEBUG2("No EAP-Identity found to start EAP conversation");
                        }
                } /* else there WAS a t->username */
 
@@ -1117,7 +1110,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                VALUE_PAIR *copy;
                vp_cursor_t cursor;
 
-               for (vp = paircursor(&cursor, &request->packet->vps); vp; vp = pairnext(&cursor)) {
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps); vp; vp = fr_cursor_next(&cursor)) {
                        /*
                         *      The attribute is a server-side thingy,
                         *      don't copy it.
@@ -1230,7 +1223,7 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                /* clean up chbind req */
                chbind_free(req);
 
-               if (chbind_rcode != PW_AUTHENTICATION_ACK)
+               if (chbind_rcode != PW_CODE_AUTHENTICATION_ACK)
                        return chbind_rcode;
        }
 
@@ -1319,14 +1312,14 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                         *      Didn't authenticate the packet, but
                         *      we're proxying it.
                         */
-                       code = PW_STATUS_CLIENT;
+                       code = PW_CODE_STATUS_CLIENT;
 
                } else
 #endif /* WITH_PROXY */
                  {
                        RDEBUG("No tunneled reply was found for request %d , and the request was not proxied: rejecting the user.",
                               request->number);
-                       code = PW_AUTHENTICATION_REJECT;
+                       code = PW_CODE_AUTHENTICATION_REJECT;
                }
                break;
 
@@ -1337,19 +1330,19 @@ int eapttls_process(eap_handler_t *handler, tls_session_t *tls_session)
                rcode = process_reply(handler, tls_session, request, fake->reply);
                switch (rcode) {
                case RLM_MODULE_REJECT:
-                       code = PW_AUTHENTICATION_REJECT;
+                       code = PW_CODE_AUTHENTICATION_REJECT;
                        break;
 
                case RLM_MODULE_HANDLED:
-                       code = PW_ACCESS_CHALLENGE;
+                       code = PW_CODE_ACCESS_CHALLENGE;
                        break;
 
                case RLM_MODULE_OK:
-                       code = PW_AUTHENTICATION_ACK;
+                       code = PW_CODE_AUTHENTICATION_ACK;
                        break;
 
                default:
-                       code = PW_AUTHENTICATION_REJECT;
+                       code = PW_CODE_AUTHENTICATION_REJECT;
                        break;
                }
                break;
index 0ee5212..575546d 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,10 +1254,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1914,7 +1914,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2136,7 +2136,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2803,8 +2803,8 @@ printf()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lc"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lc"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2820,22 +2820,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libc${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2847,22 +2847,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libc.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2910,7 +2910,7 @@ if test "x$smart_lib" != "x"; then
   SMART_LIBS="$smart_lib $SMART_LIBS"
 fi
 
-        if test "x$ac_cv_lib_c_printf" != "xyes"; then
+       if test "x$ac_cv_lib_c_printf" != "xyes"; then
                fail="$fail libc"
        fi
 
@@ -2993,22 +2993,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=stdio.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3165,11 +3165,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3643,13 +3643,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index aa54d54..b6997a6 100644 (file)
@@ -11,7 +11,7 @@ if test x$with_[]modname != xno; then
        dnl set $fail to what's missing, on fatal errors.
        dnl use AC_MSG_WARN() on important messages.
        FR_SMART_CHECK_LIB(c, printf)
-        if test "x$ac_cv_lib_c_printf" != "xyes"; then
+       if test "x$ac_cv_lib_c_printf" != "xyes"; then
                fail="$fail libc"
        fi
 
index 9454e07..3b250c4 100644 (file)
@@ -35,21 +35,21 @@ RCSID("$Id$")
  */
 typedef struct rlm_example_t {
        bool            boolean;
-       int             value;
-       char            *string;
-       uint32_t        ipaddr;
+       uint32_t        value;
+       char const      *string;
+       fr_ipaddr_t     ipaddr;
 } rlm_example_t;
 
 /*
  *     A mapping of configuration file names to internal variables.
  */
 static const CONF_PARSER module_config[] = {
-  { "integer", PW_TYPE_INTEGER,    offsetof(rlm_example_t,value), NULL,   "1" },
-  { "boolean", PW_TYPE_BOOLEAN,    offsetof(rlm_example_t,boolean), NULL, "no"},
-  { "string",  PW_TYPE_STRING_PTR, offsetof(rlm_example_t,string), NULL,  NULL},
-  { "ipaddr",  PW_TYPE_IPADDR,     offsetof(rlm_example_t,ipaddr), NULL,  "*" },
+       { "integer", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_example_t, value), "1" },
+       { "boolean", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_example_t, boolean), "no" },
+       { "string", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_example_t, string), NULL },
+       { "ipaddr", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_example_t, ipaddr), "*" },
 
-  { NULL, -1, 0, NULL, NULL }          /* end the list */
+       { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
 
@@ -84,7 +84,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
 {
        VALUE_PAIR *state;
 
@@ -108,8 +108,8 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
         *
         *  The server will take care of sending it to the user.
         */
-       request->reply->code = PW_ACCESS_CHALLENGE;
-       RDEBUG("Sending Access-Challenge.");
+       request->reply->code = PW_CODE_ACCESS_CHALLENGE;
+       RDEBUG("Sending Access-Challenge");
 
        return RLM_MODULE_HANDLED;
 }
@@ -117,7 +117,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
 /*
  *     Authenticate the user with the given password.
  */
-static rlm_rcode_t mod_authenticate(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, UNUSED REQUEST *request)
 {
        return RLM_MODULE_OK;
 }
@@ -126,7 +126,7 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance, UNUSED REQUEST *reque
 /*
  *     Massage the request before recording it or proxying it
  */
-static rlm_rcode_t mod_preacct(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_preacct(UNUSED void *instance, UNUSED REQUEST *request)
 {
        return RLM_MODULE_OK;
 }
@@ -134,7 +134,7 @@ static rlm_rcode_t mod_preacct(UNUSED void *instance, UNUSED REQUEST *request)
 /*
  *     Write accounting information to this modules database.
  */
-static rlm_rcode_t mod_accounting(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(UNUSED void *instance, UNUSED REQUEST *request)
 {
        return RLM_MODULE_OK;
 }
@@ -149,7 +149,7 @@ static rlm_rcode_t mod_accounting(UNUSED void *instance, UNUSED REQUEST *request
  *     max. number of logins, do a second pass and validate all
  *     logins by querying the terminal server (using eg. SNMP).
  */
-static rlm_rcode_t mod_checksimul(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(UNUSED void *instance, UNUSED REQUEST *request)
 {
   request->simul_count=0;
 
index 7ae0e40..77316c9 100644 (file)
@@ -34,15 +34,15 @@ typedef struct rlm_exec_t {
        char const      *xlat_name;
        int             bare;
        bool            wait;
-       char            *program;
-       char            *input;
-       char            *output;
+       char const      *program;
+       char const      *input;
+       char const      *output;
        pair_lists_t    input_list;
        pair_lists_t    output_list;
-       char            *packet_type;
+       char const      *packet_type;
        unsigned int    packet_code;
        bool            shell_escape;
-       int             timeout;
+       uint32_t        timeout;
 } rlm_exec_t;
 
 /*
@@ -55,13 +55,13 @@ typedef struct rlm_exec_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "wait", PW_TYPE_BOOLEAN, offsetof(rlm_exec_t,wait), NULL, "yes" },
-       { "program",  PW_TYPE_STRING_PTR, offsetof(rlm_exec_t,program), NULL, NULL },
-       { "input_pairs", PW_TYPE_STRING_PTR, offsetof(rlm_exec_t,input), NULL, NULL },
-       { "output_pairs",  PW_TYPE_STRING_PTR, offsetof(rlm_exec_t,output), NULL, NULL },
-       { "packet_type", PW_TYPE_STRING_PTR, offsetof(rlm_exec_t,packet_type), NULL, NULL },
-       { "shell_escape", PW_TYPE_BOOLEAN,  offsetof(rlm_exec_t,shell_escape), NULL, "yes" },
-       { "timeout", PW_TYPE_INTEGER,  offsetof(rlm_exec_t,timeout), NULL, NULL },
+       { "wait", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_exec_t, wait), "yes" },
+       { "program", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_exec_t, program), NULL },
+       { "input_pairs", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_exec_t, input), NULL },
+       { "output_pairs", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_exec_t, output), NULL },
+       { "packet_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_exec_t, packet_type), NULL },
+       { "shell_escape", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_exec_t, shell_escape), "yes" },
+       { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_exec_t, timeout), NULL },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -178,7 +178,8 @@ static ssize_t exec_xlat(void *instance, REQUEST *request, char const *fmt, char
        }
 
        /*
-        *      FIXME: Do xlat of program name?
+        *      This function does it's own xlat of the input program
+        *      to execute.
         */
        result = radius_exec_program(request, fmt, inst->wait, inst->shell_escape,
                                     out, outlen, inst->timeout,
@@ -218,10 +219,6 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        xlat_register(inst->xlat_name, exec_xlat, rlm_exec_shell_escape, inst);
 
-       /*
-        *      Check whether program actually exists
-        */
-
        if (inst->input) {
                p = inst->input;
                inst->input_list = radius_list_name(&p, PAIR_LIST_UNKNOWN);
@@ -292,7 +289,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 /*
  *  Dispatch an exec method
  */
-static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_exec_dispatch(void *instance, REQUEST *request)
 {
        rlm_exec_t      *inst = (rlm_exec_t *)instance;
        rlm_rcode_t     rcode;
@@ -345,17 +342,11 @@ static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
        /*
         *      This function does it's own xlat of the input program
         *      to execute.
-        *
-        *      FIXME: if inst->program starts with %{, then
-        *      do an xlat ourselves.  This will allow us to do
-        *      program = %{Exec-Program}, which this module
-        *      xlat's into it's string value, and then the
-        *      exec program function xlat's it's string value
-        *      into something else.
         */
        status = radius_exec_program(request, inst->program, inst->wait, inst->shell_escape,
                                     out, sizeof(out), inst->timeout,
-                                    input_pairs ? *input_pairs : NULL, &answer);
+                                    inst->input ? *input_pairs : NULL,
+                                    inst->output ? &answer : NULL);
        rcode = rlm_exec_status2rcode(request, out, strlen(out), status);
 
        /*
@@ -363,7 +354,7 @@ static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
         *
         *      If we're not waiting, then there are no output pairs.
         */
-       if (output_pairs) {
+       if (inst->output) {
                pairmove(request, output_pairs, &answer);
        }
        pairfree(&answer);
@@ -377,7 +368,7 @@ static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request)
  *
  *     Then, call exec_dispatch.
  */
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        rlm_exec_t      *inst = (rlm_exec_t *) instance;
        rlm_rcode_t     rcode;
@@ -398,7 +389,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                        return RLM_MODULE_NOOP;
                }
 
-               rcode = exec_dispatch(instance, request);
+               rcode = mod_exec_dispatch(instance, request);
                goto finish;
        }
 
@@ -419,7 +410,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                case RLM_MODULE_FAIL:
                case RLM_MODULE_INVALID:
                case RLM_MODULE_REJECT:
-                       request->reply->code = PW_AUTHENTICATION_REJECT;
+                       request->reply->code = PW_CODE_AUTHENTICATION_REJECT;
                        break;
                default:
                        break;
@@ -433,7 +424,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
  *
  *     Then, call exec_dispatch.
  */
-static  rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        rlm_exec_t      *inst = (rlm_exec_t *) instance;
        int             status;
@@ -447,7 +438,7 @@ static  rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         *      Exec-Program and Exec-Program-Wait.
         */
        if (!inst->bare) {
-               return exec_dispatch(instance, request);
+               return mod_exec_dispatch(instance, request);
        }
 
        vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY);
@@ -478,23 +469,23 @@ static  rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 module_t rlm_exec = {
        RLM_MODULE_INIT,
        "exec",                         /* Name */
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       RLM_TYPE_THREAD_SAFE,           /* type */
        sizeof(rlm_exec_t),
        module_config,
        mod_instantiate,                /* instantiation */
        NULL,                           /* detach */
        {
-               exec_dispatch,          /* authentication */
-               exec_dispatch,          /* authorization */
-               exec_dispatch,          /* pre-accounting */
+               mod_exec_dispatch,      /* authentication */
+               mod_exec_dispatch,      /* authorization */
+               mod_exec_dispatch,      /* pre-accounting */
                mod_accounting,         /* accounting */
                NULL,                   /* check simul */
-               exec_dispatch,          /* pre-proxy */
-               exec_dispatch,          /* post-proxy */
+               mod_exec_dispatch,      /* pre-proxy */
+               mod_exec_dispatch,      /* post-proxy */
                mod_post_auth           /* post-auth */
 #ifdef WITH_COA
-               , exec_dispatch,
-               exec_dispatch
+               , mod_exec_dispatch,
+               mod_exec_dispatch
 #endif
        },
 };
index 58ab8b0..44f868f 100644 (file)
@@ -31,7 +31,7 @@ RCSID("$Id$")
 /*
  *      Check if account has expired, and if user may login now.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
 {
        VALUE_PAIR *vp, *check_item = NULL;
 
@@ -63,7 +63,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+                       vp = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
                        vp->vp_date = (uint32_t) (((time_t) check_item->vp_date) - request->timestamp);
                } else if (vp->vp_date > ((uint32_t) (((time_t) check_item->vp_date) - request->timestamp))) {
                        vp->vp_date = (uint32_t) (((time_t) check_item->vp_date) - request->timestamp);
index 6f6f510..ade5b78 100644 (file)
@@ -22,6 +22,7 @@
  * @copyright 2002  Alan DeKok <aland@ox.org>
  */
 RCSID("$Id$")
+USES_APPLE_DEPRECATED_API
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/md5.h>
@@ -29,6 +30,10 @@ RCSID("$Id$")
 #include <freeradius-devel/base64.h>
 #include <freeradius-devel/modules.h>
 
+#ifdef HAVE_OPENSSL_EVP_H
+#  include <openssl/evp.h>
+#endif
+
 #include <ctype.h>
 
 #include "rlm_expr.h"
@@ -38,13 +43,11 @@ RCSID("$Id$")
  */
 typedef struct rlm_expr_t {
        char const *xlat_name;
-       char *allowed_chars;
+       char const *allowed_chars;
 } rlm_expr_t;
 
 static const CONF_PARSER module_config[] = {
-       {"safe_characters", PW_TYPE_STRING_PTR,
-        offsetof(rlm_expr_t, allowed_chars), NULL,
-       "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
+       { "safe_characters", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_expr_t, allowed_chars), "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
        {NULL, -1, 0, NULL, NULL}
 };
 
@@ -86,6 +89,12 @@ static expr_map_t map[] =
 static char randstr_punc[] = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
 static char randstr_salt[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmopqrstuvwxyz/.";
 
+/*
+ *     Characters humans rarely confuse. Reduces char set considerably
+ *     should only be used for things such as one time passwords.
+ */
+static char randstr_otp[] = "469ACGHJKLMNPQRUVWXYabdfhijkprstuvwxyz";
+
 static int get_number(REQUEST *request, char const **string, int64_t *answer)
 {
        int             i, found;
@@ -204,9 +213,9 @@ static int get_number(REQUEST *request, char const **string, int64_t *answer)
                case TOKEN_DIVIDE:
                        if (x == 0) {
                                result = 0; /* we don't have NaN for integers */
-                               break;
+                       } else {
+                               result /= x;
                        }
-                       result /= x;
                        break;
 
                case TOKEN_REMAINDER:
@@ -280,8 +289,7 @@ static ssize_t expr_xlat(UNUSED void *instance, REQUEST *request, char const *fm
        return strlen(out);
 }
 
-/**
- *  @brief Generate a random integer value
+/** Generate a random integer value
  *
  */
 static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt,
@@ -307,8 +315,7 @@ static ssize_t rand_xlat(UNUSED void *instance, UNUSED REQUEST *request, char co
        return strlen(out);
 }
 
-/**
- *  @brief Generate a string of random chars
+/** Generate a string of random chars
  *
  *  Build strings of random chars, useful for generating tokens and passcodes
  *  Format similar to String::Random.
@@ -328,75 +335,98 @@ static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        while (*p && (--freespace > 0)) {
                result = fr_rand();
                switch (*p) {
-                       /*
-                        *  Lowercase letters
-                        */
-                       case 'c':
-                               *out++ = 'a' + (result % 26);
-                               break;
+               /*
+                *  Lowercase letters
+                */
+               case 'c':
+                       *out++ = 'a' + (result % 26);
+                       break;
 
-                       /*
-                        *  Uppercase letters
-                        */
-                       case 'C':
-                               *out++ = 'A' + (result % 26);
-                               break;
+               /*
+                *  Uppercase letters
+                */
+               case 'C':
+                       *out++ = 'A' + (result % 26);
+                       break;
 
-                       /*
-                        *  Numbers
-                        */
-                       case 'n':
-                               *out++ = '0' + (result % 10);
-                               break;
+               /*
+                *  Numbers
+                */
+               case 'n':
+                       *out++ = '0' + (result % 10);
+                       break;
 
-                       /*
-                        *  Alpha numeric
-                        */
-                       case 'a':
-                               *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
-                               break;
+               /*
+                *  Alpha numeric
+                */
+               case 'a':
+                       *out++ = randstr_salt[result % (sizeof(randstr_salt) - 3)];
+                       break;
 
-                       /*
-                        *  Punctuation
-                        */
-                       case '!':
-                               *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
-                               break;
+               /*
+                *  Punctuation
+                */
+               case '!':
+                       *out++ = randstr_punc[result % (sizeof(randstr_punc) - 1)];
+                       break;
 
-                       /*
-                        *  Alpa numeric + punctuation
-                        */
-                       case '.':
-                               *out++ = '!' + (result % 95);
-                               break;
+               /*
+                *  Alpa numeric + punctuation
+                */
+               case '.':
+                       *out++ = '!' + (result % 95);
+                       break;
 
-                       /*
-                        *  Alpha numeric + salt chars './'
-                        */
-                       case 's':
-                               *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
+               /*
+                *  Alpha numeric + salt chars './'
+                */
+               case 's':
+                       *out++ = randstr_salt[result % (sizeof(randstr_salt) - 1)];
+                       break;
+
+               /*
+                *  Chars suitable for One Time Password tokens.
+                *  Alpha numeric with easily confused char pairs removed.
+                */
+               case 'o':
+                       *out++ = randstr_otp[result % (sizeof(randstr_otp) - 1)];
+                       break;
+
+               /*
+                *  Binary data as hexits (we don't really support
+                *  non printable chars).
+                */
+               case 'h':
+                       if (freespace < 2) {
                                break;
+                       }
 
-                       /*
-                        *  Binary data as hexits (we don't really support
-                        *  non printable chars).
-                        */
-                       case 'h':
-                               if (freespace < 2) {
-                                       break;
-                               }
+                       snprintf(out, 3, "%02x", result % 256);
 
-                               snprintf(out, 3, "%02x", result % 256);
+                       /* Already decremented */
+                       freespace -= 1;
+                       out += 2;
+                       break;
 
-                               /* Already decremented */
-                               freespace -= 1;
-                               out += 2;
+               /*
+                *  Binary data with uppercase hexits
+                */
+               case 'H':
+                       if (freespace < 2) {
                                break;
+                       }
 
-                       default:
-                               ERROR("rlm_expr: invalid character class '%c'", *p);
+                       snprintf(out, 3, "%02X", result % 256);
 
-                               return -1;
+                       /* Already decremented */
+                       freespace -= 1;
+                       out += 2;
+                       break;
+
+               default:
+                       ERROR("rlm_expr: invalid character class '%c'", *p);
+
+                       return -1;
                }
 
                p++;
@@ -407,8 +437,7 @@ static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        return outlen - freespace;
 }
 
-/**
- * @brief URLencode special characters
+/** URLencode special characters
  *
  * Example: "%{urlquote:http://example.org/}" == "http%3A%47%47example.org%47"
  */
@@ -428,21 +457,21 @@ static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
                }
 
                switch (*p) {
-                       case '-':
-                       case '_':
-                       case '.':
-                       case '~':
-                               *out++ = *p++;
+               case '-':
+               case '_':
+               case '.':
+               case '~':
+                       *out++ = *p++;
+                       break;
+               default:
+                       if (freespace < 3)
                                break;
-                       default:
-                               if (freespace < 3)
-                                       break;
 
-                               snprintf(out, 4, "%%%02x", *p++); /* %xx */
+                       snprintf(out, 4, "%%%02x", *p++); /* %xx */
 
-                               /* Already decremented */
-                               freespace -= 2;
-                               out += 3;
+                       /* Already decremented */
+                       freespace -= 2;
+                       out += 3;
                }
        }
 
@@ -451,8 +480,7 @@ static ssize_t urlquote_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        return outlen - freespace;
 }
 
-/**
- * @brief Equivalent to the old safe_characters functionality in rlm_sql
+/** Equivalent to the old safe_characters functionality in rlm_sql
  *
  * @verbatim Example: "%{escape:<img>foo.jpg</img>}" == "=60img=62foo.jpg=60/img=62" @endverbatim
  */
@@ -491,8 +519,7 @@ static ssize_t escape_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        return outlen - freespace;
 }
 
-/**
- * @brief Convert a string to lowercase
+/** Convert a string to lowercase
  *
  * Example "%{tolower:Bar}" == "bar"
  *
@@ -517,8 +544,7 @@ static ssize_t lc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        return strlen(out);
 }
 
-/**
- * @brief Convert a string to uppercase
+/** Convert a string to uppercase
  *
  * Example: "%{toupper:Foo}" == "FOO"
  *
@@ -543,16 +569,16 @@ static ssize_t uc_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        return strlen(out);
 }
 
-/**
- * @brief Calculate the MD5 hash of a string.
+/** Calculate the MD5 hash of a string or attribute.
  *
  * Example: "%{md5:foo}" == "acbd18db4cc2f85cedef654fccc4a4d8"
  */
 static ssize_t md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
-                       char const *fmt, char *out, size_t outlen)
+                       char const *fmt, char *out, size_t outlen)
 {
        uint8_t digest[16];
-       int i, len;
+       ssize_t i, len, inlen;
+       uint8_t const *p;
        FR_MD5_CTX ctx;
 
        /*
@@ -563,8 +589,13 @@ static ssize_t md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
                return 0;
        }
 
+       inlen = xlat_fmt_to_ref(&p, request, fmt);
+       if (inlen < 0) {
+               return -1;
+       }
+
        fr_MD5Init(&ctx);
-       fr_MD5Update(&ctx, (const void *) fmt, strlen(fmt));
+       fr_MD5Update(&ctx, p, inlen);
        fr_MD5Final(digest, &ctx);
 
        /*
@@ -581,71 +612,135 @@ static ssize_t md5_xlat(UNUSED void *instance, UNUSED REQUEST *request,
        return strlen(out);
 }
 
-/**
- * @brief Calculate the SHA1 hash of a string.
+/** Calculate the SHA1 hash of a string or attribute.
  *
  * Example: "%{sha1:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
  */
 static ssize_t sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request,
-                         char const *fmt, char *out, size_t outlen)
+                        char const *fmt, char *out, size_t outlen)
 {
-        uint8_t digest[20];
-        int i, len;
-        fr_SHA1_CTX ctx;
-
-        /*
-         *      We need room for at least one octet of output.
-         */
-        if (outlen < 3) {
-                *out = '\0';
-                return 0;
-        }
-
-        fr_SHA1Init(&ctx);
-        fr_SHA1Update(&ctx, (const void *) fmt, strlen(fmt));
-        fr_SHA1Final(digest, &ctx);
-
-        /*
-         *      Each digest octet takes two hex digits, plus one for
-         *      the terminating NUL. SHA1 is 160 bits (20 bytes)
-         */
-        len = (outlen / 2) - 1;
-        if (len > 20) len = 20;
-
-        for (i = 0; i < len; i++) {
-                snprintf(out + i * 2, 3, "%02x", digest[i]);
-        }
-
-        return strlen(out);
+       uint8_t digest[20];
+       ssize_t i, len, inlen;
+       uint8_t const *p;
+       fr_SHA1_CTX ctx;
+
+       /*
+        *      We need room for at least one octet of output.
+        */
+       if (outlen < 3) {
+               *out = '\0';
+               return 0;
+       }
+
+       inlen = xlat_fmt_to_ref(&p, request, fmt);
+       if (inlen < 0) {
+               return -1;
+       }
+
+       fr_SHA1Init(&ctx);
+       fr_SHA1Update(&ctx, p, inlen);
+       fr_SHA1Final(digest, &ctx);
+
+       /*
+        *      Each digest octet takes two hex digits, plus one for
+        *      the terminating NUL. SHA1 is 160 bits (20 bytes)
+        */
+       len = (outlen / 2) - 1;
+       if (len > 20) len = 20;
+
+       for (i = 0; i < len; i++) {
+               snprintf(out + i * 2, 3, "%02x", digest[i]);
+       }
+
+       return strlen(out);
 }
 
-/**
- * @brief Encode string as base64
+/** Calculate any digest supported by OpenSSL EVP_MD
  *
- * Example: "%{tobase64:foo}" == "Zm9v"
+ * Example: "%{sha256:foo}" == "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
+ */
+#ifdef HAVE_OPENSSL_EVP_H
+static ssize_t evp_md_xlat(UNUSED void *instance, UNUSED REQUEST *request,
+                          char const *fmt, char *out, size_t outlen, EVP_MD const *md)
+{
+       uint8_t digest[EVP_MAX_MD_SIZE];
+       unsigned int digestlen, i, len;
+       ssize_t inlen;
+       uint8_t const *p;
+
+       EVP_MD_CTX *ctx;
+
+       /*
+        *      We need room for at least one octet of output.
+        */
+       if (outlen < 3) {
+               *out = '\0';
+               return 0;
+       }
+
+       inlen = xlat_fmt_to_ref(&p, request, fmt);
+       if (inlen < 0) {
+               return -1;
+       }
+
+       ctx = EVP_MD_CTX_create();
+       EVP_DigestInit_ex(ctx, md, NULL);
+       EVP_DigestUpdate(ctx, p, inlen);
+       EVP_DigestFinal_ex(ctx, digest, &digestlen);
+       EVP_MD_CTX_destroy(ctx);
+
+       /*
+        *      Each digest octet takes two hex digits, plus one for
+        *      the terminating NUL.
+        */
+       len = (outlen / 2) - 1;
+       if (len > digestlen) len = digestlen;
+
+       for (i = 0; i < len; i++) {
+               snprintf(out + i * 2, 3, "%02x", digest[i]);
+       }
+       return strlen(out);
+}
+
+#  define EVP_MD_XLAT(_md) \
+static ssize_t _md##_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen)\
+{\
+       return evp_md_xlat(instance, request, fmt, out, outlen, EVP_##_md());\
+}
+
+EVP_MD_XLAT(sha256);
+EVP_MD_XLAT(sha512);
+#endif
+
+/** Encode string or attribute as base64
+ *
+ * Example: "%{base64:foo}" == "Zm9v"
  */
 static ssize_t base64_xlat(UNUSED void *instance, UNUSED REQUEST *request,
                           char const *fmt, char *out, size_t outlen)
 {
-       ssize_t len;
+       ssize_t inlen;
+       uint8_t const *p;
 
-       len = strlen(fmt);
+       inlen = xlat_fmt_to_ref(&p, request, fmt);
+       if (inlen < 0) {
+               return -1;
+       }
 
        /*
         *  We can accurately calculate the length of the output string
         *  if it's larger than outlen, the output would be useless so abort.
         */
-       if ((len < 0) || ((FR_BASE64_ENC_LENGTH(len) + 1) > (ssize_t) outlen)) {
-               REDEBUG("xlat failed.");
+       if ((inlen < 0) || ((FR_BASE64_ENC_LENGTH(inlen) + 1) > (ssize_t) outlen)) {
+               REDEBUG("xlat failed");
                *out = '\0';
                return -1;
        }
 
-       return fr_base64_encode((const uint8_t *) fmt, len, out, outlen);
+       return fr_base64_encode(out, outlen, p, inlen);
 }
 
-/**
- * @brief Convert base64 to hex
+/** Convert base64 to hex
  *
  * Example: "%{base64tohex:Zm9v}" == "666f6f"
  */
@@ -659,7 +754,7 @@ static ssize_t base64_to_hex_xlat(UNUSED void *instance, UNUSED REQUEST *request
 
        *out = '\0';
 
-       declen = fr_base64_decode(fmt, len, decbuf, sizeof(decbuf));
+       declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len);
        if (declen < 0) {
                REDEBUG("Base64 string invalid");
                return -1;
@@ -668,6 +763,7 @@ static ssize_t base64_to_hex_xlat(UNUSED void *instance, UNUSED REQUEST *request
        if ((size_t)((declen * 2) + 1) > outlen) {
                REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes",
                        (declen * 2) + 1, outlen);
+               return -1;
        }
 
        return fr_bin2hex(out, decbuf, declen);
@@ -706,7 +802,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        xlat_register("toupper", uc_xlat, NULL, inst);
        xlat_register("md5", md5_xlat, NULL, inst);
        xlat_register("sha1", sha1_xlat, NULL, inst);
-       xlat_register("tobase64", base64_xlat, NULL, inst);
+#ifdef HAVE_OPENSSL_EVP_H
+       xlat_register("sha256", sha256_xlat, NULL, inst);
+       xlat_register("sha512", sha512_xlat, NULL, inst);
+#endif
+       xlat_register("base64", base64_xlat, NULL, inst);
        xlat_register("base64tohex", base64_to_hex_xlat, NULL, inst);
 
        /*
@@ -728,7 +828,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 module_t rlm_expr = {
        RLM_MODULE_INIT,
        "expr",                         /* Name */
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
        sizeof(rlm_expr_t),
        module_config,
        mod_instantiate,                /* instantiation */
index e51f08f..6025a33 100644 (file)
@@ -28,41 +28,40 @@ RCSID("$Id$")
 
 #include       <ctype.h>
 #include       <fcntl.h>
-#include       <limits.h>
 
 typedef struct rlm_files_t {
-       char *compat_mode;
+       char const *compat_mode;
 
-       char *key;
+       char const *key;
 
-       char *filename;
+       char const *filename;
        fr_hash_table_t *common;
 
        /* autz */
-       char *usersfile;
+       char const *usersfile;
        fr_hash_table_t *users;
 
 
        /* authenticate */
-       char *auth_usersfile;
+       char const *auth_usersfile;
        fr_hash_table_t *auth_users;
 
        /* preacct */
-       char *acctusersfile;
+       char const *acctusersfile;
        fr_hash_table_t *acctusers;
 
 #ifdef WITH_PROXY
        /* pre-proxy */
-       char *preproxy_usersfile;
+       char const *preproxy_usersfile;
        fr_hash_table_t *preproxy_users;
 
        /* post-proxy */
-       char *postproxy_usersfile;
+       char const *postproxy_usersfile;
        fr_hash_table_t *postproxy_users;
 #endif
 
        /* post-authenticate */
-       char *postauth_usersfile;
+       char const *postauth_usersfile;
        fr_hash_table_t *postauth_users;
 } rlm_files_t;
 
@@ -79,26 +78,17 @@ static int fallthrough(VALUE_PAIR *vp)
 }
 
 static const CONF_PARSER module_config[] = {
-       { "filename",      PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,filename), NULL, NULL },
-       { "usersfile",     PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,usersfile), NULL, NULL },
-       { "acctusersfile", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,acctusersfile), NULL, NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, filename), NULL },
+       { "usersfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, usersfile), NULL },
+       { "acctusersfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, acctusersfile), NULL },
 #ifdef WITH_PROXY
-       { "preproxy_usersfile", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,preproxy_usersfile), NULL, NULL },
-       { "postproxy_usersfile", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,postproxy_usersfile), NULL, NULL },
+       { "preproxy_usersfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, preproxy_usersfile), NULL },
+       { "postproxy_usersfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, postproxy_usersfile), NULL },
 #endif
-       { "auth_usersfile", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,auth_usersfile), NULL, NULL },
-       { "postauth_usersfile", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_files_t,postauth_usersfile), NULL, NULL },
-       { "compat",        PW_TYPE_STRING_PTR,
-         offsetof(rlm_files_t,compat_mode), NULL, "cistron" },
-       { "key",           PW_TYPE_STRING_PTR,
-         offsetof(rlm_files_t,key), NULL, NULL },
+       { "auth_usersfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, auth_usersfile), NULL },
+       { "postauth_usersfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_files_t, postauth_usersfile), NULL },
+       { "compat", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_files_t, compat_mode), "cistron" },
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_files_t, key), NULL },
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -122,8 +112,7 @@ static void my_pairlist_free(void *data)
 }
 
 
-static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t **pht,
-                       char *compat_mode_str)
+static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t **pht, char const *compat_mode_str)
 {
        int rcode;
        PAIR_LIST *users = NULL;
@@ -148,7 +137,7 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
        if ((debug_flag) ||
            (strcmp(compat_mode_str, "cistron") == 0)) {
                VALUE_PAIR *vp;
-               int compat_mode = false;
+               bool compat_mode = false;
 
                if (strcmp(compat_mode_str, "cistron") == 0) {
                        compat_mode = true;
@@ -170,7 +159,7 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
                         *      and probably ':=' for server
                         *      configuration items.
                         */
-                       for (vp = paircursor(&cursor, &entry->check); vp; vp = pairnext(&cursor)) {
+                       for (vp = fr_cursor_init(&cursor, &entry->check); vp; vp = fr_cursor_next(&cursor)) {
                                /*
                                 *      Ignore attributes which are set
                                 *      properly.
@@ -187,7 +176,7 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
                                if ((vp->da->vendor != 0) ||
                                                (vp->da->attr < 0x100)) {
                                        if (!compat_mode) {
-                                               WDEBUG("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
+                                               WARN("[%s]:%d Changing '%s =' to '%s =='\n\tfor comparing RADIUS attribute in check item list for user %s",
                                                                filename, entry->lineno,
                                                                vp->da->name, vp->da->name,
                                                                entry->name);
@@ -237,7 +226,7 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
                         *      It's a common enough mistake, that it's
                         *      worth doing.
                         */
-                       for (vp = paircursor(&cursor, &entry->reply); vp; vp = pairnext(&cursor)) {
+                       for (vp = fr_cursor_init(&cursor, &entry->reply); vp; vp = fr_cursor_next(&cursor)) {
                                /*
                                 *      If it's NOT a vendor attribute,
                                 *      and it's NOT a wire protocol
@@ -246,9 +235,8 @@ static int getusersfile(TALLOC_CTX *ctx, char const *filename, fr_hash_table_t *
                                 *      good warning message.
                                 */
                                 if ((vp->da->vendor == 0) &&
-                                       (vp->da->attr > 0xff) &&
                                        (vp->da->attr > 1000)) {
-                                       WDEBUG("[%s]:%d Check item \"%s\"\n"
+                                       WARN("[%s]:%d Check item \"%s\"\n"
                                               "\tfound in reply item list for user \"%s\".\n"
                                               "\tThis attribute MUST go on the first line"
                                               " with the other check items", filename, entry->lineno, vp->da->name,
@@ -433,9 +421,9 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request,
                }
 
                check_tmp = paircopy(request, pl->check);
-               for (vp = paircursor(&cursor, &check_tmp);
+               for (vp = fr_cursor_init(&cursor, &check_tmp);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        if (radius_xlat_do(request, vp) < 0) {
                                RWARN("Failed parsing expanded value for check item, skipping entry: %s", fr_strerror());
                                pairfree(&check_tmp);
@@ -449,11 +437,8 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request,
 
                        /* ctx may be reply or proxy */
                        reply_tmp = paircopy(request, pl->reply);
-                       radius_xlat_move(request, reply_pairs, &reply_tmp);
+                       radius_pairmove(request, reply_pairs, reply_tmp, true);
                        pairmove(request, &request->config_items, &check_tmp);
-
-                       /* Cleanup any unmoved valuepairs */
-                       pairfree(&reply_tmp);
                        pairfree(&check_tmp);
 
                        /*
@@ -486,7 +471,7 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request,
  *     for this user from the database. The main code only
  *     needs to check the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_files_t *inst = instance;
 
@@ -501,7 +486,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
  *     config_items. Reply items are Not Recommended(TM) in acct_users,
  *     except for Fallthrough, which should work
  */
-static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request)
 {
        rlm_files_t *inst = instance;
 
@@ -511,7 +496,7 @@ static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
 }
 
 #ifdef WITH_PROXY
-static rlm_rcode_t file_preproxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request)
 {
        rlm_files_t *inst = instance;
 
@@ -520,7 +505,7 @@ static rlm_rcode_t file_preproxy(void *instance, REQUEST *request)
                           request->packet->vps, &request->proxy->vps);
 }
 
-static rlm_rcode_t file_postproxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *instance, REQUEST *request)
 {
        rlm_files_t *inst = instance;
 
@@ -530,7 +515,7 @@ static rlm_rcode_t file_postproxy(void *instance, REQUEST *request)
 }
 #endif
 
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_files_t *inst = instance;
 
@@ -539,7 +524,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                           request->packet->vps, &request->reply->vps);
 }
 
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        rlm_files_t *inst = instance;
 
@@ -553,7 +538,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
 module_t rlm_files = {
        RLM_MODULE_INIT,
        "files",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
+       RLM_TYPE_HUP_SAFE,
        sizeof(rlm_files_t),
        module_config,
        mod_instantiate,                /* instantiation */
@@ -565,8 +550,8 @@ module_t rlm_files = {
                NULL,                   /* accounting */
                NULL,                   /* checksimul */
 #ifdef WITH_PROXY
-               file_preproxy,          /* pre-proxy */
-               file_postproxy,         /* post-proxy */
+               mod_pre_proxy,          /* pre-proxy */
+               mod_post_proxy,         /* post-proxy */
 #else
                NULL, NULL,
 #endif
index 9ba1a84..47d48da 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,10 +1254,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1915,7 +1915,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2137,7 +2137,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2804,8 +2804,8 @@ idna_to_ascii_8z()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lidn"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lidn"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2821,22 +2821,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libidn${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2848,22 +2848,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libidn.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2911,7 +2911,7 @@ if test "x$smart_lib" != "x"; then
   SMART_LIBS="$smart_lib $SMART_LIBS"
 fi
 
-        if test "x$ac_cv_lib_idn_idna_to_ascii_8z" != "xyes"; then
+       if test "x$ac_cv_lib_idn_idna_to_ascii_8z" != "xyes"; then
                fail="$fail libidn"
        fi
 
@@ -2994,22 +2994,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=idna.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3162,11 +3162,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3672,11 +3672,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 14e6b5b..bd16a65 100644 (file)
@@ -8,7 +8,7 @@ if test x$with_[]modname != xno; then
        AC_PROG_CPP
 
        FR_SMART_CHECK_LIB(idn, idna_to_ascii_8z)
-        if test "x$ac_cv_lib_idn_idna_to_ascii_8z" != "xyes"; then
+       if test "x$ac_cv_lib_idn_idna_to_ascii_8z" != "xyes"; then
                fail="$fail libidn"
        fi
 
index 5f5f384..18c2914 100644 (file)
@@ -31,9 +31,9 @@ RCSID("$Id$")
  *      Structure for module configuration
  */
 typedef struct rlm_idn_t {
-        char const     *xlat_name;
-        bool           use_std3_ascii_rules;
-        bool           allow_unassigned;
+       char const      *xlat_name;
+       bool            use_std3_ascii_rules;
+       bool            allow_unassigned;
 } rlm_idn_t;
 
 /*
@@ -66,23 +66,23 @@ typedef struct rlm_idn_t {
 static const CONF_PARSER mod_config[] = {
        /*
         *      If a STRINGPREP profile other than NAMEPREP is ever desired,
-         *     we can implement an option, and it will default to NAMEPREP settings.
-         *     ...and if we want raw punycode or to tweak Bootstring parameters,
-         *     we can do similar things.  All defaults should result in IDNA
-         *     ToASCII with the use_std3_ascii_rules flag set, allow_unassigned unset,
-         *     because that is the forseeable use case.
-         *
-         *     Note that doing anything much different will require choosing the
-         *     appropriate libidn API functions, as we currently call the IDNA
-         *     convenience functions.
-         *
-         *     Also note that right now we do not provide ToUnicode, which may or
-         *     may not be useful as an xlat... depends on how the results need to
-         *     be used.
-         */
-
-       {"allow_unassigned", PW_TYPE_BOOLEAN, offsetof(rlm_idn_t, allow_unassigned), NULL, "no" },
-       {"use_std3_ascii_rules", PW_TYPE_BOOLEAN, offsetof(rlm_idn_t, use_std3_ascii_rules), NULL, "yes" },
+             we can implement an option, and it will default to NAMEPREP settings.
+             ...and if we want raw punycode or to tweak Bootstring parameters,
+             we can do similar things.  All defaults should result in IDNA
+             ToASCII with the use_std3_ascii_rules flag set, allow_unassigned unset,
+             because that is the forseeable use case.
+        *
+             Note that doing anything much different will require choosing the
+             appropriate libidn API functions, as we currently call the IDNA
+             convenience functions.
+        *
+             Also note that right now we do not provide ToUnicode, which may or
+             may not be useful as an xlat... depends on how the results need to
+             be used.
+        */
+
+       { "allow_unassigned", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_idn_t, allow_unassigned), "no" },
+       { "use_std3_ascii_rules", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_idn_t, use_std3_ascii_rules), "yes" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -93,16 +93,16 @@ static ssize_t xlat_idna(void *instance, UNUSED REQUEST *request, char const *fm
        char *idna = NULL;
        int res;
        size_t len;
-        int flags = 0;
+       int flags = 0;
 
-        if (inst->use_std3_ascii_rules) {
-               flags |= IDNA_USE_STD3_ASCII_RULES;
-        }
-        if (inst->allow_unassigned) {
-               flags |= IDNA_ALLOW_UNASSIGNED;
+       if (inst->use_std3_ascii_rules) {
+               flags |= IDNA_USE_STD3_ASCII_RULES;
+       }
+       if (inst->allow_unassigned) {
+               flags |= IDNA_ALLOW_UNASSIGNED;
        }
 
-        res = idna_to_ascii_8z(fmt, &idna, flags);
+       res = idna_to_ascii_8z(fmt, &idna, flags);
        if (res) {
                if (idna) {
                        free (idna); /* Docs unclear, be safe. */
@@ -112,11 +112,11 @@ static ssize_t xlat_idna(void *instance, UNUSED REQUEST *request, char const *fm
                return -1;
        }
 
-        len = strlen(idna);
+       len = strlen(idna);
 
        /* 253 is max DNS length */
-        if (!((len < (freespace - 1)) && (len <= 253))) {
-               /* Never provide a truncated result, as it may be queried. */
+       if (!((len < (freespace - 1)) && (len <= 253))) {
+               /* Never provide a truncated result, as it may be queried. */
                REDEBUG("Conversion was truncated");
 
                free(idna);
index ca16258..f81422a 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1214,9 +1214,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1259,10 +1259,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1987,7 +1987,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2209,7 +2209,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2896,22 +2896,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=gdbm.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3020,8 +3020,8 @@ gdbm_open()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lgdbm"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lgdbm"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3037,22 +3037,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libgdbm${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3064,22 +3064,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libgdbm.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3419,11 +3419,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3897,13 +3897,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 755f29d..e41ffb9 100644 (file)
@@ -60,15 +60,20 @@ RCSID("$Id$")
  *     be used as the instance handle.
  */
 typedef struct rlm_ippool_t {
-       char            *filename;
-       char            *ip_index;
-       char            *name;
-       char            *key;
+       char const      *filename;
+       char const      *ip_index;
+       char const      *name;
+       char const      *key;
+
+       fr_ipaddr_t     range_start_addr;
+       fr_ipaddr_t     range_stop_addr;
+       fr_ipaddr_t     netmask_addr;
        uint32_t        range_start;
        uint32_t        range_stop;
        uint32_t        netmask;
-       time_t          max_timeout;
-       int             cache_size;
+
+       uint32_t        max_timeout;
+       uint32_t        cache_size;
        bool            override;
        GDBM_FILE       gdbm;
        GDBM_FILE       ip;
@@ -101,30 +106,29 @@ typedef struct ippool_key {
 } ippool_key;
 
 static const CONF_PARSER module_config[] = {
-       { "session-db", PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, offsetof(rlm_ippool_t,filename), NULL, NULL },
-       { "filename", PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, offsetof(rlm_ippool_t,filename), NULL, NULL },
+       { "session-db", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_DEPRECATED, rlm_ippool_t, filename), NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_ippool_t, filename), NULL },
 
-       { "ip-index", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
-       { "ip_index", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(rlm_ippool_t,ip_index), NULL, NULL },
+       { "ip-index", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_ippool_t, ip_index), NULL },
+       { "ip_index", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_ippool_t, ip_index), NULL },
 
-       { "key", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_ippool_t,key), NULL, "%{NAS-IP-Address} %{NAS-Port}" },
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_ippool_t, key), "%{NAS-IP-Address} %{NAS-Port}" },
 
-       { "range-start", PW_TYPE_IPADDR | PW_TYPE_DEPRECATED, offsetof(rlm_ippool_t,range_start), NULL, NULL },
-       { "range_start", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_start), NULL, "0" },
+       { "range-start", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR | PW_TYPE_DEPRECATED, rlm_ippool_t, range_start_addr), NULL },
+       { "range_start", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_ippool_t, range_start_addr), "0" },
 
-       { "range-stop", PW_TYPE_IPADDR | PW_TYPE_DEPRECATED, offsetof(rlm_ippool_t,range_stop), NULL, NULL },
-       { "range_stop", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,range_stop), NULL, "0" },
+       { "range-stop", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR | PW_TYPE_DEPRECATED, rlm_ippool_t, range_stop_addr), NULL },
+       { "range_stop", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_ippool_t, range_stop_addr), "0" },
 
-       { "netmask", PW_TYPE_IPADDR, offsetof(rlm_ippool_t,netmask), NULL, "0" },
+       { "netmask", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, rlm_ippool_t, netmask_addr), "0" },
 
-       { "cache-size", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, offsetof(rlm_ippool_t,cache_size), NULL, NULL },
-       { "cache_size", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,cache_size), NULL, "1000" },
+       { "cache-size", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_ippool_t, cache_size), NULL },
+       { "cache_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ippool_t, cache_size), "1000" },
 
-       { "override", PW_TYPE_BOOLEAN, offsetof(rlm_ippool_t,override), NULL, "no" },
+       { "override", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_ippool_t, override), "no" },
 
-       { "maximum-timeout", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, offsetof(rlm_ippool_t,max_timeout), NULL, NULL },
-       { "maximum_timeout", PW_TYPE_INTEGER, offsetof(rlm_ippool_t,max_timeout), NULL, "0" },
+       { "maximum-timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_ippool_t, max_timeout), NULL },
+       { "maximum_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_ippool_t, max_timeout), "0" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -141,112 +145,173 @@ static const CONF_PARSER module_config[] = {
  */
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
-       rlm_ippool_t *inst = instance;
-       int cache_size;
-       ippool_info entry;
-       ippool_key key;
-       datum key_datum;
-       datum data_datum;
-       char const *cli = "0";
-       char const *pool_name = NULL;
+       rlm_ippool_t    *inst = instance;
+       int             cache_size;
+       ippool_info     entry;
+       ippool_key      key;
+       datum           key_datum;
+       datum           data_datum;
+
+       char const      *cli = "0";
+       char const      *pool_name = NULL;
+
+       int             rcode;
+       uint32_t        i, j;
+       uint32_t        or_result;
+       char            str[32];
+       char            init_str[17];
+
+       /*
+        *  Add the ip pool name
+        */
+       inst->name = NULL;
+       pool_name = cf_section_name2(conf);
+       if (pool_name != NULL) {
+               inst->name = talloc_typed_strdup(inst, pool_name);
+       }
 
        cache_size = inst->cache_size;
 
        rad_assert(inst->filename && *inst->filename);
        rad_assert(inst->ip_index && *inst->ip_index);
 
-       inst->range_start = htonl(inst->range_start);
-       inst->range_stop = htonl(inst->range_stop);
-       inst->netmask = htonl(inst->netmask);
+       inst->range_start = htonl(*((uint32_t *)(&(inst->range_start_addr.ipaddr.ip4addr))));
+       inst->range_stop = htonl(*((uint32_t *)(&(inst->range_stop_addr.ipaddr.ip4addr))));
+       inst->netmask = htonl(*((uint32_t *)(&(inst->netmask_addr.ipaddr.ip4addr))));
        if (inst->range_start == 0 || inst->range_stop == 0 || \
            inst->range_start >= inst->range_stop ) {
                cf_log_err_cs(conf, "Invalid data range");
                return -1;
        }
 
-       inst->gdbm = gdbm_open(inst->filename, sizeof(int),
-                       GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
+       {
+               char *file;
+
+               memcpy(&file, &inst->filename, sizeof(file));
+               inst->gdbm = gdbm_open(file, sizeof(int),
+                                      GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
+       }
+
        if (!inst->gdbm) {
-               ERROR("rlm_ippool: Failed to open file %s: %s",
-                               inst->filename, strerror(errno));
+               ERROR("rlm_ippool: Failed to open file %s: %s", inst->filename, fr_syserror(errno));
+
                return -1;
        }
-       inst->ip = gdbm_open(inst->ip_index, sizeof(int),
-                       GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
+
+       {
+               char *file;
+
+               memcpy(&file, &inst->ip_index, sizeof(file));
+               inst->ip = gdbm_open(file, sizeof(int),
+                                    GDBM_WRCREAT | GDBM_IPPOOL_OPTS, 0600, NULL);
+       }
+
        if (!inst->ip) {
-               ERROR("rlm_ippool: Failed to open file %s: %s",
-                               inst->ip_index, strerror(errno));
+               ERROR("rlm_ippool: Failed to open file %s: %s", inst->ip_index, fr_syserror(errno));
+
                return -1;
        }
-       if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
+
+       if (gdbm_setopt(inst->gdbm, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) {
                ERROR("rlm_ippool: Failed to set cache size");
-       if (gdbm_setopt(inst->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1)
+       }
+
+       if (gdbm_setopt(inst->ip, GDBM_CACHESIZE, &cache_size, sizeof(int)) == -1) {
                ERROR("rlm_ippool: Failed to set cache size");
+       }
 
-       key_datum = gdbm_firstkey(inst->gdbm);
-       if (!key_datum.dptr){
-                       /*
-                        * If the database does not exist initialize it.
-                        * We set the nas/port pairs to not existent values and
-                        * active = 0
-                        */
-               int rcode;
-               uint32_t i, j;
-               uint32_t or_result;
-               char str[32];
-               char init_str[17];
+       pthread_mutex_init(&inst->op_mutex, NULL);
 
-               DEBUG("rlm_ippool: Initializing database");
-               for(i=inst->range_start,j=~0;i<=inst->range_stop;i++,j--){
+       key_datum = gdbm_firstkey(inst->gdbm);
+       if (key_datum.dptr) {
+               free(key_datum.dptr);
+               return 0;
+       }
 
-                       /*
-                        * Net and Broadcast addresses are excluded
-                        */
-                       or_result = i | inst->netmask;
-                       if (~inst->netmask != 0 &&
-                               (or_result == inst->netmask ||
-                           (~or_result == 0))) {
-                               DEBUG("rlm_ippool: IP %s excluded",
-                                     ip_ntoa(str, ntohl(i)));
-                               continue;
-                       }
+       /*
+        *  If the database does not exist initialize it.
+        *  We set the nas/port pairs to not existent values and
+        *  active = 0
+        */
+       DEBUG("rlm_ippool: Initializing database");
+       for (i = inst->range_start, j=~0; i <= inst->range_stop; i++, j--){
+               /*
+                *  Net and Broadcast addresses are excluded
+                */
+               or_result = i | inst->netmask;
+               if (~inst->netmask != 0 && (or_result == inst->netmask || (~or_result == 0))) {
+                       DEBUG("rlm_ippool: IP %s excluded", ip_ntoa(str, ntohl(i)));
+                       continue;
+               }
 
-                       sprintf(init_str,"%016d",j);
-                       DEBUG("rlm_ippool: Initialized bucket: %s",init_str);
-                       memcpy(key.key, init_str,16);
-                       key_datum.dptr = (char *) &key;
-                       key_datum.dsize = sizeof(ippool_key);
+               sprintf(init_str,"%016d",j);
+               DEBUG("rlm_ippool: Initialized bucket: %s",init_str);
+               memcpy(key.key, init_str,16);
+               key_datum.dptr = (char *) &key;
+               key_datum.dsize = sizeof(ippool_key);
 
-                       entry.ipaddr = ntohl(i);
-                       entry.active = 0;
-                       entry.extra = 0;
-                       entry.timestamp = 0;
-                       entry.timeout = 0;
-                       strcpy(entry.cli,cli);
+               entry.ipaddr = ntohl(i);
+               entry.active = 0;
+               entry.extra = 0;
+               entry.timestamp = 0;
+               entry.timeout = 0;
+               strcpy(entry.cli,cli);
 
-                       data_datum.dptr = (char *) &entry;
-                       data_datum.dsize = sizeof(ippool_info);
+               data_datum.dptr = (char *) &entry;
+               data_datum.dsize = sizeof(ippool_info);
 
-                       rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE);
-                       if (rcode < 0) {
-                               ERROR("rlm_ippool: Failed storing data to %s: %s",
-                                               inst->filename, gdbm_strerror(gdbm_errno));
-                               gdbm_close(inst->gdbm);
-                               gdbm_close(inst->ip);
-                               return -1;
-                       }
+               rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE);
+               if (rcode < 0) {
+                       ERROR("rlm_ippool: Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
+                       gdbm_close(inst->gdbm);
+                       gdbm_close(inst->ip);
+                       return -1;
                }
        }
-       else
-               free(key_datum.dptr);
 
-       /* Add the ip pool name */
-       inst->name = NULL;
-       pool_name = cf_section_name2(conf);
-       if (pool_name != NULL)
-               inst->name = strdup(pool_name);
+       return 0;
+}
 
-       pthread_mutex_init(&inst->op_mutex, NULL);
+/** Decrease allocated count from the ip index
+ *
+ */
+static int decrease_allocated_count(rlm_ippool_t *inst, REQUEST *request, ippool_info *entry, datum *save_datum)
+{
+       datum           data_datum;
+       datum           key_datum;
+       int             num;
+
+
+       key_datum.dptr = (char *) &(entry->ipaddr);
+       key_datum.dsize = sizeof(uint32_t);
+       data_datum = gdbm_fetch(inst->ip, key_datum);
+       if (!data_datum.dptr) {
+               return 0;
+       }
+       memcpy(&num, data_datum.dptr, sizeof(int));
+       free(data_datum.dptr);
+       if (num > 0){
+               int     rcode;
+
+               num--;
+
+               RDEBUG("Allocated count now: %i", num);
+               data_datum.dptr = (char *) &num;
+               data_datum.dsize = sizeof(int);
+               rcode = gdbm_store(inst->ip, key_datum, data_datum, GDBM_REPLACE);
+               if (rcode < 0) {
+                       RDEBUG("Failed storing data to %s: %s", inst->ip_index, gdbm_strerror(gdbm_errno));
+                       return -1;
+               }
+               if ((num > 0) && entry->extra == 1){
+                       /*
+                        * We are doing MPPP and we still have nas/port entries referencing
+                        * this ip. Delete this entry so that eventually we only keep one
+                        * reference to this ip.
+                        */
+                       gdbm_delete(inst->gdbm, *save_datum);
+               }
+       }
 
        return 0;
 }
@@ -256,175 +321,164 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     Check for an Accounting-Stop
  *     If we find one and we have allocated an IP to this nas/port combination, deallocate it.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
-       rlm_ippool_t *inst = instance;
-       datum key_datum;
-       datum data_datum;
-       datum save_datum;
-       int acctstatustype = 0;
-       int rcode;
-       ippool_info entry;
-       ippool_key key;
-       int num = 0;
-       VALUE_PAIR *vp;
-       char str[32];
-       uint8_t key_str[17];
-       char hex_str[35];
-       char xlat_str[MAX_STRING_LEN];
-       FR_MD5_CTX md5_context;
-
-
-       if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL)
-               acctstatustype = vp->vp_integer;
-       else {
-               RDEBUG("Could not find account status type in packet. Return NOOP.");
-               return RLM_MODULE_NOOP;
+       rlm_ippool_t    *inst = instance;
+
+       datum           key_datum;
+       ippool_key      key;
+       datum           data_datum;
+       ippool_info     entry;
+       datum           save_datum;
+
+       int             rcode;
+       VALUE_PAIR      *vp;
+
+       char            str[32];
+       uint8_t         key_str[17];
+       char            hex_str[35];
+       char            xlat_str[MAX_STRING_LEN];
+       int             ret;
+
+       vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
+       if (!vp) {
+               RDEBUG2("Could not find account status type in packet");
+               return RLM_MODULE_INVALID;
        }
-       switch(acctstatustype){
-               case PW_STATUS_STOP:
-                       if (radius_xlat(xlat_str, sizeof(xlat_str), request, inst->key, NULL, NULL) < 0){
-                               return RLM_MODULE_NOOP;
-                       }
-                       fr_MD5Init(&md5_context);
-                       fr_MD5Update(&md5_context, (uint8_t *)xlat_str,
-                        strlen(xlat_str));
-                       fr_MD5Final(key_str, &md5_context);
-                       key_str[16] = '\0';
-                       fr_bin2hex(hex_str, key_str, 16);
-                       hex_str[32] = '\0';
-                       RDEBUG("MD5 on 'key' directive maps to: %s",hex_str);
-                       memcpy(key.key,key_str,16);
-                       break;
-               default:
-                       /* We don't care about any other accounting packet */
-                       RDEBUG("This is not an Accounting-Stop. Return NOOP.");
 
-                       return RLM_MODULE_NOOP;
+       switch (vp->vp_integer) {
+       case PW_STATUS_STOP:
+       {
+               FR_MD5_CTX md5_context;
+               if (radius_xlat(xlat_str, sizeof(xlat_str), request, inst->key, NULL, NULL) < 0){
+                       return RLM_MODULE_FAIL;
+               }
+
+               fr_MD5Init(&md5_context);
+               fr_MD5Update(&md5_context, (uint8_t *)xlat_str, strlen(xlat_str));
+               fr_MD5Final(key_str, &md5_context);
+
+               key_str[16] = '\0';
+               fr_bin2hex(hex_str, key_str, 16);
+               hex_str[32] = '\0';
+
+               RDEBUG2("MD5 on 'key' directive maps to: %s", hex_str);
+               memcpy(key.key, key_str, 16);
+               break;
        }
 
-       RDEBUG("Searching for an entry for key: '%s'",xlat_str);
+       default:
+               /* We don't care about any other accounting packet */
+               RDEBUG2("This is not an Accounting-Stop");
+
+               return RLM_MODULE_NOOP;
+       }
+
+       RDEBUG2("Searching for an entry for key: '%s'", xlat_str);
        key_datum.dptr = (char *) &key;
        key_datum.dsize = sizeof(ippool_key);
 
        pthread_mutex_lock(&inst->op_mutex);
        data_datum = gdbm_fetch(inst->gdbm, key_datum);
-       if (data_datum.dptr != NULL){
+       if (data_datum.dptr == NULL) {
+               pthread_mutex_unlock(&inst->op_mutex);
+               RDEBUG2("Entry not found");
 
-               /*
-                * If the entry was found set active to zero
-                */
-               memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
-               free(data_datum.dptr);
-               RDEBUG("Deallocated entry for ip: %s",ip_ntoa(str,entry.ipaddr));
-               entry.active = 0;
-               entry.timestamp = 0;
-               entry.timeout = 0;
+               return RLM_MODULE_NOTFOUND;
+       }
 
-               /*
-                * Save the reference to the entry
-                */
-               save_datum.dptr = key_datum.dptr;
-               save_datum.dsize = key_datum.dsize;
+       /*
+        *   If the entry was found set active to zero
+        */
+       memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
+       free(data_datum.dptr);
 
-               data_datum.dptr = (char *) &entry;
-               data_datum.dsize = sizeof(ippool_info);
+       RDEBUG("Deallocated entry for ip: %s", ip_ntoa(str, entry.ipaddr));
+       entry.active = 0;
+       entry.timestamp = 0;
+       entry.timeout = 0;
 
-               rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE);
-               if (rcode < 0) {
-                       ERROR("rlm_ippool: Failed storing data to %s: %s",
-                                       inst->filename, gdbm_strerror(gdbm_errno));
-                       pthread_mutex_unlock(&inst->op_mutex);
-                       return RLM_MODULE_FAIL;
-               }
+       /*
+        *  Save the reference to the entry
+        */
+       save_datum.dptr = key_datum.dptr;
+       save_datum.dsize = key_datum.dsize;
 
-               /*
-                * Decrease allocated count from the ip index
-                */
-               key_datum.dptr = (char *) &entry.ipaddr;
-               key_datum.dsize = sizeof(uint32_t);
-               data_datum = gdbm_fetch(inst->ip, key_datum);
-               if (data_datum.dptr != NULL){
-                       memcpy(&num, data_datum.dptr, sizeof(int));
-                       free(data_datum.dptr);
-                       if (num >0){
-                               num--;
-                               RDEBUG("num: %d",num);
-                               data_datum.dptr = (char *) &num;
-                               data_datum.dsize = sizeof(int);
-                               rcode = gdbm_store(inst->ip, key_datum, data_datum, GDBM_REPLACE);
-                               if (rcode < 0) {
-                                       ERROR("rlm_ippool: Failed storing data to %s: %s",
-                                                       inst->ip_index, gdbm_strerror(gdbm_errno));
-                                       pthread_mutex_unlock(&inst->op_mutex);
-                                       return RLM_MODULE_FAIL;
-                               }
-                               if (num >0 && entry.extra == 1){
-                                       /*
-                                        * We are doing MPPP and we still have nas/port entries referencing
-                                        * this ip. Delete this entry so that eventually we only keep one
-                                        * reference to this ip.
-                                        */
-                                       gdbm_delete(inst->gdbm,save_datum);
-                               }
-                       }
-               }
+       data_datum.dptr = (char *) &entry;
+       data_datum.dsize = sizeof(ippool_info);
+       rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE);
+       if (rcode < 0) {
                pthread_mutex_unlock(&inst->op_mutex);
+               REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
+
+               return RLM_MODULE_FAIL;
        }
-       else{
-               pthread_mutex_unlock(&inst->op_mutex);
-               RDEBUG("Entry not found");
+
+       /*
+        *  Decrease allocated count from the ip index
+        */
+       ret = decrease_allocated_count(inst, request, &entry, &save_datum);
+       pthread_mutex_unlock(&inst->op_mutex);
+       if (ret < 0) {
+               return RLM_MODULE_FAIL;
        }
 
        return RLM_MODULE_OK;
 }
 
-static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
 {
        rlm_ippool_t *inst = instance;
-       int delete = 0;
-       int found = 0;
-       int mppp = 0;
-       int extra = 0;
-       int rcode;
-       int num = 0;
-       datum key_datum;
-       datum nextkey;
-       datum data_datum;
-       datum save_datum;
-       ippool_key key;
-       ippool_info entry;
-       VALUE_PAIR *vp;
-       char const *cli = NULL;
-       char str[32];
-       uint8_t key_str[17];
-       char hex_str[35];
-       char xlat_str[MAX_STRING_LEN];
-       FR_MD5_CTX md5_context;
+
+       datum           key_datum;
+       ippool_key      key;
+       datum           nextkey;
+       datum           data_datum;
+       ippool_info     entry;
+       datum           save_datum;
+
+       int             delete = 0;
+       bool            found = false;
+       int             mppp = 0;
+       int             extra = 0;
+       int             rcode;
+       int             num = 0;
+
+       VALUE_PAIR      *vp;
+       char const      *cli = NULL;
+       char            str[32];
+       uint8_t         key_str[17];
+       char            hex_str[35];
+       char            xlat_str[MAX_STRING_LEN];
+       FR_MD5_CTX      md5_context;
+
 #ifdef WITH_DHCP
-       int dhcp = false;
+       bool dhcp = false;
 #endif
-       int attr_ipaddr = PW_FRAMED_IP_ADDRESS;
-       int attr_ipmask = PW_FRAMED_IP_NETMASK;
-       int vendor_ipaddr = 0;
+       int             attr_ipaddr = PW_FRAMED_IP_ADDRESS;
+       int             attr_ipmask = PW_FRAMED_IP_NETMASK;
+       int             vendor_ipaddr = 0;
 
-       /* Check if Pool-Name attribute exists. If it exists check our name and
-        * run only if they match
+       /*
+        *  Check if Pool-Name attribute exists. If it exists check our name and
+        *  run only if they match
         */
-       if ((vp = pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY)) != NULL){
+       vp = pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY);
+       if (vp != NULL){
                if (!inst->name || (strcmp(inst->name,vp->vp_strvalue) && strcmp(vp->vp_strvalue,"DEFAULT")))
                        return RLM_MODULE_NOOP;
        } else {
-               RDEBUG("Could not find Pool-Name attribute.");
+               RDEBUG("Could not find Pool-Name attribute");
                return RLM_MODULE_NOOP;
        }
 
-
        /*
-        * Find the caller id
+        *  Find the caller id
         */
-       if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL)
+       vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY);
+       if (vp != NULL) {
                cli = vp->vp_strvalue;
+       }
 
 #ifdef WITH_DHCP
        if (request->listener->type == RAD_LISTEN_DHCP) {
@@ -436,7 +490,7 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
 #endif
 
        if (radius_xlat(xlat_str, sizeof(xlat_str), request, inst->key, NULL, NULL) < 0){
-               return RLM_MODULE_NOOP;
+               return RLM_MODULE_FAIL;
        }
 
        fr_MD5Init(&md5_context);
@@ -445,10 +499,11 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
        key_str[16] = '\0';
        fr_bin2hex(hex_str, key_str, 16);
        hex_str[32] = '\0';
-       RDEBUG("MD5 on 'key' directive maps to: %s",hex_str);
-       memcpy(key.key,key_str,16);
 
-       RDEBUG("Searching for an entry for key: '%s'",hex_str);
+       RDEBUG("MD5 on 'key' directive maps to: %s", hex_str);
+       memcpy(key.key, key_str, 16);
+
+       RDEBUG("Searching for an entry for key: '%s'", hex_str);
        key_datum.dptr = (char *) &key;
        key_datum.dsize = sizeof(ippool_key);
 
@@ -456,20 +511,22 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
        data_datum = gdbm_fetch(inst->gdbm, key_datum);
        if (data_datum.dptr != NULL){
                /*
-                * If there is a corresponding entry in the database with active=1 it is stale.
-                * Set active to zero
+                *  If there is a corresponding entry in the database with active=1 it is stale.
+                *  Set active to zero
                 */
-               found = 1;
+               found = true;
                memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
                free(data_datum.dptr);
+
                if (entry.active){
+                       int ret;
                        RDEBUG("Found a stale entry for ip: %s",ip_ntoa(str,entry.ipaddr));
                        entry.active = 0;
                        entry.timestamp = 0;
                        entry.timeout = 0;
 
                        /*
-                        * Save the reference to the entry
+                        *  Save the reference to the entry
                         */
                        save_datum.dptr = key_datum.dptr;
                        save_datum.dsize = key_datum.dsize;
@@ -479,40 +536,18 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
 
                        rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE);
                        if (rcode < 0) {
-                               ERROR("rlm_ippool: Failed storing data to %s: %s",
-                                       inst->filename, gdbm_strerror(gdbm_errno));
+                               REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
                                pthread_mutex_unlock(&inst->op_mutex);
                                return RLM_MODULE_FAIL;
                        }
-                       /* Decrease allocated count from the ip index */
 
-                       key_datum.dptr = (char *) &entry.ipaddr;
-                       key_datum.dsize = sizeof(uint32_t);
-                       data_datum = gdbm_fetch(inst->ip, key_datum);
-                       if (data_datum.dptr != NULL){
-                               memcpy(&num, data_datum.dptr, sizeof(int));
-                               free(data_datum.dptr);
-                               if (num >0){
-                                       num--;
-                                       RDEBUG("num: %d",num);
-                                       data_datum.dptr = (char *) &num;
-                                       data_datum.dsize = sizeof(int);
-                                       rcode = gdbm_store(inst->ip, key_datum, data_datum, GDBM_REPLACE);
-                                       if (rcode < 0) {
-                                               ERROR("rlm_ippool: Failed storing data to %s: %s",
-                                                               inst->ip_index, gdbm_strerror(gdbm_errno));
-                                               pthread_mutex_unlock(&inst->op_mutex);
-                                               return RLM_MODULE_FAIL;
-                                       }
-                                       if (num >0 && entry.extra == 1){
-                                               /*
-                                                * We are doing MPPP and we still have nas/port entries referencing
-                                                * this ip. Delete this entry so that eventually we only keep one
-                                                * reference to this ip.
-                                                */
-                                               gdbm_delete(inst->gdbm,save_datum);
-                                       }
-                               }
+                       /*
+                        *  Decrease allocated count for the ip
+                        */
+                       ret = decrease_allocated_count(inst, request, &entry, &save_datum);
+                       pthread_mutex_unlock(&inst->op_mutex);
+                       if (ret < 0) {
+                               return RLM_MODULE_FAIL;
                        }
                }
        }
@@ -520,47 +555,44 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
        pthread_mutex_unlock(&inst->op_mutex);
 
        /*
-        * If there is a Framed-IP-Address (or Dhcp-Your-IP-Address)
-        * attribute in the reply, check for override
+        *  If there is a Framed-IP-Address (or Dhcp-Your-IP-Address)
+        *  attribute in the reply, check for override
         */
        if (pairfind(request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY) != NULL) {
-               RDEBUG("Found IP address attribute in reply attribute list.");
-               if (inst->override)
-               {
-                       RDEBUG("Override supplied IP address");
-                       pairdelete(&request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY);
-               } else {
-                       /* Abort */
-                       RDEBUG("override is set to no. Return NOOP.");
+               RDEBUG("Found IP address attribute in reply attribute list");
+               if (!inst->override) {
+                       RDEBUG("override is set to no. Return NOOP");
                        return RLM_MODULE_NOOP;
                }
+
+               RDEBUG("Override supplied IP address");
+               pairdelete(&request->reply->vps, attr_ipaddr, vendor_ipaddr, TAG_ANY);
        }
 
        /*
-        * Walk through the database searching for an active=0 entry.
-        * We search twice. Once to see if we have an active entry with the same caller_id
-        * so that MPPP can work ok and then once again to find a free entry.
+        *  Walk through the database searching for an active=0 entry.
+        *  We search twice. Once to see if we have an active entry with the same caller_id
+        *  so that MPPP can work ok and then once again to find a free entry.
         */
-
        pthread_mutex_lock(&inst->op_mutex);
-
        key_datum.dptr = NULL;
        if (cli != NULL){
                key_datum = gdbm_firstkey(inst->gdbm);
-               while(key_datum.dptr){
+               while (key_datum.dptr) {
                        data_datum = gdbm_fetch(inst->gdbm, key_datum);
                        if (data_datum.dptr){
                                memcpy(&entry,data_datum.dptr, sizeof(ippool_info));
                                free(data_datum.dptr);
                                /*
-                               * If we find an entry for the same caller-id with active=1
-                               * then we use that for multilink (MPPP) to work properly.
-                               */
+                               * If we find an entry for the same caller-id with active=1
+                               * then we use that for multilink (MPPP) to work properly.
+                               */
                                if (strcmp(entry.cli,cli) == 0 && entry.active){
                                        mppp = 1;
                                        break;
                                }
                        }
+
                        nextkey = gdbm_nextkey(inst->gdbm, key_datum);
                        free(key_datum.dptr);
                        key_datum = nextkey;
@@ -651,38 +683,37 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
                                rcode = gdbm_store(inst->gdbm, key_datum, data_datum_tmp, GDBM_REPLACE);
                                free(data_datum_tmp.dptr);
                                if (rcode < 0) {
-                                       ERROR("rlm_ippool: Failed storing data to %s: %s",
-                                               inst->filename, gdbm_strerror(gdbm_errno));
+                                       REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
                                                pthread_mutex_unlock(&inst->op_mutex);
                                        return RLM_MODULE_FAIL;
                                }
                        }
-               }
-               else{
+               } else{
                        /*
                         * We have not found the nas/port combination
                         */
-                       if (delete){
+                       if (delete) {
                                /*
-                                * Delete the entry so that we can change the key
-                                * All is well. We delete one entry and we add one entry
-                                */
+                                *  Delete the entry so that we can change the key
+                                *  All is well. We delete one entry and we add one entry
+                                */
                                gdbm_delete(inst->gdbm, key_datum);
-                       }
-                       else{
+                       } else{
                                /*
-                                * We are doing MPPP. (mppp should be 1)
-                                * We don't do anything.
-                                * We will create an extra not needed entry in the database in this case
-                                * but we don't really care since we always also use the ip_index database
-                                * when we search for a free entry.
-                                * We will also delete that entry on the accounting section so that we only
-                                * have one nas/port entry referencing each ip
+                                *  We are doing MPPP. (mppp should be 1)
+                                *  We don't do anything.
+                                *  We will create an extra not needed entry in the database in this case
+                                *  but we don't really care since we always also use the ip_index database
+                                *  when we search for a free entry.
+                                *  We will also delete that entry on the accounting section so that we only
+                                *  have one nas/port entry referencing each ip
                                 */
-                               if (mppp)
+                               if (mppp) {
                                        extra = 1;
-                               if (!mppp)
-                                       ERROR("rlm_ippool: mppp is not one. Please report this behaviour.");
+                               }
+                               if (!mppp) {
+                                       REDEBUG("mppp is not one. Please report this behaviour");
+                               }
                        }
                }
                free(key_datum.dptr);
@@ -692,7 +723,7 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
                        entry.timeout = (time_t) vp->vp_integer;
 #ifdef WITH_DHCP
                        if (dhcp) {
-                               vp = radius_paircreate(request, &request->reply->vps,
+                               vp = radius_paircreate(request->reply, &request->reply->vps,
                                                       PW_DHCP_IP_ADDRESS_LEASE_TIME, DHCP_MAGIC_VENDOR);
                                vp->vp_integer = entry.timeout;
                                pairdelete(&request->reply->vps, PW_SESSION_TIMEOUT, 0, TAG_ANY);
@@ -701,19 +732,20 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
                } else {
                        entry.timeout = 0;
                }
-               if (extra)
+               if (extra) {
                        entry.extra = 1;
+               }
+
                data_datum.dptr = (char *) &entry;
                data_datum.dsize = sizeof(ippool_info);
                memcpy(key.key, key_str, 16);
                key_datum.dptr = (char *) &key;
                key_datum.dsize = sizeof(ippool_key);
 
-               DEBUG2("rlm_ippool: Allocating ip to key: '%s'",hex_str);
+               RDEBUG2("Allocating ip to key: '%s'",hex_str);
                rcode = gdbm_store(inst->gdbm, key_datum, data_datum, GDBM_REPLACE);
                if (rcode < 0) {
-                       ERROR("rlm_ippool: Failed storing data to %s: %s",
-                               inst->filename, gdbm_strerror(gdbm_errno));
+                       REDEBUG("Failed storing data to %s: %s", inst->filename, gdbm_strerror(gdbm_errno));
                                pthread_mutex_unlock(&inst->op_mutex);
                        return RLM_MODULE_FAIL;
                }
@@ -725,24 +757,24 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
                if (data_datum.dptr){
                        memcpy(&num,data_datum.dptr,sizeof(int));
                        free(data_datum.dptr);
-               } else
+               } else {
                        num = 0;
+               }
+
                num++;
                RDEBUG("num: %d",num);
                data_datum.dptr = (char *) &num;
                data_datum.dsize = sizeof(int);
                rcode = gdbm_store(inst->ip, key_datum, data_datum, GDBM_REPLACE);
                if (rcode < 0) {
-                       ERROR("rlm_ippool: Failed storing data to %s: %s",
-                               inst->ip_index, gdbm_strerror(gdbm_errno));
+                       REDEBUG("Failed storing data to %s: %s", inst->ip_index, gdbm_strerror(gdbm_errno));
                        pthread_mutex_unlock(&inst->op_mutex);
                        return RLM_MODULE_FAIL;
                }
                pthread_mutex_unlock(&inst->op_mutex);
 
-
                RDEBUG("Allocated ip %s to client key: %s",ip_ntoa(str,entry.ipaddr),hex_str);
-               vp = radius_paircreate(request, &request->reply->vps,
+               vp = radius_paircreate(request->reply, &request->reply->vps,
                                       attr_ipaddr, vendor_ipaddr);
                vp->vp_ipaddr = entry.ipaddr;
 
@@ -751,7 +783,7 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
                 *      reply, add one
                 */
                if (pairfind(request->reply->vps, attr_ipmask, vendor_ipaddr, TAG_ANY) == NULL) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               attr_ipmask, vendor_ipaddr);
                        vp->vp_ipaddr = ntohl(inst->netmask);
                }
@@ -759,7 +791,7 @@ static rlm_rcode_t mod_post_auth(UNUSED void *instance, UNUSED REQUEST *request)
        }
        else{
                pthread_mutex_unlock(&inst->op_mutex);
-               RDEBUG("No available ip addresses in pool.");
+               RDEBUG("No available ip addresses in pool");
                return RLM_MODULE_NOTFOUND;
        }
 
index a04dc52..86634cb 100644 (file)
@@ -82,7 +82,7 @@ typedef struct ippool_info {
 
 typedef struct old_ippool_key {
        char nas[MAX_NAS_NAME_SIZE];
-       unsigned int port;
+       uint16_t port;
 } old_ippool_key;
 
 typedef struct ippool_key {
@@ -104,25 +104,25 @@ void usage(char *argv0);
 void addip(char *sessiondbname, char *indexdbname, char *ipaddress,
           char *NASname, char *NASport, int old)
 {
-       GDBM_FILE sessiondb;
-       GDBM_FILE indexdb;
-       datum key_datum, data_datum, save_datum;
-       datum nextkey;
-
-       ippool_key key;
-       old_ippool_key old_key;
-
-       ippool_info entry;
-       struct in_addr ipaddr;
-       uint8_t key_str[17];
-       char hex_str[35];
-       int num = 0;
-       int mppp = 0;
-       int mode = GDBM_WRITER;
-       signed int rcode;
-       int delete = 0;
-       int port;
-       int found = 0;
+       GDBM_FILE       sessiondb;
+       GDBM_FILE       indexdb;
+       datum           key_datum, data_datum, save_datum;
+       datum           nextkey;
+
+       ippool_key      key;
+       old_ippool_key  old_key;
+
+       ippool_info     entry;
+       struct in_addr  ipaddr;
+       uint8_t         key_str[17];
+       char            hex_str[35];
+       int             num = 0;
+       int             mppp = 0;
+       int             mode = GDBM_WRITER;
+       int             rcode;
+       int             delete = 0;
+       uint16_t        port;
+       bool            found = false;
 
        sessiondb = gdbm_open(sessiondbname, 512, mode, 0,NULL);
        indexdb = gdbm_open(indexdbname, 512, mode, 0,NULL);
@@ -171,7 +171,7 @@ void addip(char *sessiondbname, char *indexdbname, char *ipaddress,
 
        data_datum = gdbm_fetch(sessiondb, key_datum);
        if (data_datum.dptr != NULL){
-               found = 1;
+               found = true;
                memcpy(&entry, data_datum.dptr, sizeof(ippool_info));
 
                if (entry.active){
@@ -452,7 +452,7 @@ void viewdb(char *sessiondbname, char *indexdbname, char *ipaddress, int old) {
 
                if ((key_datum.dsize != sizeof(struct ippool_key)) &&
                    (key_datum.dsize != sizeof(struct old_ippool_key))) {
-                       goto next;
+                       goto next;
                }
 
                if (old) {
index acb67d2..5ad68e9 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1211,9 +1211,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1261,10 +1261,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1468,6 +1468,73 @@ fi
 
 } # ac_fn_c_try_link
 
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
 # ac_fn_c_try_run LINENO
 # ----------------------
 # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
@@ -1964,7 +2031,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2186,7 +2253,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2976,22 +3043,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=krb5.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3106,8 +3173,8 @@ krb5_encrypt_data()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lk5crypto"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lk5crypto"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3123,22 +3190,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libk5crypto${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3150,22 +3217,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libk5crypto.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3277,8 +3344,8 @@ DH_new()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lcrypto"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lcrypto"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3294,22 +3361,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libcrypto${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3321,22 +3388,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libcrypto.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3453,8 +3520,8 @@ set_com_err_hook()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lcom_err"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lcom_err"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3470,22 +3537,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libcom_err${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3497,22 +3564,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libcom_err.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3624,8 +3691,8 @@ krb5_verify_user_opt()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lkrb5"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lkrb5"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3641,22 +3708,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libkrb5${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3668,22 +3735,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libkrb5.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3795,8 +3862,8 @@ krb5_get_init_creds_password()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lkrb5"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lkrb5"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3812,22 +3879,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libkrb5${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3839,22 +3906,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libkrb5.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3912,6 +3979,28 @@ fi
                                LDFLAGS="${LDFLAGS} ${SMART_LIBS}"
        CFLAGS="${CFLAGS} ${SMART_CFLAGS}"
 
+                               for ac_func in krb5_get_error_message krb5_free_error_string krb5_free_error_message
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+       if test "x$ac_cv_func_krb5_get_error_message" == xyes; then
+               krb5mod_cflags="${krb5mod_cflags} -D HAVE_KRB5_GET_ERROR_MESSAGE"
+       fi
+       if test "x$ac_cv_func_krb5_free_error_message" == xyes; then
+               krb5mod_cflags="${krb5mod_cflags} -D HAVE_KRB5_FREE_ERROR_MESSAGE"
+       fi
+       if test "x$ac_cv_func_krb5_free_error_string" == xyes; then
+               krb5mod_cflags="${krb5mod_cflags} -D HAVE_KRB5_FREE_ERROR_STRING"
+       fi
+
                                if test "$krb5threadsafe" != "no"; then
                krb5threadsafe=
 
@@ -3974,8 +4063,8 @@ krb5_is_thread_safe()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lkrb5"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lkrb5"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3991,22 +4080,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libkrb5${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -4018,22 +4107,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libkrb5.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -4194,22 +4283,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=com_err.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -4339,22 +4428,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=et/com_err.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -4513,11 +4602,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -5023,11 +5112,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index bb3ebd4..253f202 100644 (file)
@@ -114,6 +114,20 @@ if test x$with_[]modname != xno; then
        CFLAGS="${CFLAGS} ${SMART_CFLAGS}"
 
        dnl #
+       dnl # Check how to free things returned by krb5_get_error_message
+       dnl #
+       AC_CHECK_FUNCS([krb5_get_error_message krb5_free_error_string krb5_free_error_message])
+       if test "x$ac_cv_func_krb5_get_error_message" == xyes; then
+               krb5mod_cflags="${krb5mod_cflags} -D HAVE_KRB5_GET_ERROR_MESSAGE"
+       fi
+       if test "x$ac_cv_func_krb5_free_error_message" == xyes; then
+               krb5mod_cflags="${krb5mod_cflags} -D HAVE_KRB5_FREE_ERROR_MESSAGE"
+       fi
+       if test "x$ac_cv_func_krb5_free_error_string" == xyes; then
+               krb5mod_cflags="${krb5mod_cflags} -D HAVE_KRB5_FREE_ERROR_STRING"
+       fi
+
+       dnl #
        dnl # Only check if version checks have not found kerberos to be thread unsafe
        dnl #
        if test "$krb5threadsafe" != "no"; then
index 81ed1d4..33123ca 100644 (file)
@@ -27,7 +27,7 @@ RCSID("$Id$")
 #include <freeradius-devel/radiusd.h>
 #include "krb5.h"
 
-#ifdef HEIMDAL_KRB5
+#ifdef HAVE_KRB5_GET_ERROR_MESSAGE
 #  define KRB5_STRERROR_BUFSIZE (2048)
 
 fr_thread_local_setup(char *, krb5_error_buffer)       /* macro */
@@ -69,7 +69,18 @@ char const *rlm_krb5_error(krb5_context context, krb5_error_code code)
        msg = krb5_get_error_message(context, code);
        if (msg) {
                strlcpy(buffer, msg, KRB5_STRERROR_BUFSIZE);
+#ifdef HAVE_KRB5_FREE_ERROR_MESSAGE
                krb5_free_error_message(context, msg);
+#elif defined(HAVE_KRB5_FREE_ERROR_STRING)
+               {
+                       char *free;
+
+                       memcpy(&free, &msg, sizeof(free));
+                       krb5_free_error_string(context, free);
+               }
+#else
+#  error "No way to free error strings, missing krb5_free_error_message() and krb5_free_error_string()"
+#endif
        } else {
                strlcpy(buffer, "Unknown error", KRB5_STRERROR_BUFSIZE);
        }
@@ -102,6 +113,13 @@ static int _free_handle(rlm_krb5_handle_t *conn) {
        if (conn->keytab) {
                krb5_kt_close(conn->context, conn->keytab);
        }
+
+#ifdef HEIMDAL_KRB5
+       if (conn->ccache) {
+               krb5_cc_destroy(conn->context, conn->ccache);
+       }
+#endif
+
        return 0;
 }
 
@@ -123,7 +141,7 @@ void *mod_conn_create(void *instance)
        MEM(conn = talloc_zero(instance, rlm_krb5_handle_t));
        ret = krb5_init_context(&conn->context);
        if (ret) {
-               EDEBUG("rlm_krb5 (%s): Context initialisation failed: %s", inst->xlat_name,
+               ERROR("rlm_krb5 (%s): Context initialisation failed: %s", inst->xlat_name,
                       rlm_krb5_error(NULL, ret));
 
                return NULL;
@@ -140,14 +158,13 @@ void *mod_conn_create(void *instance)
        }
 
 #ifdef HEIMDAL_KRB5
-       /*
-        *      Setup krb5_verify_user options
-        *
-        *      Not entirely sure this is necessary, but as we use context
-        *      to get the cache handle, we probably do have to do this with
-        *      the cloned context.
-        */
-       krb5_cc_default(conn->context, &conn->ccache);
+       ret = krb5_cc_new_unique(conn->context, "MEMORY", NULL, &conn->ccache);
+       if (ret) {
+               ERROR("rlm_krb5 (%s): Credential cache creation failed: %s", inst->xlat_name,
+                     rlm_krb5_error(conn->context, ret));
+
+               return NULL;
+       }
 
        krb5_verify_opt_init(&conn->options);
        krb5_verify_opt_set_ccache(&conn->options, conn->ccache);
index 37805a2..59b1f85 100644 (file)
@@ -79,7 +79,7 @@ typedef struct rlm_krb5_t {
  *     MIT Kerberos uses comm_err, so the macro just expands to a call
  *     to error_message.
  */
-#ifndef HEIMDAL_KRB5
+#ifndef HAVE_KRB5_GET_ERROR_MESSAGE
 #  ifdef ET_COMM_ERR
 #    include <et/com_err.h>
 #  else
index 4c96eb5..adc5e3e 100644 (file)
@@ -32,12 +32,12 @@ RCSID("$Id$")
 #include "krb5.h"
 
 static const CONF_PARSER module_config[] = {
-       { "keytab", PW_TYPE_STRING_PTR, offsetof(rlm_krb5_t, keytabname), NULL, NULL },
-       { "service_principal", PW_TYPE_STRING_PTR, offsetof(rlm_krb5_t,service_princ), NULL, NULL },
+       { "keytab", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_krb5_t, keytabname), NULL },
+       { "service_principal", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_krb5_t, service_princ), NULL },
        { NULL, -1, 0, NULL, NULL }
 };
 
-static int krb5_detach(void *instance)
+static int mod_detach(void *instance)
 {
        rlm_krb5_t *inst = instance;
 
@@ -66,7 +66,7 @@ static int krb5_detach(void *instance)
        return 0;
 }
 
-static int krb5_instantiate(CONF_SECTION *conf, void *instance)
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_krb5_t *inst = instance;
        krb5_error_code ret;
@@ -82,15 +82,32 @@ static int krb5_instantiate(CONF_SECTION *conf, void *instance)
        DEBUG("Using MIT Kerberos library");
 #endif
 
-#ifndef KRB5_IS_THREAD_SAFE
        if (!krb5_is_thread_safe()) {
-               DEBUGI("libkrb5 is not threadsafe, recompile it, and the server with thread support enabled");
-               WDEBUG("rlm_krb5 will run in single threaded mode, performance may be degraded");
+/*
+ *     rlm_krb5 was built as threadsafe
+ */
+#ifdef KRB5_IS_THREAD_SAFE
+               ERROR("Build time libkrb5 was threadsafe, but run time library claims not to be");
+               ERROR("Modify runtime linker path (LD_LIBRARY_PATH on most systems), to prefer threadsafe libkrb5");
+               return -1;
+/*
+ *     rlm_krb5 was not built as threadsafe
+ */
+#else
+               WARN("libkrb5 is not threadsafe, recompile it with thread support enabled ("
+#  ifdef HEIMDAL_KRB5
+                      "--enable-pthread-support"
+#  else
+                      "--disable-thread-support=no"
+#  endif
+                      ")");
+               WARN("rlm_krb5 will run in single threaded mode, performance may be degraded");
        } else {
-               WDEBUG("Build time libkrb5 was not threadsafe, but run time library claims to be");
-               WDEBUG("Reconfigure and recompile rlm_krb5 to enable thread support");
-       }
+               WARN("Build time libkrb5 was not threadsafe, but run time library claims to be");
+               WARN("Reconfigure and recompile rlm_krb5 to enable thread support");
 #endif
+       }
+
        inst->xlat_name = cf_section_name2(conf);
        if (!inst->xlat_name) {
                inst->xlat_name = cf_section_name1(conf);
@@ -255,7 +272,7 @@ static rlm_rcode_t krb5_parse_user(krb5_principal *client, REQUEST *request, krb
         */
        if (request->password->da->attr != PW_USER_PASSWORD) {
                REDEBUG("Attribute \"User-Password\" is required for authentication.  Cannot use \"%s\".",
-                       request->password->da->name);
+                       request->password->da->name);
 
                return RLM_MODULE_INVALID;
        }
@@ -277,12 +294,46 @@ static rlm_rcode_t krb5_parse_user(krb5_principal *client, REQUEST *request, krb
        return RLM_MODULE_OK;
 }
 
+/** Log error message and return appropriate rcode
+ *
+ * Translate kerberos error codes into return codes.
+ * @param request Current request.
+ * @param ret code from kerberos.
+ * @param conn used in the last operation.
+ */
+static rlm_rcode_t krb5_process_error(REQUEST *request, rlm_krb5_handle_t *conn, int ret)
+{
+       rad_assert(ret != 0);
+       rad_assert(conn);       /* Silences warnings */
+
+       switch (ret) {
+       case KRB5_LIBOS_BADPWDMATCH:
+       case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+               REDEBUG("Provided password was incorrect (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_REJECT;
+
+       case KRB5KDC_ERR_KEY_EXP:
+       case KRB5KDC_ERR_CLIENT_REVOKED:
+       case KRB5KDC_ERR_SERVICE_REVOKED:
+               REDEBUG("Account has been locked out (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_USERLOCK;
+
+       case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+               RDEBUG("User not found (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_NOTFOUND;
+
+       default:
+               REDEBUG("Error verifying credentials (%i): %s", ret, rlm_krb5_error(conn->context, ret));
+               return RLM_MODULE_FAIL;
+       }
+}
+
 #ifdef HEIMDAL_KRB5
 
 /*
  *     Validate user/pass (Heimdal)
  */
-static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_krb5_t *inst = instance;
        rlm_rcode_t rcode;
@@ -294,11 +345,7 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
 
 #ifdef KRB5_IS_THREAD_SAFE
        conn = fr_connection_get(inst->pool);
-       if (!conn) {
-               REDEBUG("All krb5 contexts are in use");
-
-               return RLM_MODULE_FAIL;
-       }
+       if (!conn) return RLM_MODULE_FAIL;
 #else
        conn = inst->conn;
 #endif
@@ -316,34 +363,34 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
         */
        ret = krb5_verify_user_opt(conn->context, client, request->password->vp_strvalue, &conn->options);
        if (ret) {
-               switch (ret) {
-               case KRB5_LIBOS_BADPWDMATCH:
-               case KRB5KRB_AP_ERR_BAD_INTEGRITY:
-                       REDEBUG("Provided password was incorrect (%i): %s", ret, rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_REJECT;
-                       break;
-
-               case KRB5KDC_ERR_KEY_EXP:
-               case KRB5KDC_ERR_CLIENT_REVOKED:
-               case KRB5KDC_ERR_SERVICE_REVOKED:
-                       REDEBUG("Account has been locked out (%i): %s", ret, rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_USERLOCK;
-                       break;
-
-               case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
-                       RDEBUG("User not found: %s (%i)", ret, rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_NOTFOUND;
-
-               default:
-                       REDEBUG("Error verifying credentials (%i): %s", ret, rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_FAIL;
-                       break;
-               }
-
+               rcode = krb5_process_error(request, conn, ret);
                goto cleanup;
        }
 
-       cleanup:
+       /*
+        *      krb5_verify_user_opt adds the credentials to the ccache
+        *      we specified with krb5_verify_opt_set_ccache.
+        *
+        *      To make sure we don't accumulate thousands of sets of
+        *      credentials, remove them again here.
+        *
+        * @todo This should definitely be optional, which means writing code for the MIT
+        *       variant as well.
+        */
+       {
+               krb5_cc_cursor cursor;
+               krb5_creds cred;
+
+               krb5_cc_start_seq_get(conn->context, conn->ccache, &cursor);
+               for ((ret = krb5_cc_next_cred(conn->context, conn->ccache, &cursor, &cred));
+                    ret == 0;
+                    (ret = krb5_cc_next_cred(conn->context, conn->ccache, &cursor, &cred))) {
+                    krb5_cc_remove_cred(conn->context, conn->ccache, 0, &cred);
+               }
+               krb5_cc_end_seq_get(conn->context, conn->ccache, &cursor);
+       }
+
+cleanup:
        if (client) {
                krb5_free_principal(conn->context, client);
        }
@@ -359,7 +406,7 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
 /*
  *  Validate userid/passwd (MIT)
  */
-static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_krb5_t *inst = instance;
        rlm_rcode_t rcode;
@@ -375,11 +422,7 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
 
 #ifdef KRB5_IS_THREAD_SAFE
        conn = fr_connection_get(inst->pool);
-       if (!conn) {
-               REDEBUG("All krb5 contexts are in use");
-
-               return RLM_MODULE_FAIL;
-       }
+       if (!conn) return RLM_MODULE_FAIL;
 #else
        conn = inst->conn;
 #endif
@@ -401,45 +444,21 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
         *      Retrieve the TGT from the TGS/KDC and check we can decrypt it.
         */
        memcpy(&password, &request->password->vp_strvalue, sizeof(password));
+       RDEBUG("Retrieving and decrypting TGT");
        ret = krb5_get_init_creds_password(conn->context, &init_creds, client, password,
                                           NULL, NULL, 0, NULL, inst->gic_options);
        if (ret) {
-               error:
-               switch (ret) {
-               case KRB5_LIBOS_BADPWDMATCH:
-               case KRB5KRB_AP_ERR_BAD_INTEGRITY:
-                       REDEBUG("Provided password was incorrect (%i): %s", ret, rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_REJECT;
-                       break;
-
-               case KRB5KDC_ERR_KEY_EXP:
-               case KRB5KDC_ERR_CLIENT_REVOKED:
-               case KRB5KDC_ERR_SERVICE_REVOKED:
-                       REDEBUG("Account has been locked out (%i): %s", ret, rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_USERLOCK;
-                       break;
-
-               case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
-                       REDEBUG("User not found (%i): %s", ret,  rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_NOTFOUND;
-                       break;
-
-               default:
-                       REDEBUG("Error retrieving or verifying credentials (%i): %s", ret,
-                               rlm_krb5_error(conn->context, ret));
-                       rcode = RLM_MODULE_FAIL;
-                       break;
-               }
-
+               rcode = krb5_process_error(request, conn, ret);
                goto cleanup;
        }
 
-       RDEBUG("Successfully retrieved and decrypted TGT");
-
+       RDEBUG("Attempting to authenticate against service principal");
        ret = krb5_verify_init_creds(conn->context, &init_creds, inst->server, conn->keytab, NULL, inst->vic_options);
-       if (ret) goto error;
+       if (ret) {
+               rcode = krb5_process_error(request, conn, ret);
+       }
 
-       cleanup:
+cleanup:
        if (client) {
                krb5_free_principal(conn->context, client);
        }
@@ -456,17 +475,17 @@ static rlm_rcode_t krb5_auth(void *instance, REQUEST *request)
 module_t rlm_krb5 = {
        RLM_MODULE_INIT,
        "krb5",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE
+       RLM_TYPE_HUP_SAFE
 #ifdef KRB5_IS_THREAD_SAFE
        | RLM_TYPE_THREAD_SAFE
 #endif
        ,
        sizeof(rlm_krb5_t),
        module_config,
-       krb5_instantiate,               /* instantiation */
-       krb5_detach,                    /* detach */
+       mod_instantiate,                /* instantiation */
+       mod_detach,                     /* detach */
        {
-               krb5_auth,              /* authenticate */
+               mod_authenticate,       /* authenticate */
                NULL,                   /* authorize */
                NULL,                   /* pre-accounting */
                NULL,                   /* accounting */
index 9884e15..af83c50 100644 (file)
 #include <freeradius-devel/rad_assert.h>
 #include "ldap.h"
 
+/** Callback for radius_map2request
+ *
+ * Performs exactly the same job as radius_map2vp, but pulls attribute values from LDAP entries
+ *
+ * @see radius_map2vp
+ */
 static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, void *ctx)
 {
        rlm_ldap_result_t *self = ctx;
@@ -33,26 +39,86 @@ static int rlm_ldap_map_getvalue(VALUE_PAIR **out, REQUEST *request, value_pair_
        vp_cursor_t cursor;
        int i;
 
-       paircursor(&cursor, &head);
+       fr_cursor_init(&cursor, &head);
+
+       switch (map->dst->type) {
+       /*
+        *      This is a mapping in the form of:
+        *              <list>: += <ldap attr>
+        *
+        *      Where <ldap attr> is:
+        *              <list>:<attr> <op> <value>
+        *
+        *      It is to allow for legacy installations which stored
+        *      RADIUS control and reply attributes in separate LDAP
+        *      attributes.
+        */
+       case VPT_TYPE_LIST:
+               for (i = 0; i < self->count; i++) {
+                       value_pair_map_t *attr = NULL;
+
+                       RDEBUG3("Parsing valuepair string \"%s\"", self->values[i]->bv_val);
+                       if (radius_strpair2map(&attr, request, self->values[i]->bv_val,
+                                              map->dst->vpt_request, map->dst->vpt_list,
+                                              REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+                               RWDEBUG("Failed parsing \"%s\" as valuepair, skipping...", self->values[i]->bv_val);
+                               continue;
+                       }
+
+                       if (attr->dst->vpt_request != map->dst->vpt_request) {
+                               RWDEBUG("valuepair \"%s\" has conflicting request qualifier (%s vs %s), skipping...",
+                                       self->values[i]->bv_val,
+                                       fr_int2str(request_refs, attr->dst->vpt_request, "<INVALID>"),
+                                       fr_int2str(request_refs, map->dst->vpt_request, "<INVALID>"));
+                       next_pair:
+                               talloc_free(attr);
+                               continue;
+                       }
+
+                       if ((attr->dst->vpt_list != map->dst->vpt_list)) {
+                               RWDEBUG("valuepair \"%s\" has conflicting list qualifier (%s vs %s), skipping...",
+                                       self->values[i]->bv_val,
+                                       fr_int2str(pair_lists, attr->dst->vpt_list, "<INVALID>"),
+                                       fr_int2str(pair_lists, map->dst->vpt_list, "<INVALID>"));
+                               goto next_pair;
+                       }
+
+                       if (radius_map2vp(&vp, request, attr, NULL) < 0) {
+                               RWDEBUG("Failed creating attribute for \"%s\", skipping...", self->values[i]->bv_val);
+                               goto next_pair;
+                       }
+
+                       fr_cursor_insert(&cursor, vp);
+                       talloc_free(attr);
+               }
+               break;
 
        /*
         *      Iterate over all the retrieved values,
         *      don't try and be clever about changing operators
         *      just use whatever was set in the attribute map.
         */
-       for (i = 0; i < self->count; i++) {
-               vp = pairalloc(request, map->dst->da);
-               rad_assert(vp);
+       case VPT_TYPE_ATTR:
+               for (i = 0; i < self->count; i++) {
+                       if (!self->values[i]->bv_len) continue;
+
+                       vp = pairalloc(request, map->dst->vpt_da);
+                       rad_assert(vp);
 
-               if (!pairparsevalue(vp, self->values[i])) {
-                       RDEBUG("Failed parsing value for \"%s\"", map->dst->da->name);
+                       if (pairparsevalue(vp, self->values[i]->bv_val, self->values[i]->bv_len) < 0) {
+                               RDEBUG("Failed parsing value for \"%s\"", map->dst->vpt_da->name);
 
-                       talloc_free(vp);
-                       continue;
+                               talloc_free(vp);
+                               continue;
+                       }
+
+                       vp->op = map->op;
+                       fr_cursor_insert(&cursor, vp);
                }
+               break;
 
-               vp->op = map->op;
-               pairinsert(&cursor, vp);
+       default:
+               rad_assert(0);
        }
 
        *out = head;
@@ -74,22 +140,28 @@ int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head)
         *      to do rlm_ldap specific checks here.
         */
        for (map = *head; map != NULL; map = map->next) {
-               if (map->dst->type != VPT_TYPE_ATTR) {
-                       cf_log_err(map->ci, "Left operand must be an attribute ref");
-
-                       return -1;
-               }
+               switch (map->dst->type) {
+               case VPT_TYPE_LIST:
+                       if (map->op != T_OP_ADD) {
+                               cf_log_err(map->ci, "Only '+=' operator is permitted for valuepair to list mapping");
+                               return -1;
+                       }
 
-               if (map->src->type == VPT_TYPE_LIST) {
-                       cf_log_err(map->ci, "Right operand must not be a list");
+               case VPT_TYPE_ATTR:
+                       break;
 
+               default:
+                       cf_log_err(map->ci, "valuepair destination must be an attribute or list");
                        return -1;
                }
 
-               if (map->src->type == VPT_TYPE_EXEC) {
-                       cf_log_err(map->ci, "Exec values are not allowed");
-
+               switch (map->src->type) {
+               case VPT_TYPE_LIST:
+                       cf_log_err(map->ci, "LDAP attribute name cannot be derived from a list");
                        return -1;
+
+               default:
+                       break;
                }
 
                /*
@@ -98,8 +170,8 @@ int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head)
                 *      and has no idea what they're doing, or they're authenticating the user using a different
                 *      method.
                 */
-               if (!inst->expect_password && map->dst->da && (map->dst->type == VPT_TYPE_ATTR)) {
-                       switch (map->dst->da->attr) {
+               if (!inst->expect_password && map->dst->vpt_da && (map->dst->type == VPT_TYPE_ATTR)) {
+                       switch (map->dst->vpt_da->attr) {
                        case PW_CLEARTEXT_PASSWORD:
                        case PW_NT_PASSWORD:
                        case PW_USER_PASSWORD:
@@ -109,14 +181,14 @@ int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head)
                                 *      Because you just know someone is going to map NT-Password to the
                                 *      request list, and then complain it's not working...
                                 */
-                               if (map->dst->list != PAIR_LIST_CONTROL) {
-                                       LDAP_DBGW("Mapping LDAP (%s) attribute to password \"reference\" attribute "
+                               if (map->dst->vpt_list != PAIR_LIST_CONTROL) {
+                                       LDAP_DBGW("Mapping LDAP (%s) attribute to \"known good\" password attribute "
                                                  "(%s) in %s list. This is probably *NOT* the correct list, "
-                                                 "you should prepend \"control:\" to \"reference\" attribute "
+                                                 "you should prepend \"control:\" to password attribute "
                                                  "(control:%s)",
-                                                 map->src->name, map->dst->da->name,
-                                                 fr_int2str(pair_lists, map->dst->list, "<invalid>"),
-                                                 map->dst->da->name);
+                                                 map->src->name, map->dst->vpt_da->name,
+                                                 fr_int2str(pair_lists, map->dst->vpt_list, "<invalid>"),
+                                                 map->dst->vpt_da->name);
                                }
 
                                inst->expect_password = true;
@@ -168,6 +240,7 @@ void rlm_ldap_map_xlat_free(rlm_ldap_map_xlat_t const *expanded)
                if (!name) return;
 
                switch (map->src->type) {
+               case VPT_TYPE_EXEC:
                case VPT_TYPE_XLAT:
                case VPT_TYPE_ATTR:
                        rad_const_free(name);
@@ -185,7 +258,6 @@ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_m
 {
        value_pair_map_t const *map;
        unsigned int total = 0;
-       size_t len;
 
        VALUE_PAIR *found, **from = NULL;
        REQUEST *context;
@@ -193,37 +265,57 @@ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_m
        for (map = maps; map != NULL; map = map->next) {
                switch (map->src->type) {
                case VPT_TYPE_XLAT:
-                       {
-                               char *exp = NULL;
+               {
+                       ssize_t len;
+                       char *exp = NULL;
 
-                               len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL);
-                               if (len <= 0) {
-                                       RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name);
+                       len = radius_axlat(&exp, request, map->src->name, NULL, NULL);
+                       if (len < 0) {
+                               RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name);
 
-                                       goto error;
-                               }
-
-                               expanded->attrs[total++] = exp;
-                               break;
+                               goto error;
                        }
 
+                       expanded->attrs[total++] = exp;
+                       break;
+               }
+
                case VPT_TYPE_ATTR:
                        context = request;
 
-                       if (radius_request(&context, map->src->request) == 0) {
-                               from = radius_list(context, map->src->list);
+                       if (radius_request(&context, map->src->vpt_request) == 0) {
+                               from = radius_list(context, map->src->vpt_list);
                        }
                        if (!from) continue;
 
-                       found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY);
+                       found = pairfind(*from, map->src->vpt_da->attr, map->src->vpt_da->vendor, TAG_ANY);
                        if (!found) continue;
 
-                       expanded->attrs[total++] = talloc_strdup(request, found->vp_strvalue);
+                       expanded->attrs[total++] = talloc_typed_strdup(request, found->vp_strvalue);
+                       break;
+
+               case VPT_TYPE_EXEC:
+               {
+                       char answer[1024];
+                       VALUE_PAIR **input_pairs = NULL;
+                       int result;
+
+                       input_pairs = radius_list(request, PAIR_LIST_REQUEST);
+                       result = radius_exec_program(request, map->src->name, true, true, answer,
+                                                    sizeof(answer), EXEC_TIMEOUT,
+                                                    input_pairs ? *input_pairs : NULL, NULL);
+                       if (result != 0) {
+                               return -1;
+                       }
+
+                       expanded->attrs[total++] = talloc_typed_strdup(request, answer);
+               }
                        break;
 
                case VPT_TYPE_LITERAL:
                        expanded->attrs[total++] = map->src->name;
                        break;
+
                default:
                        rad_assert(0);
                error:
@@ -233,7 +325,6 @@ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_m
 
                        return -1;
                }
-
        }
 
        rad_assert(total < LDAP_MAX_ATTRMAP);
@@ -265,7 +356,10 @@ void rlm_ldap_map_do(UNUSED const ldap_instance_t *inst, REQUEST *request, LDAP
        for (map = expanded->maps; map != NULL; map = map->next) {
                name = expanded->attrs[total++];
 
-               result.values = ldap_get_values(handle, entry, name);
+               /*
+                *      Binary safe
+                */
+               result.values = ldap_get_values_len(handle, entry, name);
                if (!result.values) {
                        RDEBUG3("Attribute \"%s\" not found in LDAP object", name);
 
@@ -276,20 +370,20 @@ void rlm_ldap_map_do(UNUSED const ldap_instance_t *inst, REQUEST *request, LDAP
                 *      Find out how many values there are for the
                 *      attribute and extract all of them.
                 */
-               result.count = ldap_count_values(result.values);
+               result.count = ldap_count_values_len(result.values);
 
                /*
                 *      If something bad happened, just skip, this is probably
                 *      a case of the dst being incorrect for the current
                 *      request context
                 */
-               if (radius_map2request(request, map, name, rlm_ldap_map_getvalue, &result) == -1) {
+               if (radius_map2request(request, map, rlm_ldap_map_getvalue, &result) == -1) {
                        return; /* Fail */
                }
 
                next:
 
-               ldap_value_free(result.values);
+               ldap_value_free_len(result.values);
        }
 
        /*
@@ -304,13 +398,20 @@ void rlm_ldap_map_do(UNUSED const ldap_instance_t *inst, REQUEST *request, LDAP
                count = ldap_count_values(values);
 
                for (i = 0; i < count; i++) {
+                       value_pair_map_t *attr;
+
                        RDEBUG3("Parsing attribute string '%s'", values[i]);
-                       if (radius_str2vp(request, values[i],
-                                         REQUEST_CURRENT, PAIR_LIST_REPLY,
-                                         REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
+                       if (radius_strpair2map(&attr, request, values[i],
+                                              REQUEST_CURRENT, PAIR_LIST_REPLY,
+                                              REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
                                RWDEBUG("Failed parsing '%s' value \"%s\" as valuepair, skipping...",
                                        inst->valuepair_attr, values[i]);
+                               continue;
+                       }
+                       if (radius_map2request(request, attr, radius_map2vp, NULL) < 0) {
+                               RWDEBUG("Failed adding \"%s\" to request, skipping...", values[i]);
                        }
+                       talloc_free(attr);
                }
 
                ldap_value_free(values);
@@ -330,7 +431,7 @@ void rlm_ldap_map_do(UNUSED const ldap_instance_t *inst, REQUEST *request, LDAP
  * @return One of the RLM_MODULE_* values.
  */
 rlm_rcode_t rlm_ldap_map_profile(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
-                                char const *dn, rlm_ldap_map_xlat_t const *expanded)
+                                char const *dn, rlm_ldap_map_xlat_t const *expanded)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
        ldap_rcode_t    status;
@@ -372,7 +473,7 @@ rlm_rcode_t rlm_ldap_map_profile(ldap_instance_t const *inst, REQUEST *request,
 
                rcode = RLM_MODULE_NOTFOUND;
 
-               goto free_result;
+               goto free_result;
        }
 
        RDEBUG("Processing profile attributes");
index 69a4aca..0141051 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1211,9 +1211,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1269,10 +1269,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1962,7 +1962,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2184,7 +2184,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2840,14 +2840,17 @@ sm_lib_safe=`echo "ldap_r" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "ldap_init" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_init in -lldap_r in $try" >&5
 $as_echo_n "checking for ldap_init in -lldap_r in $try... " >&6; }
-    LIBS="-L$try -lldap_r $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lldap_r $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char ldap_init();
@@ -2861,7 +2864,8 @@ ldap_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lldap_r -Wl,-rpath,$try"
+                smart_lib="-lldap_r"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -2874,6 +2878,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -2893,8 +2898,8 @@ ldap_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lldap_r"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lldap_r"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2910,22 +2915,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libldap_r${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2937,22 +2942,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libldap_r.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2965,7 +2970,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_init in -lldap_r in $try" >&5
 $as_echo_n "checking for ldap_init in -lldap_r in $try... " >&6; }
-    LIBS="-L$try -lldap_r $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lldap_r $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char ldap_init();
@@ -2979,7 +2985,8 @@ ldap_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lldap_r -Wl,-rpath,$try"
+                 smart_lib="-lldap_r"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -2992,12 +2999,13 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
            if test "x$ac_cv_lib_ldap_r_ldap_init" != "xyes"; then
@@ -3010,14 +3018,17 @@ sm_lib_safe=`echo "ldap" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "ldap_init" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_init in -lldap in $try" >&5
 $as_echo_n "checking for ldap_init in -lldap in $try... " >&6; }
-    LIBS="-L$try -lldap $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lldap $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char ldap_init();
@@ -3031,7 +3042,8 @@ ldap_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lldap -Wl,-rpath,$try"
+                smart_lib="-lldap"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -3044,6 +3056,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -3063,8 +3076,8 @@ ldap_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lldap"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lldap"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3080,22 +3093,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libldap${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3107,22 +3120,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libldap.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3135,7 +3148,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap_init in -lldap in $try" >&5
 $as_echo_n "checking for ldap_init in -lldap in $try... " >&6; }
-    LIBS="-L$try -lldap $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lldap $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char ldap_init();
@@ -3149,7 +3163,8 @@ ldap_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lldap -Wl,-rpath,$try"
+                 smart_lib="-lldap"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -3162,12 +3177,13 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
            if test "x$ac_cv_lib_ldap_ldap_init" != "xyes"; then
@@ -3180,7 +3196,7 @@ fi
 
 
 ac_safe=`echo "ldap.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -3188,7 +3204,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap.h in $try" >&5
 $as_echo_n "checking for ldap.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -3217,7 +3233,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -3256,22 +3272,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=ldap.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3283,7 +3299,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ldap.h in $try" >&5
 $as_echo_n "checking for ldap.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -3312,13 +3328,13 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
        if test "$ac_cv_header_ldap_h" != "yes"; then
@@ -3329,17 +3345,17 @@ fi
        if test "x$fail" = "x"; then
            ac_fn_c_check_func "$LINENO" "ldap_start_tls_s" "ac_cv_func_ldap_start_tls_s"
 if test "x$ac_cv_func_ldap_start_tls_s" = xyes; then :
-   SMART_CFLAGS="$SMART_CFLAGS -DHAVE_LDAP_START_TLS"
+   SMART_CPPFLAGS="$SMART_CPPFLAGS -DHAVE_LDAP_START_TLS"
 fi
 
            ac_fn_c_check_func "$LINENO" "ldap_initialize" "ac_cv_func_ldap_initialize"
 if test "x$ac_cv_func_ldap_initialize" = xyes; then :
-   SMART_CFLAGS="$SMART_CFLAGS -DHAVE_LDAP_INITIALIZE"
+   SMART_CPPFLAGS="$SMART_CPPFLAGS -DHAVE_LDAP_INITIALIZE"
 fi
 
 
 
-        for ac_func in ldap_set_rebind_proc
+       for ac_func in ldap_set_rebind_proc
 do :
   ac_fn_c_check_func "$LINENO" "ldap_set_rebind_proc" "ac_cv_func_ldap_set_rebind_proc"
 if test "x$ac_cv_func_ldap_set_rebind_proc" = xyes; then :
@@ -3350,17 +3366,17 @@ _ACEOF
 fi
 done
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ldap_set_rebind_proc takes 3 arguments" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ldap_set_rebind_proc takes 3 arguments" >&5
 $as_echo_n "checking whether ldap_set_rebind_proc takes 3 arguments... " >&6; }
 if ${ac_cv_ldap_set_rebind_proc+:} false; then :
   $as_echo_n "(cached) " >&6
 else
 
-        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+       cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
-        #include <lber.h>
-        #include <ldap.h>
+       #include <lber.h>
+       #include <ldap.h>
 int
 main ()
 {
@@ -3407,7 +3423,7 @@ $as_echo "$as_me: WARNING: $libsuggestion" >&2;}
 fi
 
 mod_ldflags=$SMART_LIBS
-mod_cflags="$SMART_CFLAGS -DWITH_EDIR -DLDAP_DEPRECATED -DLDAP_SET_REBIND_PROC_ARGS=$ac_cv_ldap_set_rebind_proc"
+mod_cflags="$SMART_CPPFLAGS -DWITH_EDIR -DLDAP_DEPRECATED -DLDAP_SET_REBIND_PROC_ARGS=$ac_cv_ldap_set_rebind_proc"
 
 
 
@@ -3487,11 +3503,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3997,11 +4013,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 00a10bb..f303e93 100644 (file)
@@ -119,16 +119,16 @@ if test x$with_[]modname != xno; then
 
        if test "x$fail" = "x"; then
            AC_CHECK_FUNC(ldap_start_tls_s,
-               [ SMART_CFLAGS="$SMART_CFLAGS -DHAVE_LDAP_START_TLS" ])
+               [ SMART_CPPFLAGS="$SMART_CPPFLAGS -DHAVE_LDAP_START_TLS" ])
            AC_CHECK_FUNC(ldap_initialize,
-               [ SMART_CFLAGS="$SMART_CFLAGS -DHAVE_LDAP_INITIALIZE" ])
+               [ SMART_CPPFLAGS="$SMART_CPPFLAGS -DHAVE_LDAP_INITIALIZE" ])
 
 
-        AC_CHECK_FUNCS(ldap_set_rebind_proc)
-        AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, ac_cv_ldap_set_rebind_proc, [
-        AC_TRY_COMPILE([
-        #include <lber.h>
-        #include <ldap.h>], [ldap_set_rebind_proc(0, 0, 0);],
+       AC_CHECK_FUNCS(ldap_set_rebind_proc)
+       AC_CACHE_CHECK(whether ldap_set_rebind_proc takes 3 arguments, ac_cv_ldap_set_rebind_proc, [
+       AC_TRY_COMPILE([
+       #include <lber.h>
+       #include <ldap.h>], [ldap_set_rebind_proc(0, 0, 0);],
          [ac_cv_ldap_set_rebind_proc=3],
          [ac_cv_ldap_set_rebind_proc=2]) ])
        fi
@@ -156,7 +156,7 @@ if test x"$fail" != x""; then
 fi
 
 mod_ldflags=$SMART_LIBS
-mod_cflags="$SMART_CFLAGS -DWITH_EDIR -DLDAP_DEPRECATED -DLDAP_SET_REBIND_PROC_ARGS=$ac_cv_ldap_set_rebind_proc"
+mod_cflags="$SMART_CPPFLAGS -DWITH_EDIR -DLDAP_DEPRECATED -DLDAP_SET_REBIND_PROC_ARGS=$ac_cv_ldap_set_rebind_proc"
 AC_SUBST(mod_ldflags)
 AC_SUBST(mod_cflags)
 AC_SUBST(targetname)
index 5d8a370..98d1e11 100644 (file)
@@ -77,7 +77,7 @@ static rlm_rcode_t rlm_ldap_group_name2dn(ldap_instance_t const *inst, REQUEST *
         *      It'll probably only save a few ms in network latency, but it means we can send a query
         *      for the entire group list at once.
         */
-       filter = talloc_asprintf(request, "%s%s%s",
+       filter = talloc_typed_asprintf(request, "%s%s%s",
                                 inst->groupobj_filter ? "(&" : "",
                                 inst->groupobj_filter ? inst->groupobj_filter : "",
                                 names[0] && names[1] ? "(|" : "");
@@ -225,7 +225,7 @@ static rlm_rcode_t rlm_ldap_group_dn2name(ldap_instance_t const *inst, REQUEST *
 
        RDEBUG("Group name is \"%s\"", vals[0]);
 
-       *out = talloc_strdup(request, vals[0]);
+       *out = talloc_typed_strdup(request, vals[0]);
 
        finish:
        if (result) {
@@ -286,9 +286,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
                         *      The easy case, were caching DNs and we got a DN.
                         */
                        if (is_dn) {
-                               pairmake(request, &request->config_items, inst->group_da->name, vals[i], T_OP_ADD);
-                               RDEBUG("Added %s with value \"%s\" to control list", inst->group_da->name, vals[i]);
-
+                               pairmake(request, &request->config_items, inst->cache_da->name, vals[i], T_OP_ADD);
+                               RDEBUG("Added %s with value \"%s\" to control list", inst->cache_da->name, vals[i]);
                        /*
                         *      We were told to cache DNs but we got a name, we now need to resolve
                         *      this to a DN. Store all the group names in an array so we can do one query.
@@ -303,8 +302,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
                         *      The easy case, were caching names and we got a name.
                         */
                        if (!is_dn) {
-                               pairmake(request, &request->config_items, inst->group_da->name, vals[i], T_OP_ADD);
-                               RDEBUG("Added %s with value \"%s\" to control list", inst->group_da->name, vals[i]);
+                               pairmake(request, &request->config_items, inst->cache_da->name, vals[i], T_OP_ADD);
+                               RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, vals[i]);
                        /*
                         *      We were told to cache names but we got a DN, we now need to resolve
                         *      this to a name.
@@ -319,8 +318,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
                                        return rcode;
                                }
 
-                               pairmake(request, &request->config_items, inst->group_da->name, name, T_OP_ADD);
-                               RDEBUG("Added %s with value \"%s\" to control list", inst->group_da->name, name);
+                               pairmake(request, &request->config_items, inst->cache_da->name, name, T_OP_ADD);
+                               DEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, name);
                                talloc_free(name);
                        }
                }
@@ -337,8 +336,8 @@ rlm_rcode_t rlm_ldap_cacheable_userobj(ldap_instance_t const *inst, REQUEST *req
 
        dn_p = group_dn;
        while(*dn_p) {
-               pairmake(request, &request->config_items, inst->group_da->name, *dn_p, T_OP_ADD);
-               RDEBUG("Added %s with value \"%s\" to control list", inst->group_da->name, *dn_p);
+               pairmake(request, &request->config_items, inst->cache_da->name, *dn_p, T_OP_ADD);
+               RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, *dn_p);
                ldap_memfree(*dn_p);
 
                dn_p++;
@@ -415,8 +414,8 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
        do {
                if (inst->cacheable_group_dn) {
                        dn = ldap_get_dn((*pconn)->handle, entry);
-                       pairmake(request, &request->config_items, inst->group_da->name, dn, T_OP_ADD);
-                       RDEBUG("Added %s with value \"%s\" to control list", inst->group_da->name, dn);
+                       pairmake(request, &request->config_items, inst->cache_da->name, dn, T_OP_ADD);
+                       RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, dn);
                        ldap_memfree(dn);
                }
 
@@ -426,8 +425,8 @@ rlm_rcode_t rlm_ldap_cacheable_groupobj(ldap_instance_t const *inst, REQUEST *re
                                continue;
                        }
 
-                       pairmake(request, &request->config_items, inst->group_da->name, *vals, T_OP_ADD);
-                       RDEBUG("Added %s with value \"%s\" to control list", inst->group_da->name, *vals);
+                       pairmake(request, &request->config_items, inst->cache_da->name, *vals, T_OP_ADD);
+                       RDEBUG("Added control:%s with value \"%s\"", inst->cache_da->name, *vals);
 
                        ldap_value_free(vals);
                }
@@ -535,7 +534,7 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST
 {
        rlm_rcode_t     rcode = RLM_MODULE_NOTFOUND, ret;
        ldap_rcode_t    status;
-       int             name_is_dn = false, value_is_dn = false;
+       bool            name_is_dn = false, value_is_dn = false;
 
        LDAPMessage     *result = NULL;
        LDAPMessage     *entry = NULL;
@@ -595,7 +594,7 @@ rlm_rcode_t rlm_ldap_check_userobj_dynamic(ldap_instance_t const *inst, REQUEST
                 */
                if (!name_is_dn && !value_is_dn) {
                        if (strcmp(vals[i], name) == 0){
-                               RDEBUG("User found. Comparison between membership: name, check: name]");
+                               RDEBUG("User found. Comparison between membership: name, check: name");
                                rcode = RLM_MODULE_OK;
 
                                goto finish;
@@ -702,13 +701,13 @@ rlm_rcode_t rlm_ldap_check_cached(ldap_instance_t const *inst, REQUEST *request,
        int             ret;
        vp_cursor_t     cursor;
 
-       paircursor(&cursor, &request->config_items);
-       vp = pairfindnext(&cursor, inst->group_da->attr, inst->group_da->vendor, TAG_ANY);
+       fr_cursor_init(&cursor, &request->config_items);
+       vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY);
        if (!vp) {
                return RLM_MODULE_INVALID;
        }
 
-       for (; vp; vp = pairfindnext(&cursor, inst->group_da->attr, inst->group_da->vendor, TAG_ANY)) {
+       for (; vp; vp = fr_cursor_next_by_num(&cursor, inst->cache_da->attr, inst->cache_da->vendor, TAG_ANY)) {
                ret = radius_compare_vps(request, check, vp);
                if (ret == 0) {
                        RDEBUG2("User found. Matched cached membership");
index 3a96e89..034d44d 100644 (file)
@@ -266,7 +266,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
        char *srv_err = NULL;           // Server's extended error message.
        char *p, *a;
 
-       int freeit = false;             // Whether the message should be freed after being processed.
+       bool freeit = false;            // Whether the message should be freed after being processed.
        int len;
 
        struct timeval tv;              // Holds timeout values.
@@ -368,7 +368,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
                len = rlm_ldap_common_dn(dn, part_dn);
                if (len < 0) break;
 
-               our_err = talloc_asprintf(conn, "Match stopped here: [%.*s]%s", len, dn, part_dn ? part_dn : "");
+               our_err = talloc_typed_asprintf(conn, "Match stopped here: [%.*s]%s", len, dn, part_dn ? part_dn : "");
 
                goto error_string;
 
@@ -443,7 +443,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
 
                if (lib_errno != srv_errno) {
                        a = talloc_asprintf_append(p, "LDAP lib error: %s (%u), srv error: %s (%u). ",
-                                                  ldap_err2string(lib_errno), lib_errno,
+                                                  ldap_err2string(lib_errno), lib_errno,
                                                   ldap_err2string(srv_errno), srv_errno);
                        if (!a) {
                                talloc_free(p);
@@ -489,9 +489,7 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
                ldap_memfree(part_dn);
        }
 
-       if (our_err) {
-               talloc_free(our_err);
-       }
+       talloc_free(our_err);
 
        if ((lib_errno || srv_errno) && *result) {
                ldap_msgfree(*result);
@@ -514,89 +512,103 @@ static ldap_rcode_t rlm_ldap_result(ldap_instance_t const *inst, ldap_handle_t c
  * @return one of the LDAP_PROC_* values.
  */
 ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
-                          char const *password, int retry)
+                          char const *password, bool retry)
 {
-       ldap_rcode_t    status;
+       ldap_rcode_t    status = LDAP_PROC_ERROR;
 
        int             msgid;
 
        char const      *error = NULL;
        char            *extra = NULL;
 
+       int             i, num;
+
        rad_assert(*pconn && (*pconn)->handle);
+       rad_assert(!retry || inst->pool);
 
        /*
         *      Bind as anonymous user
         */
        if (!dn) dn = "";
 
-retry:
-       msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
-       /* We got a valid message ID */
-       if (msgid >= 0) {
-               if (request) {
-                       RDEBUG2("Waiting for bind result...");
-               } else {
-                       DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
+       /*
+        *      For sanity, for when no connections are viable,
+        *      and we can't make a new one.
+        */
+       num = retry ? fr_connection_get_num(inst->pool) : 0;
+       for (i = num; i >= 0; i--) {
+               msgid = ldap_bind((*pconn)->handle, dn, password, LDAP_AUTH_SIMPLE);
+               /* We got a valid message ID */
+               if (msgid >= 0) {
+                       if (request) {
+                               RDEBUG2("Waiting for bind result...");
+                       } else {
+                               DEBUG2("rlm_ldap (%s): Waiting for bind result...", inst->xlat_name);
+                       }
                }
-       }
 
-       status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
-       switch (status) {
-       case LDAP_PROC_SUCCESS:
-               LDAP_DBG_REQ("Bind successful");
-               break;
-       case LDAP_PROC_NOT_PERMITTED:
-               LDAP_ERR_REQ("Bind was not permitted: %s", error);
-               LDAP_EXT_REQ();
+               status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+               switch (status) {
+               case LDAP_PROC_SUCCESS:
+                       LDAP_DBG_REQ("Bind successful");
+                       break;
 
-               break;
+               case LDAP_PROC_NOT_PERMITTED:
+                       LDAP_ERR_REQ("Bind was not permitted: %s", error);
+                       LDAP_EXT_REQ();
 
-       case LDAP_PROC_REJECT:
-               LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
-               LDAP_EXT_REQ();
+                       break;
 
-               break;
+               case LDAP_PROC_REJECT:
+                       LDAP_ERR_REQ("Bind credentials incorrect: %s", error);
+                       LDAP_EXT_REQ();
 
-       case LDAP_PROC_RETRY:
-               if (retry) {
-                       *pconn = fr_connection_reconnect(inst->pool, *pconn);
-                       if (*pconn) {
-                               LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
-                                             dn, inst->server, inst->port, error);
+                       break;
 
-                               talloc_free(extra); /* don't leak debug info */
+               case LDAP_PROC_RETRY:
+                       if (retry) {
+                               *pconn = fr_connection_reconnect(inst->pool, *pconn);
+                               if (*pconn) {
+                                       LDAP_DBGW_REQ("Bind with %s to %s:%d failed: %s. Got new socket, retrying...",
+                                                     dn, inst->server, inst->port, error);
 
-                               goto retry;
-                       }
-               };
+                                       talloc_free(extra); /* don't leak debug info */
 
-               status = LDAP_PROC_ERROR;
+                                       continue;
+                               }
+                       };
+                       status = LDAP_PROC_ERROR;
 
-               /*
-                *      Were not allowed to retry, or there are no more
-                *      sockets, treat this as a hard failure.
-                */
-               /* FALL-THROUGH */
-       default:
+                       /*
+                        *      Were not allowed to retry, or there are no more
+                        *      sockets, treat this as a hard failure.
+                        */
+                       /* FALL-THROUGH */
+               default:
 #ifdef HAVE_LDAP_INITIALIZE
-               if (inst->is_url) {
-                       LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
-               } else
+                       if (inst->is_url) {
+                               LDAP_ERR_REQ("Bind with %s to %s failed: %s", dn, inst->server, error);
+                       } else
 #endif
-               {
-                       LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
-                                    inst->port, error);
+                       {
+                               LDAP_ERR_REQ("Bind with %s to %s:%d failed: %s", dn, inst->server,
+                                            inst->port, error);
+                       }
+                       LDAP_EXT_REQ();
+
+                       break;
                }
-               LDAP_EXT_REQ();
 
                break;
        }
 
-       if (extra) {
-               talloc_free(extra);
+       if (retry && (i < 0)) {
+               LDAP_ERR_REQ("Hit reconnection limit");
+               status = LDAP_PROC_ERROR;
        }
 
+       talloc_free(extra);
+
        return status; /* caller closes the connection */
 }
 
@@ -633,6 +645,9 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
        char const      *error = NULL;
        char            *extra = NULL;
 
+       int             i;
+
+
        rad_assert(*pconn && (*pconn)->handle);
 
        /*
@@ -656,8 +671,13 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
                (*pconn)->rebound = false;
        }
 
-       LDAP_DBG_REQ("Performing search in '%s' with filter '%s'", dn, filter);
-
+       if (filter) {
+               LDAP_DBG_REQ("Performing search in '%s' with filter '%s', scope '%s'", dn, filter,
+                            fr_int2str(ldap_scope, scope, "<INVALID>"));
+       } else {
+               LDAP_DBG_REQ("Performing unfiltered search in '%s', scope '%s'", dn,
+                            fr_int2str(ldap_scope, scope, "<INVALID>"));
+       }
        /*
         *      If LDAP search produced an error it should also be logged
         *      to the ld. result should pick it up without us
@@ -665,14 +685,21 @@ ldap_rcode_t rlm_ldap_search(ldap_instance_t const *inst, REQUEST *request, ldap
         */
        memset(&tv, 0, sizeof(tv));
        tv.tv_sec = inst->res_timeout;
-retry:
-       (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs, 0, NULL, NULL, &tv, 0, &msgid);
 
-       LDAP_DBG_REQ("Waiting for search result...");
-       status = rlm_ldap_result(inst, *pconn, msgid, dn, &our_result, &error, &extra);
-       switch (status) {
+       /*
+        *      For sanity, for when no connections are viable,
+        *      and we can't make a new one.
+        */
+       for (i = fr_connection_get_num(inst->pool); i >= 0; i--) {
+               (void) ldap_search_ext((*pconn)->handle, dn, scope, filter, search_attrs,
+                                      0, NULL, NULL, &tv, 0, &msgid);
+
+               LDAP_DBG_REQ("Waiting for search result...");
+               status = rlm_ldap_result(inst, *pconn, msgid, dn, &our_result, &error, &extra);
+               switch (status) {
                case LDAP_PROC_SUCCESS:
                        break;
+
                case LDAP_PROC_RETRY:
                        *pconn = fr_connection_reconnect(inst->pool, *pconn);
                        if (*pconn) {
@@ -680,7 +707,7 @@ retry:
 
                                talloc_free(extra); /* don't leak debug info */
 
-                               goto retry;
+                               continue;
                        }
 
                        status = LDAP_PROC_ERROR;
@@ -691,6 +718,16 @@ retry:
                        if (extra) LDAP_ERR_REQ("%s", extra);
 
                        goto finish;
+               }
+
+               break;
+       }
+
+       if (i < 0) {
+               LDAP_ERR_REQ("Hit reconnection limit");
+               status = LDAP_PROC_ERROR;
+
+               goto finish;
        }
 
        count = ldap_count_entries((*pconn)->handle, our_result);
@@ -708,10 +745,8 @@ retry:
                our_result = NULL;
        }
 
-       finish:
-       if (extra) {
-               talloc_free(extra);
-       }
+finish:
+       talloc_free(extra);
 
        /*
         *      We always need to get the result to count entries, but the caller
@@ -750,6 +785,8 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
        char const      *error = NULL;
        char            *extra = NULL;
 
+       int             i;
+
        rad_assert(*pconn && (*pconn)->handle);
 
        /*
@@ -766,40 +803,50 @@ ldap_rcode_t rlm_ldap_modify(ldap_instance_t const *inst, REQUEST *request, ldap
                (*pconn)->rebound = false;
        }
 
-       RDEBUG2("Modifying object with DN \"%s\"", dn);
-       retry:
-       (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
+       /*
+        *      For sanity, for when no connections are viable,
+        *      and we can't make a new one.
+        */
+       for (i = fr_connection_get_num(inst->pool); i >= 0; i--) {
+               RDEBUG2("Modifying object with DN \"%s\"", dn);
+               (void) ldap_modify_ext((*pconn)->handle, dn, mods, NULL, NULL, &msgid);
+
+               RDEBUG2("Waiting for modify result...");
+               status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
+               switch (status) {
+                       case LDAP_PROC_SUCCESS:
+                               break;
+                       case LDAP_PROC_RETRY:
+                               *pconn = fr_connection_reconnect(inst->pool, *pconn);
+                               if (*pconn) {
+                                       RWDEBUG("Modify failed: %s. Got new socket, retrying...", error);
 
-       RDEBUG2("Waiting for modify result...");
-       status = rlm_ldap_result(inst, *pconn, msgid, dn, NULL, &error, &extra);
-       switch (status) {
-               case LDAP_PROC_SUCCESS:
-                       break;
-               case LDAP_PROC_RETRY:
-                       *pconn = fr_connection_reconnect(inst->pool, *pconn);
-                       if (*pconn) {
-                               RWDEBUG("Modify failed: %s. Got new socket, retrying...", error);
+                                       talloc_free(extra); /* don't leak debug info */
 
-                               talloc_free(extra); /* don't leak debug info */
+                                       continue;
+                               }
 
-                               goto retry;
-                       }
+                               status = LDAP_PROC_ERROR;
 
-                       status = LDAP_PROC_ERROR;
+                               /* FALL-THROUGH */
+                       default:
+                               REDEBUG("Failed modifying object: %s", error);
+                               REDEBUG("%s", extra);
 
-                       /* FALL-THROUGH */
-               default:
-                       REDEBUG("Failed modifying object: %s", error);
-                       REDEBUG("%s", extra);
+                               goto finish;
+               }
 
-                       goto finish;
+               break;
        }
 
-       finish:
-       if (extra) {
-               talloc_free(extra);
+       if (i < 0) {
+               LDAP_ERR_REQ("Hit reconnection limit");
+               status = LDAP_PROC_ERROR;
        }
 
+finish:
+       talloc_free(extra);
+
        return status;
 }
 
@@ -833,7 +880,7 @@ char const *rlm_ldap_find_user(ldap_instance_t const *inst, REQUEST *request, ld
        char            filter[LDAP_MAX_FILTER_STR_LEN];
        char            base_dn[LDAP_MAX_DN_STR_LEN];
 
-       int freeit = false;                                     //!< Whether the message should
+       bool freeit = false;                                    //!< Whether the message should
                                                                //!< be freed after being processed.
 
        *rcode = RLM_MODULE_FAIL;
@@ -957,7 +1004,8 @@ rlm_rcode_t rlm_ldap_check_access(ldap_instance_t const *inst, REQUEST *request,
        if (vals) {
                if (inst->access_positive) {
                        if (strncasecmp(vals[0], "false", 5) == 0) {
-                               RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out");
+                               RDEBUG("\"%s\" attribute exists but is set to 'false' - user locked out",
+                                      inst->userobj_access_attr);
                                rcode = RLM_MODULE_USERLOCK;
                        }
                        /* RLM_MODULE_OK set above... */
@@ -996,7 +1044,7 @@ void rlm_ldap_check_reply(ldap_instance_t const *inst, REQUEST *request)
                    !pairfind(request->config_items, PW_USER_PASSWORD, 0, TAG_ANY) &&
                    !pairfind(request->config_items, PW_PASSWORD_WITH_HEADER, 0, TAG_ANY) &&
                    !pairfind(request->config_items, PW_CRYPT_PASSWORD, 0, TAG_ANY)) {
-                       RWDEBUG("No \"reference\" password added. Ensure the admin user has permission to "
+                       RWDEBUG("No \"known good\" password added. Ensure the admin user has permission to "
                                "read the password attribute");
                        RWDEBUG("PAP authentication will *NOT* work with Active Directory (if that is what you "
                                "were trying to configure)");
@@ -1196,7 +1244,7 @@ void *mod_conn_create(void *instance)
         */
        if (inst->start_tls) {
                if (inst->port == 636) {
-                       WDEBUG("Told to Start TLS on LDAPS port this will probably fail, please correct the "
+                       WARN("Told to Start TLS on LDAPS port this will probably fail, please correct the "
                               "configuration");
                }
 
@@ -1251,18 +1299,9 @@ int mod_conn_delete(UNUSED void *instance, void *handle)
  * @param inst rlm_ldap configuration.
  * @param request Current request (may be NULL).
  */
-ldap_handle_t *rlm_ldap_get_socket(ldap_instance_t const *inst, REQUEST *request)
+ldap_handle_t *rlm_ldap_get_socket(ldap_instance_t const *inst, UNUSED REQUEST *request)
 {
-       ldap_handle_t *conn;
-
-       conn = fr_connection_get(inst->pool);
-       if (!conn) {
-               REDEBUG("All ldap connections are in use");
-
-               return NULL;
-       }
-
-       return conn;
+       return fr_connection_get(inst->pool);
 }
 
 
index 5d1437a..e58ba4f 100644 (file)
@@ -43,7 +43,7 @@ typedef struct ldap_instance {
        char const      *server;                        //!< Initial server to bind to.
        int             is_url;                         //!< Whether ldap_is_ldap_url says 'server' is an
                                                        //!< ldap[s]:// url.
-       int             port;                           //!< Port to use when binding to the server.
+       uint16_t        port;                           //!< Port to use when binding to the server.
 
        char const      *admin_dn;                      //!< DN we bind as when we need to query the LDAP
                                                        //!< directory.
@@ -60,11 +60,11 @@ typedef struct ldap_instance {
                                                        //!< referrals on the same server, but won't bind to other
                                                        //!< servers.
 
-       int             ldap_debug;                     //!< Debug flag for the SDK.
+       uint32_t        ldap_debug;                     //!< Debug flag for the SDK.
 
        char const      *xlat_name;                     //!< Instance name.
 
-       int             expect_password;                //!< True if the user_map included a mapping between an LDAP
+       bool            expect_password;                //!< True if the user_map included a mapping between an LDAP
                                                        //!< attribute and one of our password reference attributes.
 
        /*
@@ -81,7 +81,7 @@ typedef struct ldap_instance {
        int             userobj_scope;                  //!< Search scope.
 
        char const      *userobj_membership_attr;       //!< Attribute that describes groups the user is a member of.
-       char            *userobj_access_attr;           //!< Attribute to check to see if the user should be locked out.
+       char const      *userobj_access_attr;           //!< Attribute to check to see if the user should be locked out.
        bool            access_positive;                //!< If true the presence of the attribute will allow access,
                                                        //!< else it will deny access.
 
@@ -111,6 +111,12 @@ typedef struct ldap_instance {
                                                        //!< resolution necessary to determine the DNs of those groups,
                                                        //!< then right them to the control list (LDAP-GroupDN).
 
+       char const      *cache_attribute;               //!< Sets the attribute we use when creating and retrieving
+                                                       //!< cached group memberships.
+
+       DICT_ATTR const *cache_da;                      //!< The DA associated with this specific version of the
+                                                       //!< rlm_ldap module.
+
        DICT_ATTR const *group_da;                      //!< The DA associated with this specific version of the
                                                        //!< rlm_ldap module.
 
@@ -180,17 +186,17 @@ typedef struct ldap_instance {
         *      Options
         */
 
-       int             net_timeout;                    //!< How long we wait for new connections to the LDAP server
+       uint32_t        net_timeout;                    //!< How long we wait for new connections to the LDAP server
                                                        //!< to be established.
-       int             res_timeout;                    //!< How long we wait for a result from the server.
-       int             srv_timelimit;                  //!< How long the server should spent on a single request
+       uint32_t        res_timeout;                    //!< How long we wait for a result from the server.
+       uint32_t        srv_timelimit;                  //!< How long the server should spent on a single request
                                                        //!< (also bounded by value on the server).
 
 #ifdef WITH_EDIR
-       /*
+       /*
         *      eDir support
         */
-       bool            edir;                           //!< If true attempt to retrieve the user's Cleartext password
+       bool            edir;                           //!< If true attempt to retrieve the user's cleartext password
                                                        //!< using the Universal Password feature of Novell eDirectory.
        bool            edir_autz;                      //!< If true, and we have the Universal Password, bind with it
                                                        //!< to perform additional authorisation checks.
@@ -199,15 +205,15 @@ typedef struct ldap_instance {
         *      For keep-alives.
         */
 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
-       int             keepalive_idle;                 //!< Number of seconds a connections needs to remain idle
+       uint32_t        keepalive_idle;                 //!< Number of seconds a connections needs to remain idle
                                                        //!< before TCP starts sending keepalive probes.
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
-       int             keepalive_probes;               //!< Number of missed timeouts before the connection is
+       uint32_t        keepalive_probes;               //!< Number of missed timeouts before the connection is
                                                        //!< dropped.
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
-       int             keepalive_interval;             //!< Interval between keepalive probes.
+       uint32_t        keepalive_interval;             //!< Interval between keepalive probes.
 #endif
 
 } ldap_instance_t;
@@ -229,8 +235,9 @@ typedef struct rlm_ldap_map_xlat {
 } rlm_ldap_map_xlat_t;
 
 typedef struct rlm_ldap_result {
-       char    **values;
-       int     count;
+       struct berval   **values;                       //!< libldap struct containing bv_val (char *)
+                                                       //!< and length bv_len.
+       int             count;                          //!< Number of values.
 } rlm_ldap_result_t;
 
 typedef enum {
@@ -278,6 +285,9 @@ typedef enum {
 #define LDAP_EXT() if (extra) LDAP_ERR(extra)
 #define LDAP_EXT_REQ() do { if (extra) { if (request) REDEBUG("%s", extra); else LDAP_ERR("%s", extra); }} while (0)
 
+extern FR_NAME_NUMBER const ldap_scope[];
+extern FR_NAME_NUMBER const ldap_tls_require_cert[];
+
 /*
  *     ldap.c - Wrappers arounds OpenLDAP functions.
  */
@@ -288,7 +298,7 @@ int rlm_ldap_is_dn(char const *str);
 ssize_t rlm_ldap_xlat_filter(REQUEST *request, char const **sub, size_t sublen, char *out, size_t outlen);
 
 ldap_rcode_t rlm_ldap_bind(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn,
-                         char const *password, int retry);
+                         char const *password, bool retry);
 
 char const *rlm_ldap_error_str(ldap_handle_t const *conn);
 
@@ -348,7 +358,7 @@ void rlm_ldap_map_do(ldap_instance_t const *inst, REQUEST *request, LDAP *handle
                     rlm_ldap_map_xlat_t const *expanded, LDAPMessage *entry);
 
 rlm_rcode_t rlm_ldap_map_profile(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
-                                char const *profile, rlm_ldap_map_xlat_t const *expanded);
+                                char const *profile, rlm_ldap_map_xlat_t const *expanded);
 
 /*
  *     clients.c - Dynamic clients (bulk load).
index 65dc678..6fd8366 100644 (file)
@@ -38,7 +38,7 @@ RCSID("$Id$")
 /*
  *     Scopes
  */
-const FR_NAME_NUMBER ldap_scope[] = {
+FR_NAME_NUMBER const ldap_scope[] = {
        { "sub",        LDAP_SCOPE_SUB  },
        { "one",        LDAP_SCOPE_ONE  },
        { "base",       LDAP_SCOPE_BASE },
@@ -48,7 +48,7 @@ const FR_NAME_NUMBER ldap_scope[] = {
 };
 
 #ifdef LDAP_OPT_X_TLS_NEVER
-const FR_NAME_NUMBER ldap_tls_require_cert[] = {
+FR_NAME_NUMBER const ldap_tls_require_cert[] = {
        { "never",      LDAP_OPT_X_TLS_NEVER    },
        { "demand",     LDAP_OPT_X_TLS_DEMAND   },
        { "allow",      LDAP_OPT_X_TLS_ALLOW    },
@@ -66,37 +66,37 @@ static CONF_PARSER tls_config[] = {
        /*
         *      Deprecated attributes
         */
-       {"cacertfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_ca_file), NULL, NULL},
-       {"ca_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_ca_file), NULL, NULL},
+       { "cacertfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_ca_file), NULL },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_ca_file), NULL },
 
-       {"cacertdir", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_ca_path), NULL, NULL},
-       {"ca_path", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_ca_path), NULL, NULL},
+       { "cacertdir", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_ca_path), NULL },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_ca_path), NULL },
 
-       {"certfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_certificate_file), NULL, NULL},
-       {"certificate_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_certificate_file), NULL, NULL},
+       { "certfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_certificate_file), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_certificate_file), NULL },
 
-       {"keyfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_private_key_file), NULL, NULL}, // OK if it changes on HUP
-       {"private_key_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_private_key_file), NULL, NULL}, // OK if it changes on HUP
+       { "keyfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_private_key_file), NULL }, // OK if it changes on HUP
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_private_key_file), NULL }, // OK if it changes on HUP
 
-       {"randfile", PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, offsetof(ldap_instance_t, tls_random_file), NULL, NULL},
-       {"random_file", PW_TYPE_FILE_INPUT, offsetof(ldap_instance_t, tls_random_file), NULL, NULL},
+       { "randfile", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, ldap_instance_t, tls_random_file), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, ldap_instance_t, tls_random_file), NULL },
 
        /*
         *      LDAP Specific TLS attributes
         */
-       {"start_tls", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, start_tls), NULL, "no"},
-       {"require_cert", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, tls_require_cert_str), NULL, NULL},
+       { "start_tls", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, start_tls), "no" },
+       { "require_cert", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, tls_require_cert_str), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
 
 static CONF_PARSER profile_config[] = {
-       {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, profile_filter), NULL, "(&)"}, //!< Correct filter for
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, profile_filter), "(&)" },   //!< Correct filter for
                                                                                                //!< when the DN is
                                                                                                //!< known.
-       {"attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, profile_attr), NULL, NULL},
-       {"default", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, default_profile), NULL, NULL},
+       { "attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, profile_attr), NULL },
+       { "default", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, default_profile), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -105,12 +105,12 @@ static CONF_PARSER profile_config[] = {
  *     User configuration
  */
 static CONF_PARSER user_config[] = {
-       {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_filter), NULL, "(uid=%u)"},
-       {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_scope_str), NULL, "sub"},
-       {"base_dn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,userobj_base_dn), NULL, ""},
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_filter), "(uid=%u)" },
+       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_scope_str), "sub" },
+       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_base_dn), "" },
 
-       {"access_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_access_attr), NULL, NULL},
-       {"access_positive", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, access_positive), NULL, "yes"},
+       { "access_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_access_attr), NULL },
+       { "access_positive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, access_positive), "yes" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -119,15 +119,16 @@ static CONF_PARSER user_config[] = {
  *     Group configuration
  */
 static CONF_PARSER group_config[] = {
-       {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_filter), NULL, NULL},
-       {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_scope_str), NULL, "sub"},
-       {"base_dn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_base_dn), NULL, ""},
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_filter), NULL },
+       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_scope_str), "sub" },
+       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_base_dn), "" },
 
-       {"name_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_name_attr), NULL, "cn"},
-       {"membership_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, userobj_membership_attr), NULL, NULL},
-       {"membership_filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, groupobj_membership_filter), NULL, NULL},
-       {"cacheable_name", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_name), NULL, "no"},
-       {"cacheable_dn", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t, cacheable_group_dn), NULL, "no"},
+       { "name_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_name_attr), "cn" },
+       { "membership_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, userobj_membership_attr), NULL },
+       { "membership_filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, groupobj_membership_filter), NULL },
+       { "cacheable_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, cacheable_group_name), "no" },
+       { "cacheable_dn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, cacheable_group_dn), "no" },
+       { "cache_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, cache_attribute), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -136,22 +137,21 @@ static CONF_PARSER group_config[] = {
  *     Client configuration
  */
 static CONF_PARSER client_attribute[] = {
-       {"identifier", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_identifier), NULL, "host"},
-       {"shortname", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_shortname), NULL, "cn"},
-       {"nas_type", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_type), NULL, NULL},
-       {"secret", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_secret), NULL, NULL},
-       {"virtual_server", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_server), NULL, NULL},
-       {"require_message_authenticator", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_require_ma),
-        NULL, NULL},
+       { "identifier", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_identifier), "host" },
+       { "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_shortname), "cn" },
+       { "nas_type", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_type), NULL },
+       { "secret", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_secret), NULL },
+       { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_server), NULL },
+       { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_require_ma), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
 static CONF_PARSER client_config[] = {
-       {"filter", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_filter), NULL, NULL},
-       {"scope", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_scope_str), NULL, "sub"},
-       {"base_dn", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, clientobj_base_dn), NULL, ""},
-       {"attribute", PW_TYPE_SUBSECTION, 0, NULL, (void const *) client_attribute},
+       { "filter", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_filter), NULL },
+       { "scope", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_scope_str), "sub" },
+       { "base_dn", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, clientobj_base_dn), "" },
+       { "attribute", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_attribute },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -160,7 +160,7 @@ static CONF_PARSER client_config[] = {
  *     Reference for accounting updates
  */
 static const CONF_PARSER acct_section_config[] = {
-       {"reference", PW_TYPE_STRING_PTR, offsetof(ldap_acct_section_t, reference), NULL, "."},
+       { "reference", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_acct_section_t, reference), "." },
 
        {NULL, -1, 0, NULL, NULL}
 };
@@ -174,29 +174,29 @@ static CONF_PARSER option_config[] = {
        /*
         *      Debugging flags to the server
         */
-       {"ldap_debug", PW_TYPE_INTEGER, offsetof(ldap_instance_t,ldap_debug), NULL, "0x0000"},
+       { "ldap_debug", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, ldap_debug), "0x0000" },
 
-       {"chase_referrals", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,chase_referrals), NULL, NULL},
+       { "chase_referrals", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, chase_referrals), NULL },
 
-       {"rebind", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,rebind), NULL, NULL},
+       { "rebind", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, rebind), NULL },
 
        /* timeout on network activity */
-       {"net_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,net_timeout), NULL, "10"},
+       { "net_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, net_timeout), "10" },
 
        /* timeout for search results */
-       {"res_timeout", PW_TYPE_INTEGER, offsetof(ldap_instance_t,res_timeout), NULL, "20"},
+       { "res_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, res_timeout), "20" },
 
        /* allow server unlimited time for search (server-side limit) */
-       {"srv_timelimit", PW_TYPE_INTEGER, offsetof(ldap_instance_t,srv_timelimit), NULL, "20"},
+       { "srv_timelimit", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, srv_timelimit), "20" },
 
 #ifdef LDAP_OPT_X_KEEPALIVE_IDLE
-       {"idle", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_idle), NULL, "60"},
+       { "idle", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_idle), "60" },
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_PROBES
-       {"probes", PW_TYPE_INTEGER, offsetof(ldap_instance_t,keepalive_probes), NULL, "3"},
+       { "probes", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_probes), "3" },
 #endif
 #ifdef LDAP_OPT_X_KEEPALIVE_INTERVAL
-       {"interval", PW_TYPE_INTEGER,  offsetof(ldap_instance_t,keepalive_interval), NULL, "30"},
+       { "interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, ldap_instance_t, keepalive_interval), "30" },
 #endif
 
        { NULL, -1, 0, NULL, NULL }
@@ -204,38 +204,38 @@ static CONF_PARSER option_config[] = {
 
 
 static const CONF_PARSER module_config[] = {
-       {"server", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(ldap_instance_t,server), NULL, "localhost"},
-       {"port", PW_TYPE_INTEGER, offsetof(ldap_instance_t,port), NULL, "389"},
+       { "server", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, ldap_instance_t, server), "localhost" },
+       { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, ldap_instance_t, port), "389" },
 
-       {"password", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,password), NULL, ""},
-       {"identity", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t,admin_dn), NULL, ""},
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, ldap_instance_t, password), "" },
+       { "identity", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, admin_dn), "" },
 
-       {"valuepair_attribute", PW_TYPE_STRING_PTR, offsetof(ldap_instance_t, valuepair_attr), NULL, NULL},
+       { "valuepair_attribute", FR_CONF_OFFSET(PW_TYPE_STRING, ldap_instance_t, valuepair_attr), NULL },
 
 #ifdef WITH_EDIR
        /* support for eDirectory Universal Password */
-       {"edir", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir), NULL, NULL}, /* NULL defaults to "no" */
+       { "edir", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, edir), NULL }, /* NULL defaults to "no" */
 
        /*
-        *      Attempt to bind with the Cleartext password we got from eDirectory
+        *      Attempt to bind with the cleartext password we got from eDirectory
         *      Universal password for additional authorization checks.
         */
-       {"edir_autz", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,edir_autz), NULL, NULL}, /* NULL defaults to "no" */
+       { "edir_autz", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, edir_autz), NULL }, /* NULL defaults to "no" */
 #endif
 
-       {"read_clients", PW_TYPE_BOOLEAN, offsetof(ldap_instance_t,do_clients), NULL, NULL}, /* NULL defaults to "no" */
+       { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, ldap_instance_t, do_clients), NULL }, /* NULL defaults to "no" */
 
-       { "user", PW_TYPE_SUBSECTION, 0, NULL, (void const *) user_config },
+       { "user", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) user_config },
 
-       { "group", PW_TYPE_SUBSECTION, 0, NULL, (void const *) group_config },
+       { "group", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) group_config },
 
-       { "client", PW_TYPE_SUBSECTION, 0, NULL, (void const *) client_config },
+       { "client", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) client_config },
 
-       { "profile", PW_TYPE_SUBSECTION, 0, NULL, (void const *) profile_config },
+       { "profile", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) profile_config },
 
-       { "options", PW_TYPE_SUBSECTION, 0, NULL, (void const *) option_config },
+       { "options", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) option_config },
 
-       { "tls", PW_TYPE_SUBSECTION, 0, NULL, (void const *) tls_config },
+       { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
 
        {NULL, -1, 0, NULL, NULL}
 };
@@ -283,7 +283,7 @@ static ssize_t ldap_xlat(void *instance, REQUEST *request, char const *fmt, char
 
        if (ldap_url->lud_host &&
            ((strncmp(inst->server, ldap_url->lud_host, strlen(inst->server)) != 0) ||
-            (ldap_url->lud_port != inst->port))) {
+            ((uint32_t) ldap_url->lud_port != inst->port))) {
                RDEBUG("Requested server/port is \"%s:%i\"", ldap_url->lud_host, inst->port);
 
                goto free_urldesc;
@@ -359,8 +359,8 @@ static int rlm_ldap_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR
        ldap_instance_t *inst = instance;
        rlm_rcode_t     rcode;
 
-       int             found = false;
-       int             check_is_dn;
+       bool            found = false;
+       bool            check_is_dn;
 
        ldap_handle_t   *conn = NULL;
        char const      *user_dn;
@@ -483,7 +483,7 @@ static int mod_detach(void *instance)
  * @return 0 on success, else < 0 on failure.
  */
 static int parse_sub_section(ldap_instance_t *inst, CONF_SECTION *parent, ldap_acct_section_t **config,
-                            rlm_components_t comp)
+                            rlm_components_t comp)
 {
        CONF_SECTION *cs;
 
@@ -644,14 +644,15 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
         *      Group comparison checks.
         */
        if (cf_section_name2(conf)) {
-               ATTR_FLAGS flags;
+               static ATTR_FLAGS flags;
                char buffer[256];
 
-               snprintf(buffer, sizeof(buffer), "%s-Ldap-Group",
-                        inst->xlat_name);
-               memset(&flags, 0, sizeof(flags));
+               snprintf(buffer, sizeof(buffer), "%s-Ldap-Group", inst->xlat_name);
+               if (dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags) < 0) {
+                       LDAP_ERR("Error creating group attribute: %s", fr_strerror());
 
-               dict_addattr(buffer, -1, 0, PW_TYPE_STRING, flags);
+                       return -1;
+               }
                inst->group_da = dict_attrbyname(buffer);
                if (!inst->group_da) {
                        LDAP_ERR("Failed creating attribute %s", buffer);
@@ -672,6 +673,22 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        xlat_register(inst->xlat_name, ldap_xlat, rlm_ldap_escape_func, inst);
 
        /*
+        *      Setup the cache attribute
+        */
+       if (inst->cache_attribute) {
+               static ATTR_FLAGS flags;
+
+               if (dict_addattr(inst->cache_attribute, -1, 0, PW_TYPE_STRING, flags) < 0) {
+                       LDAP_ERR("Error creating cache attribute: %s", fr_strerror());
+
+                       return -1;
+               }
+               inst->cache_da = dict_attrbyname(inst->cache_attribute);
+       } else {
+               inst->cache_da = inst->group_da;        /* Default to the group_da */
+       }
+
+       /*
         *      Initialize the socket pool.
         */
        inst->pool = fr_connection_pool_init(inst->cs, inst, mod_conn_create, NULL, mod_conn_delete, NULL);
@@ -696,13 +713,7 @@ error:
        return -1;
 }
 
-/** Check the user's password against ldap directory
- *
- * @param instance rlm_ldap configuration.
- * @param request Current request.
- * @return one of the RLM_MODULE_* values.
- */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_rcode_t     rcode;
        ldap_rcode_t    status;
@@ -723,13 +734,13 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
 
        if (!request->password ||
            (request->password->da->attr != PW_USER_PASSWORD)) {
-               RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere.");
+               RWDEBUG("You have set \"Auth-Type := LDAP\" somewhere");
                RWDEBUG("*********************************************");
                RWDEBUG("* THAT CONFIGURATION IS WRONG.  DELETE IT.   ");
-               RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING.");
+               RWDEBUG("* YOU ARE PREVENTING THE SERVER FROM WORKING");
                RWDEBUG("*********************************************");
 
-               REDEBUG("Attribute \"User-Password\" is required for authentication.");
+               REDEBUG("Attribute \"User-Password\" is required for authentication");
 
                return RLM_MODULE_INVALID;
        }
@@ -792,10 +803,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
        return rcode;
 }
 
-/** Check if user is authorized for remote access
- *
- */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
        ldap_rcode_t    status;
@@ -810,7 +818,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        rlm_ldap_map_xlat_t     expanded; /* faster than mallocing every time */
 
        if (!request->username) {
-               RDEBUG2("Attribute \"User-Name\" is required for authorization.");
+               RDEBUG2("Attribute \"User-Name\" is required for authorization");
 
                return RLM_MODULE_NOOP;
        }
@@ -926,7 +934,12 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                pairstrcpy(vp, password);
                vp->length = pass_size;
 
-               RDEBUG2("Added eDirectory password.  control:%s += '%s'", vp->da->name, vp->vp_strvalue);
+               if (RDEBUG_ENABLED3) {
+                       RDEBUG3("Added eDirectory password.  control:%s += '%s'", vp->da->name, vp->vp_strvalue);
+               } else {
+                       RDEBUG2("Added eDirectory password");
+               }
+
                if (inst->edir_autz) {
                        RDEBUG2("Binding as user for eDirectory authorization checks");
                        /*
@@ -1088,13 +1101,13 @@ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acc
         *      Iterate over all the pairs, building our mods array
         */
        for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
-               int do_xlat = false;
+               bool do_xlat = false;
 
-               if (total == LDAP_MAX_ATTRMAP) {
-                       REDEBUG("Modify map size exceeded");
+               if (total == LDAP_MAX_ATTRMAP) {
+                       REDEBUG("Modify map size exceeded");
 
-                       goto error;
-               }
+                       goto error;
+               }
 
                if (!cf_item_is_pair(ci)) {
                        REDEBUG("Entry is not in \"ldap-attribute = value\" format");
@@ -1135,7 +1148,7 @@ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acc
                } else if (do_xlat) {
                        char *exp = NULL;
 
-                       if (radius_xlat(exp, 0, request, value, NULL, NULL) <= 0) {
+                       if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) {
                                RDEBUG("Skipping attribute \"%s\"", attr);
 
                                talloc_free(exp);
@@ -1184,7 +1197,7 @@ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acc
 #endif
                default:
                        REDEBUG("Operator '%s' is not supported for LDAP modify operations",
-                               fr_int2str(fr_tokens, op, "<INVALID>"));
+                               fr_int2str(fr_tokens, op, "<INVALID>"));
 
                        goto error;
                }
@@ -1244,7 +1257,7 @@ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acc
        return rcode;
 }
 
-static rlm_rcode_t mod_accounting(void *instance, REQUEST * request) {
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
        ldap_instance_t *inst = instance;
 
        if (inst->accounting) {
@@ -1254,7 +1267,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST * request) {
        return RLM_MODULE_NOOP;
 }
 
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST * request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request)
 {
        ldap_instance_t *inst = instance;
 
index 2727f92..c356881 100644 (file)
@@ -52,13 +52,14 @@ RCSID("$Id$")
  */
 typedef struct rlm_linelog_t {
        CONF_SECTION    *cs;
-       char            *filename;
-       char            *syslog_facility;
+       char const      *filename;
+       char const      *syslog_facility;
        int             facility;
-       int             permissions;
-       char            *group;
-       char            *line;
-       char            *reference;
+       uint32_t        permissions;
+       char const      *group;
+       char const      *line;
+       char const      *reference;
+       fr_logfile_t    *lf;
 } rlm_linelog_t;
 
 /*
@@ -71,18 +72,12 @@ typedef struct rlm_linelog_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "filename",  PW_TYPE_FILE_OUTPUT| PW_TYPE_REQUIRED,
-         offsetof(rlm_linelog_t,filename), NULL,  NULL},
-       { "syslog_facility",  PW_TYPE_STRING_PTR,
-         offsetof(rlm_linelog_t,syslog_facility), NULL,  NULL},
-       { "permissions",  PW_TYPE_INTEGER,
-         offsetof(rlm_linelog_t,permissions), NULL,  "0600"},
-       { "group",  PW_TYPE_STRING_PTR,
-         offsetof(rlm_linelog_t,group), NULL,  NULL},
-       { "format",  PW_TYPE_STRING_PTR,
-         offsetof(rlm_linelog_t,line), NULL,  NULL},
-       { "reference",  PW_TYPE_STRING_PTR,
-         offsetof(rlm_linelog_t,reference), NULL,  NULL},
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT| PW_TYPE_REQUIRED, rlm_linelog_t, filename), NULL },
+       { "syslog_facility", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, syslog_facility), NULL },
+       { "permissions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_linelog_t, permissions), "0600" },
+       { "group", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, group), NULL },
+       { "format", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, line), NULL },
+       { "reference", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, reference), NULL },
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
@@ -94,7 +89,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        rlm_linelog_t *inst = instance;
 
-       rad_assert(inst->filename && *inst->filename);
+       if (!inst->filename) {
+               cf_log_err_cs(conf, "No value provided for 'filename'");
+               return -1;
+       }
 
 #ifndef HAVE_SYSLOG_H
        if (strcmp(inst->filename, "syslog") == 0) {
@@ -121,6 +119,12 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                return -1;
        }
 
+       inst->lf = fr_logfile_init(inst);
+       if (!inst->lf) {
+               cf_log_err_cs(conf, "Failed creating log file context");
+               return -1;
+       }
+
        inst->cs = conf;
        return 0;
 }
@@ -176,7 +180,7 @@ static size_t linelog_escape_func(UNUSED REQUEST *request,
 
                default:
                        if (outlen <= 4) break;
-                       snprintf(out, outlen,  "\\%03o", *in);
+                       snprintf(out, outlen,  "\\%03o", (uint8_t) *in);
                        in++;
                        out += 4;
                        outlen -= 4;
@@ -189,7 +193,7 @@ static size_t linelog_escape_func(UNUSED REQUEST *request,
        return len;
 }
 
-static rlm_rcode_t do_linelog(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *request)
 {
        int fd = -1;
        char buffer[4096];
@@ -204,14 +208,15 @@ static rlm_rcode_t do_linelog(void *instance, REQUEST *request)
        char *endptr;
 #endif
 
+       line[0] = '\0';
+
        if (inst->reference) {
                CONF_ITEM *ci;
                CONF_PAIR *cp;
 
                p = line + 1;
 
-               if (radius_xlat(p, sizeof(line) - 2, request, inst->reference, linelog_escape_func,
-                   NULL) < 0) {
+               if (radius_xlat(p, sizeof(line) - 2, request, inst->reference, linelog_escape_func, NULL) < 0) {
                        return RLM_MODULE_FAIL;
                }
 
@@ -260,16 +265,16 @@ static rlm_rcode_t do_linelog(void *instance, REQUEST *request)
                if (p) {
                        *p = '\0';
                        if (rad_mkdir(buffer, 0700) < 0) {
-                               RERROR("rlm_linelog: Failed to create directory %s: %s", buffer, strerror(errno));
+                               RERROR("rlm_linelog: Failed to create directory %s: %s", buffer, fr_syserror(errno));
                                return RLM_MODULE_FAIL;
                        }
                        *p = '/';
                }
 
-               fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, inst->permissions);
+               fd = fr_logfile_open(inst->lf, buffer, inst->permissions);
                if (fd == -1) {
                        ERROR("rlm_linelog: Failed to open %s: %s",
-                              buffer, strerror(errno));
+                              buffer, fr_syserror(errno));
                        return RLM_MODULE_FAIL;
                }
 
@@ -297,9 +302,9 @@ static rlm_rcode_t do_linelog(void *instance, REQUEST *request)
        /*
         *      FIXME: Check length.
         */
-       if (radius_xlat(line, sizeof(line) - 1, request, value, linelog_escape_func, NULL) < 0) {
+       if (value && (radius_xlat(line, sizeof(line) - 1, request, value, linelog_escape_func, NULL) < 0)) {
                if (fd > -1) {
-                       close(fd);
+                       fr_logfile_close(inst->lf, fd);
                }
 
                return RLM_MODULE_FAIL;
@@ -309,12 +314,12 @@ static rlm_rcode_t do_linelog(void *instance, REQUEST *request)
                strcat(line, "\n");
 
                if (write(fd, line, strlen(line)) < 0) {
-                       EDEBUG("rlm_linelog: Failed writing: %s", strerror(errno));
-                       close(fd);
+                       ERROR("rlm_linelog: Failed writing: %s", fr_syserror(errno));
+                       fr_logfile_close(inst->lf, fd);
                        return RLM_MODULE_FAIL;
                }
 
-               close(fd);
+               fr_logfile_close(inst->lf, fd);
 
 #ifdef HAVE_SYSLOG_H
        } else {
@@ -332,23 +337,23 @@ static rlm_rcode_t do_linelog(void *instance, REQUEST *request)
 module_t rlm_linelog = {
        RLM_MODULE_INIT,
        "linelog",
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(rlm_linelog_t),
        module_config,
        mod_instantiate,                /* instantiation */
        NULL,                           /* detach */
        {
-               do_linelog,     /* authentication */
-               do_linelog,     /* authorization */
-               do_linelog,     /* preaccounting */
-               do_linelog,     /* accounting */
-               NULL,           /* checksimul */
-               do_linelog,     /* pre-proxy */
-               do_linelog,     /* post-proxy */
-               do_linelog      /* post-auth */
+               mod_do_linelog,         /* authentication */
+               mod_do_linelog,         /* authorization */
+               mod_do_linelog,         /* preaccounting */
+               mod_do_linelog,         /* accounting */
+               NULL,                   /* checksimul */
+               mod_do_linelog,         /* pre-proxy */
+               mod_do_linelog,         /* post-proxy */
+               mod_do_linelog          /* post-auth */
 #ifdef WITH_COA
-               , do_linelog,   /* recv-coa */
-               do_linelog      /* send-coa */
+               , mod_do_linelog,       /* recv-coa */
+               mod_do_linelog          /* send-coa */
 #endif
        },
 };
index c815f11..27cbc4d 100644 (file)
@@ -39,7 +39,7 @@ int           timestr_match(char const *, time_t);
  *     be used as the instance handle.
  */
 typedef struct rlm_logintime_t {
-       int min_time;
+       uint32_t        min_time;
 } rlm_logintime_t;
 
 /*
@@ -52,8 +52,8 @@ typedef struct rlm_logintime_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-  { "minimum-timeout", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, offsetof(rlm_logintime_t,min_time), NULL, NULL},
-  { "minimum_timeout", PW_TYPE_INTEGER, offsetof(rlm_logintime_t,min_time), NULL, "60" },
+  { "minimum-timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_logintime_t, min_time), NULL },
+  { "minimum_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_logintime_t, min_time), "60" },
 
   { NULL, -1, 0, NULL, NULL }
 };
@@ -143,11 +143,11 @@ static int time_of_day(UNUSED void *instance, REQUEST *req, UNUSED VALUE_PAIR *r
 /*
  *      Check if account has expired, and if user may login now.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_logintime_t *inst = instance;
        VALUE_PAIR *ends, *timeout;
-       int left;
+       uint32_t left;
 
        ends = pairfind(request->config_items, PW_LOGIN_TIME, 0, TAG_ANY);
        if (!ends) {
@@ -198,13 +198,13 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                        timeout->vp_integer = left;
                }
        } else {
-               timeout = radius_paircreate(request, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
+               timeout = radius_paircreate(request->reply, &request->reply->vps, PW_SESSION_TIMEOUT, 0);
                timeout->vp_integer = left;
        }
 
        RDEBUG("reply:Session-Timeout set to %i", left);
 
-       return RLM_MODULE_OK;
+       return RLM_MODULE_UPDATED;
 }
 
 
@@ -255,7 +255,7 @@ static int mod_detach(UNUSED void *instance)
 module_t rlm_logintime = {
        RLM_MODULE_INIT,
        "logintime",
-       RLM_TYPE_CHECK_CONFIG_SAFE,     /* type */
+       0,      /* type */
        sizeof(rlm_logintime_t),
        module_config,
        mod_instantiate,                /* instantiation */
index 27c9f2b..e4b473f 100644 (file)
@@ -7,9 +7,9 @@ New features were added:
 ! module supports both authorization and authentication. Authorization
   sets authentication to MS-CHAP if any NTLM-related things found.
   During authorization new attributes added to config_items:
-         LM-Password - LM-encoded password
-         NT-Password - NT-encoded password
-         SMB-Account-CTRL - account control flags in SAMBA format
+        LM-Password - LM-encoded password
+        NT-Password - NT-encoded password
+        SMB-Account-CTRL - account control flags in SAMBA format
   During  authentication  these  attributes  are  checked  against  data
   provided by NAS.
 - RFC 2433 text with MS-CHAPv1 removed. Microsoft attributes are covered
index 178a9ef..4645923 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,10 +1255,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1870,7 +1870,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2092,7 +2092,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2779,22 +2779,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=membership.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2952,11 +2952,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3430,13 +3430,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 6af61f0..611feb3 100644 (file)
@@ -52,7 +52,7 @@ int mschap_ntpwdhash(uint8_t *out, char const *password)
        ssize_t len;
        uint8_t ucs2_password[512];
 
-       len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), password, strlen(password));
+       len = fr_utf8_to_ucs2(ucs2_password, sizeof(ucs2_password), password, strlen(password));
        if (len < 0) {
                *out = '\0';
                return -1;
@@ -133,7 +133,7 @@ void mschap_auth_response(char const *username,
         *      For example,
         *      "S=0123456789ABCDEF0123456789ABCDEF01234567"
         */
-       response[0] = 'S';
+       response[0] = 'S';
        response[1] = '=';
 
        /*
index 9c4fcd8..e024016 100644 (file)
@@ -16,7 +16,7 @@ void mschap_auth_response(char const *username,
                          uint8_t const *peer_challenge, uint8_t const *auth_challenge,
                          char *response);
 void mschap_add_reply(REQUEST *request, unsigned char ident,
-                     char const *name, char const *value, int len);
+                     char const *name, char const *value, size_t len);
 
 
 #endif /*_MSCHAP_H*/
index f9202d9..dca699b 100644 (file)
@@ -65,7 +65,6 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
        tAttributeListRef       attrListRef     = 0;
        char                *pUserLocation      = NULL;
        tAttributeValueListRef  valueRef        = 0;
-       tAttributeValueEntry    *pValueEntry    = NULL;
        tDataList              *pUserNode       = NULL;
        rlm_rcode_t             result          = RLM_MODULE_FAIL;
 
@@ -142,10 +141,12 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                for (attrIndex = 1; (attrIndex <= pRecEntry->fRecordAttributeCount) && (status == eDSNoErr); attrIndex++) {
                        status = dsGetAttributeEntry(nodeRef, tDataBuff, attrListRef, attrIndex, &valueRef, &pAttrEntry);
                        if (status == eDSNoErr && pAttrEntry != NULL) {
+                               tAttributeValueEntry    *pValueEntry    = NULL;
+
                                if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrMetaNodeLocation) == 0) {
                                        status = dsGetAttributeValue(nodeRef, tDataBuff, 1, valueRef, &pValueEntry);
                                        if (status == eDSNoErr && pValueEntry != NULL) {
-                                               pUserLocation = (char *) calloc(pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char));
+                                               pUserLocation = talloc_zero_array(request, char, pValueEntry->fAttributeValueData.fBufferLength + 1);
                                                memcpy(pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength);
                                        }
                                } else if (strcmp(pAttrEntry->fAttributeSignature.fBufferData, kDSNAttrRecordName) == 0) {
@@ -156,7 +157,7 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                                        }
                                }
 
-                               if (pValueEntry != NULL) {
+                               if (pValueEntry) {
                                        dsDeallocAttributeValueEntry(dsRef, pValueEntry);
                                        pValueEntry = NULL;
                                }
@@ -168,12 +169,18 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                        }
                }
 
+               if (!pUserLocation) {
+                       DEBUG2("[mschap] OpenDirectory has no user location");
+                       result = RLM_MODULE_NOOP;
+                       break;
+               }
+
                /* OpenDirectory doesn't support mschapv2 authentication against
                 * Active Directory.  AD users need to be authenticated using the
                 * normal freeradius AD path (i.e. ntlm_auth).
                 */
                if (strncmp(pUserLocation, kActiveDirLoc, strlen(kActiveDirLoc)) == 0) {
-                       DEBUG2("[mschap] OpenDirectory authentication returning noop.  OD doesn't support MSCHAPv2 for ActiveDirectory users.");
+                       DEBUG2("[mschap] OpenDirectory authentication returning noop.  OD doesn't support MSCHAPv2 for ActiveDirectory users");
                        result = RLM_MODULE_NOOP;
                        break;
                }
@@ -206,7 +213,7 @@ static rlm_rcode_t getUserNodeRef(REQUEST *request, char* inUserName, char **out
                dsDataBufferDeAllocate(dsRef, tDataBuff);
 
        if (pUserLocation != NULL)
-               free(pUserLocation);
+               talloc_free(pUserLocation);
 
        if (pRecName != NULL) {
                dsDataListDeallocate(dsRef, pRecName);
@@ -306,7 +313,8 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
        memcpy(&(tDataBuff->fBufferData[uiCurr]), shortUserName, uiLen);
        uiCurr += uiLen;
 #ifndef NDEBUG
-       RDEBUG2("       stepbuf server challenge:\t");
+       RINDENT();
+       RDEBUG2("Stepbuf server challenge : ");
        for (t = 0; t < challenge->length; t++) {
                fprintf(stderr, "%02x", challenge->vp_strvalue[t]);
        }
@@ -322,7 +330,7 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
        uiCurr += uiLen;
 
 #ifndef NDEBUG
-       RDEBUG2("       stepbuf peer challenge:\t\t");
+       RDEBUG2("Stepbuf peer challenge   : ");
        for (t = 2; t < 18; t++) {
                fprintf(stderr, "%02x", response->vp_strvalue[t]);
        }
@@ -338,7 +346,8 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
        uiCurr += uiLen;
 
 #ifndef NDEBUG
-       RDEBUG2("       stepbuf p24:\t\t");
+       RDEBUG2("Stepbuf p24              : ");
+       REXDENT();
        for (t = 26; t < 50; t++) {
                fprintf(stderr, "%02x", response->vp_strvalue[t]);
        }
@@ -366,7 +375,7 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
                                 pStepBuff, NULL);
        if (status == eDSNoErr) {
                if (pStepBuff->fBufferLength > 4) {
-                       uint32_t len;
+                       size_t len;
 
                        memcpy(&len, pStepBuff->fBufferData, sizeof(len));
                        if (len == 40) {
@@ -379,7 +388,7 @@ rlm_rcode_t od_mschap_auth(REQUEST *request, VALUE_PAIR *challenge, VALUE_PAIR *
                                                 *response->vp_strvalue,
                                                 "MS-CHAP2-Success",
                                                 mschap_reply, len+2);
-                               RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%ld)\n", mschap_reply, len);
+                               RDEBUG2("dsDoDirNodeAuth returns stepbuff: %s (len=%zu)\n", mschap_reply, len);
                        }
                }
        }
index 27f5339..a37a140 100644 (file)
@@ -143,15 +143,15 @@ typedef struct rlm_mschap_t {
        bool            require_strong;
        bool            with_ntdomain_hack;     /* this should be in another module */
        char const      *xlat_name;
-       char            *ntlm_auth;
-       int             ntlm_auth_timeout;
-       char            *ntlm_cpw;
-       char            *ntlm_cpw_username;
-       char            *ntlm_cpw_domain;
-       char            *local_cpw;
+       char const      *ntlm_auth;
+       uint32_t        ntlm_auth_timeout;
+       char const      *ntlm_cpw;
+       char const      *ntlm_cpw_username;
+       char const      *ntlm_cpw_domain;
+       char const      *local_cpw;
        char const      *auth_type;
        bool            allow_retry;
-       char            *retry_msg;
+       char const      *retry_msg;
 #ifdef WITH_OPEN_DIRECTORY
        bool            open_directory;
 #endif
@@ -183,17 +183,16 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
        if (strncasecmp(fmt, "Challenge", 9) == 0) {
                chap_challenge = pairfind(request->packet->vps, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!chap_challenge) {
-                       RDEBUG2("No MS-CHAP-Challenge in the request.");
-                       return 0;
+                       REDEBUG("No MS-CHAP-Challenge in the request");
+                       return -1;
                }
 
                /*
                 *      MS-CHAP-Challenges are 8 octets,
-                *      for MS-CHAPv2
+                *      for MS-CHAPv1
                 */
                if (chap_challenge->length == 8) {
-                       RDEBUG2(" mschap1: %02x",
-                              chap_challenge->vp_octets[0]);
+                       RDEBUG2("mschap1: %02x", chap_challenge->vp_octets[0]);
                        data = chap_challenge->vp_octets;
                        data_len = 8;
 
@@ -207,8 +206,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
 
                        response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                        if (!response) {
-                               RDEBUG2("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge.");
-                               return 0;
+                               REDEBUG("MS-CHAP2-Response is required to calculate MS-CHAPv1 challenge");
+                               return -1;
                        }
 
                        /*
@@ -221,17 +220,17 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                         *      Responses are 50 octets.
                         */
                        if (response->length < 50) {
-                               RAUTH("MS-CHAP-Response has the wrong format.");
-                               return 0;
+                               REDEBUG("MS-CHAP-Response has the wrong format");
+                               return -1;
                        }
 
                        user_name = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                        if (!user_name) {
-                               RDEBUG2("User-Name is required to calculate MS-CHAPv1 Challenge.");
-                               return 0;
+                               REDEBUG("User-Name is required to calculate MS-CHAPv1 Challenge");
+                               return -1;
                        }
 
-                       /*
+                       /*
                         *      Check for MS-CHAP-User-Name and if found, use it
                         *      to construct the MSCHAPv1 challenge.  This is
                         *      set by rlm_eap_mschap to the MS-CHAP Response
@@ -254,7 +253,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                                if (inst->with_ntdomain_hack) {
                                        username_string++;
                                } else {
-                                       RDEBUG2("NT Domain delimiter found, should we have enabled with_ntdomain_hack?");
+                                       RWDEBUG2("NT Domain delimiter found, should we have enabled with_ntdomain_hack?");
                                        username_string = name_attr->vp_strvalue;
                                }
                        } else {
@@ -264,7 +263,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                        if (response_name &&
                            ((user_name->length != response_name->length) ||
                             (strncasecmp(user_name->vp_strvalue, response_name->vp_strvalue, user_name->length) != 0))) {
-                               RWDEBUG("User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2", user_name->vp_strvalue, response_name->vp_strvalue);
+                               RWDEBUG2("User-Name (%s) is not the same as MS-CHAP Name (%s) from EAP-MSCHAPv2",
+                                        user_name->vp_strvalue, response_name->vp_strvalue);
                        }
 
                        /*
@@ -272,16 +272,15 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                         *      from the MS-CHAPv2 peer challenge,
                         *      our challenge, and the user name.
                         */
-                       RDEBUG2("Creating challenge hash with username: %s",
-                               username_string);
+                       RDEBUG2("Creating challenge hash with username: %s", username_string);
                        mschap_challenge_hash(response->vp_octets + 2,
                                       chap_challenge->vp_octets,
                                       username_string, buffer);
                        data = buffer;
                        data_len = 8;
                } else {
-                       RDEBUG2("Invalid MS-CHAP challenge length");
-                       return 0;
+                       REDEBUG("Invalid MS-CHAP challenge length");
+                       return -1;
                }
 
                /*
@@ -292,8 +291,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!response) response = pairfind(request->packet->vps, PW_MSCHAP2_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!response) {
-                       RDEBUG2("No MS-CHAP-Response or MS-CHAP2-Response was found in the request.");
-                       return 0;
+                       REDEBUG("No MS-CHAP-Response or MS-CHAP2-Response was found in the request");
+                       return -1;
                }
 
                /*
@@ -303,8 +302,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                if ((response->da->vendor == VENDORPEC_MICROSOFT) &&
                    (response->da->attr == PW_MSCHAP_RESPONSE) &&
                    ((response->vp_octets[1] & 0x01) == 0)) {
-                       RDEBUG2("No NT-Response in MS-CHAP-Response");
-                       return 0;
+                       REDEBUG("No NT-Response in MS-CHAP-Response");
+                       return -1;
                }
 
                /*
@@ -322,8 +321,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
        } else if (strncasecmp(fmt, "LM-Response", 11) == 0) {
                response = pairfind(request->packet->vps, PW_MSCHAP_RESPONSE, VENDORPEC_MICROSOFT, TAG_ANY);
                if (!response) {
-                       RDEBUG2("No MS-CHAP-Response was found in the request.");
-                       return 0;
+                       REDEBUG("No MS-CHAP-Response was found in the request");
+                       return -1;
                }
 
                /*
@@ -331,8 +330,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                 *      if the second octet says so.
                 */
                if ((response->vp_octets[1] & 0x01) != 0) {
-                       RDEBUG2("No LM-Response in MS-CHAP-Response");
-                       return 0;
+                       REDEBUG("No LM-Response in MS-CHAP-Response");
+                       return -1;
                }
                data = response->vp_octets + 2;
                data_len = 24;
@@ -345,8 +344,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
 
                user_name = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!user_name) {
-                       RDEBUG2("No User-Name was found in the request.");
-                       return 0;
+                       REDEBUG("No User-Name was found in the request");
+                       return -1;
                }
 
                /*
@@ -378,8 +377,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                } else {
                        p = strchr(user_name->vp_strvalue, '\\');
                        if (!p) {
-                               RDEBUG2("No NT-Domain was found in the User-Name.");
-                               return 0;
+                               REDEBUG("No NT-Domain was found in the User-Name");
+                               return -1;
                        }
 
                        /*
@@ -396,12 +395,12 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                 *      Pull the User-Name out of the User-Name...
                 */
        } else if (strncasecmp(fmt, "User-Name", 9) == 0) {
-               char const *p;
+               char const *p, *q;
 
                user_name = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!user_name) {
-                       RDEBUG2("No User-Name was found in the request.");
-                       return 0;
+                       REDEBUG("No User-Name was found in the request");
+                       return -1;
                }
 
                /*
@@ -409,6 +408,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                 *      (a la Kerberos host principal)
                 */
                if (strncmp(user_name->vp_strvalue, "host/", 5) == 0) {
+                       p = user_name->vp_strvalue + 5;
                        /*
                         *      If we're getting a User-Name formatted in this way,
                         *      it's likely due to PEAP.  When authenticating this against
@@ -419,17 +419,17 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                         *      from that point to the first period into a string and appending
                         *      a $ to the end.
                         */
-                       p = strchr(user_name->vp_strvalue, '.');
+                       q = strchr(p, '.');
 
                        /*
                         * use the same hack as above
                         * only if a period was found
                         */
-                       if (p) {
+                       if (q) {
                                snprintf(out, outlen, "%.*s$",
-                                        (int) (p - user_name->vp_strvalue), user_name->vp_strvalue + 5);
+                                        (int) (q - p), p);
                        } else {
-                               snprintf(out, outlen, "%s$", user_name->vp_strvalue + 5);
+                               snprintf(out, outlen, "%s$", p);
                        }
                } else {
                        p = strchr(user_name->vp_strvalue, '\\');
@@ -462,9 +462,9 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                }
 
                if (mschap_ntpwdhash(buffer, buf2) < 0) {
-                       RERROR("Failed generating NT-Password");
+                       REDEBUG("Failed generating NT-Password");
                        *buffer = '\0';
-                       return 0;
+                       return -1;
                }
 
                fr_bin2hex(out, buffer, 16);
@@ -496,9 +496,8 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
                RDEBUG("LM-Hash of %s = %s", buf2, out);
                return 32;
        } else {
-               RDEBUG2("Unknown expansion string \"%s\"",
-                      fmt);
-               return 0;
+               REDEBUG("Unknown expansion string '%s'", fmt);
+               return -1;
        }
 
        if (outlen == 0) return 0; /* nowhere to go, don't do anything */
@@ -507,7 +506,7 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
         *      Didn't set anything: this is bad.
         */
        if (!data) {
-               RDEBUG2("Failed to do anything intelligent");
+               RWDEBUG2("Failed to do anything intelligent");
                return 0;
        }
 
@@ -531,40 +530,27 @@ static ssize_t mschap_xlat(void *instance, REQUEST *request,
 
 
 static const CONF_PARSER passchange_config[] = {
-       { "ntlm_auth",   PW_TYPE_STRING_PTR,
-         offsetof(rlm_mschap_t, ntlm_cpw), NULL,  NULL },
-       { "ntlm_auth_username",   PW_TYPE_STRING_PTR,
-         offsetof(rlm_mschap_t, ntlm_cpw_username), NULL,  NULL },
-       { "ntlm_auth_domain",   PW_TYPE_STRING_PTR,
-         offsetof(rlm_mschap_t, ntlm_cpw_domain), NULL,  NULL },
-       { "local_cpw",   PW_TYPE_STRING_PTR,
-         offsetof(rlm_mschap_t, local_cpw), NULL,  NULL },
+       { "ntlm_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, ntlm_cpw), NULL },
+       { "ntlm_auth_username", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, ntlm_cpw_username), NULL },
+       { "ntlm_auth_domain", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, ntlm_cpw_domain), NULL },
+       { "local_cpw", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, local_cpw), NULL },
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 static const CONF_PARSER module_config[] = {
        /*
         *      Cache the password by default.
         */
-       { "use_mppe",    PW_TYPE_BOOLEAN,
-         offsetof(rlm_mschap_t,use_mppe), NULL, "yes" },
-       { "require_encryption",    PW_TYPE_BOOLEAN,
-         offsetof(rlm_mschap_t,require_encryption), NULL, "no" },
-       { "require_strong",    PW_TYPE_BOOLEAN,
-         offsetof(rlm_mschap_t,require_strong), NULL, "no" },
-       { "with_ntdomain_hack",     PW_TYPE_BOOLEAN,
-         offsetof(rlm_mschap_t,with_ntdomain_hack), NULL, "yes" },
-       { "ntlm_auth",   PW_TYPE_STRING_PTR,
-         offsetof(rlm_mschap_t, ntlm_auth), NULL,  NULL },
-       { "ntlm_auth_timeout",   PW_TYPE_INTEGER,
-         offsetof(rlm_mschap_t, ntlm_auth_timeout), NULL,  NULL },
-       { "passchange", PW_TYPE_SUBSECTION, 0, NULL, (void const *) passchange_config },
-       { "allow_retry",   PW_TYPE_BOOLEAN,
-         offsetof(rlm_mschap_t, allow_retry), NULL,  "yes" },
-       { "retry_msg",   PW_TYPE_STRING_PTR,
-         offsetof(rlm_mschap_t, retry_msg), NULL,  NULL },
+       { "use_mppe", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, use_mppe), "yes" },
+       { "require_encryption", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, require_encryption), "no" },
+       { "require_strong", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, require_strong), "no" },
+       { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, with_ntdomain_hack), "yes" },
+       { "ntlm_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, ntlm_auth), NULL },
+       { "ntlm_auth_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_mschap_t, ntlm_auth_timeout), NULL },
+       { "passchange", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) passchange_config },
+       { "allow_retry", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, allow_retry), "yes" },
+       { "retry_msg", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_mschap_t, retry_msg), NULL },
 #ifdef WITH_OPEN_DIRECTORY
-       { "use_open_directory",    PW_TYPE_BOOLEAN,
-         offsetof(rlm_mschap_t,open_directory), NULL, "yes" },
+       { "use_open_directory", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_mschap_t, open_directory), "yes" },
 #endif
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
@@ -622,34 +608,44 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     attribute to reply packet
  */
 void mschap_add_reply(REQUEST *request, unsigned char ident,
-                     char const* name, char const* value, int len)
+                     char const *name, char const *value, size_t len)
 {
        VALUE_PAIR *vp;
-       uint8_t *p;
 
        vp = pairmake_reply(name, NULL, T_OP_EQ);
        if (!vp) {
-               RDEBUG("Failed to create attribute %s: %s\n", name, fr_strerror());
+               REDEBUG("Failed to create attribute %s: %s", name, fr_strerror());
                return;
        }
+
+       /* Account for the ident byte */
        vp->length = len + 1;
-       vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
+       if (vp->da->type == PW_TYPE_STRING) {
+               char *p;
+
+               vp->vp_strvalue = p = talloc_array(vp, char, vp->length + 1);
+               p[vp->length] = '\0';   /* Always \0 terminate */
+               p[0] = ident;
+               memcpy(p + 1, value, len);
+       } else {
+               uint8_t *p;
 
-       p[0] = ident;
-       memcpy(p + 1, value, len);
+               vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
+               p[0] = ident;
+               memcpy(p + 1, value, len);
+       }
 }
 
 /*
  *     Add MPPE attributes to the reply.
  */
-static void mppe_add_reply(REQUEST *request,
-                          char const* name, uint8_t const * value, int len)
+static void mppe_add_reply(REQUEST *request, char const* name, uint8_t const * value, size_t len)
 {
        VALUE_PAIR *vp;
 
        vp = pairmake_reply(name, NULL, T_OP_EQ);
        if (!vp) {
-              RDEBUG("rlm_mschap: mppe_add_reply failed to create attribute %s: %s\n", name, fr_strerror());
+              REDEBUG("mppe_add_reply failed to create attribute %s: %s", name, fr_strerror());
               return;
        }
 
@@ -672,16 +668,16 @@ static int write_all(int fd, char const *buf, int len) {
  * Perform an MS-CHAP2 password change
  */
 
-static int do_mschap_cpw(rlm_mschap_t *inst,
-                        REQUEST *request,
+static int CC_HINT(nonnull (1, 2, 4, 5)) do_mschap_cpw(rlm_mschap_t *inst,
+                                                      REQUEST *request,
 #ifdef HAVE_OPENSSL_CRYPTO_H
-                        VALUE_PAIR *nt_password,
+                                                      VALUE_PAIR *nt_password,
 #else
-                        UNUSED VALUE_PAIR *nt_password,
+                                                      UNUSED VALUE_PAIR *nt_password,
 #endif
-                        uint8_t *new_nt_password,
-                        uint8_t *old_nt_hash,
-                        int do_ntlm_auth)
+                                                      uint8_t *new_nt_password,
+                                                      uint8_t *old_nt_hash,
+                                                      bool do_ntlm_auth)
 {
        if (inst->ntlm_cpw && do_ntlm_auth) {
                /*
@@ -721,9 +717,9 @@ static int do_mschap_cpw(rlm_mschap_t *inst,
                 * Start up ntlm_auth with a pipe on stdin and stdout
                 */
 
-               pid = radius_start_program(inst->ntlm_cpw, request, 1, &to_child, &from_child, NULL, 0);
+               pid = radius_start_program(inst->ntlm_cpw, request, true, &to_child, &from_child, NULL, false);
                if (pid < 0) {
-                       RDEBUG2("could not exec ntlm_auth cpw command");
+                       REDEBUG("could not exec ntlm_auth cpw command");
                        return -1;
                }
 
@@ -741,11 +737,11 @@ static int do_mschap_cpw(rlm_mschap_t *inst,
                        buf[len] = '\0';
 
                        if (write_all(to_child, buf, len) != len) {
-                               RDEBUG2("failed to write username to child");
+                               REDEBUG("Failed to write username to child");
                                goto ntlm_auth_err;
                        }
                } else {
-                       RDEBUG("No ntlm_auth username set - passchange will definitely fail!");
+                       RWDEBUG2("No ntlm_auth username set, passchange will definitely fail!");
                }
 
                if (inst->ntlm_cpw_domain) {
@@ -758,11 +754,11 @@ static int do_mschap_cpw(rlm_mschap_t *inst,
                        buf[len] = '\0';
 
                        if (write_all(to_child, buf, len) != len) {
-                               RDEBUG2("failed to write domain to child");
+                               REDEBUG("Failed to write domain to child");
                                goto ntlm_auth_err;
                        }
                } else {
-                       RDEBUG("No ntlm_auth domain set - username must be full-username to work");
+                       RWDEBUG2("No ntlm_auth domain set, username must be full-username to work");
                }
 
                /* now the password blobs */
@@ -782,38 +778,38 @@ static int do_mschap_cpw(rlm_mschap_t *inst,
                buf[len+33] = '\0';
                len = strlen(buf);
                if (write_all(to_child, buf, len) != len) {
-                       RDEBUG2("failed to write old hash blob to child");
+                       REDEBUG("Failed to write old hash blob to child");
                        goto ntlm_auth_err;
                }
 
                /*
-                * in current samba versions, failure to supply empty
-                * LM password/hash blobs causes the change to fail
+                *  In current samba versions, failure to supply empty LM password/hash
+                *  blobs causes the change to fail.
                 */
                len = sprintf(buf, "new-lm-password-blob: %01032i\n", 0);
                if (write_all(to_child, buf, len) != len) {
-                       RDEBUG2("failed to write dummy LM password to child");
+                       REDEBUG("Failed to write dummy LM password to child");
                        goto ntlm_auth_err;
                }
                len = sprintf(buf, "old-lm-hash-blob: %032i\n", 0);
                if (write_all(to_child, buf, len) != len) {
-                       RDEBUG2("failed to write dummy LM hash to child");
+                       REDEBUG("Failed to write dummy LM hash to child");
                        goto ntlm_auth_err;
                }
                if (write_all(to_child, ".\n", 2) != 2) {
-                       RDEBUG2("failed to send finish to child");
+                       REDEBUG("Failed to send finish to child");
                        goto ntlm_auth_err;
                }
                close(to_child);
                to_child = -1;
 
                /*
-                * Read from the child
+                *  Read from the child
                 */
                len = radius_readfrom_program(request, from_child, pid, 10, buf, sizeof(buf));
                if (len < 0) {
                        /* radius_readfrom_program will have closed from_child for us */
-                       RDEBUG2("Failure reading from child");
+                       REDEBUG("Failure reading from child");
                        return -1;
                }
                close(from_child);
@@ -824,11 +820,11 @@ static int do_mschap_cpw(rlm_mschap_t *inst,
 
                child_pid = rad_waitpid(pid, &status);
                if (child_pid == 0) {
-                       RDEBUG2("Timeout waiting for child");
+                       REDEBUG("Timeout waiting for child");
                        return -1;
                }
                if (child_pid != pid) {
-                       RDEBUG("Abnormal exit status: %s", strerror(errno));
+                       REDEBUG("Abnormal exit status: %s", fr_syserror(errno));
                        return -1;
                }
 
@@ -843,7 +839,7 @@ static int do_mschap_cpw(rlm_mschap_t *inst,
                } else {
                        emsg = "could not find error";
                }
-               RDEBUG2("ntlm auth password change failed: %s", emsg);
+               REDEBUG("ntlm auth password change failed: %s", emsg);
 
 ntlm_auth_err:
                /* safe because these either need closing or are == -1 */
@@ -855,19 +851,18 @@ ntlm_auth_err:
        } else if (inst->local_cpw) {
 #ifdef HAVE_OPENSSL_CRYPTO_H
                /*
-                * decrypt the new password blob, add it as a temporary request
-                * variable, xlat the local_cpw string, then remove it
+                *  Decrypt the new password blob, add it as a temporary request
+                *  variable, xlat the local_cpw string, then remove it
                 *
-                * this allows is to write e..g
+                *  this allows is to write e..g
                 *
-                * %{sql:insert into ...}
+                *  %{sql:insert into ...}
                 *
-                * ...or...
+                *  ...or...
                 *
-                * %{exec:/path/to %{mschap:User-Name} %{MS-CHAP-New-Password}}"
+                *  %{exec:/path/to %{mschap:User-Name} %{MS-CHAP-New-Password}}"
                 *
                 */
-
                VALUE_PAIR *new_pass, *new_hash;
                uint8_t *p, *q;
                char *x;
@@ -886,40 +881,40 @@ ntlm_auth_err:
                }
 
                /*
-                * decrypt the blob
+                *  Decrypt the blob
                 */
                RC4_set_key(&key, nt_password->length, nt_password->vp_octets);
                RC4(&key, 516, new_nt_password, nt_pass_decrypted);
 
                /*
-                * pwblock is
-                * 512-N bytes random pad
-                * N bytes password as utf-16-le
-                * 4 bytes - N as big-endian int
+                *  pwblock is
+                *  512-N bytes random pad
+                *  N bytes password as utf-16-le
+                *  4 bytes - N as big-endian int
                 */
-
                passlen = nt_pass_decrypted[512];
                passlen += nt_pass_decrypted[513] << 8;
                if ((nt_pass_decrypted[514] != 0) ||
                    (nt_pass_decrypted[515] != 0)) {
-                       RDEBUG2("Decrypted new password blob claims length > 65536 - probably an invalid NT-Password");
+                       REDEBUG("Decrypted new password blob claims length > 65536, "
+                               "probably an invalid NT-Password");
                        return -1;
                }
 
                /*
-                * sanity check - passlen positive and <= 512
-                * if not, crypto has probably gone wrong
+                *  Sanity check - passlen positive and <= 512 if not, crypto has probably gone wrong
                 */
                if (passlen > 512) {
-                       RDEBUG2("Decrypted new password blob claims length %u > 512 - probably an invalid NT-Password", passlen);
+                       REDEBUG("Decrypted new password blob claims length %zu > 512, "
+                               "probably an invalid NT-Password", passlen);
                        return -1;
                }
 
                p = nt_pass_decrypted + 512 - passlen;
 
                /*
-                * the new NT hash - this should be preferred over the
-                * cleartext password as it avoids unicode hassles
+                *  The new NT hash - this should be preferred over the
+                *  cleartext password as it avoids unicode hassles.
                 */
                new_hash = pairmake_packet("MS-CHAP-New-NT-Password", NULL,
                                           T_OP_EQ);
@@ -928,35 +923,32 @@ ntlm_auth_err:
                fr_md4_calc(q, p, passlen);
 
                /*
-                * check that nt_password encrypted with new_hash
-                * matches the old_hash value from the client
+                *  Check that nt_password encrypted with new_hash
+                *  matches the old_hash value from the client.
                 */
                smbhash(old_nt_hash_expected, nt_password->vp_octets, q);
                smbhash(old_nt_hash_expected+8, nt_password->vp_octets+8, q + 7);
                if (memcmp(old_nt_hash_expected, old_nt_hash, 16)!=0) {
-                       RDEBUG2("old NT hash value from client does not match our value");
+                       REDEBUG("Old NT hash value from client does not match our value");
                        return -1;
                }
 
                /*
-                * the new cleartext password, which is utf-16
-                * do some unpleasant vileness to turn it into
-                * utf8 without pulling in libraries like iconv
+                *  The new cleartext password, which is utf-16 do some unpleasant vileness
+                *  to turn it into utf8 without pulling in libraries like iconv.
                 */
-               new_pass = pairmake_packet("MS-CHAP-New-Cleartext-Password", NULL,
-                                          T_OP_EQ);
+               new_pass = pairmake_packet("MS-CHAP-New-Cleartext-Password", NULL, T_OP_EQ);
                new_pass->length = 0;
                new_pass->vp_strvalue = x = talloc_array(new_pass, char, 254);
                i = 0;
                while (i<passlen) {
                        /*
-                        * The client-supplied password is utf-16.
-                        * We really must perform a proper conversion
-                        * to utf8 here, and the same in the other direction
-                        * when we calculate NT-Password below, else non-ascii
-                        * characters will fail - I know from experience that
-                        * UK pound and Euro symbols are common in users
-                        * passwords (money obsessed!)
+                        *  The client-supplied password is utf-16.
+                        *  We really must perform a proper conversion to utf8 here,
+                        *  and the same in the other direction when we calculate
+                        *  NT-Password below, else non-ascii characters will fail -
+                        *  I know from experience that UK pound and Euro symbols
+                        *  are common in users passwords (money obsessed!)
                         */
                        int c;
 
@@ -964,20 +956,21 @@ ntlm_auth_err:
                        c += p[i++] << 8;
 
                        /*
-                        * gah. nasty. maybe we should just pull in iconv?
+                        *  Gah. nasty. maybe we should just pull in iconv?
                         */
-
                        if (c < 0x7f) {
                                /* ascii char */
                                if (new_pass->length >= 253) {
-                                       RDEBUG("Ran out of room turning new password into utf8 at %d - cleartext will be truncated!", i);
+                                       RWDEBUG("Ran out of room turning new password into utf8 at %zu, "
+                                               "cleartext will be truncated!", i);
                                        break;
                                }
                                x[new_pass->length++] = c;
                        } else if (c < 0x7ff) {
                                /* 2-byte */
                                if (new_pass->length >= 252) {
-                                       RDEBUG("Ran out of room turning new password into utf8 at %d - cleartext will be truncated!", i);
+                                       RWDEBUG("Ran out of room turning new password into utf8 at %zu, "
+                                               "cleartext will be truncated!", i);
                                        break;
                                }
                                x[new_pass->length++] = 0xc0 + (c >> 6);
@@ -985,7 +978,8 @@ ntlm_auth_err:
                        } else {
                                /* 3-byte */
                                if (new_pass->length >= 251) {
-                                       RDEBUG("Ran out of room turning new password into utf8 at %d - cleartext will be truncated!", i);
+                                       RWDEBUG("Ran out of room turning new password into utf8 at %zu, "
+                                               "cleartext will be truncated!", i);
                                        break;
                                }
                                x[new_pass->length++] = 0xe0 + (c >> 12);
@@ -994,36 +988,34 @@ ntlm_auth_err:
                        }
                }
 
-               /*
-                * perform the xlat
-                */
+               /* Perform the xlat */
                result_len = radius_xlat(result, sizeof(result), request, inst->local_cpw, NULL, NULL);
                if (result_len < 0){
                        return -1;
                } else if (result_len == 0) {
-                       RDEBUG("Local MS-CHAPv2 password change - xlat didn't give any result, assuming failure");
+                       REDEBUG("Local MS-CHAPv2 password change - xlat didn't give any result, assuming failure");
                        return -1;
                }
 
                RDEBUG("MS-CHAPv2 password change succeeded: %s", result);
 
                /*
-                * update the NT-Password attribute with the new hash
-                * this lets us fall through to the authentication
-                * code using the new hash, not the old one
+                *  Update the NT-Password attribute with the new hash this lets us
+                *  fall through to the authentication code using the new hash,
+                *  not the old one.
                 */
                pairmemcpy(nt_password, new_hash->vp_octets, new_hash->length);
 
                /*
-                * rock on! password change succeeded
+                *  Rock on! password change succeeded.
                 */
                return 0;
 #else
-               RDEBUG("Local MS-CHAPv2 password changes require OpenSSL support");
+               REDEBUG("Local MS-CHAPv2 password changes require OpenSSL support");
                return -1;
 #endif
        } else {
-               RDEBUG("MS-CHAPv2 password change not configured");
+               REDEBUG("MS-CHAPv2 password change not configured");
        }
 
        return -1;
@@ -1036,14 +1028,11 @@ ntlm_auth_err:
  *     authentication is in one place, and we can perhaps later replace
  *     it with code to call winbindd, or something similar.
  */
-static int do_mschap(rlm_mschap_t *inst,
-                    REQUEST *request, VALUE_PAIR *password,
-                    uint8_t const *challenge, uint8_t const *response,
-                    uint8_t *nthashhash, int do_ntlm_auth)
+static int CC_HINT(nonnull (1, 2, 4, 5 ,6)) do_mschap(rlm_mschap_t *inst, REQUEST *request, VALUE_PAIR *password,
+                                                     uint8_t const *challenge, uint8_t const *response,
+                                                     uint8_t *nthashhash, bool do_ntlm_auth)
 {
-       uint8_t         calculated[24];
-
-       rad_assert(request != NULL);
+       uint8_t calculated[24];
 
        /*
         *      Do normal authentication.
@@ -1053,7 +1042,7 @@ static int do_mschap(rlm_mschap_t *inst,
                 *      No password: can't do authentication.
                 */
                if (!password) {
-                       RDEBUG2("FAILED: No NT/LM-Password.  Cannot perform authentication.");
+                       REDEBUG("FAILED: No NT/LM-Password.  Cannot perform authentication");
                        return -1;
                }
 
@@ -1089,21 +1078,19 @@ static int do_mschap(rlm_mschap_t *inst,
                        char *p;
 
                        /*
-                        * look for "Password expired", or "Must
-                        * change password".
+                        *      look for "Password expired", or "Must change password".
                         */
-                       if (strstr(buffer, "Password expired") ||
-                           strstr(buffer, "Must change password")) {
-                               RDEBUG2("ntlm_auth says %s", buffer);
+                       if (strcasestr(buffer, "Password expired") ||
+                           strcasestr(buffer, "Must change password")) {
+                               REDEBUG2("%s", buffer);
                                return -648;
                        }
 
-                       RDEBUG2("External script failed.");
+                       RDEBUG2("External script failed");
                        p = strchr(buffer, '\n');
                        if (p) *p = '\0';
 
-                       REDEBUG("External script says: %s",
-                                              buffer);
+                       REDEBUG("External script says: %s", buffer);
                        return -1;
                }
 
@@ -1114,7 +1101,7 @@ static int do_mschap(rlm_mschap_t *inst,
                 *      NT_KEY: 000102030405060708090a0b0c0d0e0f
                 */
                if (memcmp(buffer, "NT_KEY: ", 8) != 0) {
-                       RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY");
+                       REDEBUG("Invalid output from ntlm_auth: expecting NT_KEY");
                        return -1;
                }
 
@@ -1123,7 +1110,7 @@ static int do_mschap(rlm_mschap_t *inst,
                 *      with an LF at the end.
                 */
                if (strlen(buffer + 8) < 32) {
-                       RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
+                       REDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length");
                        return -1;
                }
 
@@ -1131,7 +1118,7 @@ static int do_mschap(rlm_mschap_t *inst,
                 *      Update the NT hash hash, from the NT key.
                 */
                if (fr_hex2bin(nthashhash, buffer + 8, 16) != 16) {
-                       RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values");
+                       REDEBUG("Invalid output from ntlm_auth: NT_KEY has non-hex values");
                        return -1;
                }
        }
@@ -1263,7 +1250,7 @@ static void mppe_chap2_gen_keys128(uint8_t const *nt_hashhash,uint8_t const *res
  *     it later. Add Auth-Type attribute if present in module
  *     configuration (usually Auth-Type must be "MS-CHAP")
  */
-static rlm_rcode_t mod_authorize(void * instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void * instance, REQUEST *request)
 {
        rlm_mschap_t *inst = instance;
        VALUE_PAIR *challenge = NULL;
@@ -1289,7 +1276,7 @@ static rlm_rcode_t mod_authorize(void * instance, REQUEST *request)
 
        /*
         *      Set Auth-Type to MS-CHAP.  The authentication code
-        *      will take care of turning clear-text passwords into
+        *      will take care of turning cleartext passwords into
         *      NT/LM passwords.
         */
        if (!pairmake_config("Auth-Type", inst->auth_type, T_OP_EQ)) {
@@ -1316,7 +1303,7 @@ static rlm_rcode_t mod_authorize(void * instance, REQUEST *request)
  *     If MS-CHAP2 succeeds we MUST return
  *     PW_MSCHAP2_SUCCESS
  */
-static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void * instance, REQUEST *request)
 {
 #define inst ((rlm_mschap_t *)instance)
        VALUE_PAIR *challenge = NULL;
@@ -1330,7 +1317,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
        uint8_t *p;
        char const *username_string;
        int chap = 0;
-       int             do_ntlm_auth;
+       bool do_ntlm_auth;
 
        /*
         *      If we have ntlm_auth configured, use it unless told
@@ -1344,7 +1331,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
         */
        if (do_ntlm_auth) {
                VALUE_PAIR *vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH, 0, TAG_ANY);
-               if (vp) do_ntlm_auth = vp->vp_integer;
+               if (vp) do_ntlm_auth = (vp->vp_integer > 0);
        }
 
        /*
@@ -1355,8 +1342,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
        if (!smb_ctrl) {
                password = pairfind(request->config_items, PW_SMB_ACCOUNT_CTRL_TEXT, 0, TAG_ANY);
                if (password) {
-                       smb_ctrl = pairmake_config("SMB-Account-CTRL", "0",
-                                                  T_OP_SET);
+                       smb_ctrl = pairmake_config("SMB-Account-CTRL", "0", T_OP_SET);
                        if (smb_ctrl) {
                                smb_ctrl->vp_integer = pdb_decode_acct_ctrl(password->vp_strvalue);
                        }
@@ -1372,7 +1358,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *      Password is not required.
                 */
                if ((smb_ctrl->vp_integer & ACB_PWNOTREQ) != 0) {
-                       RDEBUG2("SMB-Account-Ctrl says no password is required.");
+                       RDEBUG2("SMB-Account-Ctrl says no password is required");
                        return RLM_MODULE_OK;
                }
        }
@@ -1390,7 +1376,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                if (lm_password->length == 16) {
                        RDEBUG2("Found LM-Password");
                } else {
-                       RERROR("LM-Password has not been normalized by the \"pap\" module.  Authentication will fail.");
+                       RWDEBUG("LM-Password has not been normalized by the 'pap' module.  Authentication will fail");
                        lm_password = NULL;
                }
        /*
@@ -1407,7 +1393,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        smbdes_lmpwdhash(password->vp_strvalue, p);
                }
        } else if (!do_ntlm_auth) {
-               RDEBUG2("No Cleartext-Password configured.  Cannot create LM-Password");
+               RWDEBUG2("No Cleartext-Password configured.  Cannot create LM-Password");
        }
 
        /*
@@ -1418,7 +1404,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                if (nt_password->length == 16) {
                        RDEBUG2("Found NT-Password");
                } else {
-                       RERROR("NT-Password has not been normalized by the \"pap\" module.  Authentication will fail.");
+                       RWDEBUG("NT-Password has not been normalized by the 'pap' module.  Authentication will fail");
                        nt_password = NULL;
                }
        /*
@@ -1439,7 +1425,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        return RLM_MODULE_FAIL;
                }
        } else if (!do_ntlm_auth) {
-               RDEBUG2("No Cleartext-Password configured.  Cannot create NT-Password");
+               RWDEBUG2("No Cleartext-Password configured.  Cannot create NT-Password");
        }
 
        cpw = pairfind(request->packet->vps, PW_MSCHAP2_CPW, VENDORPEC_MICROSOFT, TAG_ANY);
@@ -1457,28 +1443,28 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                RDEBUG("MS-CHAPv2 password change request received");
 
                if (cpw->length != 68) {
-                       RDEBUG2("MS-CHAP2-CPW has the wrong format - length %d!=68", cpw->length);
+                       REDEBUG("MS-CHAP2-CPW has the wrong format: length %zu != 68", cpw->length);
                        return RLM_MODULE_INVALID;
                } else if (cpw->vp_octets[0]!=7) {
-                       RDEBUG2("MS-CHAP2-CPW has the wrong format - code %d!=7", cpw->vp_octets[0]);
+                       REDEBUG("MS-CHAP2-CPW has the wrong format: code %d != 7", cpw->vp_octets[0]);
                        return RLM_MODULE_INVALID;
                }
 
                /*
-                * look for the new (encrypted) password
-                * bah stupid composite attributes
-                * we're expecting 3 attributes with the leading bytes
-                * 06:<mschapid>:00:01:<1st chunk>
-                * 06:<mschapid>:00:02:<2nd chunk>
-                * 06:<mschapid>:00:03:<3rd chunk>
+                *  look for the new (encrypted) password
+                *  bah stupid composite attributes
+                *  we're expecting 3 attributes with the leading bytes
+                *  06:<mschapid>:00:01:<1st chunk>
+                *  06:<mschapid>:00:02:<2nd chunk>
+                *  06:<mschapid>:00:03:<3rd chunk>
                 */
                for (seq = 1; seq < 4; seq++) {
                        vp_cursor_t cursor;
                        int found = 0;
 
-                       for (nt_enc = paircursor(&cursor, &request->packet->vps);
+                       for (nt_enc = fr_cursor_init(&cursor, &request->packet->vps);
                             nt_enc;
-                            nt_enc = pairnext(&cursor)) {
+                            nt_enc = fr_cursor_next(&cursor)) {
                                if (nt_enc->da->vendor != VENDORPEC_MICROSOFT)
                                        continue;
 
@@ -1486,7 +1472,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                                        continue;
 
                                if (nt_enc->vp_octets[0] != 6) {
-                                       RDEBUG2("MS-CHAP-NT-Enc-PW with invalid format");
+                                       REDEBUG("MS-CHAP-NT-Enc-PW with invalid format");
                                        return RLM_MODULE_INVALID;
                                }
                                if (nt_enc->vp_octets[2]==0 && nt_enc->vp_octets[3]==seq) {
@@ -1496,7 +1482,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        }
 
                        if (!found) {
-                               RDEBUG2("Could not find MS-CHAP-NT-Enc-PW w/ sequence number %d", seq);
+                               REDEBUG("Could not find MS-CHAP-NT-Enc-PW w/ sequence number %d", seq);
                                return RLM_MODULE_INVALID;
                        }
 
@@ -1507,7 +1493,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        new_nt_enc_len += nt_enc->length - 4;
                }
                if (new_nt_enc_len != 516) {
-                       RDEBUG2("Unpacked MS-CHAP-NT-Enc-PW length != 516");
+                       REDEBUG("Unpacked MS-CHAP-NT-Enc-PW length != 516");
                        return RLM_MODULE_INVALID;
                }
 
@@ -1526,15 +1512,16 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 * 2 octets  - flags (ignored)
                 */
 
-               memcpy(old_nt_encrypted, cpw->vp_octets+2, sizeof(old_nt_encrypted));
+               memcpy(old_nt_encrypted, cpw->vp_octets + 2, sizeof(old_nt_encrypted));
 
                RDEBUG2("Password change payload valid");
 
                /* perform the actual password change */
+               rad_assert(nt_password);
                if (do_mschap_cpw(inst, request, nt_password, new_nt_encrypted, old_nt_encrypted, do_ntlm_auth) < 0) {
                        char buffer[128];
 
-                       RDEBUG("Password change failed");
+                       REDEBUG("Password change failed");
 
                        snprintf(buffer, sizeof(buffer), "E=709 R=0 M=Password change failed");
                        mschap_add_reply(request, cpw->vp_octets[1], "MS-CHAP-Error", buffer, strlen(buffer));
@@ -1544,22 +1531,22 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                RDEBUG("Password change successful");
 
                /*
-                * Clear any expiry bit so the user can now login;
-                * obviously the password change action will need
-                * to have cleared this bit in the config/SQL/wherever
+                *  Clear any expiry bit so the user can now login;
+                *  obviously the password change action will need
+                *  to have cleared this bit in the config/SQL/wherever
                 */
                if (smb_ctrl && smb_ctrl->vp_integer & ACB_PW_EXPIRED) {
-                       RDEBUG("clearing expiry bit in SMB-Acct-Ctrl to allow authentication");
+                       RDEBUG("Clearing expiry bit in SMB-Acct-Ctrl to allow authentication");
                        smb_ctrl->vp_integer &= ~ACB_PW_EXPIRED;
                }
 
                /*
-                * extract the challenge & response from the end of the password
-                * change, add them into the request and then continue with
-                * the authentication
+                *  Extract the challenge & response from the end of the password
+                *  change, add them into the request and then continue with
+                *  the authentication
                 */
 
-               response = radius_paircreate(request, &request->packet->vps,
+               response = radius_paircreate(request->packet, &request->packet->vps,
                                             PW_MSCHAP2_RESPONSE,
                                             VENDORPEC_MICROSOFT);
                response->length = 50;
@@ -1593,7 +1580,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *      MS-CHAPv1 challenges are 8 octets.
                 */
                if (challenge->length < 8) {
-                       RAUTH("MS-CHAP-Challenge has the wrong format.");
+                       REDEBUG("MS-CHAP-Challenge has the wrong format");
                        return RLM_MODULE_INVALID;
                }
 
@@ -1601,7 +1588,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *      Responses are 50 octets.
                 */
                if (response->length < 50) {
-                       RAUTH("MS-CHAP-Response has the wrong format.");
+                       REDEBUG("MS-CHAP-Response has the wrong format");
                        return RLM_MODULE_INVALID;
                }
 
@@ -1624,7 +1611,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 */
                if (do_mschap(inst, request, password, challenge->vp_octets, response->vp_octets + offset, nthashhash,
                              do_ntlm_auth) < 0) {
-                       RDEBUG2("MS-CHAP-Response is incorrect.");
+                       REDEBUG("MS-CHAP-Response is incorrect");
                        goto do_error;
                }
 
@@ -1639,7 +1626,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *      MS-CHAPv2 challenges are 16 octets.
                 */
                if (challenge->length < 16) {
-                       RAUTH("MS-CHAP-Challenge has the wrong format.");
+                       REDEBUG("MS-CHAP-Challenge has the wrong format");
                        return RLM_MODULE_INVALID;
                }
 
@@ -1647,7 +1634,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *      Responses are 50 octets.
                 */
                if (response->length < 50) {
-                       RAUTH("MS-CHAP-Response has the wrong format.");
+                       REDEBUG("MS-CHAP-Response has the wrong format");
                        return RLM_MODULE_INVALID;
                }
 
@@ -1656,7 +1643,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 */
                username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
                if (!username) {
-                       RAUTH("We require a User-Name for MS-CHAPv2");
+                       REDEBUG("We require a User-Name for MS-CHAPv2");
                        return RLM_MODULE_INVALID;
                }
 
@@ -1683,7 +1670,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        if (inst->with_ntdomain_hack) {
                                username_string++;
                        } else {
-                               RDEBUG2("NT Domain delimeter found, should we have enabled with_ntdomain_hack?");
+                               RWDEBUG2("NT Domain delimeter found, should with_ntdomain_hack of been enabled?");
                                username_string = name_attr->vp_strvalue;
                        }
                } else {
@@ -1706,7 +1693,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *  Otherwise OD will determine auth success/fail.
                 */
                if (!nt_password && inst->open_directory) {
-                       RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication.");
+                       RDEBUG2("No NT-Password configured. Trying OpenDirectory Authentication");
                        int odStatus = od_mschap_auth(request, challenge, username);
                        if (odStatus != RLM_MODULE_NOOP) {
                                return odStatus;
@@ -1726,7 +1713,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                                      username_string,          /* user name */
                                      mschapv1_challenge);      /* resulting challenge */
 
-               RDEBUG2("Client is using MS-CHAPv2 for %s, we need NT-Password", username_string);
+               RDEBUG2("Client is using MS-CHAPv2");
 
                mschap_result = do_mschap(inst, request, nt_password, mschapv1_challenge,
                                          response->vp_octets + 26, nthashhash, do_ntlm_auth);
@@ -1737,7 +1724,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        int i;
                        char buffer[128];
 
-                       RDEBUG2("FAILED: MS-CHAP2-Response is incorrect");
+                       REDEBUG("MS-CHAP2-Response is incorrect");
 
                do_error:
                        snprintf(buffer, sizeof(buffer), "E=691 R=%d", inst->allow_retry);
@@ -1803,7 +1790,8 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 */
                if (((smb_ctrl->vp_integer & ACB_DISABLED) != 0) ||
                    ((smb_ctrl->vp_integer & (ACB_NORMAL|ACB_WSTRUST)) == 0)) {
-                       RDEBUG2("SMB-Account-Ctrl says that the account is disabled, or is not a normal or workstation trust account");
+                       REDEBUG("SMB-Account-Ctrl says that the account is disabled, or is not a normal "
+                               "or workstation trust account");
                        mschap_add_reply(request, *response->vp_octets, "MS-CHAP-Error", "E=691 R=1", 9);
                        return RLM_MODULE_NOTFOUND;
                }
@@ -1812,7 +1800,7 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                 *      User is locked out.
                 */
                if ((smb_ctrl->vp_integer & ACB_AUTOLOCK) != 0) {
-                       RDEBUG2("SMB-Account-Ctrl says that the account is locked out");
+                       REDEBUG("SMB-Account-Ctrl says that the account is locked out");
                        mschap_add_reply(request, *response->vp_octets, "MS-CHAP-Error", "E=647 R=0", 9);
                        return RLM_MODULE_USERLOCK;
                }
@@ -1845,23 +1833,17 @@ static rlm_rcode_t mod_authenticate(void * instance, REQUEST *request)
                        memcpy(mppe_sendkey + 8, nthashhash, 16);
                        mppe_add_reply(request, "MS-CHAP-MPPE-Keys", mppe_sendkey, 32);
                } else if (chap == 2) {
-                       RDEBUG2("adding MS-CHAPv2 MPPE keys");
+                       RDEBUG2("Adding MS-CHAPv2 MPPE keys");
                        mppe_chap2_gen_keys128(nthashhash, response->vp_octets + 26, mppe_sendkey, mppe_recvkey);
 
-                       mppe_add_reply(request,
-                                      "MS-MPPE-Recv-Key",
-                                      mppe_recvkey, 16);
-                       mppe_add_reply(request,
-                                      "MS-MPPE-Send-Key",
-                                      mppe_sendkey, 16);
+                       mppe_add_reply(request, "MS-MPPE-Recv-Key", mppe_recvkey, 16);
+                       mppe_add_reply(request, "MS-MPPE-Send-Key", mppe_sendkey, 16);
 
                }
                pairmake_reply("MS-MPPE-Encryption-Policy",
-                              (inst->require_encryption)? "0x00000002":"0x00000001",
-                              T_OP_EQ);
+                              (inst->require_encryption) ? "0x00000002":"0x00000001", T_OP_EQ);
                pairmake_reply("MS-MPPE-Encryption-Types",
-                              (inst->require_strong)? "0x00000004":"0x00000006",
-                               T_OP_EQ);
+                              (inst->require_strong) ? "0x00000004":"0x00000006", T_OP_EQ);
        } /* else we weren't asked to use MPPE */
 
        return RLM_MODULE_OK;
index a29b385..802dee0 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,10 +1254,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1915,7 +1915,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2137,7 +2137,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2826,22 +2826,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=membership.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3022,11 +3022,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3532,11 +3532,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 94037ae..1a1e4b8 100644 (file)
@@ -63,7 +63,7 @@ extern int mbr_check_membership_refresh(uuid_t const user, uuid_t group, int *is
  *  Returns: ds err
  */
 
-static long od_check_passwd(char const *uname, char const *password)
+static long od_check_passwd(REQUEST *request, char const *uname, char const *password)
 {
        long                    result          = eDSAuthFailed;
        tDirReference           dsRef           = 0;
@@ -147,7 +147,7 @@ static long od_check_passwd(char const *uname, char const *password)
                                        status = dsGetAttributeValue( nodeRef, tDataBuff, 1, valueRef, &pValueEntry );
                                        if ( status == eDSNoErr && pValueEntry != NULL )
                                        {
-                                               pUserLocation = (char *) calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char) );
+                                               pUserLocation = talloc_zero_array(request, char, pValueEntry->fAttributeValueData.fBufferLength + 1);
                                                memcpy( pUserLocation, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
                                        }
                                }
@@ -157,7 +157,7 @@ static long od_check_passwd(char const *uname, char const *password)
                                        status = dsGetAttributeValue( nodeRef, tDataBuff, 1, valueRef, &pValueEntry );
                                        if ( status == eDSNoErr && pValueEntry != NULL )
                                        {
-                                               pUserName = (char *) calloc( pValueEntry->fAttributeValueData.fBufferLength + 1, sizeof(char) );
+                                               pUserName = talloc_zero_array(request, char, pValueEntry->fAttributeValueData.fBufferLength + 1);
                                                memcpy( pUserName, pValueEntry->fAttributeValueData.fBufferData, pValueEntry->fAttributeValueData.fBufferLength );
                                        }
                                }
@@ -198,6 +198,11 @@ static long od_check_passwd(char const *uname, char const *password)
                pAuthType = dsDataNodeAllocateString( dsRef, kDSStdAuthNodeNativeClearTextOK );
                uiCurr = 0;
 
+               if (!pUserName) {
+                       RDEBUG("Failed to find user name");
+                       break;
+               }
+
                /* User name */
                uiLen = (uint32_t)strlen( pUserName );
                memcpy( &(tDataBuff->fBufferData[ uiCurr ]), &uiLen, sizeof(uiLen) );
@@ -237,9 +242,13 @@ static long od_check_passwd(char const *uname, char const *password)
                pStepBuff = NULL;
        }
        if (pUserLocation != NULL) {
-               free(pUserLocation);
+               talloc_free(pUserLocation);
                pUserLocation = NULL;
        }
+       if (pUserName != NULL) {
+               talloc_free(pUserName);
+               pUserName = NULL;
+       }
        if (pRecName != NULL) {
                dsDataListDeallocate( dsRef, pRecName );
                free( pRecName );
@@ -272,7 +281,7 @@ static long od_check_passwd(char const *uname, char const *password)
  *     Check the users password against the standard UNIX
  *     password table.
  */
-static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request)
 {
        int             ret;
        long odResult = eDSAuthFailed;
@@ -295,7 +304,7 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
                return RLM_MODULE_INVALID;
        }
 
-       odResult = od_check_passwd(request->username->vp_strvalue,
+       odResult = od_check_passwd(request, request->username->vp_strvalue,
                                   request->password->vp_strvalue);
        switch(odResult) {
                case eDSNoErr:
@@ -321,7 +330,7 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
 
        if (ret != RLM_MODULE_OK) {
                RDEBUG("[%s]: Invalid password", request->username->vp_strvalue);
-               return ret;
+               return ret;
        }
 
        return RLM_MODULE_OK;
@@ -331,7 +340,7 @@ static rlm_rcode_t mod_authenticate(UNUSED void *instance, REQUEST *request)
 /*
  *     member of the radius group?
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
 {
        struct passwd *userdata = NULL;
        struct group *groupdata = NULL;
@@ -343,8 +352,8 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
        int err;
        char host_ipaddr[128] = {0};
 
-       if (!request || !request->username) {
-               RDEBUG("OpenDirectory requires a User-Name attribute.");
+       if (!request->username) {
+               RDEBUG("OpenDirectory requires a User-Name attribute");
                return RLM_MODULE_NOOP;
        }
 
@@ -403,7 +412,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
        }
 
        if (uuid_is_null(guid_sacl) && uuid_is_null(guid_nasgroup)) {
-               RDEBUG("no access control groups, all users allowed.");
+               RDEBUG("no access control groups, all users allowed");
                if (pairfind(request->config_items, PW_AUTH_TYPE, 0, TAG_ANY) == NULL) {
                        pairmake_config("Auth-Type", kAuthType, T_OP_EQ);
                        RDEBUG("Setting Auth-Type = %s", kAuthType);
index f0fbb3c..0438a21 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1252,9 +1252,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1305,10 +1305,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2130,7 +2130,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2352,7 +2352,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3433,11 +3433,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3943,11 +3943,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 452af33..ccb302f 100644 (file)
@@ -39,25 +39,25 @@ RCSIDH(extern_h, "$Id$")
 #define OTP_CHALLENGE_PROMPT "Challenge: %{reply:OTP-Challenge}\n Response: "
 
 typedef struct rlm_otp_t {
-       char const *name;       //!< Instance name for mod_authorize().
-       char *otpd_rp;          //!< Otpd rendezvous point.
-       char *chal_prompt;      //!< Text to present challenge to user
-                               //!< must have %s.
+       char const *name;               //!< Instance name for mod_authorize().
+       char const *otpd_rp;            //!< Otpd rendezvous point.
+       char const *chal_prompt;        //!< Text to present challenge to user
+                                       //!< must have %s.
 
-       uint8_t hmac_key[16];   //!< because it doesn't track State
+       uint8_t hmac_key[16];           //!< because it doesn't track State
 
-       int challenge_len;      //!< Challenge length, min 5 digits.
-       int challenge_delay;    //!< Max delay time for response, in seconds.
-       int allow_sync;         //!< Useful to override pwdfile
-                               //!< card_type settings.
-       int allow_async;        //!< C/R mode allowed?
+       uint32_t challenge_len;         //!< Challenge length, min 5 digits.
+       uint32_t challenge_delay;       //!< Max delay time for response, in seconds.
+       bool allow_sync;                //!< Useful to override pwdfile
+                                       //!< card_type settings.
+       bool allow_async;               //!< C/R mode allowed?
 
-       int mschapv2_mppe_policy;       //!< Whether or not do to mppe for
+       uint32_t mschapv2_mppe_policy;  //!< Whether or not do to mppe for
                                        //!< mschapv2.
-       int mschapv2_mppe_types;        //!< Key type/length for mschapv2/mppe.
-       int mschap_mppe_policy;         //!< Whether or not do to mppe for
+       uint32_t mschapv2_mppe_types;   //!< Key type/length for mschapv2/mppe.
+       uint32_t mschap_mppe_policy;    //!< Whether or not do to mppe for
                                        //!< mschap .
-       int mschap_mppe_types;          //!< key type/length for mschap/mppe.
+       uint32_t mschap_mppe_types;     //!< key type/length for mschap/mppe.
 } rlm_otp_t;
 
 /* otp_mppe.c */
index 65f2a8f..20b7c50 100644 (file)
@@ -301,7 +301,7 @@ otp_read(otp_fd_t *fdp, char *buf, size_t len)
                                continue;
                        } else {
                                ERROR("rlm_otp: %s: read from otpd: %s",
-                                      __func__, strerror(errno));
+                                      __func__, fr_syserror(errno));
                                otp_putfd(fdp, 1);
 
                                return -1;
@@ -323,21 +323,21 @@ otp_read(otp_fd_t *fdp, char *buf, size_t len)
 
 /*
  *     Full write with logging, and close on failure.
- *     Returns 0 on success, errno on failure.
+ *     Returns number of bytes written on success, errno on failure.
  */
 static int otp_write(otp_fd_t *fdp, char const *buf, size_t len)
 {
        size_t nleft = len;
        ssize_t nwrote;
 
-       while (nleft) {
+       while (nleft) {
                nwrote = write(fdp->fd, &buf[len - nleft], nleft);
                if (nwrote == -1) {
                        if (errno == EINTR) {
                                continue;
                        } else {
                                ERROR("rlm_otp: %s: write to otpd: %s",
-                                      __func__, strerror(errno));
+                                      __func__, fr_syserror(errno));
 
                                otp_putfd(fdp, 1);
                                return errno;
@@ -348,7 +348,7 @@ static int otp_write(otp_fd_t *fdp, char const *buf, size_t len)
                nleft -= nwrote;
        }
 
-       return 0;
+       return len - nleft;
 }
 
 /* connect to otpd and return fd */
@@ -373,7 +373,7 @@ static int otp_connect(char const *path)
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd == -1) {
                ERROR("rlm_otp: %s: socket: %s", __func__,
-                      strerror(errno));
+                      fr_syserror(errno));
 
                return -1;
        }
@@ -381,7 +381,7 @@ static int otp_connect(char const *path)
              sizeof(sa.sun_family) + sp_len) == -1) {
 
                ERROR("rlm_otp: %s: connect(%s): %s",
-                      __func__, path, strerror(errno));
+                      __func__, path, fr_syserror(errno));
 
                (void) close(fd);
 
index c39dee3..05cd652 100644 (file)
@@ -100,14 +100,14 @@ otp_pwe_init(void)
        /* MS-CHAPv2 */
        da = dict_attrbyname("MS-CHAP-Challenge");
        if (da) {
-               pwattr[6] = da;
+               pwattr[6] = da;
 
-               da = dict_attrbyname("MS-CHAP2-Response");
-               if (da) {
+               da = dict_attrbyname("MS-CHAP2-Response");
+               if (da) {
                        pwattr[7] = da;
-               } else {
-                       pwattr[6] = NULL;
-               }
+               } else {
+                       pwattr[6] = NULL;
+               }
        }
 }
 
index 05e2940..8722075 100644 (file)
@@ -73,7 +73,7 @@ void otp_async_challenge(char challenge[OTP_MAX_CHALLENGE_LEN + 1],
                challenge[i] = '0' + rawchallenge[i] % 10;
        }
 
-       challenge[len] = '\0';
+       challenge[len] = '\0';
 }
 
 /** Guaranteed initialization
@@ -86,7 +86,7 @@ void _otp_pthread_mutex_init(pthread_mutex_t *mutexp, pthread_mutexattr_t const
        rc = pthread_mutex_init(mutexp, attr);
        if (rc) {
                ERROR("rlm_otp: %s: pthread_mutex_init: %s",
-                      caller, strerror(rc));
+                      caller, fr_syserror(rc));
 
                exit(1);
        }
@@ -102,7 +102,7 @@ void _otp_pthread_mutex_lock(pthread_mutex_t *mutexp, char const *caller)
        rc = pthread_mutex_lock(mutexp);
        if (rc) {
                ERROR("rlm_otp: %s: pthread_mutex_lock: %s",
-                      caller, strerror(rc));
+                      caller, fr_syserror(rc));
 
                exit(1);
        }
@@ -118,7 +118,7 @@ int _otp_pthread_mutex_trylock(pthread_mutex_t *mutexp, char const *caller)
        rc = pthread_mutex_trylock(mutexp);
        if (rc && rc != EBUSY) {
                ERROR("rlm_otp: %s: pthread_mutex_trylock: %s",
-                      caller, strerror(rc));
+                      caller, fr_syserror(rc));
 
                exit(1);
        }
@@ -134,10 +134,10 @@ void _otp_pthread_mutex_unlock(pthread_mutex_t *mutexp, char const *caller)
        int rc;
 
        rc = pthread_mutex_unlock(mutexp);
-       if (rc) {
+       if (rc) {
                ERROR("rlm_otp: %s: pthread_mutex_unlock: %s",
-                      caller, strerror(rc));
+                      caller, fr_syserror(rc));
 
                exit(1);
-       }
+       }
 }
index 81feeab..971b01c 100644 (file)
@@ -36,27 +36,17 @@ static int ninstance = 0;   //!< Number of instances, for global init.
 
 /* A mapping of configuration file names to internal variables. */
 static const CONF_PARSER module_config[] = {
-       { "otpd_rp", PW_TYPE_STRING_PTR, offsetof(rlm_otp_t, otpd_rp),
-         NULL, OTP_OTPD_RP },
-       { "challenge_prompt", PW_TYPE_STRING_PTR,offsetof(rlm_otp_t, chal_prompt),
-         NULL, OTP_CHALLENGE_PROMPT },
-       { "challenge_length", PW_TYPE_INTEGER, offsetof(rlm_otp_t, challenge_len),
-         NULL, "6" },
-       { "challenge_delay", PW_TYPE_INTEGER, offsetof(rlm_otp_t, challenge_delay),
-         NULL, "30" },
-       { "allow_sync", PW_TYPE_BOOLEAN, offsetof(rlm_otp_t, allow_sync),
-         NULL, "yes" },
-       { "allow_async", PW_TYPE_BOOLEAN, offsetof(rlm_otp_t, allow_async),
-         NULL, "no" },
-
-       { "mschapv2_mppe", PW_TYPE_INTEGER,
-         offsetof(rlm_otp_t, mschapv2_mppe_policy), NULL, "2" },
-       { "mschapv2_mppe_bits", PW_TYPE_INTEGER,
-         offsetof(rlm_otp_t, mschapv2_mppe_types), NULL, "2" },
-       { "mschap_mppe", PW_TYPE_INTEGER,
-         offsetof(rlm_otp_t, mschap_mppe_policy), NULL, "2" },
-       { "mschap_mppe_bits", PW_TYPE_INTEGER,
-         offsetof(rlm_otp_t, mschap_mppe_types), NULL, "2" },
+       { "otpd_rp", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_otp_t, otpd_rp), OTP_OTPD_RP  },
+       { "challenge_prompt", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_otp_t, chal_prompt), OTP_CHALLENGE_PROMPT  },
+       { "challenge_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_otp_t, challenge_len), "6" },
+       { "challenge_delay", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_otp_t, challenge_delay), "30" },
+       { "allow_sync", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_otp_t, allow_sync), "yes" },
+       { "allow_async", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_otp_t, allow_async), "no" },
+
+       { "mschapv2_mppe", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_otp_t, mschapv2_mppe_policy), "2" },
+       { "mschapv2_mppe_bits", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_otp_t, mschapv2_mppe_types), "2" },
+       { "mschap_mppe", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_otp_t, mschap_mppe_policy), "2" },
+       { "mschap_mppe_bits", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_otp_t, mschap_mppe_types), "2" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -90,7 +80,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
            (inst->challenge_len > OTP_MAX_CHALLENGE_LEN)) {
                inst->challenge_len = 6;
 
-               WDEBUG("invalid challenge_length %d, "
+               WARN("invalid challenge_length %d, "
                       "range 5-%d, using default of 6",
                       inst->challenge_len, OTP_MAX_CHALLENGE_LEN);
        }
@@ -101,28 +91,24 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                return -1;
        }
 
-       if ((inst->mschapv2_mppe_policy > 2) ||
-           (inst->mschapv2_mppe_policy < 0)) {
+       if (inst->mschapv2_mppe_policy > 2) {
                inst->mschapv2_mppe_policy = 2;
-               WDEBUG("Invalid value for mschapv2_mppe, "
-                       "using default of 2");
+               WARN("Invalid value for mschapv2_mppe, using default of 2");
        }
 
-       if ((inst->mschapv2_mppe_types > 2) || (inst->mschapv2_mppe_types < 0)) {
+       if (inst->mschapv2_mppe_types > 2) {
                inst->mschapv2_mppe_types = 2;
-               WDEBUG("Invalid value for "
-                      "mschapv2_mppe_bits, using default of 2");
+               WARN("Invalid value for mschapv2_mppe_bits, using default of 2");
        }
 
-       if ((inst->mschap_mppe_policy > 2) || (inst->mschap_mppe_policy < 0)) {
+       if (inst->mschap_mppe_policy > 2) {
                inst->mschap_mppe_policy = 2;
-               WDEBUG("Invalid value for mschap_mppe, "
-                      "using default of 2");
-       }
+               WARN("Invalid value for mschap_mppe, using default of 2");
+       }
 
        if (inst->mschap_mppe_types != 2) {
                inst->mschap_mppe_types = 2;
-               WDEBUG("Invalid value for "
+               WARN("Invalid value for "
                       "mschap_mppe_bits, using default of 2");
        }
 
@@ -136,7 +122,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 /*
  *     Generate a challenge to be presented to the user.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_otp_t *inst = (rlm_otp_t *) instance;
 
@@ -150,12 +136,12 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                auth_type_found = 0;
                vp = pairfind(request->config_items, PW_AUTHTYPE, 0, TAG_ANY);
                if (vp) {
-                       auth_type_found = 1;
-                       if (strcmp(vp->vp_strvalue, inst->name)) {
+                       auth_type_found = 1;
+                       if (strcmp(vp->vp_strvalue, inst->name)) {
                                return RLM_MODULE_NOOP;
-                       }
-               }
-       }
+                       }
+               }
+       }
 
        /* The State attribute will be present if this is a response. */
        if (pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY) != NULL) {
@@ -167,7 +153,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        /* User-Name attribute required. */
        if (!request->username) {
                RWDEBUG("Attribute \"User-Name\" "
-                      "required for authentication.");
+                      "required for authentication");
 
                return RLM_MODULE_INVALID;
        }
@@ -175,7 +161,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        if (otp_pwe_present(request) == 0) {
                RWDEBUG("Attribute "
                        "\"User-Password\" or equivalent required "
-                       "for authentication.");
+                       "for authentication");
 
                return RLM_MODULE_INVALID;
        }
@@ -288,9 +274,9 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
         *      Mark the packet as an Access-Challenge packet.
         *      The server will take care of sending it to the user.
         */
-       request->reply->code = PW_ACCESS_CHALLENGE;
+       request->reply->code = PW_CODE_ACCESS_CHALLENGE;
 
-       DEBUG("rlm_otp: Sending Access-Challenge.");
+       DEBUG("rlm_otp: Sending Access-Challenge");
 
        if (!auth_type_found) {
                pairmake_config("Auth-Type", inst->name, T_OP_EQ);
@@ -303,7 +289,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
 /*
  *     Verify the response entered by the user.
  */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_otp_t *inst = instance;
 
@@ -320,7 +306,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
        /* User-Name attribute required. */
        if (!request->username) {
                RWDEBUG("Attribute \"User-Name\" required "
-                       "for authentication.");
+                       "for authentication");
 
                return RLM_MODULE_INVALID;
        }
@@ -330,7 +316,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
        pwe = otp_pwe_present(request);
        if (pwe == 0) {
                RWDEBUG("Attribute \"User-Password\" "
-                       "or equivalent required for authentication.");
+                       "or equivalent required for authentication");
 
                return RLM_MODULE_INVALID;
        }
@@ -406,7 +392,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                 *      State is valid, but check expiry.
                 */
                then = ntohl(then);
-               if (time(NULL) - then > inst->challenge_delay) {
+               if ((time(NULL) - then) > (int)inst->challenge_delay) {
                        REDEBUG("bad radstate for [%s]: expired",username);
 
                        return RLM_MODULE_REJECT;
index f9b9cdc..e6337bd 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1248,9 +1248,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1293,10 +1293,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2114,7 +2114,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2336,7 +2336,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3407,11 +3407,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3885,13 +3885,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 64c5b48..81155d5 100644 (file)
@@ -52,8 +52,7 @@ typedef struct rlm_pam_t {
 } rlm_pam_t;
 
 static const CONF_PARSER module_config[] = {
-       { "pam_auth",    PW_TYPE_STRING_PTR, offsetof(rlm_pam_t,pam_auth_name),
-         NULL, "radiusd" },
+       { "pam_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_pam_t, pam_auth_name), "radiusd" },
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -189,7 +188,7 @@ static int pam_pass(char const *name, char const *passwd, char const *pamauth)
 }
 
 /* translate between function declarations */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        int     r;
        VALUE_PAIR *pair;
@@ -202,7 +201,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         *      a User-Name attribute.
         */
        if (!request->username) {
-               AUTH("rlm_pam: Attribute \"User-Name\" is required for authentication.");
+               AUTH("rlm_pam: Attribute \"User-Name\" is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
@@ -211,7 +210,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         *      a User-Password attribute.
         */
        if (!request->password) {
-               AUTH("rlm_pam: Attribute \"User-Password\" is required for authentication.");
+               AUTH("rlm_pam: Attribute \"User-Password\" is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
@@ -228,7 +227,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         *      Let the 'users' file over-ride the PAM auth name string,
         *      for backwards compatibility.
         */
-       pair = pairfind(request->config_items, PAM_AUTH_ATTR, 0, TAG_ANY);
+       pair = pairfind(request->config_items, PW_PAM_AUTH, 0, TAG_ANY);
        if (pair) pam_auth_string = pair->vp_strvalue;
 
        r = pam_pass(request->username->vp_strvalue,
index 87522cd..260f0ec 100644 (file)
  * @copyright 2001       Kostas Kalevras <kkalev@noc.ntua.gr>
  */
 RCSID("$Id$")
+USES_APPLE_DEPRECATED_API
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
 #include <freeradius-devel/base64.h>
+#include <freeradius-devel/rad_assert.h>
 
 #include <ctype.h>
 
 #include "../../include/md5.h"
 #include "../../include/sha1.h"
 
+#ifdef HAVE_OPENSSL_EVP_H
+#  include <openssl/evp.h>
+#endif
+
 /*
  *      Define a structure for our module configuration.
  *
@@ -42,7 +48,6 @@ RCSID("$Id$")
  */
 typedef struct rlm_pap_t {
        char const      *name;  /* CONF_SECTION->name, not strdup'd */
-       bool            auto_header;
        int             auth_type;
        bool            normify;
 } rlm_pap_t;
@@ -57,8 +62,7 @@ typedef struct rlm_pap_t {
  *      buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "auto_header", PW_TYPE_BOOLEAN, offsetof(rlm_pap_t,auto_header), NULL, "no" },
-       { "normalise", PW_TYPE_BOOLEAN, offsetof(rlm_pap_t,normify), NULL, "yes" },
+       { "normalise", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_pap_t, normify), "yes" },
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -75,6 +79,11 @@ static const FR_NAME_NUMBER header_names[] = {
        { "{base64_md5}",       PW_MD5_PASSWORD },
        { "{smd5}",             PW_SMD5_PASSWORD },
        { "{crypt}",            PW_CRYPT_PASSWORD },
+#ifdef HAVE_OPENSSL_EVP_H
+       { "{sha2}",             PW_SHA2_PASSWORD },
+       { "{sha256}",           PW_SHA2_PASSWORD },
+       { "{sha512}",           PW_SHA2_PASSWORD },
+#endif
        { "{sha}",              PW_SHA_PASSWORD },
        { "{ssha}",             PW_SSHA_PASSWORD },
        { "{nt}",               PW_NT_PASSWORD },
@@ -110,10 +119,9 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 /*
  *     Hex or base64 or bin auto-discovery.
  */
-static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_length)
+static void CC_HINT(nonnull) normify(REQUEST *request, VALUE_PAIR *vp, size_t min_length)
 {
-
-       uint8_t buffer[64];
+       uint8_t buffer[256];
 
        if (min_length >= sizeof(buffer)) return; /* paranoia */
 
@@ -122,9 +130,10 @@ static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_length)
         */
        if (vp->length >= (2 * min_length)) {
                size_t decoded;
-               decoded = fr_hex2bin(buffer, vp->vp_strvalue, vp->length >> 1);
+               decoded = fr_hex2bin(buffer, vp->vp_strvalue, sizeof(buffer));
                if (decoded == (vp->length >> 1)) {
-                       RDEBUG2("Normalizing %s from hex encoding", vp->da->name);
+                       RDEBUG2("Normalizing %s from hex encoding, %zu bytes -> %zu bytes",
+                               vp->da->name, vp->length, decoded);
                        pairmemcpy(vp, buffer, decoded);
                        return;
                }
@@ -136,11 +145,11 @@ static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_length)
         */
        if ((vp->length * 3) >= ((min_length * 4))) {
                ssize_t decoded;
-               decoded = fr_base64_decode(vp->vp_strvalue, vp->length, buffer,
-                                          sizeof(buffer));
+               decoded = fr_base64_decode(buffer, sizeof(buffer), vp->vp_strvalue, vp->length);
                if (decoded < 0) return;
                if (decoded >= (ssize_t) min_length) {
-                       RDEBUG2("Normalizing %s from base64 encoding", vp->da->name);
+                       RDEBUG2("Normalizing %s from base64 encoding, %zu bytes -> %zu bytes",
+                               vp->da->name, vp->length, decoded);
                        pairmemcpy(vp, buffer, decoded);
                        return;
                }
@@ -158,94 +167,117 @@ static void normify(REQUEST *request, VALUE_PAIR *vp, size_t min_length)
  *     This isn't strictly necessary, but it does make the
  *     server simpler to configure.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_pap_t *inst = instance;
-       int auth_type = false;
-       int found_pw = false;
+       bool auth_type = false;
+       bool found_pw = false;
        VALUE_PAIR *vp;
        vp_cursor_t cursor;
 
-       for (vp = paircursor(&cursor, &request->config_items);
+       for (vp = fr_cursor_init(&cursor, &request->config_items);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                switch (vp->da->attr) {
                case PW_USER_PASSWORD: /* deprecated */
-                       RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
-                       RDEBUG("!!! Please update your configuration so that the \"known !!!");
-                       RDEBUG("!!! good\" clear text password is in Cleartext-Password, !!!");
-                       RDEBUG("!!! and NOT in User-Password.                            !!!");
-                       RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+                       RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+                       RWDEBUG("!!! Ignoring control:User-Password.  Update your        !!!");
+                       RWDEBUG("!!! configuration so that the \"known good\" clear text !!!");
+                       RWDEBUG("!!! password is in Cleartext-Password and NOT in        !!!");
+                       RWDEBUG("!!! User-Password.                                      !!!");
+                       RWDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
                        break;
 
                case PW_PASSWORD_WITH_HEADER: /* preferred */
                {
                        int attr;
                        char *p;
-                       char const *q;
-                       uint8_t *b, binbuf[128];
+                       char const *data;
+                       size_t length;
+                       uint8_t digest[128];
                        char charbuf[128];
                        VALUE_PAIR *new_vp;
 
+                       /*
+                        *      Password already exists: use
+                        *      that instead of this one.
+                        */
+                       if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
+                               RWDEBUG("Config already contains \"known good\" password.  "
+                                       "Ignoring Password-With-Header");
+                               break;
+                       }
+
                        found_pw = true;
                redo:
-                       q = vp->vp_strvalue;
-                       p = strchr(q + 1, '}');
+                       p = strchr(vp->vp_strvalue, '}');
                        if (!p) {
                                ssize_t decoded;
 
                                /*
-                                *      Password already exists: use
-                                *      that instead of this one.
-                                */
-                               if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) {
-                                       RDEBUG("Config already contains \"reference\" password.  Ignoring Password-With-Header");
-                                       break;
-                               }
-
-                               /*
                                 *      If it's binary, it may be
                                 *      base64 encoded.  Decode it,
                                 *      and re-write the attribute to
                                 *      have the decoded value.
                                 */
-                               decoded = fr_base64_decode(vp->vp_strvalue,
-                                                          vp->length,
-                                                          binbuf,
-                                                          sizeof(binbuf));
-                               if ((decoded > 0) && (binbuf[0] == '{') &&
-                                    memchr(binbuf, '}', decoded)) {
-                                       pairmemcpy(vp, binbuf, decoded);
+                               decoded = fr_base64_decode(digest, sizeof(digest), vp->vp_strvalue, vp->length);
+                               if ((decoded > 0) &&
+                                   (digest[0] == '{') &&
+                                   (memchr(digest, '}', decoded) != NULL)) {
+                                       RDEBUG3("Decoded %s to %d bytes",
+                                               vp->vp_strvalue, (int) decoded);
+                                       pairmemcpy(vp, digest, decoded);
                                        goto redo;
                                }
 
-                               RDEBUG("Failed to decode Password-With-Header = \"%s\"", vp->vp_strvalue);
-                               break;
-                       }
+                       invalid_header:
+                               if (RDEBUG_ENABLED3) {
+                                       RDEBUG3("No {...} in Password-With-Header = \"%s\", re-writing to "
+                                              "Cleartext-Password", vp->vp_strvalue);
+                               } else {
+                                       RDEBUG("No {...} in Password-With-Header, re-writing to "
+                                              "Cleartext-Password");
+                               }
 
-                       if ((size_t) (p - q) > sizeof(charbuf)) break;
+                               data = vp->vp_strvalue;
+                               new_vp = radius_paircreate(request, &request->config_items,
+                                                          PW_CLEARTEXT_PASSWORD, 0);
+                               pairstrcpy(new_vp, data);
 
-                       memcpy(charbuf, q, p - q + 1);
-                       charbuf[p - q + 1] = '\0';
+                       } else {
+                               length = (p + 1) - vp->vp_strvalue;
 
-                       attr = fr_str2int(header_names, charbuf, 0);
-                       if (!attr) {
-                               RDEBUG2("Found unknown header {%s}: Not doing anything", charbuf);
-                               break;
-                       }
+                               if (length >= sizeof(charbuf)) break;
 
-                       new_vp = radius_paircreate(request,
-                                                  &request->config_items,
-                                                  attr, 0);
+                               memcpy(charbuf, vp->vp_strvalue, length);
+                               charbuf[length] = '\0';
+
+                               attr = fr_str2int(header_names, charbuf, 0);
+                               if (!attr) {
+                                       RWDEBUG2("Found unknown header {%s}: Not doing anything", charbuf);
+                                       goto invalid_header;
+                               }
+
+                               data = vp->vp_strvalue + length;
+                               length = vp->length - length;
+
+                               new_vp = radius_paircreate(request, &request->config_items, attr, 0);
+
+                               /*
+                                *      The data after the '}' may be
+                                *      binary, so we copy it via
+                                *      memcpy.  BUT it might be a
+                                *      string, so we ensure that
+                                *      there's a trailing zero, too.
+                                */
+                               if (new_vp->da->type == PW_TYPE_OCTETS) {
+                                       pairmemcpy(new_vp, (uint8_t const *) data, length + 1);
+                                       new_vp->length = length;
+                               } else {
+                                       pairstrcpy(new_vp, data);
+                               }
+                       }
 
-                       /*
-                        *      The data after the '}' may be binary,
-                        *      so we copy it via memcpy.
-                        */
-                       new_vp->length = vp->length;
-                       new_vp->length -= (p - q + 1);
-                       new_vp->vp_octets = b = talloc_array(new_vp, uint8_t, new_vp->length);
-                       memcpy(b, p + 1, new_vp->length);
                }
                        break;
 
@@ -259,15 +291,26 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                case PW_SMD5_PASSWORD:
                case PW_NT_PASSWORD:
                case PW_LM_PASSWORD:
-                       if (inst->normify)
+                       if (inst->normify) {
                                normify(request, vp, 16); /* ensure it's in the right format */
+                       }
+                       found_pw = true;
+                       break;
+
+#ifdef HAVE_OPENSSL_EVP_H
+               case PW_SHA2_PASSWORD:
+                       if (inst->normify) {
+                               normify(request, vp, 28); /* ensure it's in the right format */
+                       }
                        found_pw = true;
                        break;
+#endif
 
                case PW_SHA_PASSWORD:
                case PW_SSHA_PASSWORD:
-                       if (inst->normify)
+                       if (inst->normify) {
                                normify(request, vp, 20); /* ensure it's in the right format */
+                       }
                        found_pw = true;
                        break;
 
@@ -327,8 +370,8 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                        return RLM_MODULE_NOOP;
                }
 
-               RWDEBUG("No \"known good\" password found for the user.  Not setting Auth-Type.");
-               RWDEBUG("Authentication will fail unless a \"known good\" password is available.");
+               RWDEBUG("No \"known good\" password found for the user.  Not setting Auth-Type");
+               RWDEBUG("Authentication will fail unless a \"known good\" password is available");
                return RLM_MODULE_NOOP;
        }
 
@@ -345,15 +388,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
         */
        if (!request->password ||
            (request->password->da->attr != PW_USER_PASSWORD)) {
-               /*
-                *      Don't print out debugging messages if we know
-                *      they're useless.
-                */
-               if (request->packet->code == PW_ACCESS_CHALLENGE) {
-                       return RLM_MODULE_NOOP;
-               }
-
-               RDEBUG2("No clear-text password in the request.  Not performing PAP.");
+               RDEBUG2("No cleartext password in the request.  Not performing PAP");
                return RLM_MODULE_NOOP;
        }
 
@@ -370,53 +405,62 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
  *     PAP authentication functions
  */
 
-static int pap_auth_clear(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_clear(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
-       RDEBUG("Using clear text password \"%s\"", vp->vp_strvalue);
+       if (RDEBUG_ENABLED3) {
+               RDEBUG3("Comparing with \"known good\" Cleartext-Password \"%s\"", vp->vp_strvalue);
+       } else {
+               RDEBUG3("Comparing with \"known good\" Cleartext-Password");
+       }
 
        if ((vp->length != request->password->length) ||
            (rad_digest_cmp(vp->vp_octets,
                            request->password->vp_octets,
                            vp->length) != 0)) {
-               REDEBUG("CLEAR TEXT password check failed");
+               REDEBUG("Cleartext password does not match \"known good\" password");
                return RLM_MODULE_REJECT;
        }
        return RLM_MODULE_OK;
 }
 
-static int pap_auth_crypt(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_crypt(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
-       RDEBUG("Using CRYPT password \"%s\"", vp->vp_strvalue);
+       if (RDEBUG_ENABLED3) {
+               RDEBUG3("Comparing with \"known good\" Crypt-Password \"%s\"", vp->vp_strvalue);
+       } else {
+               RDEBUG("Comparing with \"known-good\" Crypt-password");
+       }
 
        if (fr_crypt_check(request->password->vp_strvalue,
                           vp->vp_strvalue) != 0) {
-               REDEBUG("CRYPT password check failed");
+               REDEBUG("Crypt digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
        return RLM_MODULE_OK;
 }
 
-static int pap_auth_md5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_md5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
        FR_MD5_CTX md5_context;
-       uint8_t binbuf[128];
+       uint8_t digest[128];
 
-       RDEBUG("Using MD5 encryption.");
+       RDEBUG("Comparing with \"known-good\" MD5-Password");
 
-       if (inst->normify)
+       if (inst->normify) {
                normify(request, vp, 16);
+       }
        if (vp->length != 16) {
-               REDEBUG("Configured MD5 password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known-good\" MD5 password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        fr_MD5Init(&md5_context);
        fr_MD5Update(&md5_context, request->password->vp_octets,
                     request->password->length);
-       fr_MD5Final(binbuf, &md5_context);
+       fr_MD5Final(digest, &md5_context);
 
-       if (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0) {
-               REDEBUG("MD5 password check failed");
+       if (rad_digest_cmp(digest, vp->vp_octets, vp->length) != 0) {
+               REDEBUG("MD5 digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
@@ -424,113 +468,184 @@ static int pap_auth_md5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 }
 
 
-static int pap_auth_smd5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_smd5(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
        FR_MD5_CTX md5_context;
-       uint8_t binbuf[128];
+       uint8_t digest[128];
 
-       RDEBUG("Using SMD5 encryption.");
+       RDEBUG("Comparing with \"known-good\" SMD5-Password");
 
-       if (inst->normify)
+       if (inst->normify) {
                normify(request, vp, 16);
+       }
        if (vp->length <= 16) {
-               REDEBUG("Configured SMD5 password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known-good\" SMD5-Password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        fr_MD5Init(&md5_context);
        fr_MD5Update(&md5_context, request->password->vp_octets,
                     request->password->length);
        fr_MD5Update(&md5_context, &vp->vp_octets[16], vp->length - 16);
-       fr_MD5Final(binbuf, &md5_context);
+       fr_MD5Final(digest, &md5_context);
 
        /*
         *      Compare only the MD5 hash results, not the salt.
         */
-       if (rad_digest_cmp(binbuf, vp->vp_octets, 16) != 0) {
-               REDEBUG("SMD5 password check failed");
+       if (rad_digest_cmp(digest, vp->vp_octets, 16) != 0) {
+               REDEBUG("SMD5 digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
        return RLM_MODULE_OK;
 }
 
-static int pap_auth_sha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
        fr_SHA1_CTX sha1_context;
-       uint8_t binbuf[128];
+       uint8_t digest[128];
 
-       RDEBUG("Using SHA1 encryption.");
+       RDEBUG("Comparing with \"known-good\" SHA-Password");
 
-       if (inst->normify)
+       if (inst->normify) {
                normify(request, vp, 20);
+       }
        if (vp->length != 20) {
-               REDEBUG("SHA1 password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known-good\" SHA1-password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        fr_SHA1Init(&sha1_context);
        fr_SHA1Update(&sha1_context, request->password->vp_octets,
                      request->password->length);
-       fr_SHA1Final(binbuf,&sha1_context);
+       fr_SHA1Final(digest,&sha1_context);
 
-       if (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0) {
-               REDEBUG("SHA1 password check failed");
+       if (rad_digest_cmp(digest, vp->vp_octets, vp->length) != 0) {
+               REDEBUG("SHA1 digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
        return RLM_MODULE_OK;
 }
 
-static int pap_auth_ssha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_ssha(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
        fr_SHA1_CTX sha1_context;
-       uint8_t binbuf[128];
+       uint8_t digest[128];
 
-       RDEBUG("Using SSHA encryption.");
+       RDEBUG("Comparing with \"known-good\" SSHA-Password");
 
-       if (inst->normify)
+       if (inst->normify) {
                normify(request, vp, 20);
+       }
        if (vp->length <= 20) {
-               REDEBUG("SSHA password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known-good\" SSHA-Password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        fr_SHA1Init(&sha1_context);
        fr_SHA1Update(&sha1_context, request->password->vp_octets,
                      request->password->length);
        fr_SHA1Update(&sha1_context, &vp->vp_octets[20], vp->length - 20);
-       fr_SHA1Final(binbuf,&sha1_context);
+       fr_SHA1Final(digest,&sha1_context);
 
-       if (rad_digest_cmp(binbuf, vp->vp_octets, 20) != 0) {
-               REDEBUG("SSHA password check failed");
+       if (rad_digest_cmp(digest, vp->vp_octets, 20) != 0) {
+               REDEBUG("SSHA digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
        return RLM_MODULE_OK;
 }
 
-static int pap_auth_nt(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+#ifdef HAVE_OPENSSL_EVP_H
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_sha2(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
-       uint8_t binbuf[16];
+       EVP_MD_CTX *ctx;
+       EVP_MD const *md;
+       char const *name;
+       uint8_t digest[EVP_MAX_MD_SIZE];
+       unsigned int digestlen;
+
+       RDEBUG("Comparing with \"known-good\" SHA2-Password");
+
+       if (inst->normify) {
+               normify(request, vp, 28);
+       }
+
+       /*
+        *      All the SHA-2 algorithms produce digests of different lengths,
+        *      so it's trivial to determine which EVP_MD to use.
+        */
+       switch (vp->length) {
+       /* SHA-224 */
+       case 28:
+               name = "SHA-224";
+               md = EVP_sha224();
+               break;
+
+       /* SHA-256 */
+       case 32:
+               name = "SHA-256";
+               md = EVP_sha256();
+               break;
+
+       /* SHA-384 */
+       case 48:
+               name = "SHA-384";
+               md = EVP_sha384();
+               break;
+
+       /* SHA-512 */
+       case 64:
+               name = "SHA-512";
+               md = EVP_sha512();
+               break;
+
+       default:
+               REDEBUG("\"known good\" digest length (%zu) does not match output length of any SHA-2 digests",
+                       vp->length);
+               return RLM_MODULE_INVALID;
+       }
+
+       ctx = EVP_MD_CTX_create();
+       EVP_DigestInit_ex(ctx, md, NULL);
+       EVP_DigestUpdate(ctx, request->password->vp_octets, request->password->length);
+       EVP_DigestFinal_ex(ctx, digest, &digestlen);
+       EVP_MD_CTX_destroy(ctx);
+
+       fr_assert((size_t) digestlen == vp->length);    /* This would be an OpenSSL bug... */
+
+       if (rad_digest_cmp(digest, vp->vp_octets, vp->length) != 0) {
+               REDEBUG("%s digest does not match \"known good\" digest", name);
+               return RLM_MODULE_REJECT;
+       }
+
+       return RLM_MODULE_OK;
+}
+#endif
+
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_nt(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+{
+       uint8_t digest[16];
        char charbuf[32 + 1];
 
-       RDEBUG("Using NT encryption.");
+       RDEBUG("Comparing with \"known-good\" NT-Password");
 
-       if (inst->normify)
+       if (inst->normify) {
                normify(request, vp, 16);
+       }
        if (vp->length != 16) {
-               REDEBUG("Configured NT-Password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known good\" NT-Password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        if (radius_xlat(charbuf, sizeof(charbuf), request, "%{mschap:NT-Hash %{User-Password}}", NULL, NULL) < 0){
                return RLM_MODULE_REJECT;
        }
 
-       if ((fr_hex2bin(binbuf, charbuf, sizeof(binbuf)) != vp->length) ||
-           (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0)) {
-               REDEBUG("NT password check failed");
+       if ((fr_hex2bin(digest, charbuf, sizeof(digest)) != vp->length) ||
+           (rad_digest_cmp(digest, vp->vp_octets, vp->length) != 0)) {
+               REDEBUG("NT digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
@@ -538,53 +653,54 @@ static int pap_auth_nt(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 }
 
 
-static int pap_auth_lm(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_lm(rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
-       uint8_t binbuf[16];
+       uint8_t digest[16];
        char charbuf[32 + 1];
 
-       RDEBUG("Using LM encryption.");
+       RDEBUG("Comparing with \"known-good\" LM-Password");
 
-       if (inst->normify)
+       if (inst->normify) {
                normify(request, vp, 16);
+       }
        if (vp->length != 16) {
-               REDEBUG("Configure LM-Password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known good\" LM-Password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        if (radius_xlat(charbuf, sizeof(charbuf), request, "%{mschap:LM-Hash %{User-Password}}", NULL, NULL) < 0){
-               return RLM_MODULE_REJECT;
+               return RLM_MODULE_FAIL;
        }
 
-       if ((fr_hex2bin(binbuf, charbuf, sizeof(binbuf)) != vp->length) ||
-           (rad_digest_cmp(binbuf, vp->vp_octets, vp->length) != 0)) {
-               REDEBUG("LM password check failed");
+       if ((fr_hex2bin(digest, charbuf, sizeof(digest)) != vp->length) ||
+           (rad_digest_cmp(digest, vp->vp_octets, vp->length) != 0)) {
+               REDEBUG("LM digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
        return RLM_MODULE_OK;
 }
 
-static int pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
+static rlm_rcode_t CC_HINT(nonnull) pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_PAIR *vp)
 {
        FR_MD5_CTX md5_context;
-       uint8_t binbuf[128];
+       uint8_t digest[128];
        uint8_t buff[MAX_STRING_LEN];
        char buff2[MAX_STRING_LEN + 50];
 
-       RDEBUG("Using NT-MTA-MD5 password");
+       RDEBUG("Using NT-MTA-MD5-Password");
 
        if (vp->length != 64) {
-               REDEBUG("Configured NS-MTA-MD5-Password has incorrect length");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known good\" NS-MTA-MD5-Password has incorrect length");
+               return RLM_MODULE_INVALID;
        }
 
        /*
         *      Sanity check the value of NS-MTA-MD5-Password
         */
-       if (fr_hex2bin(binbuf, vp->vp_strvalue, 32) != 16) {
-               REDEBUG("Configured NS-MTA-MD5-Password has invalid value");
-               return RLM_MODULE_REJECT;
+       if (fr_hex2bin(digest, vp->vp_strvalue, 32) != 16) {
+               REDEBUG("\"known good\" NS-MTA-MD5-Password has invalid value");
+               return RLM_MODULE_INVALID;
        }
 
        /*
@@ -593,8 +709,8 @@ static int pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_P
         *      This really: sizeof(buff) - 2 - 2*32 - strlen(passwd)
         */
        if (request->password->length >= (sizeof(buff) - 2 - 2 * 32)) {
-               REDEBUG("Configured password is too long");
-               return RLM_MODULE_REJECT;
+               REDEBUG("\"known good\" NS-MTA-MD5-Password is too long");
+               return RLM_MODULE_INVALID;
        }
 
        /*
@@ -617,8 +733,8 @@ static int pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_P
                fr_MD5Final(buff, &md5_context);
        }
 
-       if (rad_digest_cmp(binbuf, buff, 16) != 0) {
-               REDEBUG("NS-MTA-MD5 password check failed");
+       if (rad_digest_cmp(digest, buff, 16) != 0) {
+               REDEBUG("NS-MTA-MD5 digest does not match \"known good\" digest");
                return RLM_MODULE_REJECT;
        }
 
@@ -629,14 +745,13 @@ static int pap_auth_ns_mta_md5(UNUSED rlm_pap_t *inst, REQUEST *request, VALUE_P
 /*
  *     Authenticate the user via one of any well-known password.
  */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_pap_t *inst = instance;
        VALUE_PAIR *vp;
        rlm_rcode_t rc = RLM_MODULE_INVALID;
        vp_cursor_t cursor;
-       int (*auth_func)(rlm_pap_t *, REQUEST *, VALUE_PAIR *) = NULL;
-
+       rlm_rcode_t (*auth_func)(rlm_pap_t *, REQUEST *, VALUE_PAIR *) = NULL;
 
        if (!request->password ||
            (request->password->da->attr != PW_USER_PASSWORD)) {
@@ -652,16 +767,20 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                return RLM_MODULE_INVALID;
        }
 
-       RDEBUG("login attempt with password \"%s\"", request->password->vp_strvalue);
+       if (RDEBUG_ENABLED3) {
+               RDEBUG3("Login attempt with password \"%s\"", request->password->vp_strvalue);
+       } else {
+               RDEBUG("Login attempt with password");
+       }
 
        /*
         *      Auto-detect passwords, by attribute in the
         *      config items, to find out which authentication
         *      function to call.
         */
-       for (vp = paircursor(&cursor, &request->config_items);
+       for (vp = fr_cursor_init(&cursor, &request->config_items);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                if (!vp->da->vendor) switch (vp->da->attr) {
                case PW_CLEARTEXT_PASSWORD:
                        auth_func = &pap_auth_clear;
@@ -679,6 +798,12 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                        auth_func = &pap_auth_smd5;
                        break;
 
+#ifdef HAVE_OPENSSL_EVP_H
+               case PW_SHA2_PASSWORD:
+                       auth_func = &pap_auth_sha2;
+                       break;
+#endif
+
                case PW_SHA_PASSWORD:
                        auth_func = &pap_auth_sha;
                        break;
@@ -743,7 +868,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
 module_t rlm_pap = {
        RLM_MODULE_INIT,
        "PAP",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(rlm_pap_t),
        module_config,
        mod_instantiate,                /* instantiation */
index 8101905..e8e8d4e 100644 (file)
@@ -68,10 +68,10 @@ static struct mypasswd * mypasswd_malloc(char const* buffer, int nfields, size_t
        /* reserve memory for (struct mypasswd) + listflag (nfields * sizeof (char*)) +
        ** fields (nfields * sizeof (char)) + strlen (inst->format) + 1 */
 
-       *len=sizeof (struct mypasswd) + nfields * sizeof (char*) + nfields * sizeof (char ) + strlen(buffer) + 1;
+       *len=sizeof (struct mypasswd) + nfields * sizeof (char*) + nfields * sizeof (char ) + strlen(buffer) + 1;
        t = (struct mypasswd *) rad_malloc(*len);
        if (t) memset(t, 0, *len);
-       return (t);
+       return (t);
 }
 
 static int string_to_entry(char const* string, int nfields, char delimiter,
@@ -134,8 +134,8 @@ static void release_hash_table(struct hashtable * ht){
 
        if (!ht) return;
        for (i = 0; i < ht->tablesize; i++)
-               if (ht->table[i])
-                       destroy_password(ht->table[i]);
+               if (ht->table[i])
+                       destroy_password(ht->table[i]);
        if (ht->table) {
                free(ht->table);
                ht->table = NULL;
@@ -150,7 +150,10 @@ static void release_hash_table(struct hashtable * ht){
 static void release_ht(struct hashtable * ht){
        if (!ht) return;
        release_hash_table(ht);
-       if (ht->filename) free(ht->filename);
+       if (ht->filename) {
+               free(ht->filename);
+               ht->filename = NULL;
+       }
        free(ht);
 }
 
@@ -370,46 +373,35 @@ int main(void){
 struct passwd_instance {
        struct hashtable        *ht;
        struct mypasswd         *pwdfmt;
-       char                    *filename;
-       char                    *format;
-       char                    *delimiter;
+       char const              *filename;
+       char const              *format;
+       char const              *delimiter;
        bool                    allow_multiple;
        bool                    ignore_nislike;
-       int                     hash_size;
-       int                     nfields;
-       int                     keyfield;
-       int                     listable;
+       uint32_t                hash_size;
+       uint32_t                nfields;
+       uint32_t                keyfield;
+       uint32_t                listable;
        DICT_ATTR const         *keyattr;
        bool                    ignore_empty;
 };
 
 static const CONF_PARSER module_config[] = {
-       { "filename",   PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED,
-         offsetof(struct passwd_instance, filename), NULL,  NULL },
-       { "format",   PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(struct passwd_instance, format), NULL,  NULL },
-       { "delimiter",   PW_TYPE_STRING_PTR,
-         offsetof(struct passwd_instance, delimiter), NULL,  ":" },
-
-       { "ignorenislike",   PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED,
-         offsetof(struct passwd_instance, ignore_nislike), NULL,  NULL },
-       { "ignore_nislike",   PW_TYPE_BOOLEAN,
-         offsetof(struct passwd_instance, ignore_nislike), NULL,  "yes" },
-
-       { "ignoreempty",   PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED,
-         offsetof(struct passwd_instance, ignore_empty), NULL,  NULL },
-       { "ignore_empty",   PW_TYPE_BOOLEAN,
-         offsetof(struct passwd_instance, ignore_empty), NULL,  "yes" },
-
-       { "allowmultiplekeys",   PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED,
-         offsetof(struct passwd_instance, allow_multiple), NULL,  NULL },
-       { "allow_multiple_keys",   PW_TYPE_BOOLEAN,
-         offsetof(struct passwd_instance, allow_multiple), NULL,  "no" },
-
-       { "hashsize",   PW_TYPE_INTEGER | PW_TYPE_DEPRECATED,
-         offsetof(struct passwd_instance, hash_size), NULL,  NULL },
-       { "hash_size",   PW_TYPE_INTEGER,
-         offsetof(struct passwd_instance, hash_size), NULL,  "100" },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, struct passwd_instance, filename), NULL },
+       { "format", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, struct passwd_instance, format), NULL },
+       { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, struct passwd_instance, delimiter), ":" },
+
+       { "ignorenislike", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, struct passwd_instance, ignore_nislike), NULL },
+       { "ignore_nislike", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, struct passwd_instance, ignore_nislike), "yes" },
+
+       { "ignoreempty", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, struct passwd_instance, ignore_empty), NULL },
+       { "ignore_empty",  FR_CONF_OFFSET(PW_TYPE_BOOLEAN, struct passwd_instance, ignore_empty), "yes" },
+
+       { "allowmultiplekeys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, struct passwd_instance, allow_multiple), NULL },
+       { "allow_multiple_keys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, struct passwd_instance, allow_multiple), "no" },
+
+       { "hashsize", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, struct passwd_instance, hash_size), NULL },
+       { "hash_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, struct passwd_instance, hash_size), "100" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -417,7 +409,7 @@ static const CONF_PARSER module_config[] = {
 static int mod_instantiate(CONF_SECTION *conf, void *instance)
 {
        int nfields=0, keyfield=-1, listable=0;
-       char *s;
+       char const *s;
        char *lf=NULL; /* destination list flags temporary */
        size_t len;
        int i;
@@ -432,7 +424,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                return -1;
        }
 
-       lf = talloc_strdup(inst, inst->format);
+       lf = talloc_typed_strdup(inst, inst->format);
        if ( !lf) {
                ERROR("rlm_passwd: memory allocation failed for lf");
                return -1;
@@ -473,11 +465,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        if (! (inst->pwdfmt = mypasswd_malloc(inst->format, nfields, &len)) ){
                ERROR("rlm_passwd: memory allocation failed");
                release_ht(inst->ht);
+               inst->ht = NULL;
                return -1;
        }
        if (!string_to_entry(inst->format, nfields, ':', inst->pwdfmt , len)) {
                ERROR("rlm_passwd: unable to convert format entry");
                release_ht(inst->ht);
+               inst->ht = NULL;
                return -1;
        }
 
@@ -493,11 +487,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        if (!*inst->pwdfmt->field[keyfield]) {
                cf_log_err_cs(conf, "key field is empty");
                release_ht(inst->ht);
+               inst->ht = NULL;
                return -1;
        }
        if (! (da = dict_attrbyname (inst->pwdfmt->field[keyfield])) ) {
                ERROR("rlm_passwd: unable to resolve attribute: %s", inst->pwdfmt->field[keyfield]);
                release_ht(inst->ht);
+               inst->ht = NULL;
                return -1;
        }
        inst->keyattr = da;
@@ -512,7 +508,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 static int mod_detach (void *instance) {
 #define inst ((struct passwd_instance *)instance)
-       if(inst->ht) release_ht(inst->ht);
+       if(inst->ht) {
+               release_ht(inst->ht);
+               inst->ht = NULL;
+       }
        free(inst->pwdfmt);
        return 0;
 #undef inst
@@ -520,10 +519,10 @@ static int mod_detach (void *instance) {
 
 static void addresult (struct passwd_instance * inst, REQUEST *request, TALLOC_CTX *ctx, VALUE_PAIR **vps, struct mypasswd * pw, char when, char const *listname)
 {
-       int i;
+       uint32_t i;
        VALUE_PAIR *vp;
 
-       for (i=0; i<inst->nfields; i++) {
+       for (i = 0; i < inst->nfields; i++) {
                if (inst->pwdfmt->field[i] && *inst->pwdfmt->field[i] && pw->field[i] && i != inst->keyfield  && inst->pwdfmt->listflag[i] == when) {
                        if ( !inst->ignore_empty || pw->field[i][0] != 0 ) { /* if value in key/value pair is not empty */
                                vp = pairmake(ctx, vps, inst->pwdfmt->field[i], pw->field[i], T_OP_EQ);
@@ -536,7 +535,7 @@ static void addresult (struct passwd_instance * inst, REQUEST *request, TALLOC_C
        }
 }
 
-static rlm_rcode_t passwd_map(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_passwd_map(void *instance, REQUEST *request)
 {
 #define inst ((struct passwd_instance *)instance)
        char buffer[1024];
@@ -549,9 +548,9 @@ static rlm_rcode_t passwd_map(void *instance, REQUEST *request)
                return RLM_MODULE_NOTFOUND;
        }
 
-       for (i = paircursor(&cursor, &key);
+       for (i = fr_cursor_init(&cursor, &key);
             i;
-            i = pairfindnext(&cursor, inst->keyattr->attr, inst->keyattr->vendor, TAG_ANY)) {
+            i = fr_cursor_next_by_num(&cursor, inst->keyattr->attr, inst->keyattr->vendor, TAG_ANY)) {
                /*
                 *      Ensure we have the string form of the attribute
                 */
@@ -578,23 +577,23 @@ static rlm_rcode_t passwd_map(void *instance, REQUEST *request)
 module_t rlm_passwd = {
        RLM_MODULE_INIT,
        "passwd",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(struct passwd_instance),
        module_config,
        mod_instantiate,                /* instantiation */
        mod_detach,                     /* detach */
        {
                NULL,                   /* authentication */
-               passwd_map,             /* authorization */
+               mod_passwd_map,         /* authorization */
                NULL,                   /* pre-accounting */
-               passwd_map,             /* accounting */
+               mod_passwd_map,         /* accounting */
                NULL,                   /* checksimul */
                NULL,                   /* pre-proxy */
                NULL,                   /* post-proxy */
-               passwd_map              /* post-auth */
+               mod_passwd_map          /* post-auth */
 #ifdef WITH_COA
-               , passwd_map,
-               passwd_map
+               , mod_passwd_map,
+               mod_passwd_map
 #endif
        },
 };
index b86e58b..bf6b30b 100644 (file)
@@ -8,15 +8,3 @@ SOURCES                := rlm_perl.c
 
 SRC_CFLAGS     := @mod_cflags@
 TGT_LDLIBS     := @mod_ldflags@
-
-ifneq "$(TARGETNAME)" ""
-install: $(R)$(modconfdir)/perl/example.pl
-
-$(R)$(modconfdir)/perl: | $(R)$(modconfdir)
-       @echo INSTALL $(patsubst $(R)$(raddbdir)%,raddb%,$@)
-       @$(INSTALL) -d -m 750 $@
-
-$(R)$(modconfdir)/perl/example.pl: src/modules/rlm_perl/example.pl | $(R)$(modconfdir)/perl
-       @$(ECHO) INSTALL $(notdir $<)
-       @$(INSTALL) -m 755 $< $(R)$(modconfdir)/perl
-endif
index e93cca8..2d714db 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1212,9 +1212,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1262,10 +1262,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
   PERL        Absolute path to perl executable
 
@@ -1962,7 +1962,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2184,7 +2184,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2804,26 +2804,26 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
     if test -z "$PERL"; then :
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether perl executable path has been provided" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether perl executable path has been provided" >&5
 $as_echo_n "checking whether perl executable path has been provided... " >&6; }
 
 # Check whether --with-perl was given.
 if test "${with_perl+set}" = set; then :
   withval=$with_perl;
-            if test "$withval" != yes && test "$withval" != no; then :
+           if test "$withval" != yes && test "$withval" != no; then :
 
-                PERL="$withval"
-                { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5
+               PERL="$withval"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PERL" >&5
 $as_echo "$PERL" >&6; }
 
 else
 
-                PERL=""
-                { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+               PERL=""
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-                if test "$withval" != no; then :
+               if test "$withval" != no; then :
 
-                  # Extract the first word of "perl", so it can be a program name with args.
+                 # Extract the first word of "perl", so it can be a program name with args.
 set dummy perl; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
@@ -2872,9 +2872,9 @@ fi
 
 else
 
-            { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+           { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-            # Extract the first word of "perl", so it can be a program name with args.
+           # Extract the first word of "perl", so it can be a program name with args.
 set dummy perl; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
@@ -3042,22 +3042,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=EXTERN.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3191,22 +3191,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=perl.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3430,11 +3430,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3908,13 +3908,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index e326f6f..fecc01c 100644 (file)
@@ -49,47 +49,48 @@ extern char **environ;
  */
 typedef struct rlm_perl_t {
        /* Name of the perl module */
-       char    *module;
+       char const      *module;
 
        /* Name of the functions for each module method */
-       char    *func_authorize;
-       char    *func_authenticate;
-       char    *func_accounting;
-       char    *func_start_accounting;
-       char    *func_stop_accounting;
-       char    *func_preacct;
-       char    *func_checksimul;
-       char    *func_detach;
-       char    *func_xlat;
+       char const      *func_authorize;
+       char const      *func_authenticate;
+       char const      *func_accounting;
+       char const      *func_start_accounting;
+       char const      *func_stop_accounting;
+       char const      *func_preacct;
+       char const      *func_checksimul;
+       char const      *func_detach;
+       char const      *func_xlat;
 #ifdef WITH_PROXY
-       char    *func_pre_proxy;
-       char    *func_post_proxy;
+       char const      *func_pre_proxy;
+       char const      *func_post_proxy;
 #endif
-       char    *func_post_auth;
+       char const      *func_post_auth;
 #ifdef WITH_COA
-       char    *func_recv_coa;
-       char    *func_send_coa;
+       char const      *func_recv_coa;
+       char const      *func_send_coa;
 #endif
-       char    *xlat_name;
-       char    *perl_flags;
-       PerlInterpreter *perl;
+       char const      *xlat_name;
+       char const      *perl_flags;
+       PerlInterpreter *perl;
        pthread_key_t   *thread_key;
 
 #ifdef USE_ITHREADS
-       pthread_mutex_t clone_mutex;
+       pthread_mutex_t clone_mutex;
 #endif
+
+       HV              *rad_perlconf_hv;       //!< holds "config" items (perl %RAD_PERLCONF hash).
+
 } rlm_perl_t;
 /*
  *     A mapping of configuration file names to internal variables.
  */
-#define RLM_PERL_CONF(_x) { "func_" STRINGIFY(_x), PW_TYPE_STRING_PTR, \
+#define RLM_PERL_CONF(_x) { "func_" STRINGIFY(_x), PW_TYPE_STRING, \
                        offsetof(rlm_perl_t,func_##_x), NULL, STRINGIFY(_x)}
 
 static const CONF_PARSER module_config[] = {
-       { "module",  PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED,
-         offsetof(rlm_perl_t,module), NULL,  NULL},
-       { "filename",  PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED,
-         offsetof(rlm_perl_t,module), NULL,  NULL},
+       { "module", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_DEPRECATED, rlm_perl_t, module), NULL },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, rlm_perl_t, module), NULL },
 
        RLM_PERL_CONF(authorize),
        RLM_PERL_CONF(authenticate),
@@ -108,14 +109,11 @@ static const CONF_PARSER module_config[] = {
        RLM_PERL_CONF(recv_coa),
        RLM_PERL_CONF(send_coa),
 #endif
-       { "perl_flags", PW_TYPE_STRING_PTR,
-         offsetof(rlm_perl_t,perl_flags), NULL, NULL},
+       { "perl_flags", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_perl_t, perl_flags), NULL },
 
-       { "func_start_accounting", PW_TYPE_STRING_PTR,
-         offsetof(rlm_perl_t,func_start_accounting), NULL, NULL},
+       { "func_start_accounting", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_perl_t, func_start_accounting), NULL },
 
-       { "func_stop_accounting", PW_TYPE_STRING_PTR,
-         offsetof(rlm_perl_t,func_stop_accounting), NULL, NULL},
+       { "func_stop_accounting", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_perl_t, func_stop_accounting), NULL },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -260,11 +258,11 @@ static PerlInterpreter *rlm_perl_clone(PerlInterpreter *perl, pthread_key_t *key
        PL_ptr_table = NULL;
 
        PERL_SET_CONTEXT(aTHX);
-       rlm_perl_clear_handles(aTHX);
+       rlm_perl_clear_handles(aTHX);
 
        ret = pthread_setspecific(*key, interp);
        if (ret != 0) {
-               DEBUG("rlm_perl: Failed associating interpretor with thread %s", strerror(ret));
+               DEBUG("rlm_perl: Failed associating interpretor with thread %s", fr_syserror(ret));
 
                rlm_perl_destruct(interp);
                return NULL;
@@ -318,7 +316,7 @@ static void xs_init(pTHX)
 static ssize_t perl_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
 {
 
-       rlm_perl_t      *inst= (rlm_perl_t *) instance;
+       rlm_perl_t      *inst = (rlm_perl_t *) instance;
        char            *tmp;
        char const      *p, *q;
        int             count;
@@ -375,6 +373,74 @@ static ssize_t perl_xlat(void *instance, REQUEST *request, char const *fmt, char
 
        return ret;
 }
+
+/*
+ *     Parse a configuration section, and populate a HV.
+ *     This function is recursively called (allows to have nested hashes.)
+ */
+static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv)
+{
+       if (!cs || !rad_hv) return;
+
+       int indent_section = (lvl + 1) * 4;
+       int indent_item = (lvl + 2) * 4;
+
+       DEBUG("%*s%s {", indent_section, " ", cf_section_name1(cs));
+
+       CONF_ITEM *ci;
+
+       for (ci = cf_item_find_next(cs, NULL);
+            ci;
+            ci = cf_item_find_next(cs, ci)) {
+               /*
+                *  This is a section.
+                *  Create a new HV, store it as a reference in current HV,
+                *  Then recursively call perl_parse_config with this section and the new HV.
+                */
+               if (cf_item_is_section(ci)) {
+                       CONF_SECTION    *sub_cs = cf_itemtosection(ci);
+                       char const      *key = cf_section_name1(sub_cs); /* hash key */
+                       HV              *sub_hv;
+                       SV              *ref;
+
+                       if (!key) continue;
+
+                       if (hv_exists(rad_hv, key, strlen(key))) {
+                               WARN("rlm_perl: Ignoring duplicate config section '%s'", key);
+                               continue;
+                       }
+
+                       sub_hv = newHV();
+                       ref = newRV_inc((SV*) sub_hv);
+
+                       (void)hv_store(rad_hv, key, strlen(key), ref, 0);
+
+                       perl_parse_config(sub_cs, lvl + 1, sub_hv);
+               } else if (cf_item_is_pair(ci)){
+                       CONF_PAIR       *cp = cf_itemtopair(ci);
+                       char const      *key = cf_pair_attr(cp);        /* hash key */
+                       char const      *value = cf_pair_value(cp);     /* hash value */
+
+                       if (!key || !value) continue;
+
+                       /*
+                        *  This is an item.
+                        *  Store item attr / value in current HV.
+                        */
+                       if (hv_exists(rad_hv, key, strlen(key))) {
+                               WARN("rlm_perl: Ignoring duplicate config item '%s'", key);
+                               continue;
+                       }
+
+                       (void)hv_store(rad_hv, key, strlen(key), newSVpv(value, strlen(value)), 0);
+
+                       DEBUG("%*s%s = %s", indent_item, " ", key, value);
+               }
+       }
+
+       DEBUG("%*s}", indent_section, " ");
+}
+
 /*
  *     Do any per-module initialization that is separate to each
  *     configured instance of the module.  e.g. set up connections
@@ -395,13 +461,14 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        rlm_perl_t       *inst = instance;
        AV              *end_AV;
 
+       char const **embed_c;   /* Stupid Perl and lack of const consistency */
        char **embed;
        char **envp = NULL;
        char const *xlat_name;
        int exitstatus = 0, argc=0;
 
-       MEM(embed = talloc_zero_array(inst, char *, 4));
-
+       MEM(embed_c = talloc_zero_array(inst, char const *, 4));
+       memcpy(&embed, &embed_c, sizeof(embed));
        /*
         *      Create pthread key. This key will be stored in instance
         */
@@ -417,15 +484,15 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        char arg[] = "0";
 
-       embed[0] = NULL;
+       embed_c[0] = NULL;
        if (inst->perl_flags) {
-               embed[1] = inst->perl_flags;
-               embed[2] = inst->module;
-               embed[3] = arg;
+               embed_c[1] = inst->perl_flags;
+               embed_c[2] = inst->module;
+               embed_c[3] = arg;
                argc = 4;
        } else {
-               embed[1] = inst->module;
-               embed[2] = arg;
+               embed_c[1] = inst->module;
+               embed_c[2] = arg;
                argc = 3;
        }
 
@@ -457,7 +524,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        PL_endav = Nullav;
 
        if(!exitstatus) {
-               exitstatus = perl_run(inst->perl);
+               perl_run(inst->perl);
        } else {
                ERROR("rlm_perl: perl_parse failed: %s not found or has syntax errors. \n", inst->module);
                return (-1);
@@ -472,6 +539,18 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                xlat_register(xlat_name, perl_xlat, NULL, inst);
        }
 
+       /* parse perl configuration sub-section */
+       CONF_SECTION *cs;
+       cs = cf_section_sub_find(conf, "config");
+       if (cs) {
+               DEBUG("rlm_perl (%s): parsing 'config' section...", xlat_name);
+
+               inst->rad_perlconf_hv = get_hv("RAD_PERLCONF",1);
+               perl_parse_config(cs, 0, inst->rad_perlconf_hv);
+
+               DEBUG("rlm_perl (%s): done parsing 'config'.", xlat_name);
+       }
+
        return 0;
 }
 
@@ -481,14 +560,14 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     Example for this is Cisco-AVPair that holds multiple values.
  *     Which will be available as array_ref in $RAD_REQUEST{'Cisco-AVPair'}
  */
-static void perl_store_vps(TALLOC_CTX *ctx, VALUE_PAIR *vps, HV *rad_hv)
+static void perl_store_vps(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR *vps, HV *rad_hv)
 {
        VALUE_PAIR *head, *sublist;
        AV *av;
        char const *name;
        char namebuf[256];
        char buffer[1024];
-       int len;
+       size_t len;
 
        hv_undef(rad_hv);
 
@@ -501,6 +580,7 @@ static void perl_store_vps(TALLOC_CTX *ctx, VALUE_PAIR *vps, HV *rad_hv)
 
        while (head) {
                vp_cursor_t cursor;
+
                /*
                 *      Tagged attributes are added to the hash with name
                 *      <attribute>:<tag>, others just use the normal attribute
@@ -521,19 +601,26 @@ static void perl_store_vps(TALLOC_CTX *ctx, VALUE_PAIR *vps, HV *rad_hv)
                sublist = NULL;
                pairfilter(ctx, &sublist, &head, head->da->attr, head->da->vendor, head->tag);
 
-               paircursor(&cursor, &sublist);
+               fr_cursor_init(&cursor, &sublist);
+
                /*
                 *      Attribute has multiple values
                 */
-               if (pairnext(&cursor)) {
+               if (fr_cursor_next(&cursor)) {
                        VALUE_PAIR *vp;
 
                        av = newAV();
-                       for (vp = pairfirst(&cursor);
+                       for (vp = fr_cursor_first(&cursor);
                             vp;
-                            vp = pairnext(&cursor)) {
-                               len = vp_prints_value(buffer, sizeof(buffer), vp, 0);
-                               av_push(av, newSVpv(buffer, len));
+                            vp = fr_cursor_next(&cursor)) {
+                               if (vp->da->type != PW_TYPE_STRING) {
+                                       len = vp_prints_value(buffer, sizeof(buffer), vp, 0);
+                                       av_push(av, newSVpv(buffer, truncate_len(len, sizeof(buffer))));
+                                       RDEBUG("<--  %s = %s", vp->da->name, buffer);
+                               } else {
+                                       av_push(av, newSVpv(vp->vp_strvalue, vp->length));
+                                       RDEBUG("<--  %s = %s", vp->da->name, vp->vp_strvalue);
+                               }
                        }
                        (void)hv_store(rad_hv, name, strlen(name), newRV_noinc((SV *)av), 0);
 
@@ -541,9 +628,16 @@ static void perl_store_vps(TALLOC_CTX *ctx, VALUE_PAIR *vps, HV *rad_hv)
                         *      Attribute has a single value, so its value just gets
                         *      added to the hash.
                         */
-               } else {
-                       len = vp_prints_value(buffer, sizeof(buffer), sublist, 0);
-                       (void)hv_store(rad_hv, name, strlen(name), newSVpv(buffer, len), 0);
+               } else if (sublist) {
+
+                       if (sublist->da->type != PW_TYPE_STRING) {
+                               len = vp_prints_value(buffer, sizeof(buffer), sublist, 0);
+                               (void)hv_store(rad_hv, name, strlen(name), newSVpv(buffer, truncate_len(len, sizeof(buffer))), 0);
+                               RDEBUG("<--  %s = %s", sublist->da->name, buffer);
+                       } else {
+                               (void)hv_store(rad_hv, name, strlen(name), newSVpv(sublist->vp_strvalue, sublist->length), 0);
+                               RDEBUG("<--  %s = %s", sublist->da->name, sublist->vp_strvalue);
+                       }
                }
 
                pairfree(&sublist);
@@ -558,20 +652,29 @@ static void perl_store_vps(TALLOC_CTX *ctx, VALUE_PAIR *vps, HV *rad_hv)
  *     Value Pair Format
  *
  */
-static int pairadd_sv(TALLOC_CTX *ctx, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op)
+static int pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op)
 {
        char        *val;
        VALUE_PAIR      *vp;
 
        if (SvOK(sv)) {
-               val = SvPV_nolen(sv);
-               vp = pairmake(ctx, vps, key, val, op);
-               if (vp != NULL) {
-                       DEBUG("rlm_perl: Added pair %s = %s", key, val);
-                       return 1;
+               STRLEN len;
+               val = SvPV(sv, len);
+               vp = pairmake(ctx, vps, key, NULL, op);
+               if (!vp) {
+               fail:
+                       REDEBUG("Failed to create pair %s = %s", key, val);
+                       return 0;
+               }
+
+               if (vp->da->type != PW_TYPE_STRING) {
+                       if (pairparsevalue(vp, val, 0) < 0) goto fail;
                } else {
-                       EDEBUG("rlm_perl: Failed to create pair %s = %s", key, val);
+                       pairstrncpy(vp, val, len);
                }
+
+               RDEBUG("-->  %s = %s", key, val);
+               return 1;
        }
        return 0;
 }
@@ -580,7 +683,7 @@ static int pairadd_sv(TALLOC_CTX *ctx, VALUE_PAIR **vps, char *key, SV *sv, FR_T
  *     Boyan :
  *     Gets the content from hashes
  */
-static int get_hv_content(TALLOC_CTX *ctx, HV *my_hv, VALUE_PAIR **vps)
+static int get_hv_content(TALLOC_CTX *ctx, REQUEST *request, HV *my_hv, VALUE_PAIR **vps)
 {
        SV              *res_sv, **av_sv;
        AV              *av;
@@ -596,9 +699,9 @@ static int get_hv_content(TALLOC_CTX *ctx, HV *my_hv, VALUE_PAIR **vps)
                        len = av_len(av);
                        for (j = 0; j <= len; j++) {
                                av_sv = av_fetch(av, j, 0);
-                               ret = pairadd_sv(ctx, vps, key, *av_sv, T_OP_ADD) + ret;
+                               ret = pairadd_sv(ctx, request, vps, key, *av_sv, T_OP_ADD) + ret;
                        }
-               } else ret = pairadd_sv(ctx, vps, key, res_sv, T_OP_EQ) + ret;
+               } else ret = pairadd_sv(ctx, request, vps, key, res_sv, T_OP_EQ) + ret;
        }
 
        return ret;
@@ -609,7 +712,7 @@ static int get_hv_content(TALLOC_CTX *ctx, HV *my_hv, VALUE_PAIR **vps)
  *     Store all vps in hashes %RAD_CHECK %RAD_REPLY %RAD_REQUEST
  *
  */
-static int do_perl(void *instance, REQUEST *request, char *function_name)
+static int do_perl(void *instance, REQUEST *request, char const *function_name)
 {
 
        rlm_perl_t      *inst = instance;
@@ -659,23 +762,23 @@ static int do_perl(void *instance, REQUEST *request, char *function_name)
                rad_config_hv = get_hv("RAD_CONFIG",1);
                rad_request_hv = get_hv("RAD_REQUEST",1);
 
-               perl_store_vps(request->reply, request->reply->vps, rad_reply_hv);
-               perl_store_vps(request, request->config_items, rad_check_hv);
-               perl_store_vps(request->packet, request->packet->vps, rad_request_hv);
-               perl_store_vps(request, request->config_items, rad_config_hv);
+               perl_store_vps(request->reply, request, request->reply->vps, rad_reply_hv);
+               perl_store_vps(request, request, request->config_items, rad_check_hv);
+               perl_store_vps(request->packet, request, request->packet->vps, rad_request_hv);
+               perl_store_vps(request, request, request->config_items, rad_config_hv);
 
 #ifdef WITH_PROXY
                rad_request_proxy_hv = get_hv("RAD_REQUEST_PROXY",1);
                rad_request_proxy_reply_hv = get_hv("RAD_REQUEST_PROXY_REPLY",1);
 
                if (request->proxy != NULL) {
-                       perl_store_vps(request->proxy, request->proxy->vps, rad_request_proxy_hv);
+                       perl_store_vps(request->proxy, request, request->proxy->vps, rad_request_proxy_hv);
                } else {
                        hv_undef(rad_request_proxy_hv);
                }
 
                if (request->proxy_reply !=NULL) {
-                       perl_store_vps(request->proxy_reply, request->proxy_reply->vps, rad_request_proxy_reply_hv);
+                       perl_store_vps(request->proxy_reply, request, request->proxy_reply->vps, rad_request_proxy_reply_hv);
                } else {
                        hv_undef(rad_request_proxy_reply_hv);
                }
@@ -714,7 +817,7 @@ static int do_perl(void *instance, REQUEST *request, char *function_name)
                LEAVE;
 
                vp = NULL;
-               if ((get_hv_content(request->packet, rad_request_hv, &vp)) > 0 ) {
+               if ((get_hv_content(request->packet, request, rad_request_hv, &vp)) > 0 ) {
                        pairfree(&request->packet->vps);
                        request->packet->vps = vp;
                        vp = NULL;
@@ -728,13 +831,13 @@ static int do_perl(void *instance, REQUEST *request, char *function_name)
                                request->password = pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
                }
 
-               if ((get_hv_content(request->reply, rad_reply_hv, &vp)) > 0 ) {
+               if ((get_hv_content(request->reply, request, rad_reply_hv, &vp)) > 0 ) {
                        pairfree(&request->reply->vps);
                        request->reply->vps = vp;
                        vp = NULL;
                }
 
-               if ((get_hv_content(request, rad_check_hv, &vp)) > 0 ) {
+               if ((get_hv_content(request, request, rad_check_hv, &vp)) > 0 ) {
                        pairfree(&request->config_items);
                        request->config_items = vp;
                        vp = NULL;
@@ -742,14 +845,14 @@ static int do_perl(void *instance, REQUEST *request, char *function_name)
 
 #ifdef WITH_PROXY
                if (request->proxy &&
-                   (get_hv_content(request->proxy, rad_request_proxy_hv, &vp) > 0)) {
+                   (get_hv_content(request->proxy, request, rad_request_proxy_hv, &vp) > 0)) {
                        pairfree(&request->proxy->vps);
                        request->proxy->vps = vp;
                        vp = NULL;
                }
 
                if (request->proxy_reply &&
-                   (get_hv_content(request->proxy_reply, rad_request_proxy_reply_hv, &vp) > 0)) {
+                   (get_hv_content(request->proxy_reply, request, rad_request_proxy_reply_hv, &vp) > 0)) {
                        pairfree(&request->proxy_reply->vps);
                        request->proxy_reply->vps = vp;
                        vp = NULL;
@@ -760,7 +863,7 @@ static int do_perl(void *instance, REQUEST *request, char *function_name)
        return exitstatus;
 }
 
-#define RLM_PERL_FUNC(_x) static rlm_rcode_t mod_##_x(void *instance, REQUEST *request) \
+#define RLM_PERL_FUNC(_x) static rlm_rcode_t CC_HINT(nonnull) mod_##_x(void *instance, REQUEST *request) \
        {                                                               \
                return do_perl(instance, request,                       \
                               ((rlm_perl_t *)instance)->func_##_x); \
@@ -787,7 +890,7 @@ RLM_PERL_FUNC(preacct)
 /*
  *     Write accounting information to this modules database.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        VALUE_PAIR      *pair;
        int             acctstatustype=0;
@@ -839,6 +942,8 @@ static int mod_detach(void *instance)
        rlm_perl_t      *inst = (rlm_perl_t *) instance;
        int             exitstatus = 0, count = 0;
 
+       hv_undef(inst->rad_perlconf_hv);
+
 #if 0
        /*
         *      FIXME: Call this in the destruct function?
index 5b9b5ac..c19b800 100644 (file)
@@ -30,12 +30,12 @@ RCSID("$Id$")
 #include       <ctype.h>
 
 typedef struct rlm_preprocess_t {
-       char            *huntgroup_file;
-       char            *hints_file;
+       char const      *huntgroup_file;
+       char const      *hints_file;
        PAIR_LIST       *huntgroups;
        PAIR_LIST       *hints;
        bool            with_ascend_hack;
-       int             ascend_channels_per_line;
+       uint32_t        ascend_channels_per_line;
        bool            with_ntdomain_hack;
        bool            with_specialix_jetstream_hack;
        bool            with_cisco_vsa_hack;
@@ -44,27 +44,17 @@ typedef struct rlm_preprocess_t {
 } rlm_preprocess_t;
 
 static const CONF_PARSER module_config[] = {
-       { "huntgroups",                 PW_TYPE_FILE_INPUT,
-         offsetof(rlm_preprocess_t,huntgroup_file), NULL, NULL },
-       { "hints",                      PW_TYPE_FILE_INPUT,
-         offsetof(rlm_preprocess_t,hints_file), NULL, NULL },
-       { "with_ascend_hack",           PW_TYPE_BOOLEAN,
-         offsetof(rlm_preprocess_t,with_ascend_hack), NULL, "no" },
-       { "ascend_channels_per_line",   PW_TYPE_INTEGER,
-         offsetof(rlm_preprocess_t,ascend_channels_per_line), NULL, "23" },
-
-       { "with_ntdomain_hack",         PW_TYPE_BOOLEAN,
-         offsetof(rlm_preprocess_t,with_ntdomain_hack), NULL, "no" },
-       { "with_specialix_jetstream_hack",  PW_TYPE_BOOLEAN,
-         offsetof(rlm_preprocess_t,with_specialix_jetstream_hack), NULL,
-         "no" },
-       { "with_cisco_vsa_hack",        PW_TYPE_BOOLEAN,
-         offsetof(rlm_preprocess_t,with_cisco_vsa_hack), NULL, "no" },
-       { "with_alvarion_vsa_hack",     PW_TYPE_BOOLEAN,
-         offsetof(rlm_preprocess_t,with_alvarion_vsa_hack), NULL, "no" },
+       { "huntgroups", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_preprocess_t, huntgroup_file), NULL },
+       { "hints", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_preprocess_t, hints_file), NULL },
+       { "with_ascend_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_ascend_hack), "no" },
+       { "ascend_channels_per_line", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_preprocess_t, ascend_channels_per_line), "23" },
+
+       { "with_ntdomain_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_ntdomain_hack), "no" },
+       { "with_specialix_jetstream_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_specialix_jetstream_hack), "no"  },
+       { "with_cisco_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cisco_vsa_hack), "no" },
+       { "with_alvarion_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_alvarion_vsa_hack), "no" },
 #if 0
-       { "with_cablelabs_vsa_hack",    PW_TYPE_BOOLEAN,
-         offsetof(rlm_preprocess_t,with_cablelabs_vsa_hack), NULL, NULL },
+       { "with_cablelabs_vsa_hack", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_preprocess_t, with_cablelabs_vsa_hack), NULL },
 #endif
        { NULL, -1, 0, NULL, NULL }
 };
@@ -118,9 +108,9 @@ static void cisco_vsa_hack(REQUEST *request)
        char            newattr[MAX_STRING_LEN];
        VALUE_PAIR      *vp;
        vp_cursor_t     cursor;
-       for (vp = paircursor(&cursor, &request->packet->vps);
+       for (vp = fr_cursor_init(&cursor, &request->packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                vendorcode = vp->da->vendor;
                if (!((vendorcode == 9) || (vendorcode == 6618))) {
                        continue; /* not a Cisco or Quintum VSA, continue */
@@ -153,7 +143,7 @@ static void cisco_vsa_hack(REQUEST *request)
                        char const *p;
 
                        p = vp->vp_strvalue;
-                       gettoken(&p, newattr, sizeof(newattr));
+                       gettoken(&p, newattr, sizeof(newattr), false);
 
                        if (dict_attrbyname(newattr) != NULL) {
                                pairmake_packet(newattr, ptr + 1, T_OP_EQ);
@@ -178,9 +168,9 @@ static void alvarion_vsa_hack(VALUE_PAIR *vp)
        int number = 1;
        vp_cursor_t cursor;
 
-       for (vp = paircursor(&cursor, &vp);
+       for (vp = fr_cursor_init(&cursor, &vp);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                DICT_ATTR const *da;
 
                if (vp->da->vendor != 12394) {
@@ -279,7 +269,7 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
         */
        request_pairs = request->packet->vps;
        namepair = pairfind(request_pairs, PW_USER_NAME, 0, TAG_ANY);
-       if ((!namepair) || (namepair->length <= 0)) {
+       if (!namepair || (namepair->length == 0)) {
                return;
        }
 
@@ -321,14 +311,14 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
         */
        if (pairfind(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL &&
            pairfind(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) {
-               tmp = radius_paircreate(request, &request->packet->vps, PW_SERVICE_TYPE, 0);
+               tmp = radius_paircreate(request->packet, &request->packet->vps, PW_SERVICE_TYPE, 0);
                tmp->vp_integer = PW_FRAMED_USER;
        }
 
        num_proxy_state = 0;
-       for (tmp = paircursor(&cursor, &request->packet->vps);
+       for (tmp = fr_cursor_init(&cursor, &request->packet->vps);
             tmp;
-            tmp = pairnext(&cursor)) {
+            tmp = fr_cursor_next(&cursor)) {
                if (tmp->da->vendor != 0) {
                        continue;
                }
@@ -341,8 +331,8 @@ static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
        }
 
        if (num_proxy_state > 10) {
-               RWDEBUG("There are more than 10 Proxy-State attributes in the request.");
-               RWDEBUG("You have likely configured an infinite proxy loop.");
+               RWDEBUG("There are more than 10 Proxy-State attributes in the request");
+               RWDEBUG("You have likely configured an infinite proxy loop");
        }
 }
 
@@ -360,10 +350,10 @@ static int hunt_paircmp(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check)
 
        if (!check) return 0;
 
-       for (check_item = paircursor(&cursor, &check);
+       for (check_item = fr_cursor_init(&cursor, &check);
             check_item && (result != 0);
-            check_item = pairnext(&cursor)) {
-               /* FIXME: paircopy should be removed once VALUE_PAIRs are no longer in linked lists */
+            check_item = fr_cursor_next(&cursor)) {
+               /* FIXME: paircopy should be removed once VALUE_PAIRs are no longer in linked lists */
                tmp = paircopyvp(request, check_item);
                tmp->op = check_item->op;
                result = paircompare(req, request, check_item, NULL);
@@ -410,7 +400,7 @@ static int hints_setup(PAIR_LIST *hints, REQUEST *request)
                 */
                if (((strcmp(i->name, "DEFAULT") == 0) || (strcmp(i->name, name) == 0)) &&
                    (paircompare(request, request_pairs, i->check, NULL) == 0)) {
-                       RDEBUG2("  hints: Matched %s at %d", i->name, i->lineno);
+                       RDEBUG2("hints: Matched %s at %d", i->name, i->lineno);
                        /*
                         *      Now add all attributes to the request list,
                         *      except PW_STRIP_USER_NAME and PW_FALL_THROUGH
@@ -421,9 +411,8 @@ static int hints_setup(PAIR_LIST *hints, REQUEST *request)
 
                        pairdelete(&add, PW_STRIP_USER_NAME, 0, TAG_ANY);
                        pairdelete(&add, PW_FALL_THROUGH, 0, TAG_ANY);
-                       radius_xlat_move(request, &request->packet->vps, &add);
+                       radius_pairmove(request, &request->packet->vps, add, true);
 
-                       pairfree(&add);
                        updated = 1;
                        if (!ft) {
                                break;
@@ -476,7 +465,7 @@ static int huntgroup_access(REQUEST *request, PAIR_LIST *huntgroups)
                         */
                        vp = pairfind(request_pairs, PW_HUNTGROUP_NAME, 0, TAG_ANY);
                        if (!vp) {
-                               vp = radius_paircreate(request, &request->packet->vps, PW_HUNTGROUP_NAME, 0);
+                               vp = radius_paircreate(request->packet, &request->packet->vps, PW_HUNTGROUP_NAME, 0);
                                pairstrcpy(vp, i->name);
                        }
                        r = RLM_MODULE_OK;
@@ -499,7 +488,7 @@ static int add_nas_attr(REQUEST *request)
        case AF_INET:
                nas = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS, 0, TAG_ANY);
                if (!nas) {
-                       nas = radius_paircreate(request, &request->packet->vps, PW_NAS_IP_ADDRESS, 0);
+                       nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IP_ADDRESS, 0);
                        nas->vp_ipaddr = request->packet->src_ipaddr.ipaddr.ip4addr.s_addr;
                }
                break;
@@ -507,7 +496,7 @@ static int add_nas_attr(REQUEST *request)
        case AF_INET6:
                nas = pairfind(request->packet->vps, PW_NAS_IPV6_ADDRESS, 0, TAG_ANY);
                if (!nas) {
-                       nas = radius_paircreate(request, &request->packet->vps, PW_NAS_IPV6_ADDRESS, 0);
+                       nas = radius_paircreate(request->packet, &request->packet->vps, PW_NAS_IPV6_ADDRESS, 0);
                        memcpy(&nas->vp_ipv6addr, &request->packet->src_ipaddr.ipaddr,
                               sizeof(request->packet->src_ipaddr.ipaddr));
                }
@@ -560,7 +549,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
 /*
  *     Preprocess a request.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        int r;
        rlm_preprocess_t *inst = instance;
@@ -582,7 +571,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        }
 
        if (inst->with_cisco_vsa_hack) {
-               /*
+               /*
                 *      We need to run this hack because the h323-conf-id
                 *      attribute should be used.
                 */
@@ -590,7 +579,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        }
 
        if (inst->with_alvarion_vsa_hack) {
-               /*
+               /*
                 *      We need to run this hack because the Alvarion
                 *      people are crazy.
                 */
@@ -598,7 +587,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        }
 
        if (inst->with_cablelabs_vsa_hack) {
-               /*
+               /*
                 *      We need to run this hack because the Cablelabs
                 *      people are crazy.
                 */
@@ -625,13 +614,9 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
        if (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
            pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
                VALUE_PAIR *vp;
-               uint8_t *p;
 
-               vp = radius_paircreate(request, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
-               vp->length = AUTH_VECTOR_LEN;
-               vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
-
-               memcpy(p, request->packet->vector, AUTH_VECTOR_LEN);
+               vp = radius_paircreate(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
+               pairmemcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
        }
 
        if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
@@ -649,7 +634,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
 /*
  *     Preprocess a request before accounting
  */
-static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
 {
        int r;
        VALUE_PAIR *vp;
@@ -662,7 +647,7 @@ static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
        rad_mangle(inst, request);
 
        if (inst->with_cisco_vsa_hack) {
-               /*
+               /*
                 *      We need to run this hack because the h323-conf-id
                 *      attribute should be used.
                 */
@@ -670,7 +655,7 @@ static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
        }
 
        if (inst->with_alvarion_vsa_hack) {
-               /*
+               /*
                 *      We need to run this hack because the Alvarion
                 *      people are crazy.
                 */
@@ -678,7 +663,7 @@ static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
        }
 
        if (inst->with_cablelabs_vsa_hack) {
-               /*
+               /*
                 *      We need to run this hack because the Cablelabs
                 *      people are crazy.
                 */
@@ -703,7 +688,7 @@ static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
        if (!vp) {
                VALUE_PAIR *delay;
 
-               vp = radius_paircreate(request, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
+               vp = radius_paircreate(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
                vp->vp_date = request->packet->timestamp.tv_sec;
 
                delay = pairfind(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
@@ -715,8 +700,8 @@ static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
        if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
                char buf[1024];
                RIDEBUG("No huntgroup access: [%s] (%s)",
-                       request->username ? request->username->vp_strvalue : "<NO User-Name>",
-                       auth_name(buf, sizeof(buf), request, 1));
+                       request->username ? request->username->vp_strvalue : "<NO User-Name>",
+                       auth_name(buf, sizeof(buf), request, 1));
                return r;
        }
 
@@ -727,7 +712,7 @@ static rlm_rcode_t preprocess_preaccounting(void *instance, REQUEST *request)
 module_t rlm_preprocess = {
        RLM_MODULE_INIT,
        "preprocess",
-       RLM_TYPE_CHECK_CONFIG_SAFE,             /* type */
+       0,              /* type */
        sizeof(rlm_preprocess_t),
        module_config,
        mod_instantiate,                        /* instantiation */
@@ -735,7 +720,7 @@ module_t rlm_preprocess = {
        {
                NULL,                           /* authentication */
                mod_authorize,                  /* authorization */
-               preprocess_preaccounting,       /* pre-accounting */
+               mod_preaccounting,              /* pre-accounting */
                NULL,                           /* accounting */
                NULL,                           /* checksimul */
                NULL,                           /* pre-proxy */
index ba68037..276a3a4 100644 (file)
@@ -10,12 +10,16 @@ SRC_CFLAGS  := @mod_cflags@
 TGT_LDLIBS     := @mod_ldflags@
 
 ifneq "$(TARGETNAME)" ""
-install: $(R)$(modconfdir)/python/example.py
+install: $(R)$(modconfdir)/python/radiusd.py $(R)$(modconfdir)/python/example.py
 
 $(R)$(modconfdir)/python: | $(R)$(modconfdir)
        @echo INSTALL $(patsubst $(R)$(raddbdir)%,raddb%,$@)
        @$(INSTALL) -d -m 750 $@
 
+$(R)$(modconfdir)/python/radiusd.py: src/modules/rlm_python/radiusd.py | $(R)$(modconfdir)/python
+       @$(ECHO) INSTALL $(notdir $<)
+       @$(INSTALL) -m 755 $< $(R)$(modconfdir)/python
+
 $(R)$(modconfdir)/python/example.py: src/modules/rlm_python/example.py | $(R)$(modconfdir)/python
        @$(ECHO) INSTALL $(notdir $<)
        @$(INSTALL) -m 755 $< $(R)$(modconfdir)/python
index 54e12af..7a4a76d 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1212,9 +1212,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1263,10 +1263,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1924,7 +1924,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2146,7 +2146,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2862,7 +2862,7 @@ fi
 
 
 ac_safe=`echo "Python.h" | sed 'y%./+-%__pm%'`
-old_CFLAGS="$CFLAGS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_include=
 smart_include_dir=
 
@@ -2870,7 +2870,7 @@ if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python.h in $try" >&5
 $as_echo_n "checking for Python.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -2899,7 +2899,7 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" = "x"; then
@@ -2938,22 +2938,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=Python.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2965,7 +2965,7 @@ eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
   for try in $smart_include_dir /usr/local/include /opt/include; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Python.h in $try" >&5
 $as_echo_n "checking for Python.h in $try... " >&6; }
-    CFLAGS="$old_CFLAGS -isystem $try"
+    CPPFLAGS="-isystem $try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -2994,19 +2994,19 @@ $as_echo "no" >&6; }
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
   done
-  CFLAGS="$old_CFLAGS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_include" != "x"; then
   eval "ac_cv_header_$ac_safe=yes"
-  CFLAGS="$old_CFLAGS $smart_include"
-  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+  CPPFLAGS="$smart_include $old_CPPFLAGS"
+  SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS"
 fi
 
                CFLAGS=$old_CFLAGS
 
                if test "x$ac_cv_header_Python_h" = "xyes"; then
-                       mod_cflags=${SMART_CFLAGS}
+                       mod_cflags="$SMART_CPPFLAGS"
                else
                        fail="$fail Python.h"
                        targetname=
@@ -3021,14 +3021,17 @@ sm_lib_safe=`echo "python${PY_VERSION}" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "Py_Initialize" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Py_Initialize in -lpython${PY_VERSION} in $try" >&5
 $as_echo_n "checking for Py_Initialize in -lpython${PY_VERSION} in $try... " >&6; }
-    LIBS="-L$try -lpython${PY_VERSION} $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpython${PY_VERSION} $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char Py_Initialize();
@@ -3042,7 +3045,8 @@ Py_Initialize()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lpython${PY_VERSION} -Wl,-rpath,$try"
+                smart_lib="-lpython${PY_VERSION}"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -3055,6 +3059,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -3074,8 +3079,8 @@ Py_Initialize()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lpython${PY_VERSION}"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lpython${PY_VERSION}"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3091,22 +3096,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpython${PY_VERSION}${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3118,22 +3123,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpython${PY_VERSION}.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3146,7 +3151,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Py_Initialize in -lpython${PY_VERSION} in $try" >&5
 $as_echo_n "checking for Py_Initialize in -lpython${PY_VERSION} in $try... " >&6; }
-    LIBS="-L$try -lpython${PY_VERSION} $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpython${PY_VERSION} $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char Py_Initialize();
@@ -3160,7 +3166,8 @@ Py_Initialize()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lpython${PY_VERSION} -Wl,-rpath,$try"
+                 smart_lib="-lpython${PY_VERSION}"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -3173,12 +3180,13 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
                LIBS=$old_LIBS
@@ -3194,14 +3202,17 @@ sm_lib_safe=`echo "python${PY_VERSION}m" | sed 'y%./+-%__p_%'`
 sm_func_safe=`echo "Py_Initialize" | sed 'y%./+-%__p_%'`
 
 old_LIBS="$LIBS"
+old_CPPFLAGS="$CPPFLAGS"
 smart_lib=
+smart_ldflags=
 smart_lib_dir=
 
 if test "x$smart_try_dir" != "x"; then
   for try in $smart_try_dir; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Py_Initialize in -lpython${PY_VERSION}m in $try" >&5
 $as_echo_n "checking for Py_Initialize in -lpython${PY_VERSION}m in $try... " >&6; }
-    LIBS="-L$try -lpython${PY_VERSION}m $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpython${PY_VERSION}m $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char Py_Initialize();
@@ -3215,7 +3226,8 @@ Py_Initialize()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                smart_lib="-L$try -lpython${PY_VERSION}m -Wl,-rpath,$try"
+                smart_lib="-lpython${PY_VERSION}m"
+                smart_ldflags="-L$try -Wl,-rpath,$try"
                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                 break
@@ -3228,6 +3240,7 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" = "x"; then
@@ -3247,8 +3260,8 @@ Py_Initialize()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lpython${PY_VERSION}m"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lpython${PY_VERSION}m"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3264,22 +3277,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpython${PY_VERSION}m${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3291,22 +3304,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpython${PY_VERSION}m.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3319,7 +3332,8 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
   for try in $smart_lib_dir /usr/local/lib /opt/lib; do
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Py_Initialize in -lpython${PY_VERSION}m in $try" >&5
 $as_echo_n "checking for Py_Initialize in -lpython${PY_VERSION}m in $try... " >&6; }
-    LIBS="-L$try -lpython${PY_VERSION}m $old_LIBS -Wl,-rpath,$try"
+    LIBS="-lpython${PY_VERSION}m $old_LIBS"
+    CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS"
     cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 extern char Py_Initialize();
@@ -3333,7 +3347,8 @@ Py_Initialize()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lpython${PY_VERSION}m -Wl,-rpath,$try"
+                 smart_lib="-lpython${PY_VERSION}m"
+                 smart_ldflags="-L$try -Wl,-rpath,$try"
                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
                  break
@@ -3346,12 +3361,13 @@ rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
   done
   LIBS="$old_LIBS"
+  CPPFLAGS="$old_CPPFLAGS"
 fi
 
 if test "x$smart_lib" != "x"; then
   eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
+  LIBS="$smart_ldflags $smart_lib $old_LIBS"
+  SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS"
 fi
 
                        eval t=\${ac_cv_lib_${sm_lib_safe}_${sm_func_safe}}
@@ -3460,11 +3476,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3970,11 +3986,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index b22dc1b..58b2d80 100644 (file)
@@ -72,7 +72,7 @@ if test x$with_[]modname != xno; then
                CFLAGS=$old_CFLAGS
 
                if test "x$ac_cv_header_Python_h" = "xyes"; then
-                       mod_cflags=${SMART_CFLAGS}
+                       mod_cflags="$SMART_CPPFLAGS"
                else
                        fail="$fail Python.h"
                        targetname=
@@ -94,10 +94,10 @@ if test x$with_[]modname != xno; then
                        if test "x$t" = "xyes"; then
                                mod_ldflags="$PY_LIB_LOC $PY_EXTRA_LIBS $SMART_LIBS -lm"
                                targetname=modname
-                       else    
+                       else
                                targetname=
                                fail="$fail libpython$PY_VERSION"
-                       fi              
+                       fi
                fi
        fi
 else
index 87c9b64..dd5b0b8 100644 (file)
@@ -1,58 +1,63 @@
 #! /usr/bin/env python
 #
-# Definitions for RADIUS programs
-#
-# Copyright 2002 Miguel A.L. Paraz <mparaz@mparaz.com>
-#
-# This should only be used when testing modules.
-# Inside freeradius, the 'radiusd' Python module is created by the C module
-# and the definitions are automatically created.
+# Python module example file
+# Miguel A.L. Paraz <mparaz@mparaz.com>
 #
 # $Id$
 
-# from modules.h
-
-RLM_MODULE_REJECT = 0
-RLM_MODULE_FAIL = 1
-RLM_MODULE_OK = 2
-RLM_MODULE_HANDLED = 3
-RLM_MODULE_INVALID = 4
-RLM_MODULE_USERLOCK = 5
-RLM_MODULE_NOTFOUND = 6
-RLM_MODULE_NOOP = 7    
-RLM_MODULE_UPDATED = 8
-RLM_MODULE_NUMCODES = 9
-
-
-# from radiusd.h
-L_DBG = 1
-L_AUTH = 2
-L_INFO = 3
-L_ERR = 4
-L_PROXY        = 5
-L_CONS = 128
-
-OP={       '{':2,   '}':3,   '(':4,   ')':5,   ',':6,   ';':7,  '+=':8,  '-=':9,  ':=':10,
-  '=':11, '!=':12, '>=':13,  '>':14, '<=':15,  '<':16, '=~':17, '!~':18, '=*':19, '!*':20,
- '==':21 , '#':22 }
-
-OP_TRY = (':=', '+=', '-=', '=' )
-
-def resolve(*lines):
-    tuples = []
-    for line in lines:
-        for op in OP_TRY:
-            arr = line.rsplit(op)
-            if len(arr)==2:
-                tuples.append((str(arr[0].strip()),OP[op],str(arr[1].strip())))
-                break
-    return tuple(tuples)
-
-# log function
-def radlog(level, msg):
-    import sys
-    sys.stdout.write(msg + '\n')
-
-    level = level
-
+import radiusd
+
+def instantiate(p):
+  print "*** instantiate ***"
+  print p
+
+def authorize(p):
+  print "*** authorize ***"
+  print
+  radiusd.radlog(radiusd.L_INFO, '*** radlog call in authorize ***')
+  print
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def preacct(p):
+  print "*** preacct ***"
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def accounting(p):
+  print "*** accounting ***"
+  radiusd.radlog(radiusd.L_INFO, '*** radlog call in accounting (0) ***')
+  print
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def pre_proxy(p):
+  print "*** pre_proxy ***"
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def post_proxy(p):
+  print "*** post_proxy ***"
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def post_auth(p):
+  print "*** post_auth ***"
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def recv_coa(p):
+  print "*** recv_coa ***"
+  print p
+  return radiusd.RLM_MODULE_OK
+
+def send_coa(p):
+  print "*** send_coa ***"
+  print p
+  return radiusd.RLM_MODULE_OK
+
+
+def detach():
+  print "*** goodbye from example.py ***"
+  return radiusd.RLM_MODULE_OK
 
index 8ed83c5..f8fb7c8 100644 (file)
@@ -46,7 +46,7 @@ def instantiate(p):
 
   try:
     dbHandle = MySQLdb.connect(db=configDb, host=configHost,
-                               user=configUser, passwd=configPasswd)
+                              user=configUser, passwd=configPasswd)
 
   except MySQLdb.OperationalError, e:
     # Report the error and return -1 for failure.
@@ -156,8 +156,8 @@ def authorize(authData):
   # We need to set an Auth-Type.
 
   return (radiusd.RLM_MODULE_UPDATED,
-          (('Session-Timeout', str(sessionTimeout)),),
-          (('Auth-Type', 'python'),))
+         (('Session-Timeout', str(sessionTimeout)),),
+         (('Auth-Type', 'python'),))
   # If you want to use different operators
   # you can do
   # return (radiusd.RLM_MODULE_UPDATED,
@@ -211,7 +211,7 @@ def accounting(acctData):
   # xxx This is simplistic as it does not record the time, etc.
   #
   sql = 'insert into sessions (username, seconds) values (%s, %d)' % \
-        (userName, int(acctSessionTime))
+       (userName, int(acctSessionTime))
 
   log(radiusd.L_DBG, sql)
 
index 4c2dae0..40247b3 100644 (file)
@@ -41,11 +41,11 @@ OP_TRY = (':=', '+=', '-=', '=' )
 def resolve(*lines):
     tuples = []
     for line in lines:
-        for op in OP_TRY:
-            arr = line.rsplit(op)
-            if len(arr)==2:
-                tuples.append((str(arr[0].strip()),OP[op],str(arr[1].strip())))
-                break
+       for op in OP_TRY:
+           arr = line.rsplit(op)
+           if len(arr)==2:
+               tuples.append((str(arr[0].strip()),OP[op],str(arr[1].strip())))
+               break
     return tuple(tuples)
 
 # log function
index 7a4e8e3..623eb87 100644 (file)
@@ -32,8 +32,13 @@ RCSID("$Id$")
 #include <Python.h>
 #include <dlfcn.h>
 
+#ifdef HAVE_PTHREAD_H
 #define Pyx_BLOCK_THREADS    {PyGILState_STATE __gstate = PyGILState_Ensure();
 #define Pyx_UNBLOCK_THREADS   PyGILState_Release(__gstate);}
+#else
+#define Pyx_BLOCK_THREADS
+#define Pyx_UNBLOCK_THREADS
+#endif
 
 /*
  *     TODO: The only needed thing here is function. Anything else is
@@ -41,44 +46,42 @@ RCSID("$Id$")
  *     symbolic constant here instead.
  */
 struct py_function_def {
-       PyObject *module;
-       PyObject *function;
+       PyObject        *module;
+       PyObject        *function;
 
-       char     *module_name;
-       char     *function_name;
+       char const      *module_name;
+       char const      *function_name;
 };
 
 typedef struct rlm_python_t {
+       void            *libpython;
+       PyThreadState   *main_thread_state;
+       char const      *python_path;
+
        struct py_function_def
        instantiate,
-               authorize,
-               authenticate,
-               preacct,
-               accounting,
-               checksimul,
-               pre_proxy,
-               post_proxy,
-               post_auth,
+       authorize,
+       authenticate,
+       preacct,
+       accounting,
+       checksimul,
+       pre_proxy,
+       post_proxy,
+       post_auth,
 #ifdef WITH_COA
-               recv_coa,
-               send_coa,
+       recv_coa,
+       send_coa,
 #endif
-               detach;
+       detach;
 } rlm_python_t;
 
 /*
  *     A mapping of configuration file names to internal variables.
- *
- *     Note that the string is dynamically allocated, so it MUST
- *     be freed.  When the configuration file parse re-reads the string,
- *     it free's the old one, and strdup's the new one, placing the pointer
- *     to the strdup'd string into 'config.string'.  This gets around
- *     buffer over-flows.
  */
 static CONF_PARSER module_config[] = {
 
-#define A(x) { "mod_" #x, PW_TYPE_STRING_PTR, offsetof(rlm_python_t, x.module_name), NULL, NULL }, \
-       { "func_" #x, PW_TYPE_STRING_PTR, offsetof(rlm_python_t, x.function_name), NULL, NULL },
+#define A(x) { "mod_" #x, FR_CONF_OFFSET(PW_TYPE_STRING, rlm_python_t, x.module_name), NULL }, \
+       { "func_" #x, FR_CONF_OFFSET(PW_TYPE_STRING, rlm_python_t, x.function_name), NULL },
 
        A(instantiate)
        A(authorize)
@@ -97,6 +100,8 @@ static CONF_PARSER module_config[] = {
 
 #undef A
 
+       { "python_path", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_python_t, python_path), NULL },
+
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
@@ -128,6 +133,11 @@ static struct {
        { NULL, 0 },
 };
 
+/*
+ *     This allows us to initialise PyThreadState on a per thread basis
+ */
+fr_thread_local_setup(PyThreadState *, local_thread_state);    /* macro */
+
 
 /*
  *     Let assume that radiusd module is only one since we have only
@@ -168,16 +178,11 @@ static PyMethodDef radiusd_methods[] = {
 
 static void mod_error(void)
 {
-       PyObject
-               *pType = NULL,
-               *pValue = NULL,
-               *pTraceback = NULL,
-               *pStr1 = NULL,
-               *pStr2 = NULL;
+       PyObject *pType = NULL, *pValue = NULL, *pTraceback = NULL, *pStr1 = NULL, *pStr2 = NULL;
 
-       Pyx_BLOCK_THREADS
+       /* This will be called with the GIL lock held */
 
-               PyErr_Fetch(&pType, &pValue, &pTraceback);
+       PyErr_Fetch(&pType, &pValue, &pTraceback);
        if (!pType || !pValue)
                goto failed;
        if (((pStr1 = PyObject_Str(pType)) == NULL) ||
@@ -192,11 +197,9 @@ failed:
        Py_XDECREF(pType);
        Py_XDECREF(pValue);
        Py_XDECREF(pTraceback);
+}
 
-       Pyx_UNBLOCK_THREADS
-               }
-
-static int mod_init(void)
+static int mod_init(rlm_python_t *inst)
 {
        int i;
        static char name[] = "radiusd";
@@ -206,16 +209,27 @@ static int mod_init(void)
        /*
         *      Explicitly load libpython, so symbols will be available to lib-dynload modules
         */
-       if (!dlopen("libpython" STRINGIFY(PY_MAJOR_VERSION) "." STRINGIFY(PY_MINOR_VERSION) ".so",
-                   RTLD_NOW | RTLD_GLOBAL)) {
-               WARN("Failed loading libpython symbols into global symbol table: %s", dlerror());
+       inst->libpython = dlopen("libpython" STRINGIFY(PY_MAJOR_VERSION) "." STRINGIFY(PY_MINOR_VERSION) ".so",
+                                RTLD_NOW | RTLD_GLOBAL);
+       if (!inst->libpython) {
+               WARN("Failed loading libpython symbols into global symbol table: %s", dlerror());
        }
 
        Py_SetProgramName(name);
+#ifdef HAVE_PTHREAD_H
        Py_InitializeEx(0);                             /* Don't override signal handlers */
        PyEval_InitThreads();                           /* This also grabs a lock */
+       inst->main_thread_state = PyThreadState_Get();  /* We need this for setting up thread local stuff */
+#endif
+       if (inst->python_path) {
+               char *path;
+
+               memcpy(&path, &inst->python_path, sizeof(path));
+               PySys_SetPath(path);
+       }
+
        if ((radiusd_module = Py_InitModule3("radiusd", radiusd_methods,
-                                            "FreeRADIUS Module.")) == NULL)
+                                            "FreeRADIUS Module")) == NULL)
                goto failed;
 
        for (i = 0; radiusd_constants[i].name; i++) {
@@ -225,15 +239,26 @@ static int mod_init(void)
                }
        }
 
-       PyEval_ReleaseLock(); /* Drop lock grabbed by InitThreads */
-
+#ifdef HAVE_PTHREAD_H
+       PyThreadState_Swap(NULL);       /* We have to swap out the current thread else we get deadlocks */
+       PyEval_ReleaseLock();           /* Drop lock grabbed by InitThreads */
+#endif
        DEBUG("mod_init done");
        return 0;
 
 failed:
-       mod_error();
        Py_XDECREF(radiusd_module);
+
+#ifdef HAVE_PTHREAD_H
+       PyEval_ReleaseLock();
+#endif
+
+       Pyx_BLOCK_THREADS
+       mod_error();
+       Pyx_UNBLOCK_THREADS
+
        radiusd_module = NULL;
+
        Py_Finalize();
        return -1;
 }
@@ -243,10 +268,11 @@ failed:
 static int mod_destroy(void)
 {
        Pyx_BLOCK_THREADS
-               Py_XDECREF(radiusd_module);
+       Py_XDECREF(radiusd_module);
        Py_Finalize();
        Pyx_UNBLOCK_THREADS
-               return 0;
+
+       return 0;
 }
 
 /*
@@ -362,7 +388,22 @@ failed:
        return -1;
 }
 
-static rlm_rcode_t do_python(REQUEST *request, PyObject *pFunc, char const *funcname)
+#ifdef HAVE_PTHREAD_H
+/** Cleanup any thread local storage on pthread_exit()
+ */
+static void do_python_cleanup(void *arg)
+{
+       PyThreadState   *my_thread_state = arg;
+
+       PyEval_AcquireLock();
+       PyThreadState_Swap(NULL);       /* Not entirely sure this is needed */
+       PyThreadState_Clear(my_thread_state);
+       PyThreadState_Delete(my_thread_state);
+       PyEval_ReleaseLock();
+}
+#endif
+
+static rlm_rcode_t do_python(rlm_python_t *inst, REQUEST *request, PyObject *pFunc, char const *funcname, bool worker)
 {
        vp_cursor_t     cursor;
        VALUE_PAIR      *vp;
@@ -372,11 +413,41 @@ static rlm_rcode_t do_python(REQUEST *request, PyObject *pFunc, char const *func
        int             ret;
 
        PyGILState_STATE gstate;
+       PyThreadState   *prev_thread_state = NULL;      /* -Wuninitialized */
+       memset(&gstate, 0, sizeof(gstate));             /* -Wuninitialized */
 
-       /* Return with "noop" if the function is not defined. */
+       /* Return with "OK, continue" if the function is not defined. */
        if (!pFunc)
                return RLM_MODULE_NOOP;
 
+#ifdef HAVE_PTHREAD_H
+       gstate = PyGILState_Ensure();
+       if (worker) {
+               PyThreadState *my_thread_state;
+               my_thread_state = fr_thread_local_init(local_thread_state, do_python_cleanup);
+               if (!my_thread_state) {
+                       my_thread_state = PyThreadState_New(inst->main_thread_state->interp);
+                       RDEBUG3("Initialised new thread state %p", my_thread_state);
+                       if (!my_thread_state) {
+                               REDEBUG("Failed initialising local PyThreadState on first run");
+                               PyGILState_Release(gstate);
+                               return RLM_MODULE_FAIL;
+                       }
+
+                       ret = fr_thread_local_set(local_thread_state, my_thread_state);
+                       if (ret != 0) {
+                               REDEBUG("Failed storing PyThreadState in TLS: %s", fr_syserror(ret));
+                               PyThreadState_Clear(my_thread_state);
+                               PyThreadState_Delete(my_thread_state);
+                               PyGILState_Release(gstate);
+                               return RLM_MODULE_FAIL;
+                       }
+               }
+               RDEBUG3("Using thread state %p", my_thread_state);
+               prev_thread_state = PyThreadState_Swap(my_thread_state);        /* Swap in our local thread state */
+       }
+#endif
+
        /* Default return value is "OK, continue" */
        ret = RLM_MODULE_OK;
 
@@ -390,31 +461,33 @@ static rlm_rcode_t do_python(REQUEST *request, PyObject *pFunc, char const *func
         */
        tuplelen = 0;
        if (request != NULL) {
-               for (vp = paircursor(&cursor, &request->packet->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        tuplelen++;
                }
        }
 
-       gstate = PyGILState_Ensure();
-
        if (tuplelen == 0) {
                Py_INCREF(Py_None);
                pArgs = Py_None;
        } else {
                int i = 0;
-               if ((pArgs = PyTuple_New(tuplelen)) == NULL)
-                       goto failed;
+               if ((pArgs = PyTuple_New(tuplelen)) == NULL) {
+                       ret = RLM_MODULE_FAIL;
+                       goto finish;
+               }
 
-               for (vp = paircursor(&cursor, &request->packet->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor), i++) {
+                    vp = fr_cursor_next(&cursor), i++) {
                        PyObject *pPair;
 
                        /* The inside tuple has two only: */
-                       if ((pPair = PyTuple_New(2)) == NULL)
-                               goto failed;
+                       if ((pPair = PyTuple_New(2)) == NULL) {
+                               ret = RLM_MODULE_FAIL;
+                               goto finish;
+                       }
 
                        if (mod_populate_vptuple(pPair, vp) == 0) {
                                /* Put the tuple inside the container */
@@ -430,11 +503,13 @@ static rlm_rcode_t do_python(REQUEST *request, PyObject *pFunc, char const *func
        /* Call Python function. */
        pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL);
 
-       if (!pRet)
-               goto failed;
+       if (!pRet) {
+               ret = RLM_MODULE_FAIL;
+               goto finish;
+       }
 
        if (!request)
-               goto okay;
+               goto finish;
 
        /*
         *      The function returns either:
@@ -454,13 +529,15 @@ static rlm_rcode_t do_python(REQUEST *request, PyObject *pFunc, char const *func
 
                if (PyTuple_GET_SIZE(pRet) != 3) {
                        ERROR("rlm_python:%s: tuple must be (return, replyTuple, configTuple)", funcname);
-                       goto failed;
+                       ret = RLM_MODULE_FAIL;
+                       goto finish;
                }
 
                pTupleInt = PyTuple_GET_ITEM(pRet, 0);
                if (!PyInt_CheckExact(pTupleInt)) {
                        ERROR("rlm_python:%s: first tuple element not an integer", funcname);
-                       goto failed;
+                       ret = RLM_MODULE_FAIL;
+                       goto finish;
                }
                /* Now have the return value */
                ret = PyInt_AsLong(pTupleInt);
@@ -481,22 +558,22 @@ static rlm_rcode_t do_python(REQUEST *request, PyObject *pFunc, char const *func
        } else {
                /* Not tuple or None */
                ERROR("rlm_python:%s: function did not return a tuple or None", funcname);
-               goto failed;
+               ret = RLM_MODULE_FAIL;
+               goto finish;
        }
 
-okay:
-       Py_DECREF(pArgs);
-       Py_DECREF(pRet);
-       PyGILState_Release(gstate);
-       return ret;
-
-failed:
-       mod_error();
+finish:
        Py_XDECREF(pArgs);
        Py_XDECREF(pRet);
+
+#ifdef HAVE_PTHREAD_H
+       if (worker) {
+               PyThreadState_Swap(prev_thread_state);
+       }
        PyGILState_Release(gstate);
+#endif
 
-       return RLM_MODULE_FAIL;
+       return ret;
 }
 
 /*
@@ -545,9 +622,9 @@ static void mod_objclear(PyObject **ob)
 {
        if (*ob != NULL) {
                Pyx_BLOCK_THREADS
-                       Py_DECREF(*ob);
+               Py_DECREF(*ob);
                Pyx_UNBLOCK_THREADS
-                       *ob = NULL;
+               *ob = NULL;
        }
 }
 
@@ -587,7 +664,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
 {
        rlm_python_t *inst = instance;
 
-       if (mod_init() != 0) {
+       if (mod_init(inst) != 0) {
                return -1;
        }
 
@@ -614,10 +691,11 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
         *      Call the instantiate function.  No request.  Use the
         *      return value.
         */
-       return do_python(NULL, inst->instantiate.function,
-                        "instantiate");
+       return do_python(inst, NULL, inst->instantiate.function, "instantiate", false);
 failed:
+       Pyx_BLOCK_THREADS
        mod_error();
+       Pyx_UNBLOCK_THREADS
        mod_instance_clear(inst);
        return -1;
 }
@@ -627,14 +705,19 @@ static int mod_detach(void *instance)
        rlm_python_t *inst = instance;
        int          ret;
 
-       ret = do_python(NULL, inst->detach.function, "detach");
+       /*
+        *      Master should still have no thread state
+        */
+       ret = do_python(inst, NULL, inst->detach.function, "detach", false);
 
        mod_instance_clear(inst);
+       dlclose(inst->libpython);
+
        return ret;
 }
 
-#define A(x) static rlm_rcode_t mod_##x(void *instance, REQUEST *request) { \
-               return do_python(request, ((rlm_python_t *)instance)->x.function, #x); \
+#define A(x) static rlm_rcode_t CC_HINT(nonnull) mod_##x(void *instance, REQUEST *request) { \
+               return do_python((rlm_python_t *) instance, request, ((rlm_python_t *)instance)->x.function, #x, true);\
        }
 
 A(authenticate)
index 64ac92b..aa7edfb 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1248,9 +1248,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1293,10 +1293,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2068,7 +2068,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2290,7 +2290,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3257,11 +3257,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3735,13 +3735,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 41b86b0..6fe71ba 100644 (file)
@@ -28,7 +28,6 @@ RCSID("$Id$")
 #include       <freeradius-devel/rad_assert.h>
 
 #include       <fcntl.h>
-#include       <limits.h>
 
 #include "config.h"
 
@@ -42,38 +41,30 @@ static char const porttypes[] = "ASITX";
  */
 typedef struct nas_port {
        uint32_t                nasaddr;
-       unsigned int    port;
+       uint16_t                port;
        off_t                   offset;
        struct nas_port         *next;
 } NAS_PORT;
 
 typedef struct rlm_radutmp_t {
        NAS_PORT        *nas_port_list;
-       char            *filename;
-       char            *username;
+       char const      *filename;
+       char const      *username;
        bool            case_sensitive;
        bool            check_nas;
-       int             permission;
+       uint32_t        permission;
        bool            caller_id_ok;
 } rlm_radutmp_t;
 
 static const CONF_PARSER module_config[] = {
-       { "filename", PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED,
-         offsetof(rlm_radutmp_t,filename), NULL,  RADUTMP },
-       { "username", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_radutmp_t,username), NULL,  "%{User-Name}"},
-       { "case_sensitive", PW_TYPE_BOOLEAN,
-         offsetof(rlm_radutmp_t,case_sensitive), NULL,  "yes"},
-       { "check_with_nas", PW_TYPE_BOOLEAN,
-         offsetof(rlm_radutmp_t,check_nas), NULL,  "yes"},
-       { "perm",     PW_TYPE_INTEGER | PW_TYPE_DEPRECATED,
-         offsetof(rlm_radutmp_t,permission), NULL,  NULL },
-       { "permissions",     PW_TYPE_INTEGER,
-         offsetof(rlm_radutmp_t,permission), NULL,  "0644" },
-       { "callerid", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED,
-         offsetof(rlm_radutmp_t,caller_id_ok), NULL, NULL },
-       { "caller_id", PW_TYPE_BOOLEAN,
-         offsetof(rlm_radutmp_t,caller_id_ok), NULL, "no" },
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_radutmp_t, filename), RADUTMP  },
+       { "username", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_radutmp_t, username), "%{User-Name}" },
+       { "case_sensitive", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_radutmp_t, case_sensitive), "yes" },
+       { "check_with_nas", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_radutmp_t, check_nas), "yes" },
+       { "perm", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_radutmp_t, permission), NULL },
+       { "permissions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_radutmp_t, permission), "0644" },
+       { "callerid", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_radutmp_t, caller_id_ok), NULL },
+       { "caller_id", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_radutmp_t, caller_id_ok), "no" },
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
@@ -91,7 +82,7 @@ static rlm_rcode_t radutmp_zap(REQUEST *request, char const *filename, uint32_t
 
        fd = open(filename, O_RDWR);
        if (fd < 0) {
-               REDEBUG("Error accessing file %s: %s", filename, strerror(errno));
+               REDEBUG("Error accessing file %s: %s", filename, fr_syserror(errno));
                return RLM_MODULE_FAIL;
        }
 
@@ -99,7 +90,7 @@ static rlm_rcode_t radutmp_zap(REQUEST *request, char const *filename, uint32_t
        *       Lock the utmp file, prefer lockf() over flock().
        */
        if (rad_lockfd(fd, LOCK_LEN) < 0) {
-               REDEBUG("Failed to acquire lock on file %s: %s", filename, strerror(errno));
+               REDEBUG("Failed to acquire lock on file %s: %s", filename, fr_syserror(errno));
                close(fd);
                return RLM_MODULE_FAIL;
        }
@@ -122,7 +113,7 @@ static rlm_rcode_t radutmp_zap(REQUEST *request, char const *filename, uint32_t
                u.time = t;
 
                if (write(fd, &u, sizeof(u)) < 0) {
-                       REDEBUG("Failed writing: %s", strerror(errno));
+                       REDEBUG("Failed writing: %s", fr_syserror(errno));
 
                        close(fd);
                        return RLM_MODULE_FAIL;
@@ -136,7 +127,7 @@ static rlm_rcode_t radutmp_zap(REQUEST *request, char const *filename, uint32_t
 /*
  *     Lookup a NAS_PORT in the nas_port_list
  */
-static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsigned int port)
+static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, uint16_t port)
 {
        NAS_PORT        *cl;
 
@@ -153,7 +144,7 @@ static NAS_PORT *nas_port_find(NAS_PORT *nas_port_list, uint32_t nasaddr, unsign
 /*
  *     Store logins in the RADIUS utmp file.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
        struct radutmp  ut, u;
@@ -163,7 +154,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        int             protocol = -1;
        time_t          t;
        int             fd = -1;
-       int             port_seen = 0;
+       bool            port_seen = false;
        int             off;
        rlm_radutmp_t   *inst = instance;
        char            ip_name[32]; /* 255.255.255.255 */
@@ -183,7 +174,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         *      Which type is this.
         */
        if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
-               RDEBUG("No Accounting-Status-Type record.");
+               RDEBUG("No Accounting-Status-Type record");
                return RLM_MODULE_NOOP;
        }
        status = vp->vp_integer;
@@ -215,7 +206,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                if (check1 == 0 || check2 == 0) {
                        break;
                }
-               INFO("rlm_radutmp: converting reboot records.");
+               INFO("rlm_radutmp: converting reboot records");
                if (status == PW_STATUS_STOP)
                        status = PW_STATUS_ACCOUNTING_OFF;
                if (status == PW_STATUS_START)
@@ -230,9 +221,9 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        /*
         *      First, find the interesting attributes.
         */
-       for (vp = paircursor(&cursor, &request->packet->vps);
+       for (vp = fr_cursor_init(&cursor, &request->packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                if (!vp->da->vendor) switch (vp->da->attr) {
                        case PW_LOGIN_IP_HOST:
                        case PW_FRAMED_IP_ADDRESS:
@@ -246,7 +237,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                                break;
                        case PW_NAS_PORT:
                                ut.nas_port = vp->vp_integer;
-                               port_seen = 1;
+                               port_seen = true;
                                break;
                        case PW_ACCT_DELAY_TIME:
                                ut.delay = vp->vp_integer;
@@ -390,7 +381,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         */
        fd = open(filename, O_RDWR|O_CREAT, inst->permission);
        if (fd < 0) {
-               REDEBUG("Error accessing file %s: %s", filename, strerror(errno));
+               REDEBUG("Error accessing file %s: %s", filename, fr_syserror(errno));
                rcode = RLM_MODULE_FAIL;
 
                goto finish;
@@ -399,13 +390,21 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        /*
         *      Lock the utmp file, prefer lockf() over flock().
         */
-       rad_lockfd(fd, LOCK_LEN);
+       if (rad_lockfd(fd, LOCK_LEN) < 0) {
+               REDEBUG("Error acquiring lock on %s: %s", filename, fr_syserror(errno));
+               rcode = RLM_MODULE_FAIL;
+
+               goto finish;
+       }
 
        /*
         *      Find the entry for this NAS / portno combination.
         */
        if ((cache = nas_port_find(inst->nas_port_list, ut.nas_address, ut.nas_port)) != NULL) {
-               lseek(fd, (off_t)cache->offset, SEEK_SET);
+               if (lseek(fd, (off_t)cache->offset, SEEK_SET) < 0) {
+                       rcode = RLM_MODULE_FAIL;
+                       goto finish;
+               }
        }
 
        r = 0;
@@ -497,7 +496,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 
                ut.type = P_LOGIN;
                if (write(fd, &ut, sizeof(u)) < 0) {
-                       REDEBUG("Failed writing: %s", strerror(errno));
+                       REDEBUG("Failed writing: %s", fr_syserror(errno));
 
                        rcode = RLM_MODULE_FAIL;
                        goto finish;
@@ -514,7 +513,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                        u.time = ut.time;
                        u.delay = ut.delay;
                        if (write(fd, &u, sizeof(u)) < 0) {
-                               REDEBUG("Failed writing: %s", strerror(errno));
+                               REDEBUG("Failed writing: %s", fr_syserror(errno));
 
                                rcode = RLM_MODULE_FAIL;
                                goto finish;
@@ -547,7 +546,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
  *     max. number of logins, do a second pass and validate all
  *     logins by querying the terminal server (using eg. SNMP).
  */
-static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST *request)
 {
        rlm_rcode_t     rcode = RLM_MODULE_OK;
        struct radutmp  u;
@@ -581,7 +580,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
                /*
                 *      Error accessing the file.
                 */
-               ERROR("rlm_radumtp: Error accessing file %s: %s", expanded, strerror(errno));
+               ERROR("rlm_radumtp: Error accessing file %s: %s", expanded, fr_syserror(errno));
 
                rcode = RLM_MODULE_FAIL;
 
@@ -661,6 +660,8 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
                        char session_id[sizeof(u.session_id) + 1];
                        char utmp_login[sizeof(u.login) + 1];
 
+                       /* Guarantee string is NULL terminated */
+                       u.session_id[sizeof(u.session_id) - 1] = '\0';
                        strlcpy(session_id, u.session_id, sizeof(session_id));
 
                        /*
@@ -736,7 +737,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request)
 module_t rlm_radutmp = {
        RLM_MODULE_INIT,
        "radutmp",
-       RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,        /* type */
+       RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_HUP_SAFE,     /* type */
        sizeof(rlm_radutmp_t),
        module_config,
        NULL,                          /* instantiation */
index 7b9966a..e52bba3 100644 (file)
@@ -31,37 +31,32 @@ RCSID("$Id$")
 #define  REALM_FORMAT_SUFFIX   1
 
 typedef struct realm_config_t {
-  int          format;
-  char         *formatstring;
-  char         *delim;
-  bool         ignore_default;
-  bool         ignore_null;
-  char         *default_community;
-  char         *rp_realm;
-  char         *trust_router;
-  unsigned int tr_port;
+       int             format;
+       char const      *format_string;
+       char const      *delim;
+       bool            ignore_default;
+       bool            ignore_null;
+       char            const *default_community;
+       char            const *rp_realm;
+       char const              *trust_router;
+       unsigned int    tr_port;
 } realm_config_t;
 
 #define stringify(s) #s
 
 static CONF_PARSER module_config[] = {
-  { "format", PW_TYPE_STRING_PTR,
-    offsetof(realm_config_t,formatstring), NULL, "suffix" },
-  { "delimiter", PW_TYPE_STRING_PTR,
-    offsetof(realm_config_t,delim), NULL, "@" },
-  { "ignore_default", PW_TYPE_BOOLEAN,
-    offsetof(realm_config_t,ignore_default), NULL, "no" },
-  { "ignore_null", PW_TYPE_BOOLEAN,
-    offsetof(realm_config_t,ignore_null), NULL, "no" },
-  { "default_community", PW_TYPE_STRING_PTR,
-    offsetof(realm_config_t,default_community), NULL, "none" },
-  { "rp_realm", PW_TYPE_STRING_PTR,
-    offsetof(realm_config_t,rp_realm), NULL, "none" },
-  { "trust_router", PW_TYPE_STRING_PTR,
-    offsetof(realm_config_t,trust_router), NULL, "none" },
-  { "tr_port", PW_TYPE_INTEGER,
-    offsetof(realm_config_t,tr_port), NULL, "0" },
-  //    offsetof(realm_config_t,tr_port), NULL, (stringify(TID_PORT)) },
+  { "format", FR_CONF_OFFSET(PW_TYPE_STRING, realm_config_t, format_string), "suffix" },
+  { "delimiter", FR_CONF_OFFSET(PW_TYPE_STRING, realm_config_t, delim), "@" },
+  { "ignore_default", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, realm_config_t, ignore_default), "no" },
+  { "ignore_null", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, realm_config_t, ignore_null), "no" },
+{ "default_community", FR_CONF_OFFSET(PW_TYPE_STRING,
+    realm_config_t,default_community),  "none" },
+{ "rp_realm", FR_CONF_OFFSET(PW_TYPE_STRING,
+    realm_config_t,rp_realm),  "none" },
+{ "trust_router", FR_CONF_OFFSET(PW_TYPE_STRING,
+    realm_config_t,trust_router),  "none" },
+{ "tr_port", FR_CONF_OFFSET(PW_TYPE_INTEGER,
+    realm_config_t,tr_port),  "0" },
   { NULL, -1, 0, NULL, NULL }    /* end the list */
 };
 
@@ -101,8 +96,8 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
 #endif
            ) {
 
-               RDEBUG2("Proxy reply, or no User-Name.  Ignoring.");
-               return RLM_MODULE_OK;
+               RDEBUG2("Proxy reply, or no User-Name.  Ignoring");
+               return RLM_MODULE_NOOP;
        }
 
        /*
@@ -111,15 +106,15 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         */
 
        if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL ) {
-               RDEBUG2("Request already has destination realm set.  Ignoring.");
-               return RLM_MODULE_OK;
+               RDEBUG2("Request already has destination realm set.  Ignoring");
+               return RLM_MODULE_NOOP;
        }
 
        /*
         *      We will be modifing this later, so we want our own copy
         *      of it.
         */
-       namebuf = talloc_strdup(request,  request->username->vp_strvalue);
+       namebuf = talloc_typed_strdup(request,  request->username->vp_strvalue);
        username = namebuf;
 
        switch(inst->format) {
@@ -184,7 +179,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
        }
        if( inst->ignore_default &&
            (strcmp(realm->name, "DEFAULT")) == 0) {
-               RDEBUG2("Found DEFAULT, but skipping due to config.");
+               RDEBUG2("Found DEFAULT, but skipping due to config");
                talloc_free(namebuf);
                return RLM_MODULE_NOOP;
        }
@@ -201,7 +196,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
                 *
                 */
                if (request->username->da->attr != PW_STRIPPED_USER_NAME) {
-                       vp = radius_paircreate(request, &request->packet->vps,
+                       vp = radius_paircreate(request->packet, &request->packet->vps,
                                               PW_STRIPPED_USER_NAME, 0);
                        RDEBUG2("Adding Stripped-User-Name = \"%s\"", username);
                } else {
@@ -226,7 +221,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
        RDEBUG2("Adding Realm = \"%s\"", realmname);
 
        talloc_free(namebuf);
-       realmname = username = NULL;
+       username = NULL;
 
        /*
         *      Figure out what to do with the request.
@@ -235,14 +230,14 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
        default:
                RDEBUG2("Unknown packet code %d\n",
                       request->packet->code);
-               return RLM_MODULE_OK;           /* don't do anything */
+               return RLM_MODULE_NOOP;
 
                /*
                 *      Perhaps accounting proxying was turned off.
                 */
-       case PW_ACCOUNTING_REQUEST:
+       case PW_CODE_ACCOUNTING_REQUEST:
                if (!realm->acct_pool) {
-                       RDEBUG2("Accounting realm is LOCAL.");
+                       RDEBUG2("Accounting realm is LOCAL");
                        return RLM_MODULE_OK;
                }
                break;
@@ -250,9 +245,9 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
                /*
                 *      Perhaps authentication proxying was turned off.
                 */
-       case PW_AUTHENTICATION_REQUEST:
+       case PW_CODE_AUTHENTICATION_REQUEST:
                if (!realm->auth_pool) {
-                       RDEBUG2("Authentication realm is LOCAL.");
+                       RDEBUG2("Authentication realm is LOCAL");
                        return RLM_MODULE_OK;
                }
                break;
@@ -266,7 +261,7 @@ static int check_for_realm(void *instance, REQUEST *request, REALM **returnrealm
         *      Skip additional checks if it's not an accounting
         *      request.
         */
-       if (request->packet->code != PW_ACCOUNTING_REQUEST) {
+       if (request->packet->code != PW_CODE_ACCOUNTING_REQUEST) {
                *returnrealm = realm;
                return RLM_MODULE_UPDATED;
        }
@@ -355,16 +350,15 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /* initialize the trust router integration code */
        if (tr_init() < 0)
               return -1;
-       if (strcasecmp(inst->formatstring, "suffix") == 0) {
-
+       if (strcasecmp(inst->format_string, "suffix") == 0) {
             inst->format = REALM_FORMAT_SUFFIX;
 
-       } else if (strcasecmp(inst->formatstring, "prefix") == 0) {
+       } else if (strcasecmp(inst->format_string, "prefix") == 0) {
             inst->format = REALM_FORMAT_PREFIX;
 
        } else {
                cf_log_err_cs(conf, "Invalid value \"%s\" for format",
-                             inst->formatstring);
+                             inst->format_string);
             return -1;
        }
 
@@ -385,7 +379,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *
  *  This should very nearly duplicate the old proxy_send() code
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_rcode_t rcode;
        REALM *realm;
@@ -413,7 +407,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
  * This does the exact same thing as the mod_authorize, it's just called
  * differently.
  */
-static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request)
 {
        int rcode;
        REALM *realm;
@@ -446,14 +440,14 @@ static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
  *     CoA realms via Operator-Name.  Because the realm isn't in a
  *     User-Name, concepts like "prefix" and "suffix' don't matter.
  */
-static rlm_rcode_t realm_recv_coa(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t mod_realm_recv_coa(UNUSED void *instance, REQUEST *request)
 {
        VALUE_PAIR *vp;
        REALM *realm;
 
        if (pairfind(request->packet->vps, PW_REALM, 0, TAG_ANY) != NULL) {
-               RDEBUG2("Request already has destination realm set.  Ignoring.");
-               return RLM_MODULE_OK;
+               RDEBUG2("Request already has destination realm set.  Ignoring");
+               return RLM_MODULE_NOOP;
        }
 
        vp = pairfind(request->packet->vps, PW_OPERATOR_NAME, 0, TAG_ANY);
@@ -479,7 +473,7 @@ static rlm_rcode_t realm_recv_coa(UNUSED void *instance, REQUEST *request)
 
 
        if (!realm->coa_pool) {
-               RDEBUG2("CoA realm is LOCAL.");
+               RDEBUG2("CoA realm is LOCAL");
                return RLM_MODULE_OK;
        }
 
@@ -498,7 +492,7 @@ static rlm_rcode_t realm_recv_coa(UNUSED void *instance, REQUEST *request)
 module_t rlm_realm = {
        RLM_MODULE_INIT,
        "realm",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(struct realm_config_t),
        module_config,
        mod_instantiate,                /* instantiation */
@@ -513,8 +507,9 @@ module_t rlm_realm = {
                NULL,                   /* post-proxy */
                NULL                    /* post-auth */
 #ifdef WITH_COA
-               , realm_recv_coa,       /* recv-coa */
+               , mod_realm_recv_coa,   /* recv-coa */
                NULL                    /* send-coa */
 #endif
        },
 };
+
index 5d9626e..83b37b6 100644 (file)
@@ -29,7 +29,7 @@ int tr_init(void)
 }
 
 static fr_tls_server_conf_t *construct_tls( TIDC_INSTANCE *inst,
-                                           home_server *hs,
+                                           home_server_t *hs,
                                            TID_SRVR_BLK *server)
 {
   fr_tls_server_conf_t *tls = talloc_zero( hs, fr_tls_server_conf_t);
@@ -86,7 +86,7 @@ static void tr_response_func( TIDC_INSTANCE *inst,
                             UNUSED TID_REQ *req, TID_RESP *resp,
                             void *cookie)
 {
-  home_server *hs = NULL;
+  home_server_t *hs = NULL;
   TID_SRVR_BLK *server;
   home_pool_t *pool = NULL;
   REALM *nr = NULL;
@@ -124,10 +124,9 @@ static void tr_response_func( TIDC_INSTANCE *inst,
   pool = home_pool_byname(home_pool_name, HOME_TYPE_AUTH);
   if (pool == NULL) {
     size_t i = 0;
-    pool = rad_malloc(sizeof(*pool) + num_servers *sizeof(home_server *));
+    pool = talloc_zero_size(NULL, sizeof(*pool) + num_servers *sizeof(home_server_t *));
                  
     if (pool == NULL) goto error;
-    memset(pool, 0, sizeof(*pool));
     pool->type = HOME_POOL_CLIENT_PORT_BALANCE;
     pool->server_type = HOME_TYPE_AUTH;
     pool->name = strdup(home_pool_name);
@@ -147,9 +146,8 @@ static void tr_response_func( TIDC_INSTANCE *inst,
       } else {
        char nametemp[INET_ADDRSTRLEN];
        inet_ntop(home_server_ip.af, &home_server_ip.ipaddr, nametemp, sizeof(nametemp));
-       hs = talloc_zero(NULL, home_server);
+       hs = talloc_zero(pool, home_server_t);
        if (!hs) return;
-       memset(hs, 0, sizeof(*hs));
        hs->type = HOME_TYPE_AUTH;
        hs->ipaddr = home_server_ip;
         hs->src_ipaddr.af = home_server_ip.af;
index d13cdbc..dfa38c6 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-redis-include-dir=DIR
-                          Directory where the redis includes may be found
+                         Directory where the redis includes may be found
   --with-redis-lib-dir=DIR
-                          Directory where the redis libraries may be found
+                         Directory where the redis libraries may be found
   --with-redis-dir=DIR    Base directory where redis is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1793,7 +1793,7 @@ SMART_CLFAGS=
 if test x$with_rlm_redis != xno; then
 
 
-        redis_include_dir=
+       redis_include_dir=
 
 # Check whether --with-redis-include-dir was given.
 if test "${with_redis_include_dir+set}" = set; then :
@@ -1810,7 +1810,7 @@ if test "${with_redis_include_dir+set}" = set; then :
 fi
 
 
-        redis_lib_dir=
+       redis_lib_dir=
 
 # Check whether --with-redis-lib-dir was given.
 if test "${with_redis_lib_dir+set}" = set; then :
@@ -1943,7 +1943,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2165,7 +2165,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2717,22 +2717,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=hiredis/hiredis.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2849,8 +2849,8 @@ redisConnect()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lhiredis"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lhiredis"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2866,22 +2866,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libhiredis${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2893,22 +2893,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libhiredis.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3062,11 +3062,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3572,11 +3572,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index b2e8c79..5aed762 100644 (file)
@@ -30,16 +30,11 @@ RCSID("$Id$")
 #include "rlm_redis.h"
 
 static const CONF_PARSER module_config[] = {
-       { "hostname", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(REDIS_INST, hostname), NULL, NULL},
-       { "server", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(REDIS_INST, hostname), NULL, NULL},
-       { "port", PW_TYPE_INTEGER,
-         offsetof(REDIS_INST, port), NULL, "6379"},
-       { "database", PW_TYPE_INTEGER,
-         offsetof(REDIS_INST, database), NULL, "0"},
-       { "password", PW_TYPE_STRING_PTR,
-         offsetof(REDIS_INST, password), NULL, NULL},
+       { "hostname", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, REDIS_INST, hostname), NULL },
+       { "server", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, REDIS_INST, hostname), NULL },
+       { "port", FR_CONF_OFFSET(PW_TYPE_SHORT, REDIS_INST, port), "6379" },
+       { "database", FR_CONF_OFFSET(PW_TYPE_INTEGER, REDIS_INST, database), "0" },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, REDIS_INST, password), NULL },
 
        { NULL, -1, 0, NULL, NULL} /* end the list */
 };
@@ -64,14 +59,13 @@ static void *mod_conn_create(void *ctx)
        REDIS_INST *inst = ctx;
        REDISSOCK *dissocket = NULL;
        redisContext *conn;
+       redisReply *reply = NULL;
        char buffer[1024];
 
        conn = redisConnect(inst->hostname, inst->port);
        if (conn->err) return NULL;
 
        if (inst->password) {
-               redisReply *reply = NULL;
-
                snprintf(buffer, sizeof(buffer), "AUTH %s", inst->password);
 
                reply = redisCommand(conn, buffer);
@@ -102,8 +96,6 @@ static void *mod_conn_create(void *ctx)
        }
 
        if (inst->database) {
-               redisReply *reply = NULL;
-
                snprintf(buffer, sizeof(buffer), "SELECT %d", inst->database);
 
                reply = redisCommand(conn, buffer);
@@ -146,12 +138,7 @@ static ssize_t redis_xlat(void *instance, REQUEST *request, char const *fmt, cha
        char buffer[21];
 
        dissocket = fr_connection_get(inst->pool);
-       if (!dissocket) {
-               ERROR("rlm_redis (%s): redis_get_socket() failed",
-                      inst->xlat_name);
-
-               return -1;
-       }
+       if (!dissocket) return -1;
 
        /* Query failed for some reason, release socket and return */
        if (rlm_redis_query(&dissocket, inst, fmt, request) < 0) {
index c42b198..27de8f6 100644 (file)
@@ -41,13 +41,13 @@ typedef struct redis_socket_t {
 typedef struct rlm_redis_t REDIS_INST;
 
 typedef struct rlm_redis_t {
-       char const          *xlat_name;
+       char const              *xlat_name;
 
-       char        *hostname;
-       int          port;
-       int             database;
-       char            *password;
-       fr_connection_pool_t *pool;
+       char const              *hostname;
+       uint16_t                port;
+       uint32_t                database;
+       char const              *password;
+       fr_connection_pool_t    *pool;
 
        int (*redis_query)(REDISSOCK **dissocket_p, REDIS_INST *inst, char const *query, REQUEST *request);
        int (*redis_finish_query)(REDISSOCK *dissocket);
index a8f83c8..feadee8 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-redis-include-dir=DIR
-                          Directory where the redis includes may be found
+                         Directory where the redis includes may be found
   --with-redis-lib-dir=DIR
-                          Directory where the redis libraries may be found
+                         Directory where the redis libraries may be found
   --with-redis-dir=DIR    Base directory where redis is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1793,7 +1793,7 @@ SMART_CLFAGS=
 if test x$with_rlm_rediswho != xno; then
 
 
-        redis_include_dir=
+       redis_include_dir=
 
 # Check whether --with-redis-include-dir was given.
 if test "${with_redis_include_dir+set}" = set; then :
@@ -1810,7 +1810,7 @@ if test "${with_redis_include_dir+set}" = set; then :
 fi
 
 
-        redis_lib_dir=
+       redis_lib_dir=
 
 # Check whether --with-redis-lib-dir was given.
 if test "${with_redis_lib_dir+set}" = set; then :
@@ -1943,7 +1943,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2165,7 +2165,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2717,22 +2717,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=hiredis/hiredis.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2849,8 +2849,8 @@ redisConnect()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lhiredis"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lhiredis"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2866,22 +2866,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libhiredis${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2893,22 +2893,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libhiredis.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3062,11 +3062,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3572,11 +3572,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index f9b5776..badebc6 100644 (file)
@@ -34,7 +34,7 @@ typedef struct rlm_rediswho_t {
        char const *xlat_name;
        CONF_SECTION *cs;
 
-       char *redis_instance_name;
+       char const *redis_instance_name;
        REDIS_INST *redis_inst;
 
        /*
@@ -49,15 +49,11 @@ typedef struct rlm_rediswho_t {
 } rlm_rediswho_t;
 
 static CONF_PARSER module_config[] = {
-       { "redis-instance-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_rediswho_t, redis_instance_name), NULL, NULL},
-       { "redis_module_instance", PW_TYPE_STRING_PTR,
-         offsetof(rlm_rediswho_t, redis_instance_name), NULL, "redis"},
+       { "redis-instance-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_rediswho_t, redis_instance_name), NULL },
+       { "redis_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rediswho_t, redis_instance_name), "redis" },
 
-       { "trim-count", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED,
-         offsetof(rlm_rediswho_t, trim_count), NULL, NULL},
-       { "trim_count", PW_TYPE_INTEGER,
-         offsetof(rlm_rediswho_t, trim_count), NULL, "-1"},
+       { "trim-count", FR_CONF_OFFSET(PW_TYPE_SIGNED | PW_TYPE_DEPRECATED, rlm_rediswho_t, trim_count), NULL },
+       { "trim_count", FR_CONF_OFFSET(PW_TYPE_SIGNED, rlm_rediswho_t, trim_count), "-1" },
 
        { NULL, -1, 0, NULL, NULL}
 };
@@ -165,7 +161,7 @@ static int mod_accounting_all(REDISSOCK **dissocket_p,
        return RLM_MODULE_OK;
 }
 
-static rlm_rcode_t mod_accounting(void * instance, REQUEST * request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void * instance, REQUEST * request)
 {
        rlm_rcode_t rcode;
        VALUE_PAIR * vp;
@@ -177,7 +173,7 @@ static rlm_rcode_t mod_accounting(void * instance, REQUEST * request)
 
        vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
        if (!vp) {
-               RDEBUG("Could not find account status type in packet.");
+               RDEBUG("Could not find account status type in packet");
                return RLM_MODULE_NOOP;
        }
 
@@ -194,10 +190,7 @@ static rlm_rcode_t mod_accounting(void * instance, REQUEST * request)
        }
 
        dissocket = fr_connection_get(inst->redis_inst->pool);
-       if (!dissocket) {
-               RDEBUG("cannot allocate redis connection");
-               return RLM_MODULE_FAIL;
-       }
+       if (!dissocket) return RLM_MODULE_FAIL;
 
        insert = cf_pair_value(cf_pair_find(cs, "insert"));
        trim = cf_pair_value(cf_pair_find(cs, "trim"));
index 85662f7..2ed4e71 100644 (file)
@@ -57,7 +57,7 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
        int rcode = RLM_MODULE_NOOP;
        VALUE_PAIR *vp, **vps;
        vp_cursor_t cursor;
-       home_server *home;
+       home_server_t *home;
        REALM *realm;
        home_pool_t *pool;
        RADIUS_PACKET *packet = NULL;
@@ -66,8 +66,8 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
         *      Send as many packets as necessary to different
         *      destinations.
         */
-       paircursor(&cursor, &request->config_items);
-       while ((vp = pairfindnext(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
+       fr_cursor_init(&cursor, &request->config_items);
+       while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
                realm = realm_find2(vp->vp_strvalue);
                if (!realm) {
                        REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue);
@@ -83,20 +83,20 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
                        cleanup(packet);
                        return RLM_MODULE_FAIL;
 
-               case PW_AUTHENTICATION_REQUEST:
+               case PW_CODE_AUTHENTICATION_REQUEST:
                        pool = realm->auth_pool;
                        break;
 
 #ifdef WITH_ACCOUNTING
 
-               case PW_ACCOUNTING_REQUEST:
+               case PW_CODE_ACCOUNTING_REQUEST:
                        pool = realm->acct_pool;
                        break;
 #endif
 
 #ifdef WITH_COA
-               case PW_COA_REQUEST:
-               case PW_DISCONNECT_REQUEST:
+               case PW_CODE_COA_REQUEST:
+               case PW_CODE_DISCONNECT_REQUEST:
                        pool = realm->acct_pool;
                        break;
 #endif
@@ -156,11 +156,11 @@ static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_
                         *      For CHAP, create the CHAP-Challenge if
                         *      it doesn't exist.
                         */
-                       if ((code == PW_AUTHENTICATION_REQUEST) &&
+                       if ((code == PW_CODE_AUTHENTICATION_REQUEST) &&
                            (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
                            (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
                                uint8_t *p;
-                               vp = radius_paircreate(request, &packet->vps, PW_CHAP_CHALLENGE, 0);
+                               vp = radius_paircreate(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
                                vp->length = AUTH_VECTOR_LEN;
                                vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
                                memcpy(p, request->packet->vector, AUTH_VECTOR_LEN);
@@ -219,40 +219,25 @@ static rlm_rcode_t replicate_packet(UNUSED void *instance,
 }
 #endif
 
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
 }
 
-static rlm_rcode_t mod_preaccounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
 {
        return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
 }
 
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
-{
-       return replicate_packet(instance, request, PAIR_LIST_REPLY, request->reply->code);
-}
-
 #ifdef WITH_PROXY
-static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request)
 {
        return replicate_packet(instance, request, PAIR_LIST_PROXY_REQUEST, request->proxy->code);
 }
-
-static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
-{
-       return replicate_packet(instance, request, PAIR_LIST_PROXY_REPLY, request->proxy_reply->code);
-}
 #endif
 
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
-{
-       return replicate_packet(instance, request, PAIR_LIST_REPLY, request->reply->code);
-}
-
 #ifdef WITH_COA
-static rlm_rcode_t mod_recv_coa(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_recv_coa(void *instance, REQUEST *request)
 {
        return replicate_packet(instance, request, PAIR_LIST_REQUEST, request->packet->code);
 }
@@ -279,15 +264,15 @@ module_t rlm_replicate = {
                NULL,                   /* authentication */
                mod_authorize,          /* authorization */
                mod_preaccounting,      /* preaccounting */
-               mod_accounting,         /* accounting */
+               NULL,                   /* accounting */
                NULL,                   /* checksimul */
 #ifdef WITH_PROXY
                mod_pre_proxy,          /* pre-proxy */
-               mod_post_proxy,         /* post-proxy */
+               NULL,                   /* post-proxy */
 #else
                NULL, NULL,
 #endif
-               mod_post_auth           /* post-auth */
+               NULL                    /* post-auth */
 #ifdef WITH_COA
                , mod_recv_coa,         /* coa-request */
                NULL
index bad2b4c..6952dd1 100644 (file)
@@ -1,41 +1,17 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
-/* Define to 1 if you have the <inttypes.h> header file. */
-#undef HAVE_INTTYPES_H
-
 /* Build with JSON support from json-c */
 #undef HAVE_JSON
 
-/* Define to 1 if you have the <json/json.h> header file. */
-#undef HAVE_JSON_JSON_H
+/* Define to 1 if you have the `json_c_version' function. */
+#undef HAVE_JSON_C_VERSION
+
+/* Define to 1 if you have the `json_type_to_name' function. */
+#undef HAVE_JSON_TYPE_TO_NAME
 
 /* Define to 1 if you have a functional curl library. */
 #undef HAVE_LIBCURL
 
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#undef HAVE_STDINT_H
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#undef HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#undef HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#undef HAVE_STRING_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#undef HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#undef HAVE_SYS_TYPES_H
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#undef HAVE_UNISTD_H
-
 /* Defined if libcurl supports AsynchDNS */
 #undef LIBCURL_FEATURE_ASYNCHDNS
 
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
-/* Define to 1 if you have the ANSI C header files. */
-#undef STDC_HEADERS
-
 /* Define curl_free() as free() if our version of curl lacks curl_free. */
 #undef curl_free
index 63b2ba4..5ea5783 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1217,9 +1217,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1262,21 +1262,21 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-libcurl=PREFIX   look for the curl library in PREFIX/lib and headers
-                          in PREFIX/include
+                         in PREFIX/include
   --with-jsonc-include-dir=DIR
-                          Directory where the json-c includes may be found
+                         Directory where the json-c includes may be found
   --with-jsonc-lib-dir=DIR
-                          Directory where the json-c libraries may be found
+                         Directory where the json-c libraries may be found
   --with-jsonc-dir=DIR    Base directory where json-c is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2041,7 +2041,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2263,7 +2263,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2956,9 +2956,9 @@ done
      _libcurl_try_link=yes
 
      if test -d "$_libcurl_with" ; then
-        LIBCURL_CPPFLAGS="-I$withval/include"
-        _libcurl_ldflags="-L$withval/lib"
-        # Extract the first word of "curl-config", so it can be a program name with args.
+       LIBCURL_CPPFLAGS="-I$withval/include"
+       _libcurl_ldflags="-L$withval/lib"
+       # Extract the first word of "curl-config", so it can be a program name with args.
 set dummy curl-config; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
@@ -2999,7 +2999,7 @@ fi
 
 
      else
-        # Extract the first word of "curl-config", so it can be a program name with args.
+       # Extract the first word of "curl-config", so it can be a program name with args.
 set dummy curl-config; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
@@ -3042,7 +3042,7 @@ fi
      fi
 
      if test x$_libcurl_config != "x" ; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the version of libcurl" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the version of libcurl" >&5
 $as_echo_n "checking for the version of libcurl... " >&6; }
 if ${libcurl_cv_lib_curl_version+:} false; then :
   $as_echo_n "(cached) " >&6
@@ -3052,76 +3052,76 @@ fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libcurl_cv_lib_curl_version" >&5
 $as_echo "$libcurl_cv_lib_curl_version" >&6; }
 
-        _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
-        _libcurl_wanted=`echo 7.19.1 | $_libcurl_version_parse`
+       _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
+       _libcurl_wanted=`echo 7.19.1 | $_libcurl_version_parse`
 
-        if test $_libcurl_wanted -gt 0 ; then
-           { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl >= version 7.19.1" >&5
+       if test $_libcurl_wanted -gt 0 ; then
+          { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcurl >= version 7.19.1" >&5
 $as_echo_n "checking for libcurl >= version 7.19.1... " >&6; }
 if ${libcurl_cv_lib_version_ok+:} false; then :
   $as_echo_n "(cached) " >&6
 else
 
-              if test $_libcurl_version -ge $_libcurl_wanted ; then
-                 libcurl_cv_lib_version_ok=yes
-              else
-                 libcurl_cv_lib_version_ok=no
-              fi
+             if test $_libcurl_version -ge $_libcurl_wanted ; then
+                libcurl_cv_lib_version_ok=yes
+             else
+                libcurl_cv_lib_version_ok=no
+             fi
 
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libcurl_cv_lib_version_ok" >&5
 $as_echo "$libcurl_cv_lib_version_ok" >&6; }
-        fi
-
-        if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
-           if test x"$LIBCURL_CPPFLAGS" = "x" ; then
-              LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
-           fi
-           if test x"$LIBCURL" = "x" ; then
-              LIBCURL=`$_libcurl_config --libs`
-
-              # This is so silly, but Apple actually has a bug in their
-              # curl-config script.  Fixed in Tiger, but there are still
-              # lots of Panther installs around.
-              case "${host}" in
-                 powerpc-apple-darwin7*)
-                    LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
-                 ;;
-              esac
-           fi
-
-           # All curl-config scripts support --feature
-           _libcurl_features=`$_libcurl_config --feature`
-
-           # Is it modern enough to have --protocols? (7.12.4)
-           if test $_libcurl_version -ge 461828 ; then
-              _libcurl_protocols=`$_libcurl_config --protocols`
-           fi
-        else
-           _libcurl_try_link=no
-        fi
-
-        unset _libcurl_wanted
+       fi
+
+       if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
+          if test x"$LIBCURL_CPPFLAGS" = "x" ; then
+             LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
+          fi
+          if test x"$LIBCURL" = "x" ; then
+             LIBCURL=`$_libcurl_config --libs`
+
+             # This is so silly, but Apple actually has a bug in their
+             # curl-config script.  Fixed in Tiger, but there are still
+             # lots of Panther installs around.
+             case "${host}" in
+                powerpc-apple-darwin7*)
+                   LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
+                ;;
+             esac
+          fi
+
+          # All curl-config scripts support --feature
+          _libcurl_features=`$_libcurl_config --feature`
+
+          # Is it modern enough to have --protocols? (7.12.4)
+          if test $_libcurl_version -ge 461828 ; then
+             _libcurl_protocols=`$_libcurl_config --protocols`
+          fi
+       else
+          _libcurl_try_link=no
+       fi
+
+       unset _libcurl_wanted
      fi
 
      if test $_libcurl_try_link = yes ; then
 
-        # we didn't find curl-config, so let's see if the user-supplied
-        # link line (or failing that, "-lcurl") is enough.
-        LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
+       # we didn't find curl-config, so let's see if the user-supplied
+       # link line (or failing that, "-lcurl") is enough.
+       LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libcurl is usable" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libcurl is usable" >&5
 $as_echo_n "checking whether libcurl is usable... " >&6; }
 if ${libcurl_cv_lib_curl_usable+:} false; then :
   $as_echo_n "(cached) " >&6
 else
 
-           _libcurl_save_cppflags=$CPPFLAGS
-           CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
-           _libcurl_save_libs=$LIBS
-           LIBS="$LIBCURL $LIBS"
+          _libcurl_save_cppflags=$CPPFLAGS
+          CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
+          _libcurl_save_libs=$LIBS
+          LIBS="$LIBCURL $LIBS"
 
-           cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+          cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <curl/curl.h>
 int
@@ -3152,26 +3152,26 @@ fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 
-           CPPFLAGS=$_libcurl_save_cppflags
-           LIBS=$_libcurl_save_libs
-           unset _libcurl_save_cppflags
-           unset _libcurl_save_libs
+          CPPFLAGS=$_libcurl_save_cppflags
+          LIBS=$_libcurl_save_libs
+          unset _libcurl_save_cppflags
+          unset _libcurl_save_libs
 
 fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $libcurl_cv_lib_curl_usable" >&5
 $as_echo "$libcurl_cv_lib_curl_usable" >&6; }
 
-        if test $libcurl_cv_lib_curl_usable = yes ; then
+       if test $libcurl_cv_lib_curl_usable = yes ; then
 
-           # Does curl_free() exist in this version of libcurl?
-           # If not, fake it with free()
+          # Does curl_free() exist in this version of libcurl?
+          # If not, fake it with free()
 
-           _libcurl_save_cppflags=$CPPFLAGS
-           CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
-           _libcurl_save_libs=$LIBS
-           LIBS="$LIBS $LIBCURL"
+          _libcurl_save_cppflags=$CPPFLAGS
+          CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
+          _libcurl_save_libs=$LIBS
+          LIBS="$LIBS $LIBCURL"
 
-           ac_fn_c_check_func "$LINENO" "curl_free" "ac_cv_func_curl_free"
+          ac_fn_c_check_func "$LINENO" "curl_free" "ac_cv_func_curl_free"
 if test "x$ac_cv_func_curl_free" = xyes; then :
 
 else
@@ -3181,10 +3181,10 @@ $as_echo "#define curl_free free" >>confdefs.h
 fi
 
 
-           CPPFLAGS=$_libcurl_save_cppflags
-           LIBS=$_libcurl_save_libs
-           unset _libcurl_save_cppflags
-           unset _libcurl_save_libs
+          CPPFLAGS=$_libcurl_save_cppflags
+          LIBS=$_libcurl_save_libs
+          unset _libcurl_save_cppflags
+          unset _libcurl_save_libs
 
 
 $as_echo "#define HAVE_LIBCURL 1" >>confdefs.h
@@ -3192,48 +3192,48 @@ $as_echo "#define HAVE_LIBCURL 1" >>confdefs.h
 
 
 
-           for _libcurl_feature in $_libcurl_features ; do
-              cat >>confdefs.h <<_ACEOF
+          for _libcurl_feature in $_libcurl_features ; do
+             cat >>confdefs.h <<_ACEOF
 #define `$as_echo "libcurl_feature_$_libcurl_feature" | $as_tr_cpp` 1
 _ACEOF
 
-              eval `$as_echo "libcurl_feature_$_libcurl_feature" | $as_tr_sh`=yes
-           done
+             eval `$as_echo "libcurl_feature_$_libcurl_feature" | $as_tr_sh`=yes
+          done
 
-           if test "x$_libcurl_protocols" = "x" ; then
+          if test "x$_libcurl_protocols" = "x" ; then
 
-              # We don't have --protocols, so just assume that all
-              # protocols are available
-              _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
+             # We don't have --protocols, so just assume that all
+             # protocols are available
+             _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
 
-              if test x$libcurl_feature_SSL = xyes ; then
-                 _libcurl_protocols="$_libcurl_protocols HTTPS"
+             if test x$libcurl_feature_SSL = xyes ; then
+                _libcurl_protocols="$_libcurl_protocols HTTPS"
 
-                 # FTPS wasn't standards-compliant until version
-                 # 7.11.0 (0x070b00 == 461568)
-                 if test $_libcurl_version -ge 461568; then
-                    _libcurl_protocols="$_libcurl_protocols FTPS"
-                 fi
-              fi
+                # FTPS wasn't standards-compliant until version
+                # 7.11.0 (0x070b00 == 461568)
+                if test $_libcurl_version -ge 461568; then
+                   _libcurl_protocols="$_libcurl_protocols FTPS"
+                fi
+             fi
 
-              # RTSP, IMAP, POP3 and SMTP were added in
-              # 7.20.0 (0x071400 == 463872)
-              if test $_libcurl_version -ge 463872; then
-                 _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
-              fi
-           fi
+             # RTSP, IMAP, POP3 and SMTP were added in
+             # 7.20.0 (0x071400 == 463872)
+             if test $_libcurl_version -ge 463872; then
+                _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
+             fi
+          fi
 
-           for _libcurl_protocol in $_libcurl_protocols ; do
-              cat >>confdefs.h <<_ACEOF
+          for _libcurl_protocol in $_libcurl_protocols ; do
+             cat >>confdefs.h <<_ACEOF
 #define `$as_echo "libcurl_protocol_$_libcurl_protocol" | $as_tr_cpp` 1
 _ACEOF
 
-              eval `$as_echo "libcurl_protocol_$_libcurl_protocol" | $as_tr_sh`=yes
-           done
-        else
-           unset LIBCURL
-           unset LIBCURL_CPPFLAGS
-        fi
+             eval `$as_echo "libcurl_protocol_$_libcurl_protocol" | $as_tr_sh`=yes
+          done
+       else
+          unset LIBCURL
+          unset LIBCURL_CPPFLAGS
+       fi
      fi
 
      unset _libcurl_try_link
@@ -3282,10 +3282,10 @@ if test "${with_jsonc_include_dir+set}" = set; then :
                    no)
                        as_fn_error $? "Need jsonc-include-dir" "$LINENO" 5
                    ;;
-                       yes)
+                       yes)
                    ;;
                    *)
-                       jsonc_include_dir="$withval"
+                       jsonc_include_dir="$withval"
                    ;;
                esac
 fi
@@ -3408,22 +3408,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=json/json.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3540,8 +3540,8 @@ json_c_version()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-ljson-c"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-ljson-c"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3557,22 +3557,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libjson-c${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3584,22 +3584,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libjson-c.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
 
        if test "x$ac_cv_lib_json_c_json_c_version" != "xyes"
        then
-           have_json="no"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: json-c libraries not found. Use --with-jsonc-lib-dir=<path>." >&5
-$as_echo "$as_me: WARNING: json-c libraries not found. Use --with-jsonc-lib-dir=<path>." >&2;}
+
+
+sm_lib_safe=`echo "json" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "json_tokener_new" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+smart_lib=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_tokener_new in -ljson in $try" >&5
+$as_echo_n "checking for json_tokener_new in -ljson in $try... " >&6; }
+    LIBS="-L$try -ljson $old_LIBS -Wl,-rpath,$try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_tokener_new();
+int
+main ()
+{
+json_tokener_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-L$try -ljson -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_tokener_new in -ljson" >&5
+$as_echo_n "checking for json_tokener_new in -ljson... " >&6; }
+  LIBS="-ljson $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_tokener_new();
+int
+main ()
+{
+json_tokener_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-ljson"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libjson${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libjson.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_tokener_new in -ljson in $try" >&5
+$as_echo_n "checking for json_tokener_new in -ljson in $try... " >&6; }
+    LIBS="-L$try -ljson $old_LIBS -Wl,-rpath,$try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char json_tokener_new();
+int
+main ()
+{
+json_tokener_new()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-L$try -ljson -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_lib $old_LIBS"
+  SMART_LIBS="$smart_lib $SMART_LIBS"
+fi
+
+               if test "x$ac_cv_lib_json_json_tokener_new" != "xyes"
+               then
+                       have_json="no"
+               fi
        fi
 
        if test "x$have_json" = "xyes"; then
+                               LDFLAGS="$SMART_LIBS"
+
+                               for ac_func in \
+                       json_c_version \
+                       json_type_to_name
+
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
 
 $as_echo "#define HAVE_JSON 1" >>confdefs.h
 
        else
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: json-c libraries not found. Use --with-jsonc-lib-dir=<path>." >&5
+$as_echo "$as_me: WARNING: json-c libraries not found. Use --with-jsonc-lib-dir=<path>." >&2;}
                { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently building without JSON support. requires: json-c" >&5
 $as_echo "$as_me: WARNING: silently building without JSON support. requires: json-c" >&2;}
        fi
@@ -3764,11 +3951,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -4242,13 +4429,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
@@ -4968,3 +5155,4 @@ $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
 fi
 
 
+
index c3b16e2..7a1cafe 100644 (file)
@@ -20,15 +20,15 @@ if test x$with_[]modname != xno; then
 
        LIBCURL_CHECK_CONFIG([], [7.19.1])
 
-       if test "x$libcurl_cv_lib_version_ok" != "xyes"; then 
+       if test "x$libcurl_cv_lib_version_ok" != "xyes"; then
                fail="$fail libcurl >= 7.19.2"
        elif test "x$libcurl_cv_lib_curl_usable" != "xyes"; then
                fail="$fail libcurl"
-       else    
+       else
                if test x$libcurl_protocol_HTTP != xyes; then
                        fail="$fail libcurl_protocol_http"
                fi
-       
+
                if test x$libcurl_protocol_HTTPS != xyes || test x$libcurl_feature_SSL != xyes; then
                        AC_MSG_WARN([silently building without HTTPS support. requires: libcurl_protocol_https.])
                fi
@@ -47,10 +47,10 @@ if test x$with_[]modname != xno; then
                    no)
                        AC_MSG_ERROR(Need jsonc-include-dir)
                    ;;
-                       yes)
+                       yes)
                    ;;
                    *)
-                       jsonc_include_dir="$withval"
+                       jsonc_include_dir="$withval"
                    ;;
                esac])
 
@@ -98,22 +98,39 @@ if test x$with_[]modname != xno; then
                have_json="no"
                AC_MSG_WARN([json-c headers not found. Use --with-jsonc-include-dir=<path>.])
        fi
-       
+
        dnl ############################################################
        dnl # Check for json-c libraries
        dnl ############################################################
 
        smart_try_dir="$jsonc_lib_dir"
+       dnl # Use a json-c specific function which is only
+       dnl # available in newer versions.
        FR_SMART_CHECK_LIB([json-c], [json_c_version])
        if test "x$ac_cv_lib_json_c_json_c_version" != "xyes"
        then
-           have_json="no"
-               AC_MSG_WARN([json-c libraries not found. Use --with-jsonc-lib-dir=<path>.])
+               dnl # Use a function which is included in legacy versions
+               dnl # but which may be available in other json libraries
+               FR_SMART_CHECK_LIB([json], [json_tokener_new])
+               if test "x$ac_cv_lib_json_json_tokener_new" != "xyes"
+               then
+                       have_json="no"
+               fi
        fi
-       
+
        if test "x$have_json" = "xyes"; then
+               dnl # Ensure we use the library we just found the rest of the checks
+               LDFLAGS="$SMART_LIBS"
+
+               dnl # Add any optional functions here
+               AC_CHECK_FUNCS(\
+                       json_c_version \
+                       json_type_to_name
+               )
+
                AC_DEFINE([HAVE_JSON],[1],[Build with JSON support from json-c])
        else
+               AC_MSG_WARN([json-c libraries not found. Use --with-jsonc-lib-dir=<path>.])
                AC_MSG_WARN([silently building without JSON support. requires: json-c])
        fi
 
@@ -142,3 +159,4 @@ AC_SUBST(mod_ldflags)
 
 AC_SUBST(targetname)
 AC_OUTPUT(all.mk)
+
index ec64a08..dcb521b 100755 (executable)
@@ -7,22 +7,53 @@ use HTTP::Daemon;
 use HTTP::Status;
 use HTTP::Response;
 
-my $d = new HTTP::Daemon(LocalAddr => '127.0.0.1', LocalPort => 9090);
-print "Please contact me at: <URL:", $d->url, ">\n";
-while (my $c = $d->accept) {
-       while (my $r = $c->get_request) {
-               print "Got " . $r->method . " request\n";
-               if ($r->method eq 'POST' and $r->url->path eq "/") {
+# Required else we get weird issues ports being bound after the
+# daemon exits.
+my $daemon;
+my $client;
+
+sub close_client {
+       if (defined $client) {
+               $client->shutdown(2);
+               $client->close();
+       }
+}
+
+sub close_daemon {
+       if (defined $daemon) {
+               print "Closing daemon socket\n";
+               $daemon->shutdown(2);
+               $daemon->close();
+       }
+       close_client();
+}
+
+$SIG{'INT'} = \&close_daemon;
+$SIG{'QUIT'} = \&close_daemon;
+$SIG{'PIPE'} = \&close_client;
+
+$daemon = new HTTP::Daemon(ReuseAddr => 1, LocalAddr => '127.0.0.1', LocalPort => 9090);
+if (!defined $daemon) {
+       die "Error opening socket: $!";
+}
+
+print "Please contact me at: ", $daemon->url, "\n";
+while ($client = $daemon->accept) {
+       $client->timeout(1);
+       while (my $r = $client->get_request) {
+               print "Got " . $r->method . " request for " . $r->url->path . "\n";
+               if ((($r->method eq 'POST') or ($r->method eq 'GET'))  and $r->url->path eq "/") {
                        my $resp = HTTP::Response->new( '200', 'OK' );
 
-                       $resp->header("Content-Type" => "application/x-www-form-urlencoded");
-                       $resp->content("control:Cleartext-Password=password&reply:Reply-Message=testing123");
+                       $resp->header("Content-Type" => "application/json");
+                       $resp->content("{\"control:Cleartext-Password\":\"testing123\",\"reply:Reply-Message\":\"Hello from demo.pl\"}");
 
-                       $c->send_response($resp);
+                       $client->send_response($resp);
                } else {
-                       $c->send_error(RC_FORBIDDEN)
+                       $client->send_error(RC_FORBIDDEN)
                }
        }
-       $c->close;
-       undef($c);
+
+       close_client();
+       undef($client);
 }
index d7d5a52..dabf18b 100644 (file)
@@ -73,80 +73,80 @@ AC_DEFUN([LIBCURL_CHECK_CONFIG],
      _libcurl_try_link=yes
 
      if test -d "$_libcurl_with" ; then
-        LIBCURL_CPPFLAGS="-I$withval/include"
-        _libcurl_ldflags="-L$withval/lib"
-        AC_PATH_PROG([_libcurl_config],[curl-config],[],
-                     ["$withval/bin"])
+       LIBCURL_CPPFLAGS="-I$withval/include"
+       _libcurl_ldflags="-L$withval/lib"
+       AC_PATH_PROG([_libcurl_config],[curl-config],[],
+                    ["$withval/bin"])
      else
-        AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH])
+       AC_PATH_PROG([_libcurl_config],[curl-config],[],[$PATH])
      fi
 
      if test x$_libcurl_config != "x" ; then
-        AC_CACHE_CHECK([for the version of libcurl],
-           [libcurl_cv_lib_curl_version],
-           [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`])
-
-        _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
-        _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse`
-
-        if test $_libcurl_wanted -gt 0 ; then
-           AC_CACHE_CHECK([for libcurl >= version $2],
-              [libcurl_cv_lib_version_ok],
-              [
-              if test $_libcurl_version -ge $_libcurl_wanted ; then
-                 libcurl_cv_lib_version_ok=yes
-              else
-                 libcurl_cv_lib_version_ok=no
-              fi
-              ])
-        fi
-
-        if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
-           if test x"$LIBCURL_CPPFLAGS" = "x" ; then
-              LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
-           fi
-           if test x"$LIBCURL" = "x" ; then
-              LIBCURL=`$_libcurl_config --libs`
-
-              # This is so silly, but Apple actually has a bug in their
-              # curl-config script.  Fixed in Tiger, but there are still
-              # lots of Panther installs around.
-              case "${host}" in
-                 powerpc-apple-darwin7*)
-                    LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
-                 ;;
-              esac
-           fi
-
-           # All curl-config scripts support --feature
-           _libcurl_features=`$_libcurl_config --feature`
-
-           # Is it modern enough to have --protocols? (7.12.4)
-           if test $_libcurl_version -ge 461828 ; then
-              _libcurl_protocols=`$_libcurl_config --protocols`
-           fi
-        else
-           _libcurl_try_link=no
-        fi
-
-        unset _libcurl_wanted
+       AC_CACHE_CHECK([for the version of libcurl],
+          [libcurl_cv_lib_curl_version],
+          [libcurl_cv_lib_curl_version=`$_libcurl_config --version | $AWK '{print $[]2}'`])
+
+       _libcurl_version=`echo $libcurl_cv_lib_curl_version | $_libcurl_version_parse`
+       _libcurl_wanted=`echo ifelse([$2],,[0],[$2]) | $_libcurl_version_parse`
+
+       if test $_libcurl_wanted -gt 0 ; then
+          AC_CACHE_CHECK([for libcurl >= version $2],
+             [libcurl_cv_lib_version_ok],
+             [
+             if test $_libcurl_version -ge $_libcurl_wanted ; then
+                libcurl_cv_lib_version_ok=yes
+             else
+                libcurl_cv_lib_version_ok=no
+             fi
+             ])
+       fi
+
+       if test $_libcurl_wanted -eq 0 || test x$libcurl_cv_lib_version_ok = xyes ; then
+          if test x"$LIBCURL_CPPFLAGS" = "x" ; then
+             LIBCURL_CPPFLAGS=`$_libcurl_config --cflags`
+          fi
+          if test x"$LIBCURL" = "x" ; then
+             LIBCURL=`$_libcurl_config --libs`
+
+             # This is so silly, but Apple actually has a bug in their
+             # curl-config script.  Fixed in Tiger, but there are still
+             # lots of Panther installs around.
+             case "${host}" in
+                powerpc-apple-darwin7*)
+                   LIBCURL=`echo $LIBCURL | sed -e 's|-arch i386||g'`
+                ;;
+             esac
+          fi
+
+          # All curl-config scripts support --feature
+          _libcurl_features=`$_libcurl_config --feature`
+
+          # Is it modern enough to have --protocols? (7.12.4)
+          if test $_libcurl_version -ge 461828 ; then
+             _libcurl_protocols=`$_libcurl_config --protocols`
+          fi
+       else
+          _libcurl_try_link=no
+       fi
+
+       unset _libcurl_wanted
      fi
 
      if test $_libcurl_try_link = yes ; then
 
-        # we didn't find curl-config, so let's see if the user-supplied
-        # link line (or failing that, "-lcurl") is enough.
-        LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
+       # we didn't find curl-config, so let's see if the user-supplied
+       # link line (or failing that, "-lcurl") is enough.
+       LIBCURL=${LIBCURL-"$_libcurl_ldflags -lcurl"}
 
-        AC_CACHE_CHECK([whether libcurl is usable],
-           [libcurl_cv_lib_curl_usable],
-           [
-           _libcurl_save_cppflags=$CPPFLAGS
-           CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
-           _libcurl_save_libs=$LIBS
-           LIBS="$LIBCURL $LIBS"
+       AC_CACHE_CHECK([whether libcurl is usable],
+          [libcurl_cv_lib_curl_usable],
+          [
+          _libcurl_save_cppflags=$CPPFLAGS
+          CPPFLAGS="$LIBCURL_CPPFLAGS $CPPFLAGS"
+          _libcurl_save_libs=$LIBS
+          LIBS="$LIBCURL $LIBS"
 
-           AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <curl/curl.h>]],[[
+          AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <curl/curl.h>]],[[
 /* Try and use a few common options to force a failure if we are
    missing symbols or can't link. */
 int x;
@@ -160,72 +160,72 @@ x=CURLOPT_VERBOSE;
 if (x) ;
 ]])],libcurl_cv_lib_curl_usable=yes,libcurl_cv_lib_curl_usable=no)
 
-           CPPFLAGS=$_libcurl_save_cppflags
-           LIBS=$_libcurl_save_libs
-           unset _libcurl_save_cppflags
-           unset _libcurl_save_libs
-           ])
-
-        if test $libcurl_cv_lib_curl_usable = yes ; then
-
-           # Does curl_free() exist in this version of libcurl?
-           # If not, fake it with free()
-
-           _libcurl_save_cppflags=$CPPFLAGS
-           CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
-           _libcurl_save_libs=$LIBS
-           LIBS="$LIBS $LIBCURL"
-
-           AC_CHECK_FUNC(curl_free,,
-              AC_DEFINE(curl_free,free,
-                [Define curl_free() as free() if our version of curl lacks curl_free.]))
-
-           CPPFLAGS=$_libcurl_save_cppflags
-           LIBS=$_libcurl_save_libs
-           unset _libcurl_save_cppflags
-           unset _libcurl_save_libs
-
-           AC_DEFINE(HAVE_LIBCURL,1,
-             [Define to 1 if you have a functional curl library.])
-           AC_SUBST(LIBCURL_CPPFLAGS)
-           AC_SUBST(LIBCURL)
-
-           for _libcurl_feature in $_libcurl_features ; do
-              AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1])
-              eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes
-           done
-
-           if test "x$_libcurl_protocols" = "x" ; then
-
-              # We don't have --protocols, so just assume that all
-              # protocols are available
-              _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
-
-              if test x$libcurl_feature_SSL = xyes ; then
-                 _libcurl_protocols="$_libcurl_protocols HTTPS"
-
-                 # FTPS wasn't standards-compliant until version
-                 # 7.11.0 (0x070b00 == 461568)
-                 if test $_libcurl_version -ge 461568; then
-                    _libcurl_protocols="$_libcurl_protocols FTPS"
-                 fi
-              fi
-
-              # RTSP, IMAP, POP3 and SMTP were added in
-              # 7.20.0 (0x071400 == 463872)
-              if test $_libcurl_version -ge 463872; then
-                 _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
-              fi
-           fi
-
-           for _libcurl_protocol in $_libcurl_protocols ; do
-              AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1])
-              eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes
-           done
-        else
-           unset LIBCURL
-           unset LIBCURL_CPPFLAGS
-        fi
+          CPPFLAGS=$_libcurl_save_cppflags
+          LIBS=$_libcurl_save_libs
+          unset _libcurl_save_cppflags
+          unset _libcurl_save_libs
+          ])
+
+       if test $libcurl_cv_lib_curl_usable = yes ; then
+
+          # Does curl_free() exist in this version of libcurl?
+          # If not, fake it with free()
+
+          _libcurl_save_cppflags=$CPPFLAGS
+          CPPFLAGS="$CPPFLAGS $LIBCURL_CPPFLAGS"
+          _libcurl_save_libs=$LIBS
+          LIBS="$LIBS $LIBCURL"
+
+          AC_CHECK_FUNC(curl_free,,
+             AC_DEFINE(curl_free,free,
+               [Define curl_free() as free() if our version of curl lacks curl_free.]))
+
+          CPPFLAGS=$_libcurl_save_cppflags
+          LIBS=$_libcurl_save_libs
+          unset _libcurl_save_cppflags
+          unset _libcurl_save_libs
+
+          AC_DEFINE(HAVE_LIBCURL,1,
+            [Define to 1 if you have a functional curl library.])
+          AC_SUBST(LIBCURL_CPPFLAGS)
+          AC_SUBST(LIBCURL)
+
+          for _libcurl_feature in $_libcurl_features ; do
+             AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_feature_$_libcurl_feature),[1])
+             eval AS_TR_SH(libcurl_feature_$_libcurl_feature)=yes
+          done
+
+          if test "x$_libcurl_protocols" = "x" ; then
+
+             # We don't have --protocols, so just assume that all
+             # protocols are available
+             _libcurl_protocols="HTTP FTP FILE TELNET LDAP DICT TFTP"
+
+             if test x$libcurl_feature_SSL = xyes ; then
+                _libcurl_protocols="$_libcurl_protocols HTTPS"
+
+                # FTPS wasn't standards-compliant until version
+                # 7.11.0 (0x070b00 == 461568)
+                if test $_libcurl_version -ge 461568; then
+                   _libcurl_protocols="$_libcurl_protocols FTPS"
+                fi
+             fi
+
+             # RTSP, IMAP, POP3 and SMTP were added in
+             # 7.20.0 (0x071400 == 463872)
+             if test $_libcurl_version -ge 463872; then
+                _libcurl_protocols="$_libcurl_protocols RTSP IMAP POP3 SMTP"
+             fi
+          fi
+
+          for _libcurl_protocol in $_libcurl_protocols ; do
+             AC_DEFINE_UNQUOTED(AS_TR_CPP(libcurl_protocol_$_libcurl_protocol),[1])
+             eval AS_TR_SH(libcurl_protocol_$_libcurl_protocol)=yes
+          done
+       else
+          unset LIBCURL
+          unset LIBCURL_CPPFLAGS
+       fi
      fi
 
      unset _libcurl_try_link
index c5ce2b9..323ca9a 100644 (file)
  * @brief Functions and datatypes for the REST (HTTP) transport.
  * @file rest.c
  *
- * @copyright 2012-2013  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
+ * @copyright 2012-2014  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
  */
 
 RCSID("$Id$")
 
-#include <assert.h>
 #include <ctype.h>
 #include <string.h>
 #include <time.h>
@@ -45,10 +44,12 @@ RCSID("$Id$")
  * @see http_body_type_t
  */
 const http_body_type_t http_body_type_supported[HTTP_BODY_NUM_ENTRIES] = {
-       HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_UNKOWN
+       HTTP_BODY_UNKNOWN,      // HTTP_BODY_UNKOWN
        HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_UNSUPPORTED
        HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_UNAVAILABLE
        HTTP_BODY_UNSUPPORTED,  // HTTP_BODY_INVALID
+       HTTP_BODY_NONE,         // HTTP_BODY_NONE
+       HTTP_BODY_CUSTOM,       // HTTP_BODY_CUSTOM
        HTTP_BODY_POST,         // HTTP_BODY_POST
 #ifdef HAVE_JSON
        HTTP_BODY_JSON,         // HTTP_BODY_JSON
@@ -65,27 +66,35 @@ const http_body_type_t http_body_type_supported[HTTP_BODY_NUM_ENTRIES] = {
  *     Lib CURL doesn't define symbols for unsupported auth methods
  */
 #ifndef CURLOPT_TLSAUTH_SRP
-#define CURLOPT_TLSAUTH_SRP    0
+#  define CURLOPT_TLSAUTH_SRP  0
 #endif
 #ifndef CURLAUTH_BASIC
-#define CURLAUTH_BASIC                 0
+#  define CURLAUTH_BASIC       0
 #endif
 #ifndef CURLAUTH_DIGEST
-#define CURLAUTH_DIGEST        0
+#  define CURLAUTH_DIGEST      0
 #endif
 #ifndef CURLAUTH_DIGEST_IE
-#define CURLAUTH_DIGEST_IE     0
+#  define CURLAUTH_DIGEST_IE   0
 #endif
 #ifndef CURLAUTH_GSSNEGOTIATE
-#define CURLAUTH_GSSNEGOTIATE  0
+#  define CURLAUTH_GSSNEGOTIATE        0
 #endif
 #ifndef CURLAUTH_NTLM
-#define CURLAUTH_NTLM          0
+#  define CURLAUTH_NTLM                0
 #endif
 #ifndef CURLAUTH_NTLM_WB
-#define CURLAUTH_NTLM_WB       0
+#  define CURLAUTH_NTLM_WB     0
 #endif
 
+#define SET_OPTION(_x, _y)\
+do {\
+       if ((ret = curl_easy_setopt(candle, _x, _y)) != CURLE_OK) {\
+               option = STRINGIFY(_x);\
+               goto error;\
+       }\
+} while (0)
+
 const unsigned long http_curl_auth[HTTP_AUTH_NUM_ENTRIES] = {
        0,                      // HTTP_AUTH_UNKNOWN
        0,                      // HTTP_AUTH_NONE
@@ -104,7 +113,7 @@ const unsigned long http_curl_auth[HTTP_AUTH_NUM_ENTRIES] = {
 /** Conversion table for method config values.
  *
  * HTTP verb strings for http_method_t enum values. Used by libcurl in the
- * status line of the outgoing HTTP header, by rest_write_header for decoding
+ * status line of the outgoing HTTP header, by rest_response_header for decoding
  * incoming HTTP responses, and by the configuration parser.
  *
  * @see http_method_t
@@ -134,6 +143,7 @@ const FR_NAME_NUMBER http_body_type_table[] = {
        { "unsupported",                        HTTP_BODY_UNSUPPORTED   },
        { "unavailable",                        HTTP_BODY_UNAVAILABLE   },
        { "invalid",                            HTTP_BODY_INVALID       },
+       { "none",                               HTTP_BODY_NONE          },
        { "post",                               HTTP_BODY_POST          },
        { "json",                               HTTP_BODY_JSON          },
        { "xml",                                HTTP_BODY_XML           },
@@ -161,7 +171,7 @@ const FR_NAME_NUMBER http_auth_table[] = {
 
 /** Conversion table for "Content-Type" header values.
  *
- * Used by rest_write_header for parsing incoming headers.
+ * Used by rest_response_header for parsing incoming headers.
  *
  * Values we expect to see in the 'Content-Type:' header of the incoming
  * response.
@@ -183,9 +193,18 @@ const FR_NAME_NUMBER http_content_type_table[] = {
        { "text/x-yaml",                        HTTP_BODY_YAML          },
        { "application/yaml",                   HTTP_BODY_YAML          },
        { "application/x-yaml",                 HTTP_BODY_YAML          },
+
        {  NULL , -1 }
 };
 
+/*
+ *     Encoder specific structures.
+ *     @todo split encoders/decoders into submodules.
+ */
+typedef struct rest_custom_data {
+       char *p;                //!< how much text we've sent so far.
+} rest_custom_data_t;
+
 #ifdef HAVE_JSON
 /** Flags to control the conversion of JSON values to VALUE_PAIRs.
  *
@@ -221,11 +240,14 @@ int rest_init(rlm_rest_t *instance)
 {
        CURLcode ret;
 
+       /* developer sanity */
+       rad_assert((sizeof(http_body_type_supported) / sizeof(*http_body_type_supported)) == HTTP_BODY_NUM_ENTRIES);
+
        ret = curl_global_init(CURL_GLOBAL_ALL);
        if (ret != CURLE_OK) {
                ERROR("rlm_rest (%s): CURL init returned error: %i - %s",
-                      instance->xlat_name,
-                      ret, curl_easy_strerror(ret));
+                     instance->xlat_name,
+                     ret, curl_easy_strerror(ret));
 
                curl_global_cleanup();
                return -1;
@@ -280,29 +302,27 @@ void *mod_conn_create(void *instance)
        rlm_rest_curl_context_t *ctx = NULL;
 
        CURL *candle = curl_easy_init();
-       CURLcode ret;
+
+       CURLcode ret = CURLE_OK;
+       char const *option = "unknown";
 
        if (!candle) {
                ERROR("rlm_rest (%s): Failed to create CURL handle", inst->xlat_name);
                return NULL;
        }
 
-       if (!*inst->connect_uri) {
+       if (!inst->connect_uri) {
                ERROR("rlm_rest (%s): Skipping pre-connect, connect_uri not specified", inst->xlat_name);
                return candle;
        }
 
        /*
-        *      Pre-establish TCP connection to webserver. This would usually be
-        *      done on the first request, but we do it here to minimise
-        *      latency.
+        *  re-establish TCP connection to webserver. This would usually be
+        *  done on the first request, but we do it here to minimise
+        *  latency.
         */
-       ret = curl_easy_setopt(candle, CURLOPT_CONNECT_ONLY, 1);
-       if (ret != CURLE_OK) goto error;
-
-       ret = curl_easy_setopt(candle, CURLOPT_URL,
-                              inst->connect_uri);
-       if (ret != CURLE_OK) goto error;
+       SET_OPTION(CURLOPT_CONNECT_ONLY, 1);
+       SET_OPTION(CURLOPT_URL, inst->connect_uri);
 
        DEBUG("rlm_rest (%s): Connecting to \"%s\"", inst->xlat_name, inst->connect_uri);
 
@@ -314,36 +334,35 @@ void *mod_conn_create(void *instance)
        }
 
        /*
-        *      Allocate memory for the connection handle abstraction.
+        *  Allocate memory for the connection handle abstraction.
         */
        randle = talloc_zero(inst, rlm_rest_handle_t);
        ctx = talloc_zero(randle, rlm_rest_curl_context_t);
 
        ctx->headers = NULL; /* CURL needs this to be NULL */
-       ctx->read.instance = inst;
+       ctx->request.instance = inst;
 
        randle->ctx = ctx;
        randle->handle = candle;
 
        /*
-        *      Clear any previously configured options for the first request.
+        *  Clear any previously configured options for the first request.
         */
        curl_easy_reset(candle);
 
        return randle;
 
        /*
-        *      Cleanup for error conditions.
+        *  Cleanup for error conditions.
         */
-       error:
-
-       ERROR("rlm_rest (%s): Failed setting curl option: %i - %s", inst->xlat_name, ret, curl_easy_strerror(ret));
+error:
+       ERROR("rlm_rest (%s): Failed setting curl option %s: %s (%i)", inst->xlat_name, option,
+             curl_easy_strerror(ret), ret);
 
        /*
-        *      So we don't leak CURL handles.
+        *  So we don't leak CURL handles.
         */
-       connection_error:
-
+connection_error:
        curl_easy_cleanup(candle);
        if (randle) talloc_free(randle);
 
@@ -362,9 +381,9 @@ void *mod_conn_create(void *instance)
  */
 int mod_conn_alive(void *instance, void *handle)
 {
-       rlm_rest_t *inst                = instance;
-       rlm_rest_handle_t *randle       = handle;
-       CURL *candle                    = randle->handle;
+       rlm_rest_t              *inst = instance;
+       rlm_rest_handle_t       *randle = handle;
+       CURL                    *candle = randle->handle;
 
        long last_socket;
        CURLcode ret;
@@ -392,8 +411,8 @@ int mod_conn_alive(void *instance, void *handle)
  */
 int mod_conn_delete(UNUSED void *instance, void *handle)
 {
-       rlm_rest_handle_t *randle       = handle;
-       CURL *candle                    = randle->handle;
+       rlm_rest_handle_t       *randle = handle;
+       CURL                    *candle = randle->handle;
 
        curl_easy_cleanup(candle);
 
@@ -402,6 +421,33 @@ int mod_conn_delete(UNUSED void *instance, void *handle)
        return true;
 }
 
+/** Copies a pre-expanded xlat string to the output buffer
+ *
+ * @param[out] out Char buffer to write encoded data to.
+ * @param[in] size Multiply by nmemb to get the length of ptr.
+ * @param[in] nmemb Multiply by size to get the length of ptr.
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
+ * @return length of data (including NULL) written to ptr, or 0 if no more
+ *     data to write.
+ */
+static size_t rest_encode_custom(void *out, size_t size, size_t nmemb, void *userdata)
+{
+       rlm_rest_request_t *ctx = userdata;
+       rest_custom_data_t *data = ctx->encoder;
+
+       size_t  freespace = (size * nmemb) - 1;
+       size_t  len;
+
+       len = strlcpy(out, data->p, freespace);
+       if (is_truncated(len, freespace)) {
+               data->p += (freespace - 1);
+               return freespace - 1;
+       }
+       data->p += len;
+
+       return len;
+}
+
 /** Encodes VALUE_PAIR linked list in POST format
  *
  * This is a stream function matching the rest_read_t prototype. Multiple
@@ -423,139 +469,150 @@ int mod_conn_delete(UNUSED void *instance, void *handle)
  *
  * @see rest_decode_post
  *
- * @param[out] ptr Char buffer to write encoded data to.
+ * @param[out] out Char buffer to write encoded data to.
  * @param[in] size Multiply by nmemb to get the length of ptr.
  * @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_read_t to keep encoding state between calls.
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
  * @return length of data (including NULL) written to ptr, or 0 if no more
  *     data to write.
  */
-static size_t rest_encode_post(void *ptr, size_t size, size_t nmemb,
-                              void *userdata)
+static size_t rest_encode_post(void *out, size_t size, size_t nmemb, void *userdata)
 {
-       rlm_rest_read_t *ctx    = userdata;
-       REQUEST *request        = ctx->request; /* Used by RDEBUG */
-       VALUE_PAIR *vp;
+       rlm_rest_request_t      *ctx = userdata;
+       REQUEST                 *request = ctx->request; /* Used by RDEBUG */
+       VALUE_PAIR              *vp;
 
-       char *p = ptr;  /* Position in buffer */
-       char *f = ptr;  /* Position in buffer of last fully encoded attribute or value */
-       char *escaped;  /* Pointer to current URL escaped data */
+       char *p = out;          /* Position in buffer */
+       char *encoded = p;      /* Position in buffer of last fully encoded attribute or value */
+       char *escaped;          /* Pointer to current URL escaped data */
 
-       ssize_t len = 0;
-       ssize_t s = (size * nmemb) - 1;
+       size_t len = 0;
+       size_t freespace = (size * nmemb) - 1;
 
        /* Allow manual chunking */
-       if ((ctx->chunk) && (ctx->chunk <= s)) {
-               s = (ctx->chunk - 1);
+       if ((ctx->chunk) && (ctx->chunk <= freespace)) {
+               freespace = (ctx->chunk - 1);
        }
 
        if (ctx->state == READ_STATE_END) return 0;
 
        /* Post data requires no headers */
-       if (ctx->state == READ_STATE_INIT) {
-               ctx->state = READ_STATE_ATTR_BEGIN;
-       }
+       if (ctx->state == READ_STATE_INIT) ctx->state = READ_STATE_ATTR_BEGIN;
 
-       while (s > 0) {
-               vp = paircurrent(&ctx->cursor);
+       while (freespace > 0) {
+               vp = fr_cursor_current(&ctx->cursor);
                if (!vp) {
                        ctx->state = READ_STATE_END;
 
-                       goto end_chunk;
+                       break;
                }
 
                RDEBUG2("Encoding attribute \"%s\"", vp->da->name);
 
                if (ctx->state == READ_STATE_ATTR_BEGIN) {
                        escaped = curl_escape(vp->da->name, strlen(vp->da->name));
-                       len = strlen(escaped);
+                       if (!escaped) {
+                               REDEBUG("Failed escaping string \"%s\"", vp->da->name);
+                               return 0;
+                       }
 
-                       if (s < (1 + len)) {
+                       len = strlen(escaped);
+                       if (freespace < (1 + len)) {
                                curl_free(escaped);
                                goto no_space;
                        }
 
                        len = sprintf(p, "%s=", escaped);
-
                        curl_free(escaped);
-
                        p += len;
-                       s -= len;
+                       freespace -= len;
 
                        /*
-                        *      We wrote the attribute header, record progress.
+                        *  We wrote the attribute header, record progress.
                         */
-                       f = p;
+                       encoded = p;
                        ctx->state = READ_STATE_ATTR_CONT;
                }
 
                /*
-                *      Write out single attribute string.
+                *  Write out single attribute string.
                 */
-               len = vp_prints_value(p , s, vp, 0);
-               escaped = curl_escape(p, len);
-               len = strlen(escaped);
+               len = vp_prints_value(p, freespace, vp, 0);
+               if (is_truncated(len, freespace)) goto no_space;
+
+               RDEBUG3("\tLength : %zd", len);
+               if (len > 0) {
+                       escaped = curl_escape(p, len);
+                       if (!escaped) {
+                               REDEBUG("Failed escaping string \"%s\"", vp->da->name);
+                               return 0;
+                       }
+                       len = strlen(escaped);
 
-               if (s < len) {
-                       curl_free(escaped);
-                       goto no_space;
-               }
+                       if (freespace < len) {
+                               curl_free(escaped);
+                               goto no_space;
+                       }
 
-               len = strlcpy(p, escaped, len + 1);
+                       len = strlcpy(p, escaped, len + 1);
 
-               curl_free(escaped);
+                       curl_free(escaped);
 
-               RDEBUG("\tLength : %i", len);
-               RDEBUG("\tValue  : %s", p);
+                       RDEBUG3("\tValue  : %s", p);
 
-               p += len;
-               s -= len;
+                       p += len;
+                       freespace -= len;
+               }
 
-               if (!--s) goto no_space;
-               *p++ = '&';
+               /*
+                *  there are more attributes, insert a separator
+                */
+               if (fr_cursor_next(&ctx->cursor)) {
+                       if (freespace < 1) goto no_space;
+                       *p++ = '&';
+                       freespace--;
+               }
 
                /*
-                *      We wrote one full attribute value pair, record progress.
+                *  We wrote one full attribute value pair, record progress.
                 */
-               f = p;
-               pairnext(&ctx->cursor);
+               encoded = p;
+
                ctx->state = READ_STATE_ATTR_BEGIN;
        }
 
-       end_chunk:
-
        *p = '\0';
 
-       len = p - (char*)ptr;
+       len = p - (char *)out;
 
-       RDEBUG2("POST Data: %s", (char*) ptr);
-       RDEBUG2("Returning %i bytes of POST data", len);
+       RDEBUG3("POST Data: %s", (char *)out);
+       RDEBUG3("Returning %zd bytes of POST data", len);
 
        return len;
 
        /*
-        *      Cleanup for error conditions
+        *  Cleanup for error conditions
         */
-       no_space:
-
-       *f = '\0';
+no_space:
+       *encoded = '\0';
 
-       len = f - (char*)ptr;
+       len = encoded - (char *)out;
 
-       RDEBUG2("POST Data: %s", (char*) ptr);
+       RDEBUG3("POST Data: %s", (char *)out);
 
        /*
-        *      The buffer wasn't big enough to encode a single attribute chunk.
+        *  The buffer wasn't big enough to encode a single attribute chunk.
         */
-       if (!len) {
-               REDEBUG("AVP exceeds buffer length or chunk");
+       if (len == 0) {
+               REDEBUG("Failed encoding attribute");
        } else {
-               RDEBUG2("Returning %i bytes of POST data (buffer full or chunk exceeded)", len);
+               RDEBUG3("Returning %zd bytes of POST data (buffer full or chunk exceeded)", len);
        }
 
        return len;
 }
 
+#ifdef HAVE_JSON
 /** Encodes VALUE_PAIR linked list in JSON format
  *
  * This is a stream function matching the rest_read_t prototype. Multiple
@@ -586,33 +643,32 @@ static size_t rest_encode_post(void *ptr, size_t size, size_t nmemb,
 }
 @endverbatim
  *
- * @param[out] ptr Char buffer to write encoded data to.
+ * @param[out] out Char buffer to write encoded data to.
  * @param[in] size Multiply by nmemb to get the length of ptr.
  * @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_read_t to keep encoding state between calls.
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls.
  * @return length of data (including NULL) written to ptr, or 0 if no more
  *     data to write.
  */
-static size_t rest_encode_json(void *ptr, size_t size, size_t nmemb,
-                              void *userdata)
+static size_t rest_encode_json(void *out, size_t size, size_t nmemb, void *userdata)
 {
-       rlm_rest_read_t *ctx    = userdata;
-       REQUEST *request        = ctx->request; /* Used by RDEBUG */
-       VALUE_PAIR *vp, *next;
+       rlm_rest_request_t      *ctx = userdata;
+       REQUEST                 *request = ctx->request; /* Used by RDEBUG */
+       VALUE_PAIR              *vp, *next;
 
-       char *p = ptr;  /* Position in buffer */
-       char *f = ptr;  /* Position in buffer of last fully encoded attribute or value */
+       char *p = out;          /* Position in buffer */
+       char *encoded = p;      /* Position in buffer of last fully encoded attribute or value */
 
        char const *type;
 
-       ssize_t len = 0;
-       ssize_t s = (size * nmemb) - 1;
+       size_t len = 0;
+       size_t freespace = (size * nmemb) - 1;
 
-       assert(s > 0);
+       rad_assert(freespace > 0);
 
        /* Allow manual chunking */
-       if ((ctx->chunk) && (ctx->chunk <= s)) {
-               s = (ctx->chunk - 1);
+       if ((ctx->chunk) && (ctx->chunk <= freespace)) {
+               freespace = (ctx->chunk - 1);
        }
 
        if (ctx->state == READ_STATE_END) return 0;
@@ -620,140 +676,138 @@ static size_t rest_encode_json(void *ptr, size_t size, size_t nmemb,
        if (ctx->state == READ_STATE_INIT) {
                ctx->state = READ_STATE_ATTR_BEGIN;
 
-               if (!--s) goto no_space;
+               if (freespace < 1) goto no_space;
                *p++ = '{';
+               freespace--;
        }
 
-       while (s > 0) {
-               vp = paircurrent(&ctx->cursor);
+       for (;;) {
+               vp = fr_cursor_current(&ctx->cursor);
 
                /*
-                *      We've encoded all the VPs
+                *  We've encoded all the VPs
                 */
                if (!vp) {
                        ctx->state = READ_STATE_END;
 
-                       if (!--s) goto no_space;
+                       if (freespace < 1) goto no_space;
                        *p++ = '}';
+                       freespace--;
 
-                       goto end_chunk;
+                       break;
                }
 
                /*
-                *      New attribute, write name, type, and beginning of
-                *      value array.
+                *  New attribute, write name, type, and beginning of value array.
                 */
                RDEBUG2("Encoding attribute \"%s\"", vp->da->name);
                if (ctx->state == READ_STATE_ATTR_BEGIN) {
-                       type = fr_int2str(dict_attr_types, vp->da->type, "¿Unknown?");
+                       type = fr_int2str(dict_attr_types, vp->da->type, "<INVALID>");
 
-                       len  = strlen(type);
-                       len += strlen(vp->da->name);
-
-                       if (s < (23 + len)) goto no_space;
-
-                       len = sprintf(p, "\"%s\":{\"type\":\"%s\",\"value\":[" , vp->da->name, type);
+                       len = snprintf(p, freespace + 1, "\"%s\":{\"type\":\"%s\",\"value\":[", vp->da->name, type);
+                       if (len >= freespace) goto no_space;
                        p += len;
-                       s -= len;
+                       freespace -= len;
 
-                       RDEBUG2("\tType   : %s", type);
+                       RDEBUG3("\tType   : %s", type);
 
                        /*
-                        *      We wrote the attribute header, record progress
-                        */
-                       f = p;
+                        *  We wrote the attribute header, record progress
+                        */
+                       encoded = p;
                        ctx->state = READ_STATE_ATTR_CONT;
                }
 
                for (;;) {
-                       len = vp_prints_value_json(p, s, vp);
-                       if (len >= s) goto no_space;
+                       len = vp_prints_value_json(p, freespace, vp);
+                       if (is_truncated(len, freespace)) goto no_space;
 
                        /*
-                        *      Show actual value length minus quotes
+                        *  Show actual value length minus quotes
                         */
-                       RDEBUG2("\tLength : %i", (*p == '"') ? (len - 2) : len);
-                       RDEBUG2("\tValue  : %s", p);
+                       RDEBUG3("\tLength : %zu", (size_t) (*p == '"') ? (len - 2) : len);
+                       RDEBUG3("\tValue  : %s", p);
 
                        p += len;
-                       s -= len;
+                       freespace -= len;
 
                        /*
-                        *      Multivalued attribute, we sorted all the attributes earlier, so multiple
-                        *      instances should occur in a contiguous block.
+                        *  Multivalued attribute, we sorted all the attributes earlier, so multiple
+                        *  instances should occur in a contiguous block.
                         */
-                       if ((next = pairnext(&ctx->cursor)) && (vp->da == next->da)) {
+                       if ((next = fr_cursor_next(&ctx->cursor)) && (vp->da == next->da)) {
+                               if (freespace < 1) goto no_space;
                                *p++ = ',';
+                               freespace--;
 
                                /*
-                                *      We wrote one attribute value, record
-                                *      progress.
+                                *  We wrote one attribute value, record progress.
                                 */
-                               f = p;
+                               encoded = p;
                                vp = next;
                                continue;
                        }
                        break;
                }
 
-               if (!(s -= 2)) goto no_space;
+               if (freespace < 2) goto no_space;
                *p++ = ']';
                *p++ = '}';
+               freespace -= 2;
 
                if (next) {
-                       if (!--s) goto no_space;
+                       if (freespace < 1) goto no_space;
                        *p++ = ',';
+                       freespace--;
                }
 
                /*
-                *      We wrote one full attribute value pair, record progress.
+                *  We wrote one full attribute value pair, record progress.
                 */
-               f = p;
+               encoded = p;
                ctx->state = READ_STATE_ATTR_BEGIN;
        }
 
-       end_chunk:
-
        *p = '\0';
 
-       len = p - (char*)ptr;
+       len = p - (char *)out;
 
-       RDEBUG2("JSON Data: %s", (char*) ptr);
-       RDEBUG2("Returning %i bytes of JSON data", len);
+       RDEBUG3("JSON Data: %s", (char *)out);
+       RDEBUG3("Returning %zd bytes of JSON data", len);
 
        return len;
 
        /*
-        * Were out of buffer space
+        *  Were out of buffer space
         */
-       no_space:
-
-       *f = '\0';
+no_space:
+       *encoded = '\0';
 
-       len = f - (char*)ptr;
+       len = encoded - (char *)out;
 
-       RDEBUG2("JSON Data: %s", (char*) ptr);
+       RDEBUG3("JSON Data: %s", (char *)out);
 
        /*
-        *      The buffer wasn't big enough to encode a single attribute chunk.
+        *  The buffer wasn't big enough to encode a single attribute chunk.
         */
-       if (!len) {
+       if (len == 0) {
                REDEBUG("AVP exceeds buffer length or chunk");
        } else {
-               RDEBUG2("Returning %i bytes of JSON data (buffer full or chunk exceeded)", len);
+               RDEBUG2("Returning %zd bytes of JSON data (buffer full or chunk exceeded)", len);
        }
 
        return len;
 }
+#endif
 
 /** Emulates successive libcurl calls to an encoding function
  *
  * This function is used when the request will be sent to the HTTP server as one
- * contiguous entity. A buffer of REST_BODY_INCR bytes is allocated and passed
+ * contiguous entity. A buffer of REST_BODY_INIT bytes is allocated and passed
  * to the stream encoding function.
  *
  * If the stream function does not return 0, a new buffer is allocated which is
- * the size of the previous buffer + REST_BODY_INCR bytes, the data from the
+ * the size of the previous buffer + REST_BODY_INIT bytes, the data from the
  * previous buffer is copied, and freed, and another call is made to the stream
  * function, passing a pointer into the new buffer at the end of the previously
  * written data.
@@ -765,22 +819,21 @@ static size_t rest_encode_json(void *ptr, size_t size, size_t nmemb,
  *     be written.
  * @param[in] func Stream function.
  * @param[in] limit Maximum buffer size to alloc.
- * @param[in] userdata rlm_rest_read_t to keep encoding state between calls to
+ * @param[in] userdata rlm_rest_request_t to keep encoding state between calls to
  *     stream function.
  * @return the length of the data written to the buffer (excluding NULL) or -1
  *     if alloc >= limit.
  */
-static ssize_t rest_read_wrapper(char **buffer, rest_read_t func,
-                                size_t limit, void *userdata)
+static ssize_t rest_request_encode_wrapper(char **buffer, rest_read_t func, size_t limit, void *userdata)
 {
        char *previous = NULL;
-       char *current;
+       char *current = NULL;
 
-       size_t alloc = REST_BODY_INCR;  /* Size of buffer to alloc */
-       size_t used  = 0;               /* Size of data written */
-       size_t len   = 0;
+       size_t alloc = REST_BODY_INIT;  /* Size of buffer to alloc */
+       size_t used = 0;                /* Size of data written */
+       size_t len = 0;
 
-       while (alloc < limit) {
+       while (alloc <= limit) {
                current = rad_malloc(alloc);
 
                if (previous) {
@@ -788,14 +841,14 @@ static ssize_t rest_read_wrapper(char **buffer, rest_read_t func,
                        free(previous);
                }
 
-               len = func(current + used, REST_BODY_INCR, 1, userdata);
+               len = func(current + used, alloc - used, 1, userdata);
                used += len;
                if (!len) {
                        *buffer = current;
                        return used;
                }
 
-               alloc += REST_BODY_INCR;
+               alloc = alloc * 2;
                previous = current;
        };
 
@@ -804,16 +857,16 @@ static ssize_t rest_read_wrapper(char **buffer, rest_read_t func,
        return -1;
 }
 
-/** (Re-)Initialises the data in a rlm_rest_read_t.
+/** (Re-)Initialises the data in a rlm_rest_request_t.
  *
- * Resets the values of a rlm_rest_read_t to their defaults.
+ * Resets the values of a rlm_rest_request_t to their defaults.
  *
  * @param[in] request Current request.
  * @param[in] ctx to initialise.
  * @param[in] sort If true VALUE_PAIRs will be sorted within the VALUE_PAIR
  *     pointer array.
  */
-static void rest_read_ctx_init(REQUEST *request, rlm_rest_read_t *ctx, bool sort)
+static void rest_request_init(REQUEST *request, rlm_rest_request_t *ctx, bool sort)
 {
        /*
         *      Setup stream read data
@@ -825,9 +878,9 @@ static void rest_read_ctx_init(REQUEST *request, rlm_rest_read_t *ctx, bool sort
         *      Sorts pairs in place, oh well...
         */
        if (sort) {
-               pairsort(&request->packet->vps, true);
+               pairsort(&request->packet->vps, attrtagcmp);
        }
-       paircursor(&ctx->cursor, &request->packet->vps);
+       fr_cursor_init(&ctx->cursor, &request->packet->vps);
 }
 
 /** Converts POST response into VALUE_PAIRs and adds them to the request
@@ -851,13 +904,11 @@ static void rest_read_ctx_init(REQUEST *request, rlm_rest_read_t *ctx, bool sort
  * @param[in] rawlen Length of data in raw buffer.
  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
  */
-static int rest_decode_post(UNUSED rlm_rest_t *instance,
-                           UNUSED rlm_rest_section_t *section,
-                           REQUEST *request, void *handle, char *raw,
-                           UNUSED size_t rawlen)
+static int rest_decode_post(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
+                           REQUEST *request, void *handle, char *raw, UNUSED size_t rawlen)
 {
-       rlm_rest_handle_t *randle = handle;
-       CURL *candle              = randle->handle;
+       rlm_rest_handle_t       *randle = handle;
+       CURL                    *candle = randle->handle;
 
        char const *p = raw, *q;
 
@@ -870,12 +921,11 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
        DICT_ATTR const *da;
        VALUE_PAIR *vp;
 
-       DICT_ATTR const **current, *processed[REST_BODY_MAX_ATTRS + 1];
-
        pair_lists_t list_name;
        request_refs_t request_name;
        REQUEST *reference = request;
        VALUE_PAIR **vps;
+       TALLOC_CTX *ctx;
 
        size_t len;
        int curl_len; /* Length from last curl_easy_unescape call */
@@ -883,24 +933,25 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
        int count = 0;
        int ret;
 
-       processed[0] = NULL;
-
        /*
-        * Empty response?
+        *      Empty response?
         */
        while (isspace(*p)) p++;
        if (*p == '\0') return 0;
 
-       while (((q = strchr(p, '=')) != NULL) &&
-              (count < REST_BODY_MAX_ATTRS)) {
-               attribute = name;
+       while (((q = strchr(p, '=')) != NULL) && (count < REST_BODY_MAX_ATTRS)) {
                reference = request;
 
                name = curl_easy_unescape(candle, p, (q - p), &curl_len);
                p = (q + 1);
 
-               RDEBUG("Decoding attribute \"%s\"", name);
+               RDEBUG2("Parsing attribute \"%s\"", name);
 
+               /*
+                *  The attribute pointer is updated to point to the portion of
+                *  the string after the list qualifier.
+                */
+               attribute = name;
                request_name = radius_request_name(&attribute, REQUEST_CURRENT);
                if (request_name == REQUEST_UNKNOWN) {
                        RWDEBUG("Invalid request qualifier, skipping");
@@ -910,9 +961,8 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
                        continue;
                }
 
-               if (!radius_request(&reference, request_name)) {
-                       RWDEBUG("Attribute name refers to outer request"
-                              " but not in a tunnel, skipping");
+               if (radius_request(&reference, request_name) < 0) {
+                       RWDEBUG("Attribute name refers to outer request but not in a tunnel, skipping");
 
                        curl_free(name);
 
@@ -930,8 +980,7 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
 
                da = dict_attrbyname(attribute);
                if (!da) {
-                       RWDEBUG("Attribute \"%s\" unknown, skipping",
-                              attribute);
+                       RWDEBUG("Attribute \"%s\" unknown, skipping", attribute);
 
                        curl_free(name);
 
@@ -939,10 +988,11 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
                }
 
                vps = radius_list(reference, list_name);
+               rad_assert(vps);
 
-               assert(vps);
+               RDEBUG3("\tType  : %s", fr_int2str(dict_attr_types, da->type, "<INVALID>"));
 
-               RDEBUG2("\tType  : %s", fr_int2str(dict_attr_types, da->type, "¿Unknown?"));
+               ctx = radius_list_ctx(reference, list_name);
 
                q = strchr(p, '&');
                len = (!q) ? (rawlen - (p - raw)) : (unsigned)(q - p);
@@ -950,22 +1000,22 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
                value = curl_easy_unescape(candle, p, len, &curl_len);
 
                /*
-                *      If we found a delimiter we want to skip over it,
-                *      if we didn't we do *NOT* want to skip over the end
-                *      of the buffer...
+                *  If we found a delimiter we want to skip over it,
+                *  if we didn't we do *NOT* want to skip over the end
+                *  of the buffer...
                 */
                p += (!q) ? len : (len + 1);
 
-               RDEBUG2("\tLength : %i", curl_len);
-               RDEBUG2("\tValue  : \"%s\"", value);
+               RDEBUG3("\tLength : %i", curl_len);
+               RDEBUG3("\tValue  : \"%s\"", value);
 
-               RDEBUG("Performing xlat expansion of response value");
+               RDEBUG2("Performing xlat expansion of response value");
 
                if (radius_axlat(&expanded, request, value, NULL, NULL) < 0) {
                        goto skip;
                }
 
-               vp = pairalloc(request, da);
+               vp = pairalloc(ctx, da);
                if (!vp) {
                        REDEBUG("Failed creating valuepair");
                        talloc_free(expanded);
@@ -973,48 +1023,25 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
                        goto error;
                }
 
-               vp->op = T_OP_SET;
-
-               /*
-                *      Check to see if we've already processed an
-                *      attribute of the same type if we have, change the op
-                *      from T_OP_ADD to T_OP_SET.
-                */
-               current = processed;
-               while (*current++) {
-                       if (current[0] == da) {
-                               vp->op = T_OP_ADD;
-                               break;
-                       }
-               }
-
-               if (vp->op != T_OP_ADD) {
-                       current[0] = da;
-                       current[1] = NULL;
-               }
-
-               ret = pairparsevalue(vp, expanded);
-               talloc_free(expanded);
-               if (!ret) {
+               ret = pairparsevalue(vp, expanded, 0);
+               TALLOC_FREE(expanded);
+               if (ret < 0) {
                        RWDEBUG("Incompatible value assignment, skipping");
                        talloc_free(vp);
                        goto skip;
                }
 
-               if (++count == REST_BODY_MAX_ATTRS) {
-                       REDEBUG("At maximum attribute limit");
-                       return count;
-               }
+               pairadd(vps, vp);
 
-               skip:
+               count++;
 
+       skip:
                curl_free(name);
                curl_free(value);
 
                continue;
 
-               error:
-
+       error:
                curl_free(name);
                curl_free(value);
 
@@ -1044,8 +1071,7 @@ static int rest_decode_post(UNUSED rlm_rest_t *instance,
  * @param[in] leaf object containing the VALUE_PAIR value.
  * @return The VALUE_PAIR just created, or NULL on error.
  */
-static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance,
-                                     UNUSED rlm_rest_section_t *section,
+static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
                                      REQUEST *request, DICT_ATTR const *da,
                                      json_flags_t *flags, json_object *leaf)
 {
@@ -1062,10 +1088,9 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance,
         */
        value = json_object_get_string(leaf);
 
-       RDEBUG2("\tType   : %s", fr_int2str(dict_attr_types, da->type,
-                                           "¿Unknown?"));
-       RDEBUG2("\tLength : %i", strlen(value));
-       RDEBUG2("\tValue  : \"%s\"", value);
+       RDEBUG3("\tType   : %s", fr_int2str(dict_attr_types, da->type, "<INVALID>"));
+       RDEBUG3("\tLength : %zu", strlen(value));
+       RDEBUG3("\tValue  : \"%s\"", value);
 
        if (flags->do_xlat) {
                if (radius_axlat(&expanded, request, value, NULL, NULL) < 0) {
@@ -1077,9 +1102,9 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance,
                to_parse = value;
        }
 
-       vp = paircreate(NULL, da->attr, da->vendor);
+       vp = paircreate(request, da->attr, da->vendor);
        if (!vp) {
-               REDEBUG("Failed creating valuepair");
+               RWDEBUG("Failed creating valuepair, skipping...");
                talloc_free(expanded);
 
                return NULL;
@@ -1087,10 +1112,10 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance,
 
        vp->op = flags->op;
 
-       ret = pairparsevalue(vp, to_parse);
+       ret = pairparsevalue(vp, to_parse, 0);
        talloc_free(expanded);
-       if (!ret) {
-               RDEBUG("Incompatible value assignment, skipping");
+       if (ret < 0) {
+               RWDEBUG("Incompatible value assignment, skipping...");
                talloc_free(vp);
 
                return NULL;
@@ -1145,110 +1170,77 @@ static VALUE_PAIR *json_pairmake_leaf(UNUSED rlm_rest_t *instance,
  * @param[in] request Current request.
  * @param[in] object containing root node, or parent node.
  * @param[in] level Current nesting level.
- * @param[in] max_attrs counter, decremented after each VALUE_PAIR is created,
+ * @param[in] max counter, decremented after each VALUE_PAIR is created,
  *           when 0 no more attributes will be processed.
- * @return VALUE_PAIR or NULL on error.
+ * @return number of attributes created or < 0 on error.
  */
-static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
-                                UNUSED rlm_rest_section_t *section,
-                                REQUEST *request, json_object *object,
-                                UNUSED int level, int *max_attrs)
+static int json_pairmake(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
+                        REQUEST *request, json_object *object, UNUSED int level, int max)
 {
-       char const *p;
-       char *q;
-
-       char const *name, *attribute;
-
-       struct json_object *value, *idx, *tmp;
        struct lh_entry *entry;
-       json_flags_t flags;
-
-       DICT_ATTR const *da;
-       VALUE_PAIR *vp = NULL;
-
-       request_refs_t request_name;
-       pair_lists_t list_name;
-       REQUEST *reference = request;
-       VALUE_PAIR **vps;
-
-       int i, len;
+       int max_attrs = max;
 
        if (!json_object_is_type(object, json_type_object)) {
-               RDEBUG("Can't process VP container, expected JSON object, got \"%s\", skipping",
-                      json_type_to_name(json_object_get_type(object)));
-               return NULL;
-       }
+               REDEBUG("Can't process VP container, expected JSON object"
+#ifdef HAVE_JSON_TYPE_TO_NAME
+                       "got \"%s\", skipping...",
+                       json_type_to_name(json_object_get_type(object)));
+#else
+                       ", skipping...");
+#endif
+               return -1;
+       }
 
        /*
         *      Process VP container
         */
-       entry = json_object_get_object(object)->head;
-       while (entry) {
-               flags.op = T_OP_SET;
-               flags.do_xlat  = 1;
-               flags.is_json  = 0;
+       for (entry = json_object_get_object(object)->head;
+            entry;
+            entry = entry->next) {
+               int i = 0, elements;
+               struct json_object *value, *element, *tmp;
 
-               name = (char*)entry->k;
+               char const *name = (char const *)entry->k;
 
-               /* Fix the compiler warnings regarding const... */
-               memcpy(&value, &entry->v, sizeof(value));
+               json_flags_t flags = {
+                       .op = T_OP_SET,
+                       .do_xlat = 1,
+                       .is_json = 0
+               };
 
-               entry = entry->next;
+               value_pair_tmpl_t dst;
+               REQUEST *current = request;
+               VALUE_PAIR **vps, *vp = NULL;
 
-               /*
-                *      For people handcrafting JSON responses
-                */
-               p = name;
-               while ((p = q = strchr(p, '|'))) {
-                       *q = ':';
-                       p++;
-               }
+               memset(&dst, 0, sizeof(dst));
 
-               attribute = name;
-               reference = request;
+               /* Fix the compiler warnings regarding const... */
+               memcpy(&value, &entry->v, sizeof(value));
 
                /*
-                *      Resolve attribute name to a dictionary entry and
-                *      pairlist.
+                *  Resolve attribute name to a dictionary entry and pairlist.
                 */
-               RDEBUG2("Decoding attribute \"%s\"", name);
-
-               request_name = radius_request_name(&attribute, REQUEST_CURRENT);
-               if (request_name == REQUEST_UNKNOWN) {
-                       RWDEBUG("Request qualifier unknown, skipping");
-
-                       continue;
-               }
-
-               if (!radius_request(&reference, request_name)) {
-                       RWDEBUG("Attribute name refers to outer request"
-                              " but not in a tunnel, skipping");
+               RDEBUG2("Parsing attribute \"%s\"", name);
 
+               if (radius_parse_attr(&dst, name, REQUEST_CURRENT, PAIR_LIST_REPLY) < 0) {
+                       RWDEBUG("Failed parsing attribute: %s, skipping...", fr_strerror());
                        continue;
                }
 
-               list_name = radius_list_name(&attribute, PAIR_LIST_REPLY);
-               if (list_name == PAIR_LIST_UNKNOWN) {
-                       RWDEBUG("Invalid list qualifier, skipping");
-
+               if (radius_request(&current, dst.vpt_request) < 0) {
+                       RWDEBUG("Attribute name refers to outer request but not in a tunnel, skipping...");
                        continue;
                }
 
-               da = dict_attrbyname(attribute);
-               if (!da) {
-                       RWDEBUG("Attribute \"%s\" unknown, skipping",
-                              attribute);
-
+               vps = radius_list(current, dst.vpt_list);
+               if (!vps) {
+                       RWDEBUG("List not valid in this context, skipping...");
                        continue;
                }
 
-               vps = radius_list(reference, list_name);
-
-               assert(vps);
-
                /*
-                *      Alternate JSON structure that allows operator,
-                *      and other flags to be specified.
+                *  Alternative JSON structure which allows operator,
+                *  and other flags to be specified.
                 *
                 *      "<name>":{
                 *              "do_xlat":<bool>,
@@ -1264,20 +1256,20 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                 */
                if (json_object_is_type(value, json_type_object)) {
                        /*
-                        *      Process operator if present.
+                        *  Process operator if present.
                         */
                        tmp = json_object_object_get(value, "op");
                        if (tmp) {
                                flags.op = fr_str2int(fr_tokens, json_object_get_string(tmp), 0);
                                if (!flags.op) {
-                                       RDEBUG("Invalid operator value \"%s\","
-                                              " skipping", tmp);
+                                       RWDEBUG("Invalid operator value \"%s\", skipping...",
+                                               json_object_get_string(tmp));
                                        continue;
                                }
                        }
 
                        /*
-                        *      Process optional do_xlat bool.
+                        *  Process optional do_xlat bool.
                         */
                        tmp = json_object_object_get(value, "do_xlat");
                        if (tmp) {
@@ -1285,7 +1277,7 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                        }
 
                        /*
-                        *      Process optional is_json bool.
+                        *  Process optional is_json bool.
                         */
                        tmp = json_object_object_get(value, "is_json");
                        if (tmp) {
@@ -1293,69 +1285,71 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
                        }
 
                        /*
-                        *      Value key must be present if were using
-                        *      the expanded syntax.
+                        *  Value key must be present if were using the expanded syntax.
                         */
                        value = json_object_object_get(value, "value");
                        if (!value) {
-                               RDEBUG("Value key missing, skipping", value);
+                               RWDEBUG("Value key missing, skipping...");
                                continue;
                        }
-               }
+               }
 
                /*
-                *      Setup pairmake / recursion loop.
+                *  Setup pairmake / recursion loop.
                 */
-               if (!flags.is_json &&
-                   json_object_is_type(value, json_type_array)) {
-                       len = json_object_array_length(value);
-                       if (!len) {
-                               RDEBUG("Zero length value array, skipping",
-                                      value);
+               if (!flags.is_json && json_object_is_type(value, json_type_array)) {
+                       elements = json_object_array_length(value);
+                       if (!elements) {
+                               RWDEBUG("Zero length value array, skipping...");
                                continue;
                        }
-                       idx = json_object_array_get_idx(value, 0);
+                       element = json_object_array_get_idx(value, 0);
                } else {
-                       len = 1;
-                       idx = value;
+                       elements = 1;
+                       element = value;
                }
 
-               i = 0;
+               /*
+                *  A JSON 'value' key, may have multiple elements, iterate
+                *  over each of them, creating a new VALUE_PAIR.
+                */
                do {
-                       if (!(*max_attrs)--) {
-                                       REDEBUG("At maximum attribute limit");
-                                       return NULL;
+                       if (max_attrs-- <= 0) {
+                               RWDEBUG("At maximum attribute limit");
+                               return max;
                        }
 
                        /*
-                        *      Automagically switch the op for multivalued
-                        *      attributes.
+                        *  Automagically switch the op for multivalued attributes.
                         */
-                       if (((flags.op == T_OP_SET) ||
-                            (flags.op == T_OP_EQ)) && (len > 1)) {
+                       if (((flags.op == T_OP_SET) || (flags.op == T_OP_EQ)) && (i >= 1)) {
                                flags.op = T_OP_ADD;
                        }
 
-                       if (!flags.is_json && json_object_is_type(value, json_type_object)) {
+                       if (json_object_is_type(element, json_type_object) && !flags.is_json) {
                                /* TODO: Insert nested VP into VP structure...*/
-                               REDEBUG("Found nested VP, these are not yet supported");
+                               RWDEBUG("Found nested VP, these are not yet supported, skipping...");
 
-                               return NULL;
+                               continue;
 
                                /*
                                vp = json_pairmake(instance, section,
                                                   request, value,
                                                   level + 1, max_attrs);*/
                        } else {
-                               vp = json_pairmake_leaf(instance, section,
-                                                       request, da, &flags,
-                                                       idx);
+                               vp = json_pairmake_leaf(instance, section, request, dst.vpt_da, &flags, element);
+                               if (!vp) continue;
                        }
-               } while ((++i < len) &&
-                        (idx = json_object_array_get_idx(value, i)));
-   }
+                       debug_pair(vp);
+                       radius_pairmove(current, vps, vp, false);
+               /*
+                *  If we call json_object_array_get_idx on something that's not an array
+                *  the behaviour appears to be to occasionally segfault.
+                */
+               } while ((++i < elements) && (element = json_object_array_get_idx(value, i)));
+       }
 
-   return vp;
+       return max - max_attrs;
 }
 
 /** Converts JSON response into VALUE_PAIRs and adds them to the request.
@@ -1376,38 +1370,35 @@ static VALUE_PAIR *json_pairmake(rlm_rest_t *instance,
  * @param[in] rawlen Length of data in raw buffer.
  * @return the number of VALUE_PAIRs processed or -1 on unrecoverable error.
  */
-static int rest_decode_json(rlm_rest_t *instance,
-                           UNUSED rlm_rest_section_t *section,
-                           REQUEST *request, UNUSED void *handle,
-                           char *raw, UNUSED size_t rawlen)
+static int rest_decode_json(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
+                           REQUEST *request, UNUSED void *handle, char *raw, UNUSED size_t rawlen)
 {
        char const *p = raw;
 
        struct json_object *json;
 
-       int max = REST_BODY_MAX_ATTRS;
+       int ret;
 
        /*
-        *      Empty response?
+        *  Empty response?
         */
        while (isspace(*p)) p++;
        if (*p == '\0') return 0;
 
        json = json_tokener_parse(p);
        if (!json) {
-               RDEBUG("Malformed JSON data \"%s\"", raw);
+               REDEBUG("Malformed JSON data \"%s\"", raw);
                return -1;
        }
 
-       json_pairmake(instance, section, request, json, 0, &max);
+       ret = json_pairmake(instance, section, request, json, 0, REST_BODY_MAX_ATTRS);
 
        /*
-        *      Decrement reference count for root object, should free entire
-        *      JSON tree.
+        *  Decrement reference count for root object, should free entire JSON tree.
         */
        json_object_put(json);
 
-       return (REST_BODY_MAX_ATTRS - max);
+       return ret;
 }
 #endif
 
@@ -1419,73 +1410,86 @@ static int rest_decode_json(rlm_rest_t *instance,
  * Matches prototype for CURLOPT_HEADERFUNCTION, and will be called directly
  * by libcurl.
  *
- * @param[in] ptr Char buffer where inbound header data is written.
+ * @param[in] in Char buffer where inbound header data is written.
  * @param[in] size Multiply by nmemb to get the length of ptr.
  * @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
+ * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
  * @return Length of data processed, or 0 on error.
  */
-static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
-                               void *userdata)
+static size_t rest_response_header(void *in, size_t size, size_t nmemb, void *userdata)
 {
-       rlm_rest_write_t *ctx  = userdata;
-       REQUEST *request       = ctx->request; /* Used by RDEBUG */
+       rlm_rest_response_t *ctx = userdata;
+       REQUEST *request = ctx->request; /* Used by RDEBUG */
 
-       char const *p = ptr, *q;
-       char *tmp;
+       char const *p = in, *q;
 
        size_t const t = (size * nmemb);
        size_t s = t;
        size_t len;
 
        http_body_type_t type;
-       http_body_type_t supp;
+
+       /*
+        *  Curl seems to throw these (\r\n) in before the next set of headers when
+        *  looks like it's just a body separator and safe to ignore after we
+        *  receive a 100 Continue.
+        */
+       if (t == 2 && ((p[0] == '\r') && (p[1] == '\n'))) return t;
 
        switch (ctx->state) {
        case WRITE_STATE_INIT:
-               RDEBUG("Processing header");
+               RDEBUG2("Processing response header");
 
                /*
-                * HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
+                *  HTTP/<version> <reason_code>[ <reason_phrase>]\r\n
                 *
-                * "HTTP/1.1 " (8) + "100 " (4) + "\r\n" (2) = 14
+                *  "HTTP/1.1 " (8) + "100 " (4) + "\r\n" (2) = 14
                 */
-               if (s < 14) goto malformed;
-
+               if (s < 14) {
+                       REDEBUG("Malformed HTTP header: Status line too short");
+                       goto malformed;
+               }
                /*
-                * Check start of header matches...
+                *  Check start of header matches...
                 */
-               if (strncasecmp("HTTP/", p, 5) != 0) goto malformed;
-
+               if (strncasecmp("HTTP/", p, 5) != 0) {
+                       REDEBUG("Malformed HTTP header: Missing HTTP version");
+                       goto malformed;
+               }
                p += 5;
                s -= 5;
 
                /*
-                * Skip the version field, next space should mark start
-                * of reason_code.
+                *  Skip the version field, next space should mark start of reason_code.
                 */
                q = memchr(p, ' ', s);
-               if (!q) goto malformed;
+               if (!q) {
+                       RDEBUG("Malformed HTTP header: Missing reason code");
+                       goto malformed;
+               }
 
                s -= (q - p);
                p  = q;
 
                /*
-                * Process reason_code.
+                *  Process reason_code.
                 *
-                * " 100" (4) + "\r\n" (2) = 6
+                *  " 100" (4) + "\r\n" (2) = 6
                 */
-               if (s < 6) goto malformed;
+               if (s < 6) {
+                       REDEBUG("Malformed HTTP header: Reason code too short");
+                       goto malformed;
+               }
                p++;
                s--;
 
-               /* Char after reason code must be a space, or \r */
+               /*  Char after reason code must be a space, or \r */
                if (!((p[3] == ' ') || (p[3] == '\r'))) goto malformed;
 
                ctx->code = atoi(p);
 
                /*
-                *      Process reason_phrase (if present).
+                *  Process reason_phrase (if present).
                 */
                if (p[3] == ' ') {
                        p += 4;
@@ -1496,14 +1500,9 @@ static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
 
                        len = (q - p);
 
-                       tmp = rad_malloc(len + 1);
-                       strlcpy(tmp, p, len + 1);
-
-                       RDEBUG("\tStatus : %i (%s)", ctx->code, tmp);
-
-                       free(tmp);
+                       RDEBUG2("\tStatus : %i (%.*s)", ctx->code, (int) len, p);
                } else {
-                       RDEBUG("\tStatus : %i", ctx->code);
+                       RDEBUG2("\tStatus : %i", ctx->code);
                }
 
                ctx->state = WRITE_STATE_PARSE_HEADERS;
@@ -1517,66 +1516,62 @@ static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
                        s -= 14;
 
                        /*
-                        *      Check to see if there's a parameter
-                        *      separator.
+                        *  Check to see if there's a parameter separator.
                         */
                        q = memchr(p, ';', s);
 
                        /*
-                        *      If there's not, find the end of this
-                        *      header.
+                        *  If there's not, find the end of this header.
                         */
                        if (!q) q = memchr(p, '\r', s);
 
-                       len = (!q) ? s : (unsigned)(q - p);
+                       len = !q ? s : (size_t) (q - p);
+                       type = fr_substr2int(http_content_type_table, p, HTTP_BODY_UNKNOWN, len);
 
-                       type = fr_substr2int(http_content_type_table,
-                               p, HTTP_BODY_UNKNOWN,
-                               len);
 
-                       supp = http_body_type_supported[type];
+                       RDEBUG2("\tType   : %s (%.*s)", fr_int2str(http_body_type_table, type, "<INVALID>"),
+                               (int) len, p);
 
-                       tmp = rad_malloc(len + 1);
-                       strlcpy(tmp, p, len + 1);
-
-                       RDEBUG("\tType   : %s (%s)",
-                               fr_int2str(http_body_type_table, type,
-                                       "¿Unknown?"), tmp);
-
-                       free(tmp);
-
-                       if (type == HTTP_BODY_UNKNOWN) {
-                               RDEBUG("Couldn't determine type, using"
-                                      " request type \"%s\".",
-                                      fr_int2str(http_body_type_table,
-                                                 ctx->type,
-                                                 "¿Unknown?"));
+                       /*
+                        *  Figure out if the type is supported by one of the decoders.
+                        */
+                       if (ctx->force_to != HTTP_BODY_UNKNOWN) {
+                               if (ctx->force_to != ctx->type) {
+                                       RDEBUG3("Forcing body type to \"%s\"",
+                                               fr_int2str(http_body_type_table, ctx->force_to, "<INVALID>"));
+                                       ctx->type = ctx->force_to;
+                               }
+                       /*
+                        *  Assume the force_to value has already been validation.
+                        */
+                       } else switch (http_body_type_supported[ctx->type]) {
+                       case HTTP_BODY_UNKNOWN:
+                               RWDEBUG("Couldn't determine type, using the request's type \"%s\".",
+                                       fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
+                               break;
 
-                       } else if (supp == HTTP_BODY_UNSUPPORTED) {
-                               RDEBUG("Type \"%s\" is currently"
-                                      " unsupported",
-                                      fr_int2str(http_body_type_table,
-                                                 type, "¿Unknown?"));
-                               ctx->type = HTTP_BODY_UNSUPPORTED;
-                       } else if (supp == HTTP_BODY_UNAVAILABLE) {
-                               RDEBUG("Type \"%s\" is currently"
-                                      " unavailable, please rebuild"
-                                      " this module with the required"
-                                      " headers",
-                                      fr_int2str(http_body_type_table,
-                                                 type, "¿Unknown?"));
+                       case HTTP_BODY_UNSUPPORTED:
+                               REDEBUG("Type \"%s\" is currently unsupported",
+                                       fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
                                ctx->type = HTTP_BODY_UNSUPPORTED;
+                               break;
 
-                       } else if (supp == HTTP_BODY_INVALID) {
-                               RDEBUG("Type \"%s\" is not a valid web"
-                                      " API data markup format",
-                                      fr_int2str(http_body_type_table,
-                                                 type, "¿Unknown?"));
+                       case HTTP_BODY_UNAVAILABLE:
+                               REDEBUG("Type \"%s\" is unavailable, please rebuild this module with the required "
+                                       "library", fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
+                               ctx->type = HTTP_BODY_UNAVAILABLE;
+                               break;
 
+                       case HTTP_BODY_INVALID:
+                               REDEBUG("Type \"%s\" is not a valid web API data markup format",
+                                       fr_int2str(http_body_type_table, ctx->type, "<INVALID>"));
                                ctx->type = HTTP_BODY_INVALID;
+                               break;
 
-                       } else if (type != ctx->type) {
+                       /* supported type */
+                       default:
                                ctx->type = type;
+                               break;
                        }
                }
                break;
@@ -1584,12 +1579,28 @@ static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
        default:
                break;
        }
+
+       /*
+        *  If we got a 100 Continue, we need to send additional payload data.
+        *  reset the state to WRITE_STATE_INIT, so that when were called again
+        *  we overwrite previous header data with that from the proper header.
+        */
+       if (ctx->code == 100) {
+               RDEBUG2("Continuing...");
+               ctx->state = WRITE_STATE_INIT;
+       }
+
        return t;
 
-       malformed:
+malformed:
+       {
+               char escaped[1024];
+
+               fr_print_string((char *) in, t, escaped, sizeof(escaped));
 
-       RDEBUG("Incoming header was malformed");
-       ctx->code = -1;
+               REDEBUG("Received %zu bytes of response data: %s", t, escaped);
+               ctx->code = -1;
+       }
 
        return (t - s);
 }
@@ -1602,22 +1613,23 @@ static size_t rest_write_header(void *ptr, size_t size, size_t nmemb,
  * @param[in] ptr Char buffer where inbound header data is written
  * @param[in] size Multiply by nmemb to get the length of ptr.
  * @param[in] nmemb Multiply by size to get the length of ptr.
- * @param[in] userdata rlm_rest_write_t to keep parsing state between calls.
+ * @param[in] userdata rlm_rest_response_t to keep parsing state between calls.
  * @return length of data processed, or 0 on error.
  */
-static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
-                             void *userdata)
+static size_t rest_response_body(void *ptr, size_t size, size_t nmemb, void *userdata)
 {
-       rlm_rest_write_t *ctx  = userdata;
-       REQUEST *request       = ctx->request; /* Used by RDEBUG */
+       rlm_rest_response_t *ctx = userdata;
+       REQUEST *request = ctx->request; /* Used by RDEBUG */
 
-       char const *p = ptr;
+       char const *p = ptr, *q;
        char *tmp;
 
        size_t const t = (size * nmemb);
 
+       if (t == 0) return 0;
+
        /*
-        *      Any post processing of headers should go here...
+        *  Any post processing of headers should go here...
         */
        if (ctx->state == WRITE_STATE_PARSE_HEADERS) {
                ctx->state = WRITE_STATE_PARSE_CONTENT;
@@ -1625,22 +1637,34 @@ static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
 
        switch (ctx->type) {
        case HTTP_BODY_UNSUPPORTED:
-               return t;
-
+       case HTTP_BODY_UNAVAILABLE:
        case HTTP_BODY_INVALID:
-               tmp = rad_malloc(t + 1);
-               strlcpy(tmp, p, t + 1);
+               while ((q = memchr(p, '\n', t - (p - (char *)ptr)))) {
+                       REDEBUG("%.*s", (int) (q - p), p);
+                       p = q + 1;
+               }
+
+               if (*p != '\0') {
+                       REDEBUG("%.*s", (int)(t - (p - (char *)ptr)), p);
+               }
+
+               return t;
 
-               RDEBUG2("%s", tmp);
+       case HTTP_BODY_NONE:
+               while ((q = memchr(p, '\n', t - (p - (char *)ptr)))) {
+                       RDEBUG3("%.*s", (int) (q - p), p);
+                       p = q + 1;
+               }
 
-               free(tmp);
+               if (*p != '\0') {
+                       RDEBUG3("%.*s", (int)(t - (p - (char *)ptr)), p);
+               }
 
                return t;
 
        default:
                if (t > (ctx->alloc - ctx->used)) {
-                       ctx->alloc += ((t + 1) > REST_BODY_INCR) ?
-                               t + 1 : REST_BODY_INCR;
+                       ctx->alloc += ((t + 1) > REST_BODY_INIT) ? t + 1 : REST_BODY_INIT;
 
                        tmp = ctx->buffer;
 
@@ -1648,8 +1672,7 @@ static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
 
                        /* If data has been written previously */
                        if (tmp) {
-                               strlcpy(ctx->buffer, tmp,
-                                      (ctx->used + 1));
+                               strlcpy(ctx->buffer, tmp, (ctx->used + 1));
                                free(tmp);
                        }
                }
@@ -1662,39 +1685,43 @@ static size_t rest_write_body(void *ptr, size_t size, size_t nmemb,
        return t;
 }
 
-/** (Re-)Initialises the data in a rlm_rest_write_t.
+/** (Re-)Initialises the data in a rlm_rest_response_t.
  *
- * This resets the values of the a rlm_rest_write_t to their defaults.
+ * This resets the values of the a rlm_rest_response_t to their defaults.
  * Must be called between encoding sessions.
  *
- * @see rest_write_body
- * @see rest_write_header
+ * @see rest_response_body
+ * @see rest_response_header
  *
  * @param[in] request Current request.
  * @param[in] ctx data to initialise.
  * @param[in] type Default http_body_type to use when decoding raw data, may be
- * overwritten by rest_write_header.
+ * overwritten by rest_response_header.
  */
-static void rest_write_ctx_init(REQUEST *request, rlm_rest_write_t *ctx,
-                               http_body_type_t type)
+static void rest_response_init(REQUEST *request, rlm_rest_response_t *ctx, http_body_type_t type)
 {
-       ctx->request    = request;
-       ctx->type       = type;
-       ctx->state      = WRITE_STATE_INIT;
-       ctx->alloc      = 0;
-       ctx->used       = 0;
-       ctx->buffer     = NULL;
+       ctx->request = request;
+       ctx->type = type;
+       ctx->state = WRITE_STATE_INIT;
+       ctx->alloc = 0;
+       ctx->used = 0;
+       ctx->buffer = NULL;
 }
 
-/** Frees the intermediary buffer created by rest_write.
+/** Extracts pointer to buffer containing response data
  *
- * @param[in] ctx data to be freed.
+ * @param[out] out Where to write the pointer to the buffer.
+ * @param[in] handle used for the last request.
+ * @return > 0 if data is available.
  */
-static void rest_write_free(rlm_rest_write_t *ctx)
+size_t rest_get_handle_data(char const **out, rlm_rest_handle_t *handle)
 {
-       if (ctx->buffer != NULL) {
-               free(ctx->buffer);
-       }
+       rlm_rest_curl_context_t *ctx = handle->ctx;
+
+       rad_assert(ctx->response.buffer || (!ctx->response.buffer && !ctx->response.used));
+
+       *out = ctx->response.buffer;
+       return ctx->response.used;
 }
 
 /** Configures body specific curlopts.
@@ -1711,42 +1738,54 @@ static void rest_write_free(rlm_rest_write_t *ctx)
  *           transfers (NULL if not using chunked mode).
  * @return 0 on success -1 on error.
  */
-static int rest_request_config_body(UNUSED rlm_rest_t *instance,
-                                   rlm_rest_section_t *section,
-                                   REQUEST *request,
-                                   rlm_rest_handle_t *handle,
-                                   rest_read_t func)
+static int rest_request_config_body(UNUSED rlm_rest_t *instance, rlm_rest_section_t *section,
+                                   REQUEST *request, rlm_rest_handle_t *handle, rest_read_t func)
 {
        rlm_rest_curl_context_t *ctx = handle->ctx;
-       CURL *candle                 = handle->handle;
+       CURL                    *candle = handle->handle;
+
+       CURLcode ret = CURLE_OK;
+       char const *option = "unknown";
 
        ssize_t len;
-       CURLcode ret;
 
-       if (section->chunk > 0) {
-               ret = curl_easy_setopt(candle, CURLOPT_READDATA, &ctx->read);
-               if (ret != CURLE_OK) goto error;
+       /*
+        *  We were provided with no read function, assume this means
+        *  no body should be sent.
+        */
+       if (!func) {
+               SET_OPTION(CURLOPT_POSTFIELDSIZE, 0);
+               return 0;
+       }
 
-               ret = curl_easy_setopt(candle, CURLOPT_READFUNCTION, rest_encode_json);
-               if (ret != CURLE_OK) goto error;
-       } else {
-               len = rest_read_wrapper(&ctx->body, func, REST_BODY_MAX_LEN,
-                                       &ctx->read);
-               if (len <= 0) {
-                       REDEBUG("Failed creating HTTP body content");
-               }
+       /*
+        *  Chunked transfer encoding means the body will be sent in
+        *  multiple parts.
+        */
+       if (section->chunk > 0) {
+               SET_OPTION(CURLOPT_READDATA, &ctx->request);
+               SET_OPTION(CURLOPT_READFUNCTION, func);
 
-               ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDS, ctx->body);
-               if (ret != CURLE_OK) goto error;
+               return 0;
+       }
 
-               ret = curl_easy_setopt(candle, CURLOPT_POSTFIELDSIZE, len);
-               if (ret != CURLE_OK) goto error;
+       /*
+        *  If were not doing chunked encoding then we read the entire
+        *  body into a buffer, and send it in one go.
+        */
+       len = rest_request_encode_wrapper(&ctx->body, func, REST_BODY_MAX_LEN, &ctx->request);
+       if (len <= 0) {
+               REDEBUG("Failed creating HTTP body content");
+               return -1;
        }
 
+       SET_OPTION(CURLOPT_POSTFIELDS, ctx->body);
+       SET_OPTION(CURLOPT_POSTFIELDSIZE, len);
+
        return 0;
 
-       error:
-       REDEBUG("Failed setting curl option: %i - %s", ret, curl_easy_strerror(ret));
+error:
+       REDEBUG("Failed setting curl option %s: %s (%i)", option, curl_easy_strerror(ret), ret);
 
        return -1;
 }
@@ -1779,13 +1818,15 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
                        http_body_type_t type,
                        char const *uri, char const *username, char const *password)
 {
-       rlm_rest_handle_t *randle       = handle;
-       rlm_rest_curl_context_t *ctx    = randle->ctx;
-       CURL *candle                    = randle->handle;
+       rlm_rest_handle_t       *randle = handle;
+       rlm_rest_curl_context_t *ctx = randle->ctx;
+       CURL                    *candle = randle->handle;
 
        http_auth_type_t auth = section->auth;
 
-       CURLcode ret;
+       CURLcode ret = CURLE_OK;
+       char const *option = "unknown";
+       char const *content_type;
        long val = 1;
 
        char buffer[512];
@@ -1797,37 +1838,31 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
        /*
         *      Setup any header options and generic headers.
         */
-       ret = curl_easy_setopt(candle, CURLOPT_URL, uri);
-       if (ret != CURLE_OK) goto error;
+       SET_OPTION(CURLOPT_URL, uri);
+       SET_OPTION(CURLOPT_USERAGENT, "FreeRADIUS " RADIUSD_VERSION_STRING);
 
-       ret = curl_easy_setopt(candle, CURLOPT_USERAGENT, "FreeRADIUS");
-       if (ret != CURLE_OK) goto error;
-
-       snprintf(buffer, (sizeof(buffer) - 1), "Content-Type: %s",
-                fr_int2str(http_content_type_table, type, "¿Unknown?"));
+       content_type = fr_int2str(http_content_type_table, type, section->body_str);
+       snprintf(buffer, sizeof(buffer), "Content-Type: %s", content_type);
        ctx->headers = curl_slist_append(ctx->headers, buffer);
        if (!ctx->headers) goto error_header;
 
        if (section->timeout) {
-               ret = curl_easy_setopt(candle, CURLOPT_TIMEOUT,
-                                      section->timeout);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_TIMEOUT, section->timeout);
        }
 
-       ret = curl_easy_setopt(candle, CURLOPT_PROTOCOLS,
-                              (CURLPROTO_HTTP | CURLPROTO_HTTPS));
-       if (ret != CURLE_OK) goto error;
+       SET_OPTION(CURLOPT_PROTOCOLS, (CURLPROTO_HTTP | CURLPROTO_HTTPS));
 
        /*
         *      FreeRADIUS custom headers
         */
-       snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Section: %s",
-                section->name);
+       RDEBUG3("Adding custom headers:");
+       snprintf(buffer, sizeof(buffer), "X-FreeRADIUS-Section: %s", section->name);
+       RDEBUG3("\t%s", buffer);
        ctx->headers = curl_slist_append(ctx->headers, buffer);
        if (!ctx->headers) goto error_header;
 
-       snprintf(buffer, (sizeof(buffer) - 1), "X-FreeRADIUS-Server: %s",
-                request->server);
+       snprintf(buffer, sizeof(buffer), "X-FreeRADIUS-Server: %s", request->server);
+       RDEBUG3("\t%s", buffer);
        ctx->headers = curl_slist_append(ctx->headers, buffer);
        if (!ctx->headers) goto error_header;
 
@@ -1836,50 +1871,29 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
         */
        switch (method) {
        case HTTP_METHOD_GET :
-               ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
-                                      val);
-               if (ret != CURLE_OK) goto error;
-
+               SET_OPTION(CURLOPT_HTTPGET, val);
                break;
 
        case HTTP_METHOD_POST :
-               ret = curl_easy_setopt(candle, CURLOPT_POST,
-                                      val);
-               if (ret != CURLE_OK) goto error;
-
+               SET_OPTION(CURLOPT_POST, val);
                break;
 
        case HTTP_METHOD_PUT :
-               ret = curl_easy_setopt(candle, CURLOPT_PUT,
-                                      val);
-               if (ret != CURLE_OK) goto error;
-
+               SET_OPTION(CURLOPT_PUT, val);
                break;
 
        case HTTP_METHOD_DELETE :
-               ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
-                                      val);
-               if (ret != CURLE_OK) goto error;
-
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_CUSTOMREQUEST, "DELETE");
-               if (ret != CURLE_OK) goto error;
-
+               SET_OPTION(CURLOPT_HTTPGET, val);
+               SET_OPTION(CURLOPT_CUSTOMREQUEST, "DELETE");
                break;
 
        case HTTP_METHOD_CUSTOM :
-               ret = curl_easy_setopt(candle, CURLOPT_HTTPGET,
-                                      val);
-               if (ret != CURLE_OK) goto error;
-
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_CUSTOMREQUEST,
-                                      section->method);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_HTTPGET, val);
+               SET_OPTION(CURLOPT_CUSTOMREQUEST, section->method_str);
                break;
 
        default:
-               assert(0);
+               rad_assert(0);
                break;
        };
 
@@ -1888,71 +1902,50 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
         */
        if (auth) {
                if ((auth >= HTTP_AUTH_BASIC) &&
-                   (auth <= HTTP_AUTH_ANY_SAFE)) {
-                       ret = curl_easy_setopt(candle, CURLOPT_HTTPAUTH, http_curl_auth[auth]);
-                       if (ret != CURLE_OK) goto error;
+                   (auth <= HTTP_AUTH_ANY_SAFE)) {
+                       SET_OPTION(CURLOPT_HTTPAUTH, http_curl_auth[auth]);
 
                        if (username) {
-                               ret = curl_easy_setopt(candle, CURLOPT_USERNAME, username);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_USERNAME, username);
                        } else if (section->username) {
                                if (radius_xlat(buffer, sizeof(buffer), request, section->username, NULL, NULL) < 0) {
+                                       option = STRINGIFY(CURLOPT_USERNAME);
                                        goto error;
                                }
-                               ret = curl_easy_setopt(candle, CURLOPT_USERNAME, buffer);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_USERNAME, buffer);
                        }
 
                        if (password) {
-                               ret = curl_easy_setopt(candle, CURLOPT_PASSWORD, password);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_PASSWORD, password);
                        } else if (section->password) {
                                if (radius_xlat(buffer, sizeof(buffer), request, section->password, NULL, NULL) < 0) {
+                                       option = STRINGIFY(CURLOPT_PASSWORD);
                                        goto error;
                                }
-                               ret = curl_easy_setopt(candle, CURLOPT_PASSWORD, buffer);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_PASSWORD, buffer);
                        }
 #ifdef CURLOPT_TLSAUTH_USERNAME
                } else if (type == HTTP_AUTH_TLS_SRP) {
-                       ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_TYPE, http_curl_auth[auth]);
+                       SET_OPTION(CURLOPT_TLSAUTH_TYPE, http_curl_auth[auth]);
 
                        if (username) {
-                               ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_USERNAME, username);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_TLSAUTH_USERNAME, username);
                        } else if (section->username) {
                                if (radius_xlat(buffer, sizeof(buffer), request, section->username, NULL, NULL) < 0) {
+                                       option = STRINGIFY(CURLOPT_TLSAUTH_USERNAME);
                                        goto error;
                                }
-                               ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_USERNAME, buffer);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_TLSAUTH_USERNAME, buffer);
                        }
 
                        if (password) {
-                               ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_PASSWORD, password);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_TLSAUTH_PASSWORD, password);
                        } else if (section->password) {
                                if (radius_xlat(buffer, sizeof(buffer), request, section->password, NULL, NULL) < 0) {
+                                       option = STRINGIFY(CURLOPT_TLSAUTH_PASSWORD);
                                        goto error;
                                }
-                               ret = curl_easy_setopt(candle, CURLOPT_TLSAUTH_PASSWORD, buffer);
-                               if (ret != CURLE_OK) {
-                                       goto error;
-                               }
+                               SET_OPTION(CURLOPT_TLSAUTH_PASSWORD, buffer);
                        }
 #endif
                }
@@ -1962,147 +1955,150 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
         *      Set SSL/TLS authentication parameters
         */
        if (section->tls_certificate_file) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_SSLCERT,
-                                      section->tls_certificate_file);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_SSLCERT, section->tls_certificate_file);
        }
 
        if (section->tls_private_key_file) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_SSLKEY,
-                                      section->tls_private_key_file);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_SSLKEY, section->tls_private_key_file);
        }
 
        if (section->tls_private_key_password) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_KEYPASSWD,
-                                      section->tls_private_key_password);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_KEYPASSWD, section->tls_private_key_password);
        }
 
        if (section->tls_ca_file) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_ISSUERCERT,
-                                      section->tls_ca_file);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_ISSUERCERT, section->tls_ca_file);
        }
 
        if (section->tls_ca_path) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_CAPATH,
-                                      section->tls_ca_path);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_CAPATH, section->tls_ca_path);
        }
 
        if (section->tls_random_file) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_RANDOM_FILE,
-                                      section->tls_random_file);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_RANDOM_FILE, section->tls_random_file);
        }
 
        if (section->tls_check_cert) {
-               ret = curl_easy_setopt(candle,
-                                      CURLOPT_SSL_VERIFYHOST,
-                                      (section->tls_check_cert_cn == true) ?
-                                       2 : 0);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_SSL_VERIFYHOST, (section->tls_check_cert_cn == true) ? 2 : 0);
        } else {
-               ret = curl_easy_setopt(candle,
-                      CURLOPT_SSL_VERIFYPEER,
-                      0);
-               if (ret != CURLE_OK) goto error;
+               SET_OPTION(CURLOPT_SSL_VERIFYPEER, 0);
        }
 
        /*
-        *      Tell CURL how to get HTTP body content, and how to process
-        *      incoming data.
+        *      Tell CURL how to get HTTP body content, and how to process incoming data.
         */
-       rest_write_ctx_init(request, &ctx->write, type);
+       rest_response_init(request, &ctx->response, type);
 
-       ret = curl_easy_setopt(candle, CURLOPT_HEADERFUNCTION,
-                              rest_write_header);
-       if (ret != CURLE_OK) goto error;
+       SET_OPTION(CURLOPT_HEADERFUNCTION, rest_response_header);
+       SET_OPTION(CURLOPT_HEADERDATA, &ctx->response);
+       SET_OPTION(CURLOPT_WRITEFUNCTION, rest_response_body);
+       SET_OPTION(CURLOPT_WRITEDATA, &ctx->response);
 
-       ret = curl_easy_setopt(candle, CURLOPT_HEADERDATA,
-                              &ctx->write);
-       if (ret != CURLE_OK) goto error;
-
-       ret = curl_easy_setopt(candle, CURLOPT_WRITEFUNCTION,
-                              rest_write_body);
-       if (ret != CURLE_OK) goto error;
-
-       ret = curl_easy_setopt(candle, CURLOPT_WRITEDATA,
-                              &ctx->write);
-       if (ret != CURLE_OK) goto error;
+       /*
+        *  Force parsing the body text as a particular encoding.
+        */
+       ctx->response.force_to = section->force_to;
 
        switch (method) {
        case HTTP_METHOD_GET :
        case HTTP_METHOD_DELETE :
-               return 0;
+               RDEBUG3("Using a HTTP method which does not require a body.  Forcing request body type to \"none\"");
+               goto finish;
 
        case HTTP_METHOD_POST :
        case HTTP_METHOD_PUT :
        case HTTP_METHOD_CUSTOM :
                if (section->chunk > 0) {
-                       ctx->read.chunk = section->chunk;
+                       ctx->request.chunk = section->chunk;
 
-                       ctx->headers = curl_slist_append(ctx->headers,
-                                                        "Expect:");
+                       ctx->headers = curl_slist_append(ctx->headers, "Expect:");
                        if (!ctx->headers) goto error_header;
 
-                       ctx->headers = curl_slist_append(ctx->headers,
-                                                        "Transfer-Encoding: chunked");
+                       ctx->headers = curl_slist_append(ctx->headers, "Transfer-Encoding: chunked");
                        if (!ctx->headers) goto error_header;
                }
 
-               switch (type) {
-#ifdef HAVE_JSON
-               case HTTP_BODY_JSON:
-                       rest_read_ctx_init(request, &ctx->read, 1);
+               RDEBUG3("Request body content-type will be \"%s\"",
+                       fr_int2str(http_content_type_table, type, section->body_str));
+               break;
 
-                       if (rest_request_config_body(instance, section, request, handle,
-                                                    rest_encode_json) < 0) {
-                               return -1;
-                       }
+       default:
+               rad_assert(0);
+       };
 
-                       break;
-#endif
+       /*
+        *  Setup encoder specific options
+        */
+       switch (type) {
+       case HTTP_BODY_NONE:
+               if (rest_request_config_body(instance, section, request, handle,
+                                            NULL) < 0) {
+                       return -1;
+               }
 
-               case HTTP_BODY_POST:
-                       rest_read_ctx_init(request, &ctx->read, 0);
+               break;
 
-                       if (rest_request_config_body(instance, section, request, handle,
-                                                    rest_encode_post) < 0) {
-                               return -1;
-                       }
+       case HTTP_BODY_CUSTOM:
+       {
+               rest_custom_data_t *data;
+               char *expanded = NULL;
 
-                       break;
+               if (radius_axlat(&expanded, request, section->data, NULL, NULL) < 0) {
+                       return -1;
+               }
+
+               data = talloc_zero(request, rest_custom_data_t);
+               data->p = expanded;
+
+               /* Use the encoder specific pointer to store the data we need to encode */
+               ctx->request.encoder = data;
+               if (rest_request_config_body(instance, section, request, handle,
+                                            rest_encode_custom) < 0) {
+                       TALLOC_FREE(ctx->request.encoder);
+                       return -1;
+               }
+
+               break;
+       }
 
-               default:
-                       assert(0);
+#ifdef HAVE_JSON
+       case HTTP_BODY_JSON:
+               rest_request_init(request, &ctx->request, true);
+
+               if (rest_request_config_body(instance, section, request, handle,
+                                            rest_encode_json) < 0) {
+                       return -1;
                }
 
-               ret = curl_easy_setopt(candle, CURLOPT_HTTPHEADER,
-                                      ctx->headers);
-               if (ret != CURLE_OK) goto error;
+               break;
+#endif
+
+       case HTTP_BODY_POST:
+               rest_request_init(request, &ctx->request, false);
+
+               if (rest_request_config_body(instance, section, request, handle,
+                                            rest_encode_post) < 0) {
+                       return -1;
+               }
 
                break;
 
        default:
-               assert(0);
-       };
+               rad_assert(0);
+       }
+
+
+finish:
+       SET_OPTION(CURLOPT_HTTPHEADER, ctx->headers);
 
        return 0;
 
-       error:
-       RDEBUG("Failed setting curl option: %i - %s", ret, curl_easy_strerror(ret));
+error:
+       REDEBUG("Failed setting curl option %s: %s (%i)", option, curl_easy_strerror(ret), ret);
        return -1;
 
-       error_header:
-       RDEBUG("Failed creating header", instance->xlat_name);
+error_header:
+       REDEBUG("Failed creating header");
        return -1;
 }
 
@@ -2117,13 +2113,12 @@ int rest_request_config(rlm_rest_t *instance, rlm_rest_section_t *section,
  * @param[in] handle to use.
  * @return 0 on success or -1 on error.
  */
-int rest_request_perform(UNUSED rlm_rest_t *instance,
-                        UNUSED rlm_rest_section_t *section,
+int rest_request_perform(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
                         REQUEST *request, void *handle)
 {
-       rlm_rest_handle_t *randle = handle;
-       CURL *candle              = randle->handle;
-       CURLcode ret;
+       rlm_rest_handle_t       *randle = handle;
+       CURL                    *candle = randle->handle;
+       CURLcode                ret;
 
        ret = curl_easy_perform(candle);
        if (ret != CURLE_OK) {
@@ -2137,7 +2132,7 @@ int rest_request_perform(UNUSED rlm_rest_t *instance,
 
 /** Sends the response to the correct decode function.
  *
- * Uses the Content-Type information written in rest_write_header to
+ * Uses the Content-Type information written in rest_response_header to
  * determine the correct decode function to use. The decode function will
  * then convert the raw received data into VALUE_PAIRs.
  *
@@ -2147,42 +2142,42 @@ int rest_request_perform(UNUSED rlm_rest_t *instance,
  * @param[in] handle to use.
  * @return 0 on success or -1 on error.
  */
-int rest_request_decode(rlm_rest_t *instance,
-                       UNUSED rlm_rest_section_t *section,
-                       REQUEST *request, void *handle)
+int rest_response_decode(rlm_rest_t *instance, UNUSED rlm_rest_section_t *section,
+                        REQUEST *request, void *handle)
 {
-       rlm_rest_handle_t *randle       = handle;
-       rlm_rest_curl_context_t *ctx    = randle->ctx;
+       rlm_rest_handle_t       *randle = handle;
+       rlm_rest_curl_context_t *ctx = randle->ctx;
 
        int ret = -1;   /* -Wsometimes-uninitialized */
 
-       if (!ctx->write.buffer) {
-               RDEBUG("Skipping attribute processing, no body data received");
+       if (!ctx->response.buffer) {
+               RDEBUG2("Skipping attribute processing, no valid body data received");
                return ret;
        }
 
-       RDEBUG("Processing body");
+       RDEBUG3("Processing response body");
+
+       switch (ctx->response.type) {
+       case HTTP_BODY_NONE:
+               return 0;
 
-       switch (ctx->write.type) {
        case HTTP_BODY_POST:
-               ret = rest_decode_post(instance, section, request,
-                                      handle, ctx->write.buffer,
-                                      ctx->write.used);
+               ret = rest_decode_post(instance, section, request, handle, ctx->response.buffer, ctx->response.used);
                break;
+
 #ifdef HAVE_JSON
        case HTTP_BODY_JSON:
-               ret = rest_decode_json(instance, section, request,
-                                      handle, ctx->write.buffer,
-                                      ctx->write.used);
+               ret = rest_decode_json(instance, section, request, handle, ctx->response.buffer, ctx->response.used);
                break;
 #endif
+
        case HTTP_BODY_UNSUPPORTED:
        case HTTP_BODY_UNAVAILABLE:
        case HTTP_BODY_INVALID:
                return -1;
 
        default:
-               assert(0);
+               rad_assert(0);
        }
 
        return ret;
@@ -2193,42 +2188,50 @@ int rest_request_decode(rlm_rest_t *instance,
  * Resets all options associated with a CURL handle, and frees any headers
  * associated with it.
  *
- * Calls rest_read_ctx_free and rest_write_free to free any memory used by
+ * Calls rest_read_ctx_free and rest_response_free to free any memory used by
  * context data.
  *
  * @param[in] instance configuration data.
  * @param[in] section configuration data.
  * @param[in] handle to cleanup.
  */
-void rest_request_cleanup(UNUSED rlm_rest_t *instance,
-                         UNUSED rlm_rest_section_t *section, void *handle)
+void rest_request_cleanup(UNUSED rlm_rest_t *instance, UNUSED rlm_rest_section_t *section, void *handle)
 {
-       rlm_rest_handle_t *randle       = handle;
-       rlm_rest_curl_context_t *ctx    = randle->ctx;
-       CURL *candle                    = randle->handle;
+       rlm_rest_handle_t       *randle = handle;
+       rlm_rest_curl_context_t *ctx = randle->ctx;
+       CURL                    *candle = randle->handle;
 
        /*
-        * Clear any previously configured options
-        */
-       curl_easy_reset(candle);
+        *  Clear any previously configured options
+        */
+       curl_easy_reset(candle);
+
+       /*
+        *  Free header list
+        */
+       if (ctx->headers != NULL) {
+               curl_slist_free_all(ctx->headers);
+               ctx->headers = NULL;
+       }
 
        /*
-        * Free header list
-        */
-       if (ctx->headers != NULL) {
-               curl_slist_free_all(ctx->headers);
-               ctx->headers = NULL;
-       }
+        *  Free body data (only used if chunking is disabled)
+        */
+       if (ctx->body != NULL) {
+               free(ctx->body);
+               ctx->body = NULL;
+       }
 
        /*
-        * Free body data (only used if chunking is disabled)
-        */
-       if (ctx->body != NULL) free(ctx->body);
-
-       /*
-        * Free other context info
-        */
-       rest_write_free(&ctx->write);
+        *  Free response data
+        */
+       if (ctx->response.buffer) {
+               free(ctx->response.buffer);
+               ctx->response.buffer = NULL;
+       }
+
+       TALLOC_FREE(ctx->request.encoder);
+       TALLOC_FREE(ctx->response.decoder);
 }
 
 /** URL encodes a string.
@@ -2242,8 +2245,7 @@ void rest_request_cleanup(UNUSED rlm_rest_t *instance,
  * @param[in] arg pointer, gives context for escaping.
  * @return length of data written to out (excluding NULL).
  */
-static size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen,
-                             char const *raw, UNUSED void *arg)
+size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *raw, UNUSED void *arg)
 {
        char *escaped;
 
@@ -2262,25 +2264,25 @@ static size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen,
  *
  * @param[out] out Where to write the pointer to the new buffer containing the escaped URI.
  * @param[in] instance configuration data.
- * @param[in] section configuration data.
+ * @param[in] uri configuration data.
  * @param[in] request Current request
  * @return length of data written to buffer (excluding NULL) or < 0 if an error
  *     occurred.
  */
-ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, rlm_rest_section_t *section, REQUEST *request)
+ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, REQUEST *request, char const *uri)
 {
-       char const *p;
-       char *path_exp = NULL;
+       char const      *p;
+       char            *path_exp = NULL;
 
-       char *scheme;
-       char const *path;
+       char            *scheme;
+       char const      *path;
 
-       ssize_t len, outlen;
+       ssize_t         len;
 
-       p = section->uri;
+       p = uri;
 
        /*
-        *      All URLs must contain at least <scheme>://<server>/
+        *  All URLs must contain at least <scheme>://<server>/
         */
        p = strchr(p, ':');
        if (!p || (*++p != '/') || (*++p != '/')) {
@@ -2293,15 +2295,15 @@ ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, rlm_rest_section
                goto malformed;
        }
 
-       len = (p - section->uri);
+       len = (p - uri);
 
        /*
-        *      Allocate a temporary buffer to hold the first part of the URI
+        *  Allocate a temporary buffer to hold the first part of the URI
         */
        scheme = talloc_array(request, char, len + 1);
-       strlcpy(scheme, section->uri, len + 1);
+       strlcpy(scheme, uri, len + 1);
 
-       path = (section->uri + len);
+       path = (uri + len);
 
        len = radius_axlat(out, request, scheme, NULL, NULL);
        talloc_free(scheme);
@@ -2311,8 +2313,6 @@ ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, rlm_rest_section
                return 0;
        }
 
-       outlen = len;
-
        len = radius_axlat(&path_exp, request, path, rest_uri_escape, NULL);
        if (len < 0) {
                TALLOC_FREE(*out);
@@ -2320,8 +2320,66 @@ ssize_t rest_uri_build(char **out, UNUSED rlm_rest_t *instance, rlm_rest_section
                return 0;
        }
 
-       *out = talloc_strdup_append(*out, path_exp);
+       MEM(*out = talloc_strdup_append(*out, path_exp));
        talloc_free(path_exp);
 
-       return outlen += len;
+       return talloc_array_length(*out) - 1;   /* array_length includes \0 */
+}
+
+/** Unescapes the host portion of a URI string
+ *
+ * This is required because the xlat functions which operate on the input string
+ * cannot distinguish between host and path components.
+ *
+ * @param[out] out Where to write the pointer to the new buffer containing the escaped URI.
+ * @param[in] instance configuration data.
+ * @param[in] request Current request
+ * @param[in] handle to use.
+ * @param[in] uri configuration data.
+ * @return length of data written to buffer (excluding NULL) or < 0 if an error
+ *     occurred.
+ */
+ssize_t rest_uri_host_unescape(char **out, UNUSED rlm_rest_t *instance, REQUEST *request,
+                              void *handle, char const *uri)
+{
+       rlm_rest_handle_t       *randle = handle;
+       CURL                    *candle = randle->handle;
+
+       char const              *p;
+
+       char                    *scheme;
+
+       ssize_t                 len;
+
+       p = uri;
+
+       /*
+        *  All URLs must contain at least <scheme>://<server>/
+        */
+       p = strchr(p, ':');
+       if (!p || (*++p != '/') || (*++p != '/')) {
+               malformed:
+               REDEBUG("Error URI is malformed, can't find start of path");
+               return -1;
+       }
+       p = strchr(p + 1, '/');
+       if (!p) {
+               goto malformed;
+       }
+
+       len = (p - uri);
+
+       /*
+        *  Unescape any special sequences in the first part of the URI
+        */
+       scheme = curl_easy_unescape(candle, uri, len, NULL);
+       if (!scheme) {
+               REDEBUG("Error unescaping host");
+               return -1;
+       }
+
+       MEM(*out = talloc_typed_asprintf(request, "%s%s", scheme, p));
+       curl_free(scheme);
+
+       return talloc_array_length(*out) - 1;   /* array_length includes \0 */
 }
index 6ef505a..a9f33da 100644 (file)
@@ -20,7 +20,7 @@
  * @brief Function prototypes and datatypes for the REST (HTTP) transport.
  * @file rest.h
  *
- * @copyright 2012-2013  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
+ * @copyright 2012-2014  Arran Cudbard-Bell <a.cudbard-bell@freeradius.org>
  */
 
 RCSIDH(other_h, "$Id$")
@@ -41,7 +41,7 @@ RCSIDH(other_h, "$Id$")
 
 #define REST_URI_MAX_LEN               2048
 #define REST_BODY_MAX_LEN              8192
-#define REST_BODY_INCR                 512
+#define REST_BODY_INIT                 1024
 #define REST_BODY_MAX_ATTRS            256
 
 typedef enum {
@@ -57,6 +57,8 @@ typedef enum {
        HTTP_BODY_UNSUPPORTED,
        HTTP_BODY_UNAVAILABLE,
        HTTP_BODY_INVALID,
+       HTTP_BODY_NONE,
+       HTTP_BODY_CUSTOM,
        HTTP_BODY_POST,
        HTTP_BODY_JSON,
        HTTP_BODY_XML,
@@ -95,55 +97,62 @@ extern const FR_NAME_NUMBER http_method_table[];
 
 extern const FR_NAME_NUMBER http_body_type_table[];
 
-extern const FR_NAME_NUMBER http_content_header_table[];
+extern const FR_NAME_NUMBER http_content_type_table[];
 
 /*
  *     Structure for section configuration
  */
 typedef struct rlm_rest_section_t {
-       char const *name;
-       char *uri;
-
-       char *method_str;
-       http_method_t method;
-
-       char *body_str;
-       http_body_type_t body;
-
-       char *username;
-       char *password;
-       char *auth_str;
-       http_auth_type_t auth;
-       bool require_auth;
-
-       char *tls_certificate_file;
-       char *tls_private_key_file;
-       char *tls_private_key_password;
-       char *tls_ca_file;
-       char *tls_ca_path;
-       char *tls_random_file;
-       bool tls_check_cert;
-       bool tls_check_cert_cn;
-
-       int timeout;
-       unsigned int chunk;
+       char const              *name;          //!< Section name.
+       char const              *uri;           //!< URI to send HTTP request to.
+
+       char const              *method_str;    //!< The string version of the HTTP method.
+       http_method_t           method;         //!< What HTTP method should be used, GET, POST etc...
+
+       char const              *body_str;      //!< The string version of the encoding/content type.
+       http_body_type_t        body;           //!< What encoding type should be used.
+
+       http_body_type_t        force_to;       //!< Override the Content-Type header in the response
+                                               //!< to force decoding as a particular type.
+
+       char const              *data;          //!< Custom body data (optional).
+
+       char const              *auth_str;      //!< The string version of the Auth-Type.
+       http_auth_type_t        auth;           //!< HTTP auth type.
+       bool                    require_auth;   //!< Whether HTTP-Auth is required or not.
+       char const              *username;      //!< Username used for HTTP-Auth
+       char const              *password;      //!< Password used for HTTP-Auth
+
+       char const              *tls_certificate_file;
+       char const              *tls_private_key_file;
+       char const              *tls_private_key_password;
+       char const              *tls_ca_file;
+       char const              *tls_ca_path;
+       char const              *tls_random_file;
+       bool                    tls_check_cert;
+       bool                    tls_check_cert_cn;
+
+       uint32_t                timeout;        //!< Timeout passed to CURL.
+       uint32_t                chunk;          //!< Max chunk-size (mainly for testing the encoders)
 } rlm_rest_section_t;
 
 /*
  *     Structure for module configuration
  */
 typedef struct rlm_rest_t {
-       char const *xlat_name;
+       char const              *xlat_name;     //!< Instance name.
 
-       char *connect_uri;
+       char const              *connect_uri;   //!< URI we attempt to connect to, to pre-establish
+                                               //!< TCP connections.
 
-       fr_connection_pool_t *conn_pool;
+       fr_connection_pool_t    *conn_pool;     //!< Pointer to the connection pool.
 
-       rlm_rest_section_t authorize;
-       rlm_rest_section_t authenticate;
-       rlm_rest_section_t accounting;
-       rlm_rest_section_t checksimul;
-       rlm_rest_section_t postauth;
+       rlm_rest_section_t      authorize;      //!< Configuration specific to authorisation.
+       rlm_rest_section_t      authenticate;   //!< Configuration specific to authentication.
+       rlm_rest_section_t      accounting;     //!< Configuration specific to accounting.
+       rlm_rest_section_t      checksimul;     //!< Configuration specific to simultaneous session
+                                               //!< checking.
+       rlm_rest_section_t      post_auth;      //!< Configuration specific to Post-auth
 } rlm_rest_t;
 
 /*
@@ -169,49 +178,58 @@ typedef enum {
 /*
  *     Outbound data context (passed to CURLOPT_READFUNCTION as CURLOPT_READDATA)
  */
-typedef struct rlm_rest_read_t {
-       rlm_rest_t      *instance;
-       REQUEST         *request;
-       read_state_t    state;
+typedef struct rlm_rest_request_t {
+       rlm_rest_t              *instance;      //!< This instance of rlm_rest.
+       REQUEST                 *request;       //!< Current request.
+       read_state_t            state;          //!< Encoder state
+
+       vp_cursor_t             cursor;         //!< Cursor pointing to the start of the list to encode.
 
-       vp_cursor_t     cursor;
+       size_t                  chunk;          //!< Chunk size
 
-       unsigned int    chunk;
-} rlm_rest_read_t;
+       void                    *encoder;       //!< Encoder specific data.
+} rlm_rest_request_t;
 
 /*
  *     Curl inbound data context (passed to CURLOPT_WRITEFUNCTION and
  *     CURLOPT_HEADERFUNCTION as CURLOPT_WRITEDATA and CURLOPT_HEADERDATA)
  */
-typedef struct rlm_rest_write_t {
-       rlm_rest_t       *instance;
-       REQUEST          *request;
-       write_state_t    state;
+typedef struct rlm_rest_response_t {
+       rlm_rest_t              *instance;      //!< This instance of rlm_rest.
+       REQUEST                 *request;       //!< Current request.
+       write_state_t           state;          //!< Decoder state.
+
+       char                    *buffer;        //!< Raw incoming HTTP data.
+       size_t                  alloc;          //!< Space allocated for buffer.
+       size_t                  used;           //!< Space used in buffer.
 
-       char             *buffer;       /* HTTP incoming raw data */
-       size_t           alloc;         /* Space allocated for buffer */
-       size_t           used;          /* Space used in buffer */
+       int                     code;           //!< HTTP Status Code.
+       http_body_type_t        type;           //!< HTTP Content Type.
+       http_body_type_t        force_to;       //!< Force decoding the body type as a particular encoding.
 
-       int              code;          /* HTTP Status Code */
-       http_body_type_t type;          /* HTTP Content Type */
-} rlm_rest_write_t;
+       void                    *decoder;       //!< Decoder specific data.
+} rlm_rest_response_t;
 
 /*
  *     Curl context data
  */
 typedef struct rlm_rest_curl_context_t {
-       struct curl_slist       *headers;
-       char                    *body;
-       rlm_rest_read_t         read;
-       rlm_rest_write_t        write;
+       struct curl_slist       *headers;       //!< Any HTTP headers which will be sent with the
+                                               //!< request.
+
+       char                    *body;          //!< Pointer to the buffer which contains body data/
+                                               //!< Only used when not performing chunked encoding.
+
+       rlm_rest_request_t      request;        //!< Request context data.
+       rlm_rest_response_t     response;       //!< Response context data.
 } rlm_rest_curl_context_t;
 
 /*
  *     Connection API handle
  */
 typedef struct rlm_rest_handle_t {
-       void    *handle;        /* Real Handle */
-       void    *ctx;           /* Context */
+       void                    *handle;        //!< Real Handle.
+       rlm_rest_curl_context_t *ctx;           //!< Context.
 } rlm_rest_handle_t;
 
 /*
@@ -241,24 +259,29 @@ int rest_request_config(rlm_rest_t *instance,
                        rlm_rest_section_t *section, REQUEST *request,
                        void *handle, http_method_t method,
                        http_body_type_t type, char const *uri,
-                       char const *username, char const *password);
+                       char const *username, char const *password) CC_HINT(nonnull (1,2,3,4,7));
 
 int rest_request_perform(rlm_rest_t *instance,
                         rlm_rest_section_t *section, REQUEST *request,
                         void *handle);
 
-int rest_request_decode(rlm_rest_t *instance,
+int rest_response_decode(rlm_rest_t *instance,
                        UNUSED rlm_rest_section_t *section, REQUEST *request,
                        void *handle);
 
 void rest_request_cleanup(rlm_rest_t *instance, rlm_rest_section_t *section,
                          void *handle);
 
-#define rest_get_handle_code(handle)(((rlm_rest_curl_context_t*)((rlm_rest_handle_t*)handle)->ctx)->write.code)
+#define rest_get_handle_code(handle)(((rlm_rest_curl_context_t*)((rlm_rest_handle_t*)handle)->ctx)->response.code)
+
+#define rest_get_handle_type(handle)(((rlm_rest_curl_context_t*)((rlm_rest_handle_t*)handle)->ctx)->response.type)
 
-#define rest_get_handle_type(handle)(((rlm_rest_curl_context_t*)((rlm_rest_handle_t*)handle)->ctx)->write.type)
+size_t rest_get_handle_data(char const **out, rlm_rest_handle_t *handle);
 
 /*
  *     Helper functions
  */
-ssize_t rest_uri_build(char **out, rlm_rest_t *instance, rlm_rest_section_t *section, REQUEST *request);
+size_t rest_uri_escape(UNUSED REQUEST *request, char *out, size_t outlen, char const *raw, UNUSED void *arg);
+ssize_t rest_uri_build(char **out, rlm_rest_t *instance, REQUEST *request, char const *uri);
+ssize_t rest_uri_host_unescape(char **out, UNUSED rlm_rest_t *instance, REQUEST *request,
+                              void *handle, char const *uri);
index fb9e184..e64bf2b 100644 (file)
  * @file rlm_rest.c
  * @brief Integrate FreeRADIUS with RESTfull APIs
  *
- * @copyright 2012-2013  Arran Cudbard-Bell <arran.cudbardb@freeradius.org>
+ * @copyright 2012-2014  Arran Cudbard-Bell <arran.cudbardb@freeradius.org>
  */
 RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
 #include <freeradius-devel/token.h>
+#include <freeradius-devel/rad_assert.h>
 
 #include "rest.h"
 
@@ -32,22 +33,14 @@ RCSID("$Id$")
  *     TLS Configuration
  */
 static CONF_PARSER tls_config[] = {
-       { "ca_file", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_rest_section_t,tls_ca_file), NULL, NULL},
-       { "ca_path", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_rest_section_t,tls_ca_path), NULL, NULL},
-       { "certificate_file", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_rest_section_t,tls_certificate_file), NULL, NULL},
-       { "private_key_file", PW_TYPE_FILE_INPUT,
-         offsetof(rlm_rest_section_t,tls_private_key_file), NULL, NULL },
-       { "private_key_password", PW_TYPE_STRING_PTR,
-         offsetof(rlm_rest_section_t, tls_private_key_password), NULL, NULL },
-       { "random_file", PW_TYPE_STRING_PTR, /* OK if it changes on HUP */
-         offsetof(rlm_rest_section_t,tls_random_file), NULL, NULL },
-       { "check_cert", PW_TYPE_BOOLEAN,
-         offsetof(rlm_rest_section_t, tls_check_cert), NULL, "yes" },
-       { "check_cert_cn", PW_TYPE_BOOLEAN,
-         offsetof(rlm_rest_section_t, tls_check_cert_cn), NULL, "yes" },
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_ca_file), NULL },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_ca_path), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_certificate_file), NULL },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_rest_section_t, tls_private_key_file), NULL },
+       { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_rest_section_t, tls_private_key_password), NULL },
+       { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, tls_random_file), NULL },
+       { "check_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_rest_section_t, tls_check_cert), "yes" },
+       { "check_cert_cn", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_rest_section_t, tls_check_cert_cn), "yes" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -62,47 +55,37 @@ static CONF_PARSER tls_config[] = {
  *     buffer over-flows.
  */
 static const CONF_PARSER section_config[] = {
-       { "uri", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_section_t, uri),        NULL, ""  },
-       { "method", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_section_t, method_str), NULL, "GET" },
-       { "body", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_section_t, body_str),   NULL, "post" },
+       { "uri", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, uri), ""   },
+       { "method", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, method_str), "GET" },
+       { "body", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, body_str), "none" },
+       { "data", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, data), NULL },
 
        /* User authentication */
-       { "auth", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_section_t, auth_str),   NULL, "none" },
-       { "username", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_section_t, username),   NULL, NULL },
-       { "password", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_section_t, password),   NULL, NULL },
-       { "require_auth", PW_TYPE_BOOLEAN,
-        offsetof(rlm_rest_section_t, require_auth), NULL, "no"},
+       { "auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, auth_str), "none" },
+       { "username", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, username), NULL },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_section_t, password), NULL },
+       { "require_auth", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_rest_section_t, require_auth), "no" },
 
        /* Transfer configuration */
-       { "timeout", PW_TYPE_INTEGER,
-        offsetof(rlm_rest_section_t, timeout),    NULL, "0" },
-       { "chunk", PW_TYPE_INTEGER,
-        offsetof(rlm_rest_section_t, chunk),      NULL, "0" },
+       { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_rest_section_t, timeout), "4" },
+       { "chunk", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_rest_section_t, chunk), "0" },
 
        /* TLS Parameters */
-       { "tls", PW_TYPE_SUBSECTION, 0, NULL, (void const *) tls_config },
+       { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
 static const CONF_PARSER module_config[] = {
-       { "connect_uri", PW_TYPE_STRING_PTR,
-        offsetof(rlm_rest_t, connect_uri), NULL, "http://localhost/" },
+       { "connect_uri", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_rest_t, connect_uri), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
-static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section,
-                           void *handle, REQUEST *request,
+static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section, void *handle, REQUEST *request,
                            char const *username, char const *password)
 {
-       size_t uri_len;
+       ssize_t uri_len;
        char *uri = NULL;
 
        int ret;
@@ -110,17 +93,17 @@ static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section,
        RDEBUG("Expanding URI components");
 
        /*
-        *      Build xlat'd URI, this allows REST servers to be specified by
-        *      request attributes.
+        *  Build xlat'd URI, this allows REST servers to be specified by
+        *  request attributes.
         */
-       uri_len = rest_uri_build(&uri, instance, section, request);
+       uri_len = rest_uri_build(&uri, instance, request, section->uri);
        if (uri_len <= 0) return -1;
 
        RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section->method, NULL), uri);
 
        /*
-        *      Configure various CURL options, and initialise the read/write
-        *      context data.
+        *  Configure various CURL options, and initialise the read/write
+        *  context data.
         */
        ret = rest_request_config(instance, section, request, handle, section->method, section->body,
                                  uri, username, password);
@@ -128,8 +111,8 @@ static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section,
        if (ret < 0) return -1;
 
        /*
-        *      Send the CURL request, pre-parse headers, aggregate incoming
-        *      HTTP body data into a single contiguous buffer.
+        *  Send the CURL request, pre-parse headers, aggregate incoming
+        *  HTTP body data into a single contiguous buffer.
         */
        ret = rest_request_perform(instance, section, request, handle);
        if (ret < 0) return -1;
@@ -137,134 +120,119 @@ static int rlm_rest_perform(rlm_rest_t *instance, rlm_rest_section_t *section,
        return 0;
 }
 
-static void rlm_rest_cleanup(rlm_rest_t *instance, rlm_rest_section_t *section,
-                            void *handle)
+static void rlm_rest_cleanup(rlm_rest_t *instance, rlm_rest_section_t *section, void *handle)
 {
        rest_request_cleanup(instance, section, handle);
 };
 
-static int parse_sub_section(CONF_SECTION *parent,
-                            rlm_rest_section_t *config,
-                            rlm_components_t comp)
+/*
+ *     Simple xlat to read text data from a URL
+ */
+static ssize_t rest_xlat(void *instance, REQUEST *request,
+                        char const *fmt, char *out, size_t freespace)
 {
-       CONF_SECTION *cs;
-
-       char const *name = section_type_value[comp].section;
+       rlm_rest_t      *inst = instance;
+       void            *handle;
+       int             hcode;
+       int             ret;
+       ssize_t         len, outlen = 0;
+       char            *uri = NULL;
+       char const      *body;
+
+       /* There are no configurable parameters other than the URI */
+       static rlm_rest_section_t section = {
+               .name = "xlat",
+               .method = HTTP_METHOD_GET,
+               .body = HTTP_BODY_NONE,
+               .require_auth = false,
+               .timeout = 4,
+               .force_to = HTTP_BODY_PLAIN
+       };
+
+       *out = '\0';
+
+       rad_assert(fmt);
 
-       cs = cf_section_sub_find(parent, name);
-       if (!cs) {
-               /* TODO: Should really setup section with default values */
-               return 0;
-       }
+       RDEBUG("Expanding URI components");
 
-       if (cf_section_parse(cs, config, section_config) < 0) {
-               return -1;
-       }
+       handle = fr_connection_get(inst->conn_pool);
+       if (!handle) return -1;
 
        /*
-        *      Add section name (Maybe add to headers later?).
+        *  Unescape parts of xlat'd URI, this allows REST servers to be specified by
+        *  request attributes.
         */
-       config->name = name;
+       len = rest_uri_host_unescape(&uri, instance, request, handle, fmt);
+       if (len <= 0) {
+               outlen = -1;
+               goto end;
+       }
+
+       RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section.method, NULL), uri);
 
        /*
-        *      Sanity check
+        *  Configure various CURL options, and initialise the read/write
+        *  context data.
+        *
+        *  @todo We could extract the User-Name and password from the URL string.
         */
-        if ((config->username && !config->password) || (!config->username && config->password)) {
-               cf_log_err_cs(cs, "'username' and 'password' must both be set or both be absent");
-
-               return -1;
-        }
+       ret = rest_request_config(instance, &section, request, handle, section.method, section.body,
+                                 uri, NULL, NULL);
+       talloc_free(uri);
+       if (ret < 0) return -1;
 
        /*
-        *      Convert HTTP method auth and body type strings into their
-        *      integer equivalents.
+        *  Send the CURL request, pre-parse headers, aggregate incoming
+        *  HTTP body data into a single contiguous buffer.
         */
-       config->auth = fr_str2int(http_auth_table, config->auth_str, HTTP_AUTH_UNKNOWN);
-       if (config->auth == HTTP_AUTH_UNKNOWN) {
-               cf_log_err_cs(cs, "Unknown HTTP auth type '%s'", config->auth_str);
-               return -1;
-       } else if ((config->auth != HTTP_AUTH_NONE) && !http_curl_auth[config->auth]) {
-               cf_log_err_cs(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
-                             "configuration, then recompile this module", config->auth_str);
-
-               return -1;
-       }
-
-       config->method = fr_str2int(http_method_table, config->method_str,
-                                   HTTP_METHOD_CUSTOM);
+       ret = rest_request_perform(instance, &section, request, handle);
+       if (ret < 0) return -1;
 
-       config->body = fr_str2int(http_body_type_table, config->body_str,
-                                 HTTP_BODY_UNKNOWN);
+       hcode = rest_get_handle_code(handle);
+       switch (hcode) {
+       case 404:
+       case 410:
+       case 403:
+       case 401:
+               outlen = -1;
+               goto end;
 
-       if (config->body == HTTP_BODY_UNKNOWN) {
-               cf_log_err_cs(cs, "Unknown HTTP body type '%s'",
-                             config->body_str);
-               return -1;
-       }
+       case 204:
+               goto end;
 
-       if (http_body_type_supported[config->body] == HTTP_BODY_UNSUPPORTED) {
-               cf_log_err_cs(cs, "Unsupported HTTP body type \"%s\""
-                      ", please submit patches",
-                      config->body_str);
-               return -1;
+       default:
+               /*
+                *      Attempt to parse content if there was any.
+                */
+               if ((hcode >= 200) && (hcode < 300)) {
+                       break;
+               } else if (hcode < 500) {
+                       outlen = -2;
+                       goto end;
+               } else {
+                       outlen = -1;
+                       goto end;
+               }
        }
 
-       return 1;
-}
-
-/*
- *     Do any per-module initialization that is separate to each
- *     configured instance of the module.  e.g. set up connections
- *     to external databases, read configuration files, set up
- *     dictionary entries, etc.
- *
- *     If configuration information is given in the config section
- *     that must be referenced in later calls, store a handle to it
- *     in *instance otherwise put a null pointer there.
- */
-static int mod_instantiate(CONF_SECTION *conf, void *instance)
-{
-       rlm_rest_t *inst = instance;
-       char const *xlat_name;
-
-       xlat_name = cf_section_name2(conf);
-       if (!xlat_name) {
-               xlat_name = cf_section_name1(conf);
+       len = rest_get_handle_data(&body, handle);
+       if ((size_t) len >= freespace) {
+               REDEBUG("Insufficient space to write HTTP response, needed %zu bytes, have %zu bytes", len + 1,
+                       freespace);
+               outlen = -1;
+               goto end;
        }
-
-       inst->xlat_name = xlat_name;
-
-       /*
-        *      Parse sub-section configs.
-        */
-       if (
-               (parse_sub_section(conf, &inst->authorize,
-                                  RLM_COMPONENT_AUTZ) < 0) ||
-               (parse_sub_section(conf, &inst->authenticate,
-                                  RLM_COMPONENT_AUTH) < 0) ||
-               (parse_sub_section(conf, &inst->accounting,
-                                  RLM_COMPONENT_ACCT) < 0) ||
-               (parse_sub_section(conf, &inst->checksimul,
-                                  RLM_COMPONENT_SESS) < 0) ||
-               (parse_sub_section(conf, &inst->postauth,
-                                  RLM_COMPONENT_POST_AUTH) < 0))
-       {
-               return -1;
+       if (len > 0) {
+               outlen = len;
+               strlcpy(out, body, len + 1);    /* strlcpy takes the size of the buffer */
        }
 
-       /*
-        *      Initialise REST libraries.
-        */
-       if (rest_init(inst) < 0) {
-               return -1;
-       }
+end:
+       rlm_rest_cleanup(instance, &section, handle);
 
-       inst->conn_pool = fr_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, mod_conn_delete, NULL);
-       if (!inst->conn_pool) {
-               return -1;
-       }
+       fr_connection_release(inst->conn_pool, handle);
 
-       return 0;
+       return outlen;
 }
 
 /*
@@ -273,7 +241,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_rest_t *inst = instance;
        rlm_rest_section_t *section = &inst->authorize;
@@ -294,43 +262,47 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
 
        hcode = rest_get_handle_code(handle);
        switch (hcode) {
-               case 404:
-               case 410:
-                       rcode = RLM_MODULE_NOTFOUND;
+       case 404:
+       case 410:
+               rcode = RLM_MODULE_NOTFOUND;
+               break;
+
+       case 403:
+               rcode = RLM_MODULE_USERLOCK;
+               break;
+
+       case 401:
+               /*
+                *      Attempt to parse content if there was any.
+                */
+               ret = rest_response_decode(inst, section, request, handle);
+               if (ret < 0) {
+                       rcode = RLM_MODULE_FAIL;
                        break;
-               case 403:
-                       rcode = RLM_MODULE_USERLOCK;
-                       break;
-               case 401:
-                       /*
-                        *      Attempt to parse content if there was any.
-                        */
-                       ret = rest_request_decode(inst, section, request, handle);
-                       if (ret < 0) {
-                               rcode = RLM_MODULE_FAIL;
-                               break;
-                       }
+               }
 
-                       rcode = RLM_MODULE_REJECT;
-                       break;
-               case 204:
-                       rcode = RLM_MODULE_OK;
+               rcode = RLM_MODULE_REJECT;
+               break;
+
+       case 204:
+               rcode = RLM_MODULE_OK;
+               break;
+
+       default:
+               /*
+                *      Attempt to parse content if there was any.
+                */
+               if ((hcode >= 200) && (hcode < 300)) {
+                       ret = rest_response_decode(inst, section, request, handle);
+                       if (ret < 0)       rcode = RLM_MODULE_FAIL;
+                       else if (ret == 0) rcode = RLM_MODULE_OK;
+                       else               rcode = RLM_MODULE_UPDATED;
                        break;
-               default:
-                       /*
-                        *      Attempt to parse content if there was any.
-                        */
-                       if ((hcode >= 200) && (hcode < 300)) {
-                               ret = rest_request_decode(inst, section, request, handle);
-                               if (ret < 0)       rcode = RLM_MODULE_FAIL;
-                               else if (ret == 0) rcode = RLM_MODULE_OK;
-                               else               rcode = RLM_MODULE_UPDATED;
-                               break;
-                       } else if (hcode < 500) {
-                               rcode = RLM_MODULE_INVALID;
-                       } else {
-                               rcode = RLM_MODULE_FAIL;
-                       }
+               } else if (hcode < 500) {
+                       rcode = RLM_MODULE_INVALID;
+               } else {
+                       rcode = RLM_MODULE_FAIL;
+               }
        }
 
        end:
@@ -345,7 +317,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
 /*
  *     Authenticate the user with the given password.
  */
-static rlm_rcode_t mod_authenticate(void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, UNUSED REQUEST *request)
 {
        rlm_rest_t *inst = instance;
        rlm_rest_section_t *section = &inst->authenticate;
@@ -358,17 +330,17 @@ static rlm_rcode_t mod_authenticate(void *instance, UNUSED REQUEST *request)
        VALUE_PAIR const *username;
        VALUE_PAIR const *password;
 
-       username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
-       if (!username) {
+       username = request->username;
+       if (!request->username) {
                REDEBUG("Can't perform authentication, 'User-Name' attribute not found in the request");
 
                return RLM_MODULE_INVALID;
        }
 
-       password = pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
-       if (!password) {
-               REDEBUG("Can't perform authentication, 'Cleartext-Password' attribute not found in the control list");
-
+       password = request->password;
+       if (!password ||
+           (password->da->attr != PW_USER_PASSWORD)) {
+               REDEBUG("You set 'Auth-Type = REST' for a request that does not contain a User-Password attribute!");
                return RLM_MODULE_INVALID;
        }
 
@@ -383,43 +355,47 @@ static rlm_rcode_t mod_authenticate(void *instance, UNUSED REQUEST *request)
 
        hcode = rest_get_handle_code(handle);
        switch (hcode) {
-               case 404:
-               case 410:
-                       rcode = RLM_MODULE_NOTFOUND;
+       case 404:
+       case 410:
+               rcode = RLM_MODULE_NOTFOUND;
+               break;
+
+       case 403:
+               rcode = RLM_MODULE_USERLOCK;
+               break;
+
+       case 401:
+               /*
+                *      Attempt to parse content if there was any.
+                */
+               ret = rest_response_decode(inst, section, request, handle);
+               if (ret < 0) {
+                       rcode = RLM_MODULE_FAIL;
                        break;
-               case 403:
-                       rcode = RLM_MODULE_USERLOCK;
-                       break;
-               case 401:
-                       /*
-                        *      Attempt to parse content if there was any.
-                        */
-                       ret = rest_request_decode(inst, section, request, handle);
-                       if (ret < 0) {
-                               rcode = RLM_MODULE_FAIL;
-                               break;
-                       }
+               }
 
-                       rcode = RLM_MODULE_REJECT;
-                       break;
-               case 204:
-                       rcode = RLM_MODULE_OK;
+               rcode = RLM_MODULE_REJECT;
+               break;
+
+       case 204:
+               rcode = RLM_MODULE_OK;
+               break;
+
+       default:
+               /*
+                *      Attempt to parse content if there was any.
+                */
+               if ((hcode >= 200) && (hcode < 300)) {
+                       ret = rest_response_decode(inst, section, request, handle);
+                       if (ret < 0)       rcode = RLM_MODULE_FAIL;
+                       else if (ret == 0) rcode = RLM_MODULE_OK;
+                       else               rcode = RLM_MODULE_UPDATED;
                        break;
-               default:
-                       /*
-                        *      Attempt to parse content if there was any.
-                        */
-                       if ((hcode >= 200) && (hcode < 300)) {
-                               ret = rest_request_decode(inst, section, request, handle);
-                               if (ret < 0)       rcode = RLM_MODULE_FAIL;
-                               else if (ret == 0) rcode = RLM_MODULE_OK;
-                               else               rcode = RLM_MODULE_UPDATED;
-                               break;
-                       } else if (hcode < 500) {
-                               rcode = RLM_MODULE_INVALID;
-                       } else {
-                               rcode = RLM_MODULE_FAIL;
-                       }
+               } else if (hcode < 500) {
+                       rcode = RLM_MODULE_INVALID;
+               } else {
+                       rcode = RLM_MODULE_FAIL;
+               }
        }
 
        end:
@@ -432,9 +408,9 @@ static rlm_rcode_t mod_authenticate(void *instance, UNUSED REQUEST *request)
 }
 
 /*
- *     Write accounting information to this modules database.
+ *     Send accounting info to a REST API endpoint
  */
-static rlm_rcode_t mod_accounting(void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, UNUSED REQUEST *request)
 {
        rlm_rest_t *inst = instance;
        rlm_rest_section_t *section = &inst->accounting;
@@ -459,7 +435,7 @@ static rlm_rcode_t mod_accounting(void *instance, UNUSED REQUEST *request)
        } else if (hcode == 204) {
                rcode = RLM_MODULE_OK;
        } else if ((hcode >= 200) && (hcode < 300)) {
-               ret = rest_request_decode(inst, section, request, handle);
+               ret = rest_response_decode(inst, section, request, handle);
                if (ret < 0)       rcode = RLM_MODULE_FAIL;
                else if (ret == 0) rcode = RLM_MODULE_OK;
                else               rcode = RLM_MODULE_UPDATED;
@@ -467,8 +443,51 @@ static rlm_rcode_t mod_accounting(void *instance, UNUSED REQUEST *request)
                rcode = RLM_MODULE_INVALID;
        }
 
-       end:
+end:
+       rlm_rest_cleanup(inst, section, handle);
+
+       fr_connection_release(inst->conn_pool, handle);
+
+       return rcode;
+}
+
+/*
+ *     Send post-auth info to a REST API endpoint
+ */
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, UNUSED REQUEST *request)
+{
+       rlm_rest_t *inst = instance;
+       rlm_rest_section_t *section = &inst->post_auth;
+
+       void *handle;
+       int hcode;
+       int rcode = RLM_MODULE_OK;
+       int ret;
+
+       handle = fr_connection_get(inst->conn_pool);
+       if (!handle) return RLM_MODULE_FAIL;
+
+       ret = rlm_rest_perform(inst, section, handle, request, NULL, NULL);
+       if (ret < 0) {
+               rcode = RLM_MODULE_FAIL;
+               goto end;
+       }
 
+       hcode = rest_get_handle_code(handle);
+       if (hcode >= 500) {
+               rcode = RLM_MODULE_FAIL;
+       } else if (hcode == 204) {
+               rcode = RLM_MODULE_OK;
+       } else if ((hcode >= 200) && (hcode < 300)) {
+               ret = rest_response_decode(inst, section, request, handle);
+               if (ret < 0)       rcode = RLM_MODULE_FAIL;
+               else if (ret == 0) rcode = RLM_MODULE_OK;
+               else               rcode = RLM_MODULE_UPDATED;
+       } else {
+               rcode = RLM_MODULE_INVALID;
+       }
+
+end:
        rlm_rest_cleanup(inst, section, handle);
 
        fr_connection_release(inst->conn_pool, handle);
@@ -476,6 +495,162 @@ static rlm_rcode_t mod_accounting(void *instance, UNUSED REQUEST *request)
        return rcode;
 }
 
+static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, rlm_components_t comp)
+{
+       CONF_SECTION *cs;
+
+       char const *name = section_type_value[comp].section;
+
+       cs = cf_section_sub_find(parent, name);
+       if (!cs) {
+               /* TODO: Should really setup section with default values */
+               return 0;
+       }
+
+       if (cf_section_parse(cs, config, section_config) < 0) {
+               return -1;
+       }
+
+       /*
+        *  Add section name (Maybe add to headers later?).
+        */
+       config->name = name;
+
+       /*
+        *  Sanity check
+        */
+        if ((config->username && !config->password) || (!config->username && config->password)) {
+               cf_log_err_cs(cs, "'username' and 'password' must both be set or both be absent");
+
+               return -1;
+        }
+
+       /*
+        *  Convert HTTP method auth and body type strings into their integer equivalents.
+        */
+       config->auth = fr_str2int(http_auth_table, config->auth_str, HTTP_AUTH_UNKNOWN);
+       if (config->auth == HTTP_AUTH_UNKNOWN) {
+               cf_log_err_cs(cs, "Unknown HTTP auth type '%s'", config->auth_str);
+
+               return -1;
+       } else if ((config->auth != HTTP_AUTH_NONE) && !http_curl_auth[config->auth]) {
+               cf_log_err_cs(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
+                             "configuration, then recompile this module", config->auth_str);
+
+               return -1;
+       }
+
+       config->method = fr_str2int(http_method_table, config->method_str, HTTP_METHOD_CUSTOM);
+
+       /*
+        *  We don't have any custom user data, so we need to select the right encoder based
+        *  on the body type.
+        *
+        *  To make this slightly more/less confusing, we accept both canonical body_types,
+        *  and content_types.
+        */
+       if (!config->data) {
+               config->body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
+               if (config->body == HTTP_BODY_UNKNOWN) {
+                       config->body = fr_str2int(http_content_type_table, config->body_str, HTTP_BODY_UNKNOWN);
+               }
+
+               if (config->body == HTTP_BODY_UNKNOWN) {
+                       cf_log_err_cs(cs, "Unknown HTTP body type '%s'", config->body_str);
+                       return -1;
+               }
+
+               switch (http_body_type_supported[config->body])
+               {
+                       case HTTP_BODY_UNSUPPORTED:
+                               cf_log_err_cs(cs, "Unsupported HTTP body type \"%s\", please submit patches",
+                                             config->body_str);
+                               return -1;
+
+                       case HTTP_BODY_INVALID:
+                               cf_log_err_cs(cs, "Invalid HTTP body type.  \"%s\" is not a valid web API data "
+                                             "markup format", config->body_str);
+                               return -1;
+
+                       default:
+                               break;
+               }
+       /*
+        *  We have custom body data so we set HTTP_BODY_CUSTOM, but also need to try and
+        *  figure out what content-type to use. So if they've used the canonical form we
+        *  need to convert it back into a proper HTTP content_type value.
+        */
+       } else {
+               http_body_type_t body;
+
+               config->body = HTTP_BODY_CUSTOM;
+
+               body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
+               if (body != HTTP_BODY_UNKNOWN) {
+                       config->body_str = fr_int2str(http_content_type_table, body, config->body_str);
+               }
+       }
+
+       return 0;
+}
+
+/*
+ *     Do any per-module initialization that is separate to each
+ *     configured instance of the module.  e.g. set up connections
+ *     to external databases, read configuration files, set up
+ *     dictionary entries, etc.
+ *
+ *     If configuration information is given in the config section
+ *     that must be referenced in later calls, store a handle to it
+ *     in *instance otherwise put a null pointer there.
+ */
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+       rlm_rest_t *inst = instance;
+       char const *xlat_name;
+
+       xlat_name = cf_section_name2(conf);
+       if (!xlat_name) {
+               xlat_name = cf_section_name1(conf);
+       }
+
+       inst->xlat_name = xlat_name;
+
+       /*
+        *      Register the rest xlat function
+        */
+       xlat_register(inst->xlat_name, rest_xlat, rest_uri_escape, inst);
+
+       /*
+        *      Parse sub-section configs.
+        */
+       if (
+               (parse_sub_section(conf, &inst->authorize, RLM_COMPONENT_AUTZ) < 0) ||
+               (parse_sub_section(conf, &inst->authenticate, RLM_COMPONENT_AUTH) < 0) ||
+               (parse_sub_section(conf, &inst->accounting, RLM_COMPONENT_ACCT) < 0) ||
+
+/* @todo add behaviour for checksimul */
+/*             (parse_sub_section(conf, &inst->checksimul, RLM_COMPONENT_SESS) < 0) || */
+               (parse_sub_section(conf, &inst->post_auth, RLM_COMPONENT_POST_AUTH) < 0))
+       {
+               return -1;
+       }
+
+       /*
+        *      Initialise REST libraries.
+        */
+       if (rest_init(inst) < 0) {
+               return -1;
+       }
+
+       inst->conn_pool = fr_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, mod_conn_delete, NULL);
+       if (!inst->conn_pool) {
+               return -1;
+       }
+
+       return 0;
+}
+
 /*
  *     Only free memory we allocated.  The strings allocated via
  *     cf_section_parse() do not need to be freed.
@@ -486,6 +661,8 @@ static int mod_detach(void *instance)
 
        fr_connection_pool_delete(inst->conn_pool);
 
+       xlat_unregister(inst->xlat_name, rest_xlat, instance);
+
        /* Free any memory used by libcurl */
        rest_cleanup();
 
@@ -517,6 +694,6 @@ module_t rlm_rest = {
                NULL,                   /* checksimul */
                NULL,                   /* pre-proxy */
                NULL,                   /* post-proxy */
-               NULL                    /* post-auth */
+               mod_post_auth           /* post-auth */
        },
 };
index 36b6c4f..a9fd713 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1222,9 +1222,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1272,10 +1272,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
   RUBY        Absolute path to ruby executable
 
@@ -2159,7 +2159,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2381,7 +2381,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3000,26 +3000,26 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
     if test -z "$RUBY"; then :
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ruby executable path has been provided" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ruby executable path has been provided" >&5
 $as_echo_n "checking whether ruby executable path has been provided... " >&6; }
 
 # Check whether --with-ruby was given.
 if test "${with_ruby+set}" = set; then :
   withval=$with_ruby;
-            if test "$withval" != yes && test "$withval" != no; then :
+           if test "$withval" != yes && test "$withval" != no; then :
 
-                RUBY="$withval"
-                { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY" >&5
+               RUBY="$withval"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY" >&5
 $as_echo "$RUBY" >&6; }
 
 else
 
-                RUBY=""
-                { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+               RUBY=""
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-                if test "$withval" != no; then :
+               if test "$withval" != no; then :
 
-                  # Extract the first word of "ruby", so it can be a program name with args.
+                 # Extract the first word of "ruby", so it can be a program name with args.
 set dummy ruby; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
@@ -3068,9 +3068,9 @@ fi
 
 else
 
-            { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+           { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-            # Extract the first word of "ruby", so it can be a program name with args.
+           # Extract the first word of "ruby", so it can be a program name with args.
 set dummy ruby; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
 $as_echo_n "checking for $ac_word... " >&6; }
@@ -3131,7 +3131,7 @@ $as_echo_n "checking for a sed that does not truncate output... " >&6; }
 if ${ac_cv_path_SED+:} false; then :
   $as_echo_n "(cached) " >&6
 else
-            ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+           ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
      for ac_i in 1 2 3 4 5 6 7; do
        ac_script="$ac_script$as_nl$ac_script"
      done
@@ -3310,14 +3310,14 @@ done
 
     if test -n "$RUBY"; then :
 
-        ax_ruby_version="1.8"
+       ax_ruby_version="1.8"
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ruby version" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ruby version" >&5
 $as_echo_n "checking for ruby version... " >&6; }
 
-        ruby_version=`$RUBY --version 2>&1 | $GREP "^ruby " | $SED -e 's/^.* \([0-9]*\.[0-9]*\.[0-9]*\) .*/\1/'`
+       ruby_version=`$RUBY --version 2>&1 | $GREP "^ruby " | $SED -e 's/^.* \([0-9]*\.[0-9]*\.[0-9]*\) .*/\1/'`
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ruby_version" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ruby_version" >&5
 $as_echo "$ruby_version" >&6; }
 
        RUBY_VERSION=$ruby_version
@@ -3336,17 +3336,17 @@ $as_echo "$ruby_version" >&6; }
   # digits, and non digits are removed.
 
   ax_compare_version_A=`echo "$ax_ruby_version" | sed -e 's/\([0-9]*\)/Z\1Z/g' \
-                     -e 's/Z\([0-9]\)Z/Z0\1Z/g' \
-                     -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \
-                     -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \
-                     -e 's/[^0-9]//g'`
+                    -e 's/Z\([0-9]\)Z/Z0\1Z/g' \
+                    -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \
+                    -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \
+                    -e 's/[^0-9]//g'`
 
 
   ax_compare_version_B=`echo "$ruby_version" | sed -e 's/\([0-9]*\)/Z\1Z/g' \
-                     -e 's/Z\([0-9]\)Z/Z0\1Z/g' \
-                     -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \
-                     -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \
-                     -e 's/[^0-9]//g'`
+                    -e 's/Z\([0-9]\)Z/Z0\1Z/g' \
+                    -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \
+                    -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \
+                    -e 's/[^0-9]//g'`
 
 
     ax_compare_version=`echo "x$ax_compare_version_A
@@ -3368,7 +3368,7 @@ x$ax_compare_version_B" | sed 's/^ *//' | sort | sed "s/x${ax_compare_version_A}
 
 else
 
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find the ruby interpreter" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: could not find the ruby interpreter" >&5
 $as_echo "$as_me: WARNING: could not find the ruby interpreter" >&2;}
 
 
@@ -3383,12 +3383,12 @@ fi
 $as_echo_n "checking for the mkmf Ruby package... " >&6; }
     ac_mkmf_result=`$RUBY -rmkmf -e ";" 2>&1`
     if test -z "$ac_mkmf_result"; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
     else
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot import Ruby module \"mkmf\".
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cannot import Ruby module \"mkmf\".
 Please check your Ruby installation. The error was:
 $ac_distutils_result" >&5
 $as_echo "$as_me: WARNING: cannot import Ruby module \"mkmf\".
@@ -3400,76 +3400,76 @@ $ac_distutils_result" >&2;}
     # Check for Ruby include path
     #
     if test -z "$RUBY_CFLAGS"; then
-        #
-        # Check for Ruby cflags
-        #
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby cflags" >&5
+       #
+       # Check for Ruby cflags
+       #
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby cflags" >&5
 $as_echo_n "checking for Ruby cflags... " >&6; }
-        if test -z "$RUBY_CFLAGS"; then
-            RUBY_CFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(CFLAGS))'`
-        fi
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_CFLAGS" >&5
+       if test -z "$RUBY_CFLAGS"; then
+           RUBY_CFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(CFLAGS))'`
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_CFLAGS" >&5
 $as_echo "$RUBY_CFLAGS" >&6; }
 
-        #
-        # Check for Ruby include path
-        #
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby include path" >&5
+       #
+       # Check for Ruby include path
+       #
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby include path" >&5
 $as_echo_n "checking for Ruby include path... " >&6; }
-        ruby_path=`$RUBY -rmkmf -e 'c = RbConfig::CONFIG; print c.has_key?(%q(rubyhdrdir)) ? \
-            c.fetch(%q(rubyhdrdir)) : c.fetch(%q(archdir))'`
-
-        ruby_arch=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(arch))'`
-
-        if test -n "${ruby_path}"; then
-            #
-            #  For some reason ruby 1.9.1 on linux seems to put its
-            #  config.h file in ${ruby_path}/${ruby_arch}/ruby/config.h
-            #  Aside from the fact that it is WRONG to include your own
-            #  config.h file, it means we can't use the headers unless we
-            #  add both paths.
-            #
-            if test -d "${ruby_path}/${ruby_arch}"; then
-                 ruby_path=" -I${ruby_path} -I${ruby_path}/${ruby_arch}"
-            else
-                 ruby_path=" -I${ruby_path}"
-            fi
-        fi
-
-        RUBY_CFLAGS+="$ruby_path"
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ruby_path" >&5
+       ruby_path=`$RUBY -rmkmf -e 'c = RbConfig::CONFIG; print c.has_key?(%q(rubyhdrdir)) ? \
+           c.fetch(%q(rubyhdrdir)) : c.fetch(%q(archdir))'`
+
+       ruby_arch=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(arch))'`
+
+       if test -n "${ruby_path}"; then
+           #
+           #  For some reason ruby 1.9.1 on linux seems to put its
+           #  config.h file in ${ruby_path}/${ruby_arch}/ruby/config.h
+           #  Aside from the fact that it is WRONG to include your own
+           #  config.h file, it means we can't use the headers unless we
+           #  add both paths.
+           #
+           if test -d "${ruby_path}/${ruby_arch}"; then
+                ruby_path=" -I${ruby_path} -I${ruby_path}/${ruby_arch}"
+           else
+                ruby_path=" -I${ruby_path}"
+           fi
+       fi
+
+       RUBY_CFLAGS+="$ruby_path"
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ruby_path" >&5
 $as_echo "$ruby_path" >&6; }
     fi
 
 
 
     if test -z "$RUBY_LDFLAGS"; then
-        #
-        # Check for Ruby library path
-        #
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby library path" >&5
+       #
+       # Check for Ruby library path
+       #
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby library path" >&5
 $as_echo_n "checking for Ruby library path... " >&6; }
-        if test -z "$RUBY_LIBRARY_PATH"; then
-            RUBY_LIBRARY_PATH=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(libdir))'`
-            if test -n "${RUBY_LIBRARY_PATH}"; then
-                RUBY_LIBRARY_PATH=" -L$RUBY_LIBRARY_PATH"
-            fi
-        fi
-
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_LIBRARY_PATH" >&5
+       if test -z "$RUBY_LIBRARY_PATH"; then
+           RUBY_LIBRARY_PATH=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(libdir))'`
+           if test -n "${RUBY_LIBRARY_PATH}"; then
+               RUBY_LIBRARY_PATH=" -L$RUBY_LIBRARY_PATH"
+           fi
+       fi
+
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_LIBRARY_PATH" >&5
 $as_echo "$RUBY_LIBRARY_PATH" >&6; }
 
-        #
-        # Check for Ruby linking flags
-        #
-        { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby linking flags" >&5
+       #
+       # Check for Ruby linking flags
+       #
+       { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby linking flags" >&5
 $as_echo_n "checking for Ruby linking flags... " >&6; }
 
-        RUBY_LDFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(LIBRUBYARG_SHARED))'`
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_LDFLAGS" >&5
+       RUBY_LDFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(LIBRUBYARG_SHARED))'`
+       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_LDFLAGS" >&5
 $as_echo "$RUBY_LDFLAGS" >&6; }
 
-        RUBY_LDFLAGS="${RUBY_LIBRARY_PATH} ${RUBY_LDFLAGS}"
+       RUBY_LDFLAGS="${RUBY_LIBRARY_PATH} ${RUBY_LDFLAGS}"
     fi
 
 
@@ -3480,7 +3480,7 @@ $as_echo "$RUBY_LDFLAGS" >&6; }
     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for Ruby site-packages path" >&5
 $as_echo_n "checking for Ruby site-packages path... " >&6; }
     if test -z "$RUBY_SITE_PKG"; then
-        RUBY_SITE_PKG=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(sitearchdir))'`
+       RUBY_SITE_PKG=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(sitearchdir))'`
     fi
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RUBY_SITE_PKG" >&5
 $as_echo "$RUBY_SITE_PKG" >&6; }
@@ -3589,7 +3589,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
                if test -n "$RUBY_VERSION"; then
                        mod_cflags=$(echo "${RUBY_CFLAGS}" | sed 's/-I[ ]*/-isystem /g' | sed 's/-arch [^ ]*[ ]*//g')
 
-            { $as_echo "$as_me:${as_lineno-$LINENO}: Sanitized cflags are \"${mod_cflags}\"" >&5
+           { $as_echo "$as_me:${as_lineno-$LINENO}: Sanitized cflags are \"${mod_cflags}\"" >&5
 $as_echo "$as_me: Sanitized cflags are \"${mod_cflags}\"" >&6;}
 
                        mod_ldflags="${RUBY_LDFLAGS} ${RUBY_EXTRA_LIBS}"
@@ -3698,11 +3698,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -4208,11 +4208,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 262d46c..9306382 100644 (file)
@@ -26,8 +26,8 @@ if test x$with_[]modname != xno; then
                if test -n "$RUBY_VERSION"; then
                        mod_cflags=[$(echo "${RUBY_CFLAGS}" | sed 's/-I[ ]*/-isystem /g' | sed 's/-arch [^ ]*[ ]*//g')]
                        
-            AC_MSG_NOTICE([Sanitized cflags are \"${mod_cflags}\"])
-            
+           AC_MSG_NOTICE([Sanitized cflags are \"${mod_cflags}\"])
+           
                        mod_ldflags="${RUBY_LDFLAGS} ${RUBY_EXTRA_LIBS}"
                else
                        fail="ruby"
index 032f4ad..169e059 100644 (file)
@@ -1,25 +1,25 @@
 #This is example radius.rb script
 module Radiusd
     def Radiusd.instantiate(arg)
-        radlog(L_DBG,"[ruby]Running ruby instantiate")
-        p arg
-        return Radiusd::RLM_MODULE_OK
+       radlog(L_DBG,"[ruby]Running ruby instantiate")
+       p arg
+       return Radiusd::RLM_MODULE_OK
     end
     def Radiusd.authenticate(arg)
-        radlog(L_DBG,"[ruby]Running ruby authenticate")
-        p arg
-        return Radiusd::RLM_MODULE_NOOP
+       radlog(L_DBG,"[ruby]Running ruby authenticate")
+       p arg
+       return Radiusd::RLM_MODULE_NOOP
     end
     def Radiusd.authorize(arg)
-        radlog(L_DBG,"[ruby]Running ruby authorize")
-        p arg
-        #Here we return Cleartext-Password, which could have been retrieved from DB.
-        return [Radiusd::RLM_MODULE_UPDATED, [],[["Cleartext-Password","pass"]]]
+       radlog(L_DBG,"[ruby]Running ruby authorize")
+       p arg
+       #Here we return Cleartext-Password, which could have been retrieved from DB.
+       return [Radiusd::RLM_MODULE_UPDATED, [],[["Cleartext-Password","pass"]]]
     end
     def Radiusd.accounting(arg)
-        radlog(L_DBG,"[ruby]Running ruby accounting")
-        p arg
-        return Radiusd::RLM_MODULE_NOOP
+       radlog(L_DBG,"[ruby]Running ruby accounting")
+       p arg
+       return Radiusd::RLM_MODULE_NOOP
     end
 
 end
index 9fa4645..e14ca60 100644 (file)
@@ -42,25 +42,25 @@ AC_DEFUN([AX_PROG_RUBY_VERSION],[
     AC_REQUIRE([AC_PROG_GREP])
 
     AS_IF([test -n "$RUBY"],[
-        ax_ruby_version="$1"
+       ax_ruby_version="$1"
 
-        AC_MSG_CHECKING([for ruby version])
-        changequote(<<,>>)
-        ruby_version=`$RUBY --version 2>&1 | $GREP "^ruby " | $SED -e 's/^.* \([0-9]*\.[0-9]*\.[0-9]*\) .*/\1/'`
-        changequote([,])
-        AC_MSG_RESULT($ruby_version)
+       AC_MSG_CHECKING([for ruby version])
+       changequote(<<,>>)
+       ruby_version=`$RUBY --version 2>&1 | $GREP "^ruby " | $SED -e 's/^.* \([0-9]*\.[0-9]*\.[0-9]*\) .*/\1/'`
+       changequote([,])
+       AC_MSG_RESULT($ruby_version)
 
        AC_SUBST([RUBY_VERSION],[$ruby_version])
 
-        AX_COMPARE_VERSION([$ax_ruby_version],[le],[$ruby_version],[
+       AX_COMPARE_VERSION([$ax_ruby_version],[le],[$ruby_version],[
            :
-            $2
-        ],[
+           $2
+       ],[
            :
-            $3
-        ])
+           $3
+       ])
     ],[
-        AC_MSG_WARN([could not find the ruby interpreter])
-        $3
+       AC_MSG_WARN([could not find the ruby interpreter])
+       $3
     ])
 ])
index 8bae738..bdc345b 100644 (file)
@@ -61,10 +61,10 @@ AC_DEFUN([AX_RUBY_DEVEL],[
     AC_MSG_CHECKING([for the mkmf Ruby package])
     ac_mkmf_result=`$RUBY -rmkmf -e ";" 2>&1`
     if test -z "$ac_mkmf_result"; then
-        AC_MSG_RESULT([yes])
+       AC_MSG_RESULT([yes])
     else
-        AC_MSG_RESULT([no])
-        AC_MSG_WARN([cannot import Ruby module "mkmf".
+       AC_MSG_RESULT([no])
+       AC_MSG_WARN([cannot import Ruby module "mkmf".
 Please check your Ruby installation. The error was:
 $ac_distutils_result])
     fi
@@ -73,68 +73,68 @@ $ac_distutils_result])
     # Check for Ruby include path
     #
     if test -z "$RUBY_CFLAGS"; then
-        #
-        # Check for Ruby cflags
-        #
-        AC_MSG_CHECKING([for Ruby cflags])
-        if test -z "$RUBY_CFLAGS"; then
-            RUBY_CFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(CFLAGS))'`
-        fi
-        AC_MSG_RESULT([$RUBY_CFLAGS])
+       #
+       # Check for Ruby cflags
+       #
+       AC_MSG_CHECKING([for Ruby cflags])
+       if test -z "$RUBY_CFLAGS"; then
+           RUBY_CFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(CFLAGS))'`
+       fi
+       AC_MSG_RESULT([$RUBY_CFLAGS])
     
-        #
-        # Check for Ruby include path
-        #
-        AC_MSG_CHECKING([for Ruby include path])
-        ruby_path=`$RUBY -rmkmf -e 'c = RbConfig::CONFIG; print c.has_key?(%q(rubyhdrdir)) ? \
-            c.fetch(%q(rubyhdrdir)) : c.fetch(%q(archdir))'`
-            
-        ruby_arch=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(arch))'`
-            
-        if test -n "${ruby_path}"; then
-            #
-            #  For some reason ruby 1.9.1 on linux seems to put its
-            #  config.h file in ${ruby_path}/${ruby_arch}/ruby/config.h
-            #  Aside from the fact that it is WRONG to include your own
-            #  config.h file, it means we can't use the headers unless we
-            #  add both paths.
-            #
-            if test -d "${ruby_path}/${ruby_arch}"; then
-                 ruby_path=" -I${ruby_path} -I${ruby_path}/${ruby_arch}"
-            else
-                 ruby_path=" -I${ruby_path}"
-            fi
-        fi
-        
-        RUBY_CFLAGS+="$ruby_path"
-        AC_MSG_RESULT([$ruby_path])
+       #
+       # Check for Ruby include path
+       #
+       AC_MSG_CHECKING([for Ruby include path])
+       ruby_path=`$RUBY -rmkmf -e 'c = RbConfig::CONFIG; print c.has_key?(%q(rubyhdrdir)) ? \
+           c.fetch(%q(rubyhdrdir)) : c.fetch(%q(archdir))'`
+           
+       ruby_arch=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(arch))'`
+           
+       if test -n "${ruby_path}"; then
+           #
+           #  For some reason ruby 1.9.1 on linux seems to put its
+           #  config.h file in ${ruby_path}/${ruby_arch}/ruby/config.h
+           #  Aside from the fact that it is WRONG to include your own
+           #  config.h file, it means we can't use the headers unless we
+           #  add both paths.
+           #
+           if test -d "${ruby_path}/${ruby_arch}"; then
+                ruby_path=" -I${ruby_path} -I${ruby_path}/${ruby_arch}"
+           else
+                ruby_path=" -I${ruby_path}"
+           fi
+       fi
+       
+       RUBY_CFLAGS+="$ruby_path"
+       AC_MSG_RESULT([$ruby_path])
     fi
     
     AC_SUBST([RUBY_CFLAGS])
 
     if test -z "$RUBY_LDFLAGS"; then
-        #
-        # Check for Ruby library path
-        #
-        AC_MSG_CHECKING([for Ruby library path])
-        if test -z "$RUBY_LIBRARY_PATH"; then
-            RUBY_LIBRARY_PATH=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(libdir))'`
-            if test -n "${RUBY_LIBRARY_PATH}"; then
-                RUBY_LIBRARY_PATH=" -L$RUBY_LIBRARY_PATH"
-            fi
-        fi
-        
-        AC_MSG_RESULT([$RUBY_LIBRARY_PATH])  
-        
-        #
-        # Check for Ruby linking flags
-        #
-        AC_MSG_CHECKING([for Ruby linking flags])
+       #
+       # Check for Ruby library path
+       #
+       AC_MSG_CHECKING([for Ruby library path])
+       if test -z "$RUBY_LIBRARY_PATH"; then
+           RUBY_LIBRARY_PATH=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(libdir))'`
+           if test -n "${RUBY_LIBRARY_PATH}"; then
+               RUBY_LIBRARY_PATH=" -L$RUBY_LIBRARY_PATH"
+           fi
+       fi
+       
+       AC_MSG_RESULT([$RUBY_LIBRARY_PATH])  
+       
+       #
+       # Check for Ruby linking flags
+       #
+       AC_MSG_CHECKING([for Ruby linking flags])
     
-        RUBY_LDFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(LIBRUBYARG_SHARED))'`
-        AC_MSG_RESULT([$RUBY_LDFLAGS])
+       RUBY_LDFLAGS=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(LIBRUBYARG_SHARED))'`
+       AC_MSG_RESULT([$RUBY_LDFLAGS])
 
-        RUBY_LDFLAGS="${RUBY_LIBRARY_PATH} ${RUBY_LDFLAGS}"
+       RUBY_LDFLAGS="${RUBY_LIBRARY_PATH} ${RUBY_LDFLAGS}"
     fi
 
     AC_SUBST([RUBY_LDFLAGS])
@@ -144,7 +144,7 @@ $ac_distutils_result])
     #
     AC_MSG_CHECKING([for Ruby site-packages path])
     if test -z "$RUBY_SITE_PKG"; then
-        RUBY_SITE_PKG=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(sitearchdir))'`
+       RUBY_SITE_PKG=`$RUBY -rmkmf -e 'print RbConfig::CONFIG.fetch(%q(sitearchdir))'`
     fi
     AC_MSG_RESULT([$RUBY_SITE_PKG])
     AC_SUBST([RUBY_SITE_PKG])
@@ -183,9 +183,9 @@ $ac_distutils_result])
     ac_save_CFLAGS="$CFLAGS"
     CFLAGS="$ac_save_CFLAGS $RUBY_CFLAGS"
     AC_LINK_IFELSE(
-        [AC_LANG_PROGRAM([#include <ruby.h>],[ruby_init()])],
-        [rubyexists=yes],
-        [rubyexists=no])
+       [AC_LANG_PROGRAM([#include <ruby.h>],[ruby_init()])],
+       [rubyexists=yes],
+       [rubyexists=no])
 
     AC_MSG_RESULT([$rubyexists])
 
index 0d22337..84ad3b1 100644 (file)
@@ -68,8 +68,8 @@ typedef struct rlm_ruby_t {
 #endif
        RLM_RUBY_STRUCT(detach);
 
-       char *filename;
-       char *module_name;
+       char const *filename;
+       char const *module_name;
        VALUE module;
 
 } rlm_ruby_t;
@@ -84,11 +84,9 @@ typedef struct rlm_ruby_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "filename", PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED,
-         offsetof(struct rlm_ruby_t, filename), NULL, NULL},
-       { "module", PW_TYPE_STRING_PTR,
-         offsetof(struct rlm_ruby_t, module_name), NULL, "Radiusd"},
-       { NULL, -1, 0, NULL, NULL} /* end of module_config */
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, struct rlm_ruby_t, filename), NULL },
+       { "module", FR_CONF_OFFSET(PW_TYPE_STRING, struct rlm_ruby_t, module_name), "Radiusd" },
+       { NULL, -1, 0, NULL, NULL } /* end of module_config */
 };
 
 
@@ -194,8 +192,9 @@ static void add_vp_tuple(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vpp, VA
  */
 
 #define BUF_SIZE 1024
-static rlm_rcode_t do_ruby(REQUEST *request, unsigned long func,
-                          VALUE module, char const *function_name) {
+static rlm_rcode_t CC_HINT(nonnull (4)) do_ruby(REQUEST *request, unsigned long func,
+                                               VALUE module, char const *function_name)
+{
        rlm_rcode_t rcode = RLM_MODULE_OK;
        vp_cursor_t cursor;
 
@@ -215,25 +214,24 @@ static rlm_rcode_t do_ruby(REQUEST *request, unsigned long func,
        }
 
        n_tuple = 0;
-
        if (request) {
-               for (vp = paircursor(&cursor, &request->packet->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
-                        n_tuple++;
+                    vp = fr_cursor_next(&cursor)) {
+                        n_tuple++;
                }
        }
 
-
        /*
          Creating ruby array, that contains arrays of [name,value]
          Maybe we should use hash instead? Can this names repeat?
        */
        rb_request = rb_ary_new2(n_tuple);
+
        if (request) {
-               for (vp = paircursor(&cursor, &request->packet->vps);
+               for (vp = fr_cursor_init(&cursor, &request->packet->vps);
                     vp;
-                    vp = pairnext(&cursor)) {
+                    vp = fr_cursor_next(&cursor)) {
                        VALUE tmp = rb_ary_new2(2);
 
                        /* The name. logic from vp_prints, lib/print.c */
@@ -262,8 +260,7 @@ static rlm_rcode_t do_ruby(REQUEST *request, unsigned long func,
         */
        if (TYPE(rb_result) == T_ARRAY) {
                if (!FIXNUM_P(rb_ary_entry(rb_result, 0))) {
-                       REDEBUG("First element of an array was not a "
-                               "FIXNUM (Which has to be a return_value)");
+                       ERROR("First element of an array was not a FIXNUM (Which has to be a return_value)");
 
                        rcode = RLM_MODULE_FAIL;
                        goto finish;
@@ -365,7 +362,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
         */
        module = inst->module = rb_define_module(inst->module_name);
        if (!module) {
-               EDEBUG("Ruby rb_define_module failed");
+               ERROR("Ruby rb_define_module failed");
 
                return -1;
        }
@@ -385,7 +382,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
        DEBUG("Loading file %s...", inst->filename);
        rb_load_protect(rb_str_new2(inst->filename), 0, &status);
        if (status) {
-               EDEBUG("Error loading file %s status: %d", inst->filename, status);
+               ERROR("Error loading file %s status: %d", inst->filename, status);
 
                return -1;
        }
@@ -417,7 +414,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
        return do_ruby(NULL, inst->func_instantiate, inst->module, "instantiate");
 }
 
-#define RLM_RUBY_FUNC(foo) static rlm_rcode_t mod_##foo(void *instance, REQUEST *request) \
+#define RLM_RUBY_FUNC(foo) static rlm_rcode_t CC_HINT(nonnull) mod_##foo(void *instance, REQUEST *request) \
        { \
                return do_ruby(request, \
                               ((struct rlm_ruby_t *)instance)->func_##foo,((struct rlm_ruby_t *)instance)->module, \
index 201f2d0..d3463c5 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-securid-include-dir=DIR
-                          Directory where the securid includes may be found
+                         Directory where the securid includes may be found
   --with-securid-lib-dir=DIR
-                          Directory where the securid libraries may be found
+                         Directory where the securid libraries may be found
   --with-securid-dir=DIR  Base directory where securid is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1793,7 +1793,7 @@ SMART_CLFAGS=
 if test x$with_rlm_securid != xno; then
 
 
-        securid_include_dir=
+       securid_include_dir=
 
 # Check whether --with-securid-include-dir was given.
 if test "${with_securid_include_dir+set}" = set; then :
@@ -1810,7 +1810,7 @@ if test "${with_securid_include_dir+set}" = set; then :
 fi
 
 
-        securid_lib_dir=
+       securid_lib_dir=
 
 # Check whether --with-securid-lib-dir was given.
 if test "${with_securid_lib_dir+set}" = set; then :
@@ -1943,7 +1943,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2165,7 +2165,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2714,22 +2714,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=acexport.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2786,7 +2786,7 @@ $as_echo "$as_me: WARNING: securid headers not found. Use --with-securid-include
     fi
 
 
-        smart_try_dir="$securid_lib_dir"
+       smart_try_dir="$securid_lib_dir"
 
 
 sm_lib_safe=`echo "aceclnt" | sed 'y%./+-%__p_%'`
@@ -2846,8 +2846,8 @@ SD_Init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-laceclnt"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-laceclnt"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2863,22 +2863,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libaceclnt${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2890,22 +2890,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libaceclnt.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3059,11 +3059,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3569,11 +3569,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 8031da2..6ae91be 100644 (file)
@@ -72,7 +72,7 @@ void securid_sessionlist_free(rlm_securid_t *inst,REQUEST *request)
 
        pthread_mutex_lock(&(inst->session_mutex));
 
-               for (node = inst->session_head; node != NULL; node = next) {
+       for (node = inst->session_head; node != NULL; node = next) {
                next = node->next;
                securid_session_free(inst,request,node);
        }
@@ -90,15 +90,11 @@ void securid_sessionlist_free(rlm_securid_t *inst,REQUEST *request)
  *     Since we're adding it to the list, we guess that this means
  *     the packet needs a State attribute.  So add one.
  */
-int securid_sessionlist_add(rlm_securid_t *inst,REQUEST *request,
-                           SECURID_SESSION *session)
+int securid_sessionlist_add(rlm_securid_t *inst,REQUEST *request, SECURID_SESSION *session)
 {
        int             status = 0;
        VALUE_PAIR      *state;
 
-       rad_assert(session != NULL);
-       rad_assert(request != NULL);
-
        /*
         *      The time at which this request was made was the time
         *      at which it was received by the RADIUS server.
@@ -222,7 +218,7 @@ SECURID_SESSION *securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request)
         *      Might not have been there.
         */
        if (!session) {
-               ERROR("rlm_securid: No SECURID session matching the State variable.");
+               ERROR("rlm_securid: No SECURID session matching the State variable");
                return NULL;
        }
 
@@ -285,7 +281,7 @@ static void securid_sessionlist_clean_expired(rlm_securid_t *inst, REQUEST *requ
         *      Delete old sessions from the list
         *
         */
-               while((session = inst->session_head)) {
+       while((session = inst->session_head)) {
                if ((timestamp - session->timestamp) > inst->timer_limit) {
                        rbnode_t *node;
                        node = rbtree_find(inst->session_tree, session);
index 99fde74..149eac5 100644 (file)
@@ -39,14 +39,10 @@ typedef enum {
 
 
 static const CONF_PARSER module_config[] = {
-       { "timer_expire", PW_TYPE_INTEGER,
-         offsetof(rlm_securid_t, timer_limit), NULL, "600"},
-       { "max_sessions", PW_TYPE_INTEGER,
-         offsetof(rlm_securid_t, max_sessions), NULL, "2048"},
-       { "max_trips_per_session", PW_TYPE_INTEGER,
-         offsetof(rlm_securid_t, max_trips_per_session), NULL, NULL},
-       { "max_round_trips", PW_TYPE_INTEGER,
-         offsetof(rlm_securid_t, max_trips_per_session), NULL, "6"},
+       { "timer_expire", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_securid_t, timer_limit), "600" },
+       { "max_sessions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_securid_t, max_sessions), "2048" },
+       { "max_trips_per_session", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_securid_t, max_trips_per_session), NULL },
+       { "max_round_trips", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_securid_t, max_trips_per_session), "6" },
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 
@@ -146,7 +142,7 @@ static SECURID_AUTH_RC securidAuth(void *instance, REQUEST *request,
                        securid_session->identity = strdup(username);
 
                        /* Get PIN requirements */
-                       acm_ret = AceGetPinParams(sdiHandle, &pin_params);
+                       (void) AceGetPinParams(sdiHandle, &pin_params);
 
                        /* If a system-generated PIN is required */
                        if (pin_params.Selectable == CANNOT_CHOOSE_PIN) {
@@ -442,7 +438,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
         */
        inst->session_tree = rbtree_create(securid_session_cmp, NULL, 0);
        if (!inst->session_tree) {
-               ERROR("rlm_securid: Cannot initialize session tree.");
+               ERROR("rlm_securid: Cannot initialize session tree");
                return -1;
        }
 
@@ -454,7 +450,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
 /*
  *     Authenticate the user via one of any well-known password.
  */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        int rcode;
        rlm_securid_t *inst = instance;
@@ -467,12 +463,12 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         *      a User-Name attribute.
         */
        if (!request->username) {
-               AUTH("rlm_securid: Attribute \"User-Name\" is required for authentication.");
+               AUTH("rlm_securid: Attribute \"User-Name\" is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
        if (!request->password) {
-               RAUTH("Attribute \"Password\" is required for authentication.");
+               RAUTH("Attribute \"Password\" is required for authentication");
                return RLM_MODULE_INVALID;
        }
 
@@ -498,8 +494,11 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
        username = request->username->vp_strvalue;
        password = request->password->vp_strvalue;
 
-       RDEBUG("User [%s] login attempt with password [%s]",
-              username, password);
+       if (RDEBUG_ENABLED3) {
+               RDEBUG3("Login attempt with password \"%s\"", password);
+       } else {
+               RDEBUG("Login attempt with password");
+       }
 
        rcode = securidAuth(inst, request, username, password,
                            buffer, sizeof(buffer));
@@ -520,8 +519,8 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                pairadd(&request->reply->vps, vp);
 
                /* Mark the packet as a Acceess-Challenge Packet */
-               request->reply->code = PW_ACCESS_CHALLENGE;
-               RDEBUG("Sending Access-Challenge.");
+               request->reply->code = PW_CODE_ACCESS_CHALLENGE;
+               RDEBUG("Sending Access-Challenge");
                rcode = RLM_MODULE_HANDLED;
                break;
 
@@ -551,7 +550,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
 module_t rlm_securid = {
        RLM_MODULE_INIT,
        "securid",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(rlm_securid_t),
        module_config,
        mod_instantiate,                /* instantiation */
index 72e083c..82ed77e 100644 (file)
@@ -46,7 +46,7 @@ typedef struct _securid_session_t {
        fr_ipaddr_t               src_ipaddr;
        time_t                    timestamp;
        unsigned int              session_id;
-       int                       trips;
+       uint32_t                  trips;
 
        char                      *pin;      /* previous pin if user entered it during NEW-PIN mode process */
        char                      *identity; /* save user's identity name for future use */
@@ -73,19 +73,21 @@ typedef struct rlm_securid_t {
        /*
         *      Configuration items.
         */
-       int             timer_limit;
-       int             max_sessions;
-       int             max_trips_per_session;
+       uint32_t        timer_limit;
+       uint32_t        max_sessions;
+       uint32_t        max_trips_per_session;
 } rlm_securid_t;
 
 /* Memory Management */
 SECURID_SESSION*     securid_session_alloc(void);
-void                securid_session_free(rlm_securid_t *inst, REQUEST *request,SECURID_SESSION *session);
+void                securid_session_free(rlm_securid_t *inst, REQUEST *request,SECURID_SESSION *session)
+                    CC_HINT(nonnull);
 
-void                securid_sessionlist_free(rlm_securid_t *inst,REQUEST *request);
+void                securid_sessionlist_free(rlm_securid_t *inst,REQUEST *request) CC_HINT(nonnull);
 
-int                 securid_sessionlist_add(rlm_securid_t *inst, REQUEST *request, SECURID_SESSION *session);
-SECURID_SESSION*     securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request);
+int                 securid_sessionlist_add(rlm_securid_t *inst, REQUEST *request, SECURID_SESSION *session)
+                    CC_HINT(nonnull);
+SECURID_SESSION             *securid_sessionlist_find(rlm_securid_t *inst, REQUEST *request) CC_HINT(nonnull);
 
 
 #endif
index 5010379..a3a79cf 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1248,9 +1248,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1293,10 +1293,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2068,7 +2068,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2290,7 +2290,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3276,11 +3276,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3754,13 +3754,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 6a94d92..e47cefe 100644 (file)
@@ -28,21 +28,16 @@ RCSID("$Id$")
 #include <sys/un.h>
 
 typedef struct rlm_smsotp_t {
-       char            *socket;
-       char            *challenge;
-       char            *authtype;
+       char const      *socket;
+       char const      *challenge;
+       char const      *authtype;
        fr_connection_pool_t *pool;
 } rlm_smsotp_t;
 
 static const CONF_PARSER module_config[] = {
-       { "socket", PW_TYPE_STRING_PTR,
-         offsetof(rlm_smsotp_t, socket),
-         NULL, "/var/run/smsotp_socket" },
-       { "challenge_message", PW_TYPE_STRING_PTR,
-         offsetof(rlm_smsotp_t, challenge), NULL, "Enter Mobile PIN" },
-       { "challenge_type", PW_TYPE_STRING_PTR,
-         offsetof(rlm_smsotp_t, authtype),
-         NULL, "smsotp-reply" },
+       { "socket", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_smsotp_t, socket), "/var/run/smsotp_socket" },
+       { "challenge_message", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_smsotp_t, challenge), "Enter Mobile PIN" },
+       { "challenge_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_smsotp_t, authtype), "smsotp-reply" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -62,13 +57,13 @@ static void *mod_conn_create(void *instance)
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0) {
                ERROR("Failed opening SMSOTP file %s: %s",
-                      inst->socket, strerror(errno));
+                      inst->socket, fr_syserror(errno));
                return NULL;
        }
 
        if (connect(fd, (struct sockaddr *) &sa, socklen) < -1) {
                ERROR("Failed connecting to SMSOTP file %s: %s",
-                      inst->socket, strerror(errno));
+                      inst->socket, fr_syserror(errno));
                return NULL;
        }
 
@@ -192,7 +187,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 /*
  *     Authenticate the user with the given password.
  */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_smsotp_t *inst = instance;
        VALUE_PAIR *state;
@@ -203,10 +198,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
        char output[1000];
 
        fdp = fr_connection_get(inst->pool);
-       if (!fdp) {
-               REDEBUG("Failed to get handle from connection pool");
-               return RLM_MODULE_FAIL;
-       }
+       if (!fdp) return RLM_MODULE_FAIL;
 
        /* Get greeting */
        bufsize = read_all(fdp, buffer, sizeof(buffer));
@@ -220,7 +212,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         */
 #define WRITE_ALL(_a,_b,_c) if (write_all(_a,_b,_c) < 0) goto done;
        state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
-       if (!state) {
+       if (state) {
                RDEBUG("Found reply to access challenge");
 
                /* send username */
@@ -228,26 +220,26 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                         request->username->vp_strvalue);
                WRITE_ALL(fdp, output, strlen(output));
 
-               bufsize = read_all(fdp, buffer, sizeof(buffer));
+               (void) read_all(fdp, buffer, sizeof(buffer));
 
                /* send password */
                snprintf(output, sizeof(output), "user otp is %s\n",
                         request->password->vp_strvalue);
                WRITE_ALL(fdp, output, strlen(output));
 
-               bufsize = read_all(fdp, buffer, sizeof(buffer));
+               (void) read_all(fdp, buffer, sizeof(buffer));
 
                /* set uuid */
                snprintf(output, sizeof(output), "otp id is %s\n",
                         state->vp_strvalue);
                WRITE_ALL(fdp, output, strlen(output));
 
-               bufsize = read_all(fdp, buffer, sizeof(buffer));
+               (void) read_all(fdp, buffer, sizeof(buffer));
 
                /* now check the otp */
                WRITE_ALL(fdp, "get check result\n", 17);
 
-               bufsize = read_all(fdp, buffer, sizeof(buffer));
+               (void) read_all(fdp, buffer, sizeof(buffer));
 
                /* end the sesssion */
                WRITE_ALL(fdp, "quit\n", 5);
@@ -267,7 +259,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
                 request->username->vp_strvalue);
        WRITE_ALL(fdp, output, strlen(output));
 
-       bufsize = read_all(fdp, buffer, sizeof(buffer));
+       (void) read_all(fdp, buffer, sizeof(buffer));
 
        /* end the sesssion */
        WRITE_ALL(fdp, "quit\n", 5);
@@ -291,8 +283,8 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
         *
         *  The server will take care of sending it to the user.
         */
-       request->reply->code = PW_ACCESS_CHALLENGE;
-       DEBUG("rlm_smsotp: Sending Access-Challenge.");
+       request->reply->code = PW_CODE_ACCESS_CHALLENGE;
+       DEBUG("rlm_smsotp: Sending Access-Challenge");
 
        rcode = RLM_MODULE_HANDLED;
 
@@ -307,7 +299,7 @@ done:
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
 {
        VALUE_PAIR *state;
        rlm_smsotp_t *inst = instance;
index c8bc53b..0de7d7e 100644 (file)
@@ -98,7 +98,7 @@ static const CONF_PARSER module_config[] = {
        /*
         * Do SoH over DHCP?
         */
-       { "dhcp",    PW_TYPE_BOOLEAN, offsetof(rlm_soh_t,dhcp), NULL, "no" },
+       { "dhcp", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_soh_t, dhcp), "no" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -118,11 +118,14 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        return 0;
 }
 
-static rlm_rcode_t mod_post_auth(UNUSED void * instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(UNUSED void * instance, UNUSED REQUEST *request)
 {
 #ifdef WITH_DHCP
        int rcode;
        VALUE_PAIR *vp;
+       rlm_soh_t *inst = instance;
+
+       if (!inst->dhcp) return RLM_MODULE_NOOP;
 
        vp = pairfind(request->packet->vps, 43, DHCP_MAGIC_VENDOR, TAG_ANY);
        if (vp) {
@@ -181,7 +184,7 @@ static rlm_rcode_t mod_post_auth(UNUSED void * instance, REQUEST *request)
        return RLM_MODULE_NOOP;
 }
 
-static rlm_rcode_t mod_authorize(UNUSED void * instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void * instance, REQUEST *request)
 {
        VALUE_PAIR *vp;
        int rv;
index 2e69a0e..3738d45 100644 (file)
@@ -31,12 +31,12 @@ RCSID("$Id$")
  *     going to return.
  */
 typedef struct rlm_sometimes_t {
-       char                    *rcode_str;
-       int                     rcode;
-       int                     start;
-       int                     end;
-       char                    *key;
-       DICT_ATTR const         *da;
+       char const      *rcode_str;
+       rlm_rcode_t     rcode;
+       uint32_t        start;
+       uint32_t        end;
+       char const      *key;
+       DICT_ATTR const *da;
 } rlm_sometimes_t;
 
 /*
@@ -49,10 +49,10 @@ typedef struct rlm_sometimes_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "rcode",      PW_TYPE_STRING_PTR, offsetof(rlm_sometimes_t,rcode_str), NULL, "fail" },
-       { "key", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,    offsetof(rlm_sometimes_t,key), NULL, "User-Name" },
-       { "start", PW_TYPE_INTEGER,    offsetof(rlm_sometimes_t,start), NULL, "0" },
-       { "end", PW_TYPE_INTEGER,    offsetof(rlm_sometimes_t,end), NULL, "127" },
+       { "rcode", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sometimes_t, rcode_str), "fail" },
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_ATTRIBUTE, rlm_sometimes_t, key), "User-Name" },
+       { "start", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sometimes_t, start), "0" },
+       { "end", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sometimes_t, end), "127" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -67,8 +67,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /*
         *      Convert the rcode string to an int, and get rid of it
         */
-       inst->rcode = fr_str2int(mod_rcode_table, inst->rcode_str, -1);
-       if (inst->rcode == -1) {
+       inst->rcode = fr_str2int(mod_rcode_table, inst->rcode_str, RLM_MODULE_UNKNOWN);
+       if (inst->rcode == RLM_MODULE_UNKNOWN) {
                cf_log_err_cs(conf, "Unknown module return code '%s'", inst->rcode_str);
                return -1;
        }
@@ -82,11 +82,10 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 /*
  *     A lie!  It always returns!
  */
-static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet,
-                                   RADIUS_PACKET *reply)
+static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet, RADIUS_PACKET *reply)
 {
        uint32_t hash;
-       int value;
+       uint32_t value;
        rlm_sometimes_t *inst = instance;
        VALUE_PAIR *vp;
 
@@ -119,20 +118,20 @@ static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet,
         */
        if ((inst->rcode == RLM_MODULE_HANDLED) && reply) {
                switch (packet->code) {
-               case PW_AUTHENTICATION_REQUEST:
-                       reply->code = PW_AUTHENTICATION_ACK;
+               case PW_CODE_AUTHENTICATION_REQUEST:
+                       reply->code = PW_CODE_AUTHENTICATION_ACK;
                        break;
 
-               case PW_ACCOUNTING_REQUEST:
-                       reply->code = PW_ACCOUNTING_RESPONSE;
+               case PW_CODE_ACCOUNTING_REQUEST:
+                       reply->code = PW_CODE_ACCOUNTING_RESPONSE;
                        break;
 
-               case PW_COA_REQUEST:
-                       reply->code = PW_COA_ACK;
+               case PW_CODE_COA_REQUEST:
+                       reply->code = PW_CODE_COA_ACK;
                        break;
 
-               case PW_DISCONNECT_REQUEST:
-                       reply->code = PW_DISCONNECT_ACK;
+               case PW_CODE_DISCONNECT_REQUEST:
+                       reply->code = PW_CODE_DISCONNECT_ACK;
                        break;
 
                default:
@@ -143,25 +142,25 @@ static rlm_rcode_t sometimes_return(void *instance, RADIUS_PACKET *packet,
        return inst->rcode;
 }
 
-static rlm_rcode_t sometimes_packet(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_sometimes_packet(void *instance, REQUEST *request)
 {
        return sometimes_return(instance, request->packet, request->reply);
 }
 
-static rlm_rcode_t sometimes_reply(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_sometimes_reply(void *instance, REQUEST *request)
 {
        return sometimes_return(instance, request->reply, NULL);
 }
 
 #ifdef WITH_PROXY
-static rlm_rcode_t mod_pre_proxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request)
 {
        if (!request->proxy) return RLM_MODULE_NOOP;
 
        return sometimes_return(instance, request->proxy, request->proxy_reply);
 }
 
-static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_proxy(void *instance, REQUEST *request)
 {
        if (!request->proxy_reply) return RLM_MODULE_NOOP;
 
@@ -172,28 +171,28 @@ static rlm_rcode_t mod_post_proxy(void *instance, REQUEST *request)
 module_t rlm_sometimes = {
        RLM_MODULE_INIT,
        "sometimes",
-       RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,         /* type */
+       RLM_TYPE_HUP_SAFE,      /* type */
        sizeof(rlm_sometimes_t),
        module_config,
        mod_instantiate,                /* instantiation */
        NULL,                           /* detach */
        {
-               sometimes_packet,       /* authentication */
-               sometimes_packet,       /* authorization */
-               sometimes_packet,       /* preaccounting */
-               sometimes_packet,       /* accounting */
+               mod_sometimes_packet,   /* authentication */
+               mod_sometimes_packet,   /* authorization */
+               mod_sometimes_packet,   /* preaccounting */
+               mod_sometimes_packet,   /* accounting */
                NULL,
 #ifdef WITH_PROXY
-               mod_pre_proxy,  /* pre-proxy */
-               mod_post_proxy, /* post-proxy */
+               mod_pre_proxy,          /* pre-proxy */
+               mod_post_proxy,         /* post-proxy */
 #else
                NULL, NULL,
 #endif
-               sometimes_reply         /* post-auth */
+               mod_sometimes_reply     /* post-auth */
 #ifdef WITH_COA
                ,
-               sometimes_packet,       /* recv-coa */
-               sometimes_reply         /* send-coa */
+               mod_sometimes_packet,   /* recv-coa */
+               mod_sometimes_reply     /* send-coa */
 #endif
        },
 };
index a1c27bd..c7ba7f2 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,10 +1254,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1834,7 +1834,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2056,7 +2056,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2684,11 +2684,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3194,11 +3194,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 27e10b3..24fe9cc 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-ibmdb2-include-dir=DIR
-                          Directory where the IBM-DB2 includes may be found
+                         Directory where the IBM-DB2 includes may be found
   --with-ibmdb2-lib-dir=DIR
-                          Directory where the IBM-DB2 libraries may be found
+                         Directory where the IBM-DB2 libraries may be found
   --with-ibmdb2-dir=DIR   Base directory where IBM-DB2 is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1942,7 +1942,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2164,7 +2164,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2693,8 +2693,8 @@ SQLConnect()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-ldb2"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-ldb2"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2710,22 +2710,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libdb2${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2737,22 +2737,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libdb2.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2884,22 +2884,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=sqlcli.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3052,11 +3052,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3562,11 +3562,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 7782488..01fd6ef 100644 (file)
@@ -265,7 +265,7 @@ static char const *sql_error(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
 
        TALLOC_FREE(conn->error);
        SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, (SQLCHAR *) sqlstate, &err, (SQLCHAR *) msg, sizeof(msg), &rl);
-       conn->error = talloc_asprintf(conn, "sqlstate %s: %s", sqlstate, msg);
+       conn->error = talloc_typed_asprintf(conn, "sqlstate %s: %s", sqlstate, msg);
 
        return conn->error;
 }
index 3a67d3a..4ac0616 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-firebird-include-dir=DIR
-                          Directory where the firebird includes may be found
+                         Directory where the firebird includes may be found
   --with-firebird-lib-dir=DIR
-                          Directory where the firebird libraries may be found
+                         Directory where the firebird libraries may be found
   --with-firebird-dir=DIR Base directory where firebird is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1941,7 +1941,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2163,7 +2163,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2692,8 +2692,8 @@ isc_attach_database()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lfbclient"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lfbclient"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2709,22 +2709,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libfbclient${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2736,22 +2736,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libfbclient.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2883,22 +2883,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=ibase.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3051,11 +3051,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3561,11 +3561,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index a2207c9..af02727 100644 (file)
@@ -44,7 +44,7 @@ static int sql_socket_destructor(void *c)
                isc_detach_database(conn->status, &(conn->dbh));
 
                if (fb_error(conn)) {
-                       WDEBUG("rlm_sql_firebird: Got error "
+                       WARN("rlm_sql_firebird: Got error "
                               "when closing socket: %s", conn->error);
                }
        }
@@ -110,29 +110,29 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
         *      Try again query when deadlock, beacuse in any case it
         *      will be retried.
         */
-       if (fb_sql_query(conn, query)) {
+       if (fb_sql_query(conn, query)) {
                /* but may be lost for short sessions */
-               if ((conn->sql_code == DEADLOCK_SQL_CODE) &&
-                   !deadlock) {
-                       DEBUG("conn_id deadlock. Retry query %s", query);
+               if ((conn->sql_code == DEADLOCK_SQL_CODE) &&
+                   !deadlock) {
+                       DEBUG("conn_id deadlock. Retry query %s", query);
 
                        /*
                         *      @todo For non READ_COMMITED transactions put
                         *      rollback here
                         *      fb_rollback(conn);
                         */
-                       deadlock = 1;
-                       goto try_again;
-               }
+                       deadlock = 1;
+                       goto try_again;
+               }
 
                ERROR("conn_id rlm_sql_firebird,sql_query error: sql_code=%li, error='%s', query=%s",
                      (long int) conn->sql_code, conn->error, query);
 
                if (conn->sql_code == DOWN_SQL_CODE) {
+               reconnect:
 #ifdef _PTHREAD_H
-                       pthread_mutex_lock(&conn->mut);
+                       pthread_mutex_unlock(&conn->mut);
 #endif
-
                        return RLM_SQL_RECONNECT;
                }
 
@@ -141,18 +141,23 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
                        //assume the network is down if rollback had failed
                        ERROR("Fail to rollback transaction after previous error: %s", conn->error);
 
-                       return RLM_SQL_RECONNECT;
+                       goto reconnect;
                }
                //   conn->in_use=0;
+       fail:
+#ifdef _PTHREAD_H
+               pthread_mutex_unlock(&conn->mut);
+#endif
                return -1;
-       }
+       }
 
        if (conn->statement_type != isc_info_sql_stmt_select) {
-               if (fb_commit(conn)) {
-                       return -1;
-               }
+               if (fb_commit(conn)) goto fail;
        }
 
+#ifdef _PTHREAD_H
+       pthread_mutex_unlock(&conn->mut);
+#endif
        return 0;
 }
 
@@ -200,13 +205,13 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config
                res = fb_fetch(conn);
                if (res == 100) {
                        return 0;
-               }
+               }
 
-               if (res) {
-                       ERROR("rlm_sql_firebird. Fetch problem: %s", conn->error);
+               if (res) {
+                       ERROR("rlm_sql_firebird. Fetch problem: %s", conn->error);
 
-                       return -1;
-               }
+                       return -1;
+               }
        } else {
                conn->statement_type=0;
        }
index 0f0a5b7..51641a7 100644 (file)
@@ -113,7 +113,7 @@ int fb_error(rlm_sql_firebird_conn_t *conn)
                 *      API.
                 */
                isc_interprete(&error[0], &pstatus);
-               conn->error = talloc_asprintf(conn, "%s. ", &error[0]);
+               conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]);
 
                while (isc_interprete(&error[0], &pstatus)) {
                        conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]);
@@ -204,7 +204,7 @@ void fb_store_row(rlm_sql_firebird_conn_t *conn)
                                conn->row_sizes[i] = vary->vary_length + 1;
                                conn->row[i] = realloc(conn->row[i],
                                                       conn->row_sizes[i]);
-                       }
+                       }
                        memmove(conn->row[i], vary->vary_string, vary->vary_length);
                        conn->row[i][vary->vary_length] = 0;
 
index d187360..b794e23 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-freetds-include-dir=DIR
-                          Directory where the freetds includes may be found
+                         Directory where the freetds includes may be found
   --with-freetds-lib-dir=DIR
-                          Directory where the freetds libraries may be found
+                         Directory where the freetds libraries may be found
   --with-freetds-dir=DIR  Base directory where freetds is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1793,7 +1793,7 @@ SMART_CLFAGS=
 if test x$with_rlm_sql_freetds != xno; then
 
 
-        freetds_include_dir=
+       freetds_include_dir=
 
 # Check whether --with-freetds-include-dir was given.
 if test "${with_freetds_include_dir+set}" = set; then :
@@ -1810,7 +1810,7 @@ if test "${with_freetds_include_dir+set}" = set; then :
 fi
 
 
-        freetds_lib_dir=
+       freetds_lib_dir=
 
 # Check whether --with-freetds-lib-dir was given.
 if test "${with_freetds_lib_dir+set}" = set; then :
@@ -1944,7 +1944,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2166,7 +2166,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2715,22 +2715,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=ctpublic.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2787,7 +2787,7 @@ $as_echo "$as_me: WARNING: freetds headers not found. Use --with-freetds-include
     fi
 
 
-        smart_try_dir="$freetds_lib_dir"
+       smart_try_dir="$freetds_lib_dir"
 
 
 sm_lib_safe=`echo "ct" | sed 'y%./+-%__p_%'`
@@ -2847,8 +2847,8 @@ ct_command()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lct"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lct"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2864,22 +2864,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libct${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2891,22 +2891,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libct.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3060,11 +3060,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3570,11 +3570,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 6ffc216..e64b4b3 100644 (file)
@@ -76,7 +76,7 @@ static CS_RETCODE CS_PUBLIC clientmsg_callback(CS_CONTEXT *context, UNUSED CS_CO
 
        if (this->error) TALLOC_FREE(this->error);
 
-       this->error = talloc_asprintf(this, "client error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
+       this->error = talloc_typed_asprintf(this, "client error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
                                      (long)CS_SEVERITY(emsgp->severity), (long)CS_NUMBER(emsgp->msgnumber),
                                      (long)CS_ORIGIN(emsgp->msgnumber), (long)CS_LAYER(emsgp->msgnumber),
                                      emsgp->msgstring);
@@ -120,7 +120,7 @@ static CS_RETCODE CS_PUBLIC csmsg_callback(CS_CONTEXT *context, CS_CLIENTMSG *em
 
        if (this->error) TALLOC_FREE(this->error);
 
-       this->error = talloc_asprintf(this, "cs error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
+       this->error = talloc_typed_asprintf(this, "cs error: severity(%ld), number(%ld), origin(%ld), layer(%ld): %s",
                                      (long)CS_SEVERITY(emsgp->severity), (long)CS_NUMBER(emsgp->msgnumber),
                                      (long)CS_ORIGIN(emsgp->msgnumber), (long)CS_LAYER(emsgp->msgnumber),
                                      emsgp->msgstring);
@@ -169,12 +169,12 @@ static CS_RETCODE CS_PUBLIC servermsg_callback(UNUSED CS_CONTEXT *context, UNUSE
        } else {
                if (this->error) TALLOC_FREE(this->error);
 
-               this->error = talloc_asprintf(this, "server msg from \"%s\": severity(%ld), number(%ld), origin(%ld), "
-                                             "layer(%ld), procedure \"%s\": %s",
-                                             (msgp->svrnlen > 0) ? msgp->svrname : "unknown",
-                                             (long)msgp->msgnumber, (long)msgp->severity, (long)msgp->state,
-                                             (long)msgp->line,
-                                             (msgp->proclen > 0) ? msgp->proc : "none", msgp->text);
+               this->error = talloc_typed_asprintf(this, "server msg from \"%s\": severity(%ld), number(%ld), origin(%ld), "
+                                             "layer(%ld), procedure \"%s\": %s",
+                                             (msgp->svrnlen > 0) ? msgp->svrname : "unknown",
+                                             (long)msgp->msgnumber, (long)msgp->severity, (long)msgp->state,
+                                             (long)msgp->line,
+                                             (msgp->proclen > 0) ? msgp->proc : "none", msgp->text);
        }
 
        return CS_SUCCEED;
@@ -288,7 +288,7 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
        case CS_FAIL: /* Serious failure, freetds requires us to cancel and maybe even close db */
                ERROR("rlm_sql_freetds: failure retrieving query results");
                if ((ret = ct_cancel(NULL, conn->command, CS_CANCEL_ALL)) == CS_FAIL) {
-                       INFO("rlm_sql_freetds: cleaning up.");
+                       INFO("rlm_sql_freetds: cleaning up");
 
                        return RLM_SQL_RECONNECT;
                }
@@ -490,7 +490,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
                ERROR("rlm_sql_freetds: failure retrieving query results");
 
                if ((ret = ct_cancel(NULL, conn->command, CS_CANCEL_ALL)) == CS_FAIL) {
-                       ERROR("rlm_sql_freetds: cleaning up.");
+                       ERROR("rlm_sql_freetds: cleaning up");
 
                        return RLM_SQL_RECONNECT;
                }
@@ -571,7 +571,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, UNUSED rlm_sql_config
                 */
                ERROR("rlm_sql_freetds: failure fetching row data");
                if ((ret = ct_cancel(NULL, conn->command, CS_CANCEL_ALL)) == CS_FAIL) {
-                       ERROR("rlm_sql_freetds: cleaning up.");
+                       ERROR("rlm_sql_freetds: cleaning up");
                } else {
                        conn->command = NULL;
                }
index 2a28f6f..60a7bd1 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-iodbc-include-dir=DIR
-                          Directory where the Iodbc includes may be found
+                         Directory where the Iodbc includes may be found
   --with-iodbc-lib-dir=DIR
-                          Directory where the Iodbc libraries may be found
+                         Directory where the Iodbc libraries may be found
   --with-iodbc-dir=DIR    Base directory where Iodbc is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1942,7 +1942,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2164,7 +2164,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2693,8 +2693,8 @@ SQLConnect()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-liodbc"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-liodbc"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2710,22 +2710,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libiodbc${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2737,22 +2737,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libiodbc.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2884,22 +2884,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=isql.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3052,11 +3052,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3562,11 +3562,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 42e8ce2..e7295c2 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1212,9 +1212,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1257,9 +1257,9 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-mysql-include-dir=DIR
-                          Directory where the mysql includes may be found
+                         Directory where the mysql includes may be found
   --with-mysql-lib-dir=DIR
-                          Directory where the mysql libraries may be found
+                         Directory where the mysql libraries may be found
   --with-mysql-dir=DIR    Base directory where mysql is installed
   --with-threads          use threads, if available. (default=yes)
 
@@ -1267,10 +1267,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1798,7 +1798,7 @@ SMART_CLFAGS=
 if test x$with_rlm_sql_mysql != xno; then
 
 
-        mysql_include_dir=
+       mysql_include_dir=
 
 # Check whether --with-mysql-include-dir was given.
 if test "${with_mysql_include_dir+set}" = set; then :
@@ -1815,7 +1815,7 @@ if test "${with_mysql_include_dir+set}" = set; then :
 fi
 
 
-        mysql_lib_dir=
+       mysql_lib_dir=
 
 # Check whether --with-mysql-lib-dir was given.
 if test "${with_mysql_lib_dir+set}" = set; then :
@@ -1849,7 +1849,7 @@ if test "${with_mysql_dir+set}" = set; then :
 fi
 
 
-        mysql_with_threads=yes
+       mysql_with_threads=yes
 
 # Check whether --with-threads was given.
 if test "${with_threads+set}" = set; then :
@@ -1962,7 +1962,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2184,7 +2184,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2693,7 +2693,7 @@ fi
 
 
 
-            if test "x$mysql_with_threads" = "xyes"; then
+           if test "x$mysql_with_threads" = "xyes"; then
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
 $as_echo_n "checking for pthread_create in -lpthread... " >&6; }
@@ -2825,7 +2825,7 @@ $as_echo "no" >&6; }
            fi
        fi
        if test "x$have_libmysqlclient_r" != "xyes"; then
-                   smart_try_dir="$mysql_lib_dir /usr/lib /usr/lib/mysql \
+                   smart_try_dir="$mysql_lib_dir /usr/lib /usr/lib/mysql \
                /usr/local/lib/mysql /usr/local/mysql/lib/mysql"
 
 
@@ -2886,8 +2886,8 @@ mysql_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lmysqlclient_r"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lmysqlclient_r"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2903,22 +2903,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libmysqlclient_r${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2930,22 +2930,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libmysqlclient_r.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3043,7 +3043,7 @@ $as_echo "no" >&6; }
            fi
        fi
        if test "x$have_libmysqlclient" != "xyes"; then
-                   smart_try_dir="$mysql_lib_dir /usr/lib /usr/lib/mysql \
+                   smart_try_dir="$mysql_lib_dir /usr/lib /usr/lib/mysql \
                /usr/local/lib/mysql /usr/local/mysql/lib/mysql"
 
 
@@ -3104,8 +3104,8 @@ mysql_init()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lmysqlclient"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lmysqlclient"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3121,22 +3121,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libmysqlclient${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3148,22 +3148,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libmysqlclient.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3273,17 +3273,17 @@ if ac_fn_c_try_compile "$LINENO"; then :
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
            if test "x$have_mysql_h" = "xyes"; then
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 $as_echo "#define HAVE_MYSQL_H /**/" >>confdefs.h
 
-               SMART_CFLAGS="$SMART_CFLAGS $mod_cflags"
+               SMART_CFLAGS="$SMART_CFLAGS $mod_cflags"
            else
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
            fi
-        fi
+       fi
 
        CFLAGS="$old_CFLAGS"
     fi
@@ -3369,22 +3369,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=mysql/mysql.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3439,7 +3439,7 @@ fi
 $as_echo "#define HAVE_MYSQL_MYSQL_H /**/" >>confdefs.h
 
        else
-                   { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: MySQL headers not found. Use --with-mysql-include-dir=<path>." >&5
+                   { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: MySQL headers not found. Use --with-mysql-include-dir=<path>." >&5
 $as_echo "$as_me: WARNING: MySQL headers not found. Use --with-mysql-include-dir=<path>." >&2;}
            fail="$fail mysql.h"
        fi
@@ -3544,11 +3544,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -4022,13 +4022,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 25364d2..e51b2b7 100644 (file)
@@ -181,15 +181,15 @@ if test x$with_[]modname != xno; then
            CFLAGS="$old_CFLAGS $mod_cflags"
            AC_MSG_CHECKING([for mysql.h (using mysql_config --include)])
            AC_TRY_COMPILE([#include <mysql.h>], [int a = 1;],
-                          have_mysql_h=yes)
+                          have_mysql_h=yes)
            if test "x$have_mysql_h" = "xyes"; then
-               AC_MSG_RESULT(yes)
-               AC_DEFINE(HAVE_MYSQL_H, [], [Define if you have <mysql.h>])
-               SMART_CFLAGS="$SMART_CFLAGS $mod_cflags"
+               AC_MSG_RESULT(yes)
+               AC_DEFINE(HAVE_MYSQL_H, [], [Define if you have <mysql.h>])
+               SMART_CFLAGS="$SMART_CFLAGS $mod_cflags"
            else
-               AC_MSG_RESULT(no)
+               AC_MSG_RESULT(no)
            fi
-        fi
+       fi
 
        CFLAGS="$old_CFLAGS"
     fi
index 2a76008..a6d4f76 100644 (file)
@@ -31,50 +31,50 @@ RCSID("$Id$")
 #include "config.h"
 
 #ifdef HAVE_MYSQL_MYSQL_H
-#include <mysql/mysql_version.h>
-#include <mysql/errmsg.h>
-#include <mysql/mysql.h>
-#else
-#ifdef HAVE_MYSQL_H
-#include <mysql_version.h>
-#include <errmsg.h>
-#include <mysql.h>
-#endif
+#  include <mysql/mysql_version.h>
+#  include <mysql/errmsg.h>
+#  include <mysql/mysql.h>
+#elif defined(HAVE_MYSQL_H)
+#  include <mysql_version.h>
+#  include <errmsg.h>
+#  include <mysql.h>
 #endif
 
 #include "rlm_sql.h"
 
+static int mysql_instance_count = 0;
+
 typedef struct rlm_sql_mysql_conn {
-       MYSQL db;
-       MYSQL *sock;
-       MYSQL_RES *result;
-       rlm_sql_row_t row;
+       MYSQL           db;
+       MYSQL           *sock;
+       MYSQL_RES       *result;
+       rlm_sql_row_t   row;
 } rlm_sql_mysql_conn_t;
 
 typedef struct rlm_sql_mysql_config {
-       char const *tls_ca_file;
-       char const *tls_ca_path;
-       char const *tls_certificate_file;
-       char const *tls_private_key_file;
-       char const *tls_cipher;
+       char const      *tls_ca_file;
+       char const      *tls_ca_path;
+       char const      *tls_certificate_file;
+       char const      *tls_private_key_file;
+       char const      *tls_cipher;
 } rlm_sql_mysql_config_t;
 
 static CONF_PARSER tls_config[] = {
-       {"ca_file", PW_TYPE_FILE_INPUT, offsetof(rlm_sql_mysql_config_t, tls_ca_file), NULL, NULL},
-       {"ca_path", PW_TYPE_FILE_INPUT, offsetof(rlm_sql_mysql_config_t, tls_ca_path), NULL, NULL},
-       {"certificate_file", PW_TYPE_FILE_INPUT, offsetof(rlm_sql_mysql_config_t, tls_certificate_file), NULL, NULL},
-       {"private_key_file", PW_TYPE_FILE_INPUT, offsetof(rlm_sql_mysql_config_t, tls_private_key_file), NULL, NULL},
+       { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_ca_file), NULL },
+       { "ca_path", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_ca_path), NULL },
+       { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_certificate_file), NULL },
+       { "private_key_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_mysql_config_t, tls_private_key_file), NULL },
 
        /*
         *      MySQL Specific TLS attributes
         */
-       {"cipher", PW_TYPE_STRING_PTR, offsetof(rlm_sql_mysql_config_t, tls_cipher), NULL, NULL},
+       { "cipher", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_mysql_config_t, tls_cipher), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
 
 static const CONF_PARSER driver_config[] = {
-       { "tls", PW_TYPE_SUBSECTION, 0, NULL, (void const *) tls_config },
+       { "tls", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) tls_config },
 
        {NULL, -1, 0, NULL, NULL}
 };
@@ -95,11 +95,32 @@ static int sql_socket_destructor(void *c)
        return 0;
 }
 
+static int _mod_destructor(UNUSED rlm_sql_mysql_config_t *driver)
+{
+       mysql_instance_count--;
+
+       if (mysql_instance_count == 0) {
+                mysql_library_end();
+       }
+
+       return 0;
+}
+
 static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
 {
        rlm_sql_mysql_config_t *driver;
 
+       if (mysql_instance_count == 0) {
+               if (mysql_library_init(0, NULL, NULL)) {
+                       ERROR("Could not initialise MySQL library");
+
+                       return -1;
+               }
+       }
+       mysql_instance_count++;
+
        MEM(driver = config->driver = talloc_zero(config, rlm_sql_mysql_config_t));
+       talloc_set_destructor(driver, _mod_destructor);
 
        if (cf_section_parse(conf, driver, driver_config) < 0) {
                return -1;
@@ -153,12 +174,9 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                 */
                if (timeout > 3) timeout /= 3;
 
-               mysql_options(&(conn->db), MYSQL_OPT_CONNECT_TIMEOUT,
-                             &timeout);
-               mysql_options(&(conn->db), MYSQL_OPT_READ_TIMEOUT,
-                             &timeout);
-               mysql_options(&(conn->db), MYSQL_OPT_WRITE_TIMEOUT,
-                             &timeout);
+               mysql_options(&(conn->db), MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
+               mysql_options(&(conn->db), MYSQL_OPT_READ_TIMEOUT, &timeout);
+               mysql_options(&(conn->db), MYSQL_OPT_WRITE_TIMEOUT, &timeout);
        }
 #endif
 
@@ -180,19 +198,15 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
                                        NULL,
                                        sql_flags);
        if (!conn->sock) {
-               ERROR("rlm_sql_mysql: Couldn't connect socket to MySQL "
-                      "server %s@%s:%s", config->sql_login, config->sql_server,
-                      config->sql_db);
-               ERROR("rlm_sql_mysql: Mysql error '%s'",
-                      mysql_error(&conn->db));
+               ERROR("rlm_sql_mysql: Couldn't connect socket to MySQL server %s@%s:%s", config->sql_login,
+                     config->sql_server, config->sql_db);
+               ERROR("rlm_sql_mysql: Mysql error '%s'", mysql_error(&conn->db));
 
                conn->sock = NULL;
-
-               return -1;
+               return RLM_SQL_ERROR;
        }
 
-
-       return 0;
+       return RLM_SQL_OK;
 }
 
 /*************************************************************************
@@ -202,25 +216,24 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
  *     Purpose: check the error to see if the server is down
  *
  *************************************************************************/
-static int sql_check_error(int error)
+static sql_rcode_t sql_check_error(int error)
 {
        switch(error) {
+       case 0:
+               return RLM_SQL_OK;
+
        case CR_SERVER_GONE_ERROR:
        case CR_SERVER_LOST:
        case -1:
                DEBUG("rlm_sql_mysql: MYSQL check_error: %d, returning RLM_SQL_RECONNECT", error);
                return RLM_SQL_RECONNECT;
-               break;
-       case 0:
-               return 0;
-               break;
+
        case CR_OUT_OF_MEMORY:
        case CR_COMMANDS_OUT_OF_SYNC:
        case CR_UNKNOWN_ERROR:
        default:
                DEBUG("rlm_sql_mysql: MYSQL check_error: %d received", error);
-               return -1;
-               break;
+               return RLM_SQL_ERROR;
        }
 }
 
@@ -235,6 +248,8 @@ static int sql_check_error(int error)
 static sql_rcode_t sql_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config, char const *query)
 {
        rlm_sql_mysql_conn_t *conn = handle->conn;
+       sql_rcode_t rcode;
+       char const *info;
 
        if (!conn->sock) {
                ERROR("rlm_sql_mysql: Socket not connected");
@@ -242,7 +257,16 @@ static sql_rcode_t sql_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t
        }
 
        mysql_query(conn->sock, query);
-       return sql_check_error(mysql_errno(conn->sock));
+       rcode = sql_check_error(mysql_errno(conn->sock));
+       if (rcode != RLM_SQL_OK) {
+               return rcode;
+       }
+
+       /* Only returns non-null string for INSERTS */
+       info = mysql_info(conn->sock);
+       if (info) DEBUG2("rlm_sql_mysql: %s", info);
+
+       return RLM_SQL_OK;
 }
 
 
@@ -258,35 +282,37 @@ static sql_rcode_t sql_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t
 static sql_rcode_t sql_store_result(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
 {
        rlm_sql_mysql_conn_t *conn = handle->conn;
-       int status;
+       sql_rcode_t rcode;
+       int ret;
 
        if (!conn->sock) {
                ERROR("rlm_sql_mysql: Socket not connected");
                return RLM_SQL_RECONNECT;
        }
+
 retry_store_result:
        if (!(conn->result = mysql_store_result(conn->sock))) {
-               status = sql_check_error(mysql_errno(conn->sock));
-               if (status != 0) {
+               rcode = sql_check_error(mysql_errno(conn->sock));
+               if (rcode != RLM_SQL_OK) {
                        ERROR("rlm_sql_mysql: Cannot store result");
-                       ERROR("rlm_sql_mysql: MySQL error '%s'",
-                              mysql_error(conn->sock));
-                       return status;
+                       ERROR("rlm_sql_mysql: MySQL error '%s'", mysql_error(conn->sock));
+
+                       return rcode;
                }
 #if (MYSQL_VERSION_ID >= 40100)
-               status = mysql_next_result(conn->sock);
-               if (status == 0) {
+               ret = mysql_next_result(conn->sock);
+               if (ret == 0) {
                        /* there are more results */
                        goto retry_store_result;
-               } else if (status > 0) {
+               } else if (ret > 0) {
                        ERROR("rlm_sql_mysql: Cannot get next result");
-                       ERROR("rlm_sql_mysql: MySQL error '%s'",
-                              mysql_error(conn->sock));
-                       return sql_check_error(status);
+                       ERROR("rlm_sql_mysql: MySQL error '%s'", mysql_error(conn->sock));
+
+                       return sql_check_error(ret);
                }
 #endif
        }
-       return 0;
+       return RLM_SQL_OK;
 }
 
 
@@ -298,9 +324,9 @@ retry_store_result:
  *            of columns from query
  *
  *************************************************************************/
-static int sql_num_fields(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
+static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config)
 {
-       int     num = 0;
+       int num = 0;
        rlm_sql_mysql_conn_t *conn = handle->conn;
 
 #if MYSQL_VERSION_ID >= 32224
@@ -309,8 +335,7 @@ static int sql_num_fields(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *co
        if (!(num = mysql_num_fields(conn->sock))) {
 #endif
                ERROR("rlm_sql_mysql: MYSQL Error: No Fields");
-               ERROR("rlm_sql_mysql: MYSQL error: %s",
-                      mysql_error(conn->sock));
+               ERROR("rlm_sql_mysql: MYSQL error: %s", mysql_error(conn->sock));
        }
        return num;
 }
@@ -325,14 +350,16 @@ static int sql_num_fields(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *co
  *************************************************************************/
 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config, char const *query)
 {
-       int ret;
+       sql_rcode_t rcode;
 
-       ret = sql_query(handle, config, query);
-       if(ret)
-               return ret;
-       ret = sql_store_result(handle, config);
-       if (ret) {
-               return ret;
+       rcode = sql_query(handle, config, query);
+       if (rcode != RLM_SQL_OK) {
+               return rcode;
+       }
+
+       rcode = sql_store_result(handle, config);
+       if (rcode != RLM_SQL_OK) {
+               return rcode;
        }
 
        /* Why? Per http://www.mysql.com/doc/n/o/node_591.html,
@@ -341,7 +368,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t *
         */
        sql_num_fields(handle, config);
 
-       return ret;
+       return rcode;
 }
 
 
@@ -357,8 +384,9 @@ static int sql_num_rows(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *conf
 {
        rlm_sql_mysql_conn_t *conn = handle->conn;
 
-       if (conn->result)
+       if (conn->result) {
                return mysql_num_rows(conn->result);
+       }
 
        return 0;
 }
@@ -376,7 +404,8 @@ static int sql_num_rows(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *conf
 static sql_rcode_t sql_fetch_row(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config)
 {
        rlm_sql_mysql_conn_t *conn = handle->conn;
-       int status;
+       sql_rcode_t rcode;
+       int ret;
 
        /*
         *  Check pointer before de-referencing it.
@@ -387,32 +416,33 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t * handle, UNUSED rlm_sql_confi
 
 retry_fetch_row:
        handle->row = mysql_fetch_row(conn->result);
-
        if (!handle->row) {
-               status = sql_check_error(mysql_errno(conn->sock));
-               if (status != 0) {
+               rcode = sql_check_error(mysql_errno(conn->sock));
+               if (rcode != RLM_SQL_OK) {
                        ERROR("rlm_sql_mysql: Cannot fetch row");
-                       ERROR("rlm_sql_mysql: MySQL error '%s'",
-                              mysql_error(conn->sock));
-                       return status;
+                       ERROR("rlm_sql_mysql: MySQL error '%s'", mysql_error(conn->sock));
+
+                       return rcode;
                }
+
 #if (MYSQL_VERSION_ID >= 40100)
                sql_free_result(handle, config);
-               status = mysql_next_result(conn->sock);
-               if (status == 0) {
+
+               ret = mysql_next_result(conn->sock);
+               if (ret == 0) {
                        /* there are more results */
-                       if ((sql_store_result(handle, config) == 0)
-                        && (conn->result != NULL))
+                       if ((sql_store_result(handle, config) == 0) && (conn->result != NULL)) {
                                goto retry_fetch_row;
-               } else if (status > 0) {
+                       }
+               } else if (ret > 0) {
                        ERROR("rlm_sql_mysql: Cannot get next result");
-                       ERROR("rlm_sql_mysql: MySQL error '%s'",
-                              mysql_error(conn->sock));
-                       return sql_check_error(status);
+                       ERROR("rlm_sql_mysql: MySQL error '%s'", mysql_error(conn->sock));
+
+                       return sql_check_error(ret);
                }
 #endif
        }
-       return 0;
+       return RLM_SQL_OK;
 }
 
 
@@ -453,6 +483,7 @@ static char const *sql_error(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t
        if (!conn || !conn->sock) {
                return "rlm_sql_mysql: no connection to db";
        }
+
        return mysql_error(conn->sock);
 }
 
@@ -469,28 +500,30 @@ static sql_rcode_t sql_finish_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_co
 {
 #if (MYSQL_VERSION_ID >= 40100)
        rlm_sql_mysql_conn_t *conn = handle->conn;
-       int status;
+       sql_rcode_t rcode;
+       int ret;
 
 skip_next_result:
-       status = sql_store_result(handle, config);
-       if (status != 0) {
-               return status;
+       rcode = sql_store_result(handle, config);
+       if (rcode != RLM_SQL_OK) {
+               return rcode;
        } else if (conn->result != NULL) {
                DEBUG("rlm_sql_mysql: SQL statement returned unexpected result");
                sql_free_result(handle, config);
        }
-       status = mysql_next_result(conn->sock);
-       if (status == 0) {
+
+       ret = mysql_next_result(conn->sock);
+       if (ret == 0) {
                /* there are more results */
                goto skip_next_result;
-       }  else if (status > 0) {
+       }  else if (ret > 0) {
                ERROR("rlm_sql_mysql: Cannot get next result");
-               ERROR("rlm_sql_mysql: MySQL error '%s'",
-                      mysql_error(conn->sock));
-               return sql_check_error(status);
+               ERROR("rlm_sql_mysql: MySQL error '%s'", mysql_error(conn->sock));
+
+               return sql_check_error(ret);
        }
 #endif
-       return 0;
+       return RLM_SQL_OK;
 }
 
 
@@ -505,23 +538,23 @@ skip_next_result:
 static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config)
 {
 #if (MYSQL_VERSION_ID >= 40100)
-       int status;
+       int ret;
        rlm_sql_mysql_conn_t *conn = handle->conn;
 #endif
        sql_free_result(handle, config);
 #if (MYSQL_VERSION_ID >= 40100)
-       status = mysql_next_result(conn->sock);
-       if (status == 0) {
+       ret = mysql_next_result(conn->sock);
+       if (ret == 0) {
                /* there are more results */
                sql_finish_query(handle, config);
-       }  else if (status > 0) {
+       }  else if (ret > 0) {
                ERROR("rlm_sql_mysql: Cannot get next result");
-               ERROR("rlm_sql_mysql: MySQL error '%s'",
-                      mysql_error(conn->sock));
-               return sql_check_error(status);
+               ERROR("rlm_sql_mysql: MySQL error '%s'",  mysql_error(conn->sock));
+
+               return sql_check_error(ret);
        }
 #endif
-       return 0;
+       return RLM_SQL_OK;
 }
 
 
index 21452e7..ea1456d 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-oracle-include-dir=DIR
-                          Directory where the oracle includes may be found
+                         Directory where the oracle includes may be found
   --with-oracle-lib-dir=DIR
-                          Directory where the oracle libraries may be found
+                         Directory where the oracle libraries may be found
   --with-oracle-dir=DIR   Base directory where oracle is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1793,37 +1793,37 @@ mod_cflags=
 if test x$with_rlm_sql_oracle != xno; then
 
 
-        oracle_include_dir=
+       oracle_include_dir=
 
 # Check whether --with-oracle-include-dir was given.
 if test "${with_oracle_include_dir+set}" = set; then :
   withval=$with_oracle_include_dir; case "$withval" in
-            no)
-            as_fn_error $? "Need oracle-include-dir" "$LINENO" 5
-            ;;
-            yes)
-            ;;
-            *)
-            oracle_include_dir="$withval"
-            ;;
-        esac
+           no)
+           as_fn_error $? "Need oracle-include-dir" "$LINENO" 5
+           ;;
+           yes)
+           ;;
+           *)
+           oracle_include_dir="$withval"
+           ;;
+       esac
 fi
 
 
-        oracle_lib_dir=
+       oracle_lib_dir=
 
 # Check whether --with-oracle-lib-dir was given.
 if test "${with_oracle_lib_dir+set}" = set; then :
   withval=$with_oracle_lib_dir; case "$withval" in
-            no)
-            as_fn_error $? "Need oracle-lib-dir" "$LINENO" 5
-            ;;
-            yes)
-            ;;
-            *)
-            oracle_lib_dir="$withval"
-            ;;
-        esac
+           no)
+           as_fn_error $? "Need oracle-lib-dir" "$LINENO" 5
+           ;;
+           yes)
+           ;;
+           *)
+           oracle_lib_dir="$withval"
+           ;;
+       esac
 fi
 
 
@@ -1831,16 +1831,16 @@ fi
 # Check whether --with-oracle-dir was given.
 if test "${with_oracle_dir+set}" = set; then :
   withval=$with_oracle_dir; case "$withval" in
-            no)
-            as_fn_error $? "Need oracle-dir" "$LINENO" 5
-            ;;
-            yes)
-            ;;
-            *)
-            oracle_lib_dir="$withval/lib"
-            oracle_include_dir="$withval/include"
-            ;;
-        esac
+           no)
+           as_fn_error $? "Need oracle-dir" "$LINENO" 5
+           ;;
+           yes)
+           ;;
+           *)
+           oracle_lib_dir="$withval/lib"
+           oracle_include_dir="$withval/include"
+           ;;
+       esac
 fi
 
 
@@ -1848,7 +1848,7 @@ fi
     smart_try_dir="$oracle_include_dir /usr/local/instaclient/include"
 
     if test "x$ORACLE_HOME" != "x"; then
-       smart_try_dir+="${ORACLE_HOME}/include"
+       smart_try_dir+="${ORACLE_HOME}/include"
     fi
 
     ac_ext=c
@@ -1949,7 +1949,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2171,7 +2171,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2720,22 +2720,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=oci.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2786,56 +2786,56 @@ if test "x$smart_include" != "x"; then
 fi
 
     if test "x$ac_cv_header_oci_h" != "xyes"; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: oracle headers not found. Use --with-oracle-include-dir=<path> or set ORACLE_HOME." >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: oracle headers not found. Use --with-oracle-include-dir=<path> or set ORACLE_HOME." >&5
 $as_echo "$as_me: WARNING: oracle headers not found. Use --with-oracle-include-dir=<path> or set ORACLE_HOME." >&2;}
-        fail="$fail oci.h"
+       fail="$fail oci.h"
     fi
 
 
     old_LIBS="$LIBS"
 
     if test "x$oracle_lib_dir" != "x" ; then
-        lib_path="${oracle_lib_dir} "
+       lib_path="${oracle_lib_dir} "
     elif test "x$ORACLE_HOME" != "x" ; then
-        lib_path="${ORACLE_HOME}/lib "
+       lib_path="${ORACLE_HOME}/lib "
     fi
 
     for path in $lib_path "/usr/local/instaclient/lib" "" "/opt/lib"; do
-        for oracle_version in 11 10 9 ""; do
-            if test "$path" != ""; then
-                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OCIInitialize in nnz${oracle_version} in $path" >&5
+       for oracle_version in 11 10 9 ""; do
+           if test "$path" != ""; then
+               { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OCIInitialize in nnz${oracle_version} in $path" >&5
 $as_echo_n "checking for OCIInitialize in nnz${oracle_version} in $path... " >&6; }
-            else
-                { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OCIInitialize in nnz${oracle_version}" >&5
+           else
+               { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OCIInitialize in nnz${oracle_version}" >&5
 $as_echo_n "checking for OCIInitialize in nnz${oracle_version}... " >&6; }
-            fi
+           fi
 
-            LIBS="$old_LIBS -L$path -lclntsh -lnnz${oracle_version}"
-            cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+           LIBS="$old_LIBS -L$path -lclntsh -lnnz${oracle_version}"
+           cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 #include <oci.h>
 
-                static OCIEnv           *p_env;
-                static OCIError         *p_err;
-                static OCISvcCtx        *p_svc;
-                static OCIStmt          *p_sql;
-                static OCIDefine        *p_dfn    = (OCIDefine *) 0;
-                static OCIBind          *p_bnd    = (OCIBind *) 0;
+               static OCIEnv           *p_env;
+               static OCIError         *p_err;
+               static OCISvcCtx        *p_svc;
+               static OCIStmt          *p_sql;
+               static OCIDefine        *p_dfn    = (OCIDefine *) 0;
+               static OCIBind          *p_bnd    = (OCIBind *) 0;
 
 int
 main ()
 {
 
-                int             p_bvi;
-                char            p_sli[20];
-                int             rc;
-                char            errbuf[100];
-                int             errcode;
+               int             p_bvi;
+               char            p_sli[20];
+               int             rc;
+               char            errbuf[100];
+               int             errcode;
 
-                rc = OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0,  /* Initialize OCI */
-                                   (dvoid * (*)(dvoid *, size_t)) 0,
-                                   (dvoid * (*)(dvoid *, dvoid *, size_t))0,
-                                   (void (*)(dvoid *, dvoid *)) 0 );
+               rc = OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0,  /* Initialize OCI */
+                                  (dvoid * (*)(dvoid *, size_t)) 0,
+                                  (dvoid * (*)(dvoid *, dvoid *, size_t))0,
+                                  (void (*)(dvoid *, dvoid *)) 0 );
 
 
   ;
@@ -2847,26 +2847,26 @@ if ac_fn_c_try_link "$LINENO"; then :
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
-            if test "x$mod_ldflags" != "x"; then
-                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+           if test "x$mod_ldflags" != "x"; then
+                   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-                    break
-            fi
-            { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+                   break
+           fi
+           { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-        done
+       done
 
-        if test "x$mod_ldflags" != "x"; then
-                break
-        fi
+       if test "x$mod_ldflags" != "x"; then
+               break
+       fi
     done
 
     LIBS="$old_LIBS"
 
     if test "x$mod_ldflags" = "x"; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: oracle libraries not found.  Use --with-oracle-lib-dir=<path> or set ORACLE_HOME." >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: oracle libraries not found.  Use --with-oracle-lib-dir=<path> or set ORACLE_HOME." >&5
 $as_echo "$as_me: WARNING: oracle libraries not found.  Use --with-oracle-lib-dir=<path> or set ORACLE_HOME." >&2;}
-        fail="$fail libclntsh libnnz[9-11]"
+       fail="$fail libclntsh libnnz[9-11]"
     fi
 
     targetname=rlm_sql_oracle
@@ -2967,11 +2967,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3477,11 +3477,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index d154e18..4063263 100644 (file)
@@ -16,48 +16,48 @@ if test x$with_[]modname != xno; then
     AC_ARG_WITH(oracle-include-dir,
        [AS_HELP_STRING([--with-oracle-include-dir=DIR],
                [Directory where the oracle includes may be found])],
-        [case "$withval" in
-            no)
-            AC_MSG_ERROR(Need oracle-include-dir)
-            ;;
-            yes)
-            ;;
-            *)
-            oracle_include_dir="$withval"
-            ;;
-        esac])
+       [case "$withval" in
+           no)
+           AC_MSG_ERROR(Need oracle-include-dir)
+           ;;
+           yes)
+           ;;
+           *)
+           oracle_include_dir="$withval"
+           ;;
+       esac])
 
     dnl extra argument: --with-oracle-lib-dir=DIR
     oracle_lib_dir=
     AC_ARG_WITH(oracle-lib-dir,
        [AS_HELP_STRING([--with-oracle-lib-dir=DIR],
                [Directory where the oracle libraries may be found])],
-        [case "$withval" in
-            no)
-            AC_MSG_ERROR(Need oracle-lib-dir)
-            ;;
-            yes)
-            ;;
-            *)
-            oracle_lib_dir="$withval"
-            ;;
-        esac])
+       [case "$withval" in
+           no)
+           AC_MSG_ERROR(Need oracle-lib-dir)
+           ;;
+           yes)
+           ;;
+           *)
+           oracle_lib_dir="$withval"
+           ;;
+       esac])
 
     dnl extra argument: --with-oracle-dir=DIR
     AC_ARG_WITH(oracle-dir,
        [AS_HELP_STRING([--with-oracle-dir=DIR],
                [Base directory where oracle is installed])],
-        [case "$withval" in
-            no)
-            AC_MSG_ERROR(Need oracle-dir)
-            ;;
-            yes)
-            ;;
-            *)
-            oracle_lib_dir="$withval/lib"
-            oracle_include_dir="$withval/include"
-            ;;
-        esac])
+       [case "$withval" in
+           no)
+           AC_MSG_ERROR(Need oracle-dir)
+           ;;
+           yes)
+           ;;
+           *)
+           oracle_lib_dir="$withval/lib"
+           oracle_include_dir="$withval/include"
+           ;;
+       esac])
 
     dnl ############################################################
     dnl # Check for header files
@@ -66,13 +66,13 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$oracle_include_dir /usr/local/instaclient/include"
     
     if test "x$ORACLE_HOME" != "x"; then
-       smart_try_dir+="${ORACLE_HOME}/include"
+       smart_try_dir+="${ORACLE_HOME}/include"
     fi
     
     FR_SMART_CHECK_INCLUDE(oci.h)
     if test "x$ac_cv_header_oci_h" != "xyes"; then
-        AC_MSG_WARN([oracle headers not found. Use --with-oracle-include-dir=<path> or set ORACLE_HOME.])
-        fail="$fail oci.h"
+       AC_MSG_WARN([oracle headers not found. Use --with-oracle-include-dir=<path> or set ORACLE_HOME.])
+       fail="$fail oci.h"
     fi
 
     dnl ############################################################
@@ -82,61 +82,61 @@ if test x$with_[]modname != xno; then
     old_LIBS="$LIBS"
     
     if test "x$oracle_lib_dir" != "x" ; then
-        lib_path="${oracle_lib_dir} "
+       lib_path="${oracle_lib_dir} "
     elif test "x$ORACLE_HOME" != "x" ; then
-        lib_path="${ORACLE_HOME}/lib "
+       lib_path="${ORACLE_HOME}/lib "
     fi
 
     for path in $lib_path "/usr/local/instaclient/lib" "" "/opt/lib"; do
-        for oracle_version in 11 10 9 ""; do
-            if test "$path" != ""; then
-                AC_MSG_CHECKING([for OCIInitialize in nnz${oracle_version} in $path])
-            else
-                AC_MSG_CHECKING([for OCIInitialize in nnz${oracle_version}])
-            fi
-            
-            LIBS="$old_LIBS -L$path -lclntsh -lnnz${oracle_version}"
-            AC_TRY_LINK([#include <oci.h>
+       for oracle_version in 11 10 9 ""; do
+           if test "$path" != ""; then
+               AC_MSG_CHECKING([for OCIInitialize in nnz${oracle_version} in $path])
+           else
+               AC_MSG_CHECKING([for OCIInitialize in nnz${oracle_version}])
+           fi
+           
+           LIBS="$old_LIBS -L$path -lclntsh -lnnz${oracle_version}"
+           AC_TRY_LINK([#include <oci.h>
 
-                static OCIEnv           *p_env;
-                static OCIError         *p_err;
-                static OCISvcCtx        *p_svc;
-                static OCIStmt          *p_sql;
-                static OCIDefine        *p_dfn    = (OCIDefine *) 0;
-                static OCIBind          *p_bnd    = (OCIBind *) 0;
-                ],
-                [
-                int             p_bvi;
-                char            p_sli[20];
-                int             rc;
-                char            errbuf[100];
-                int             errcode;
-            
-                rc = OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0,  /* Initialize OCI */
-                                   (dvoid * (*)(dvoid *, size_t)) 0,
-                                   (dvoid * (*)(dvoid *, dvoid *, size_t))0,
-                                   (void (*)(dvoid *, dvoid *)) 0 );
+               static OCIEnv           *p_env;
+               static OCIError         *p_err;
+               static OCISvcCtx        *p_svc;
+               static OCIStmt          *p_sql;
+               static OCIDefine        *p_dfn    = (OCIDefine *) 0;
+               static OCIBind          *p_bnd    = (OCIBind *) 0;
+               ],
+               [
+               int             p_bvi;
+               char            p_sli[20];
+               int             rc;
+               char            errbuf[100];
+               int             errcode;
+           
+               rc = OCIInitialize((ub4) OCI_DEFAULT, (dvoid *)0,  /* Initialize OCI */
+                                  (dvoid * (*)(dvoid *, size_t)) 0,
+                                  (dvoid * (*)(dvoid *, dvoid *, size_t))0,
+                                  (void (*)(dvoid *, dvoid *)) 0 );
     
-                ],
-                [mod_ldflags="$LIBS"],
-            )
-            if test "x$mod_ldflags" != "x"; then
-                    AC_MSG_RESULT(yes)
-                    break
-            fi
-            AC_MSG_RESULT(no)
-        done
-        
-        if test "x$mod_ldflags" != "x"; then
-                break
-        fi
+               ],
+               [mod_ldflags="$LIBS"],
+           )
+           if test "x$mod_ldflags" != "x"; then
+                   AC_MSG_RESULT(yes)
+                   break
+           fi
+           AC_MSG_RESULT(no)
+       done
+       
+       if test "x$mod_ldflags" != "x"; then
+               break
+       fi
     done
 
     LIBS="$old_LIBS"
 
     if test "x$mod_ldflags" = "x"; then
-        AC_MSG_WARN([oracle libraries not found.  Use --with-oracle-lib-dir=<path> or set ORACLE_HOME.])
-        fail=["$fail libclntsh libnnz[9-11]"]
+       AC_MSG_WARN([oracle libraries not found.  Use --with-oracle-lib-dir=<path> or set ORACLE_HOME.])
+       fail=["$fail libclntsh libnnz[9-11]"]
     fi
     
     targetname=modname
index fa1685d..14aeaf7 100644 (file)
@@ -174,8 +174,8 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
         */
        if (OCILogon(conn->env, conn->error, &conn->ctx,
                     (OraText const *)config->sql_login, strlen(config->sql_login),
-                     (OraText const *)config->sql_password, strlen(config->sql_password),
-                     (OraText const *)config->sql_db, strlen(config->sql_db))) {
+                    (OraText const *)config->sql_password, strlen(config->sql_password),
+                    (OraText const *)config->sql_db, strlen(config->sql_db))) {
                ERROR("rlm_sql_oracle: Oracle logon failed: '%s'", sql_error(handle, config));
 
                return -1;
diff --git a/src/modules/rlm_sql/drivers/rlm_sql_postgresql/config.h.in b/src/modules/rlm_sql/drivers/rlm_sql_postgresql/config.h.in
new file mode 100644 (file)
index 0000000..866ce58
--- /dev/null
@@ -0,0 +1,31 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Whether the PGRES_COPY_BOTH constant is defined */
+#undef HAVE_PGRES_COPY_BOTH
+
+/* Whether the PGRES_SINGLE_TUPLE constant is defined */
+#undef HAVE_PGRES_SINGLE_TUPLE
+
+/* Define to 1 if you have the `PQinitOpenSSL' function. */
+#undef HAVE_PQINITOPENSSL
+
+/* Define to 1 if you have the `PQinitSSL' function. */
+#undef HAVE_PQINITSSL
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
index 482b2f4..c4f7258 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1260,10 +1260,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1428,6 +1428,73 @@ fi
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -1886,7 +1953,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2108,7 +2175,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2695,22 +2762,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpq-fe.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2761,7 +2828,72 @@ if test "x$smart_include" != "x"; then
 fi
 
        if test "x$ac_cv_header_libpqmfe_h" != "xyes"; then
-         fail="$fail libpq-fe.h"
+               fail="$fail libpq-fe.h"
+       else
+               CPPFLAGS="$SMART_CPPFLAGS"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PGRES_SINGLE_TUPLE" >&5
+$as_echo_n "checking for PGRES_SINGLE_TUPLE... " >&6; }
+               cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <libpq-fe.h>
+int
+main ()
+{
+
+                   if (PGRES_SINGLE_TUPLE) return 0;
+                   return 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+
+$as_echo "#define HAVE_PGRES_SINGLE_TUPLE 1" >>confdefs.h
+
+                   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+
+                   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+               { $as_echo "$as_me:${as_lineno-$LINENO}: checking for PGRES_COPY_BOTH" >&5
+$as_echo_n "checking for PGRES_COPY_BOTH... " >&6; }
+               cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <libpq-fe.h>
+int
+main ()
+{
+
+                   if (PGRES_COPY_BOTH) return 0;
+                   return 1;
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+
+$as_echo "#define HAVE_PGRES_COPY_BOTH 1" >>confdefs.h
+
+                   { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+
+                   { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
        fi
 
        smart_try_dir="$rlm_sql_postgresql_lib_dir /usr/lib /usr/local/pgsql/lib"
@@ -2824,8 +2956,8 @@ PQconnectdb()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lpq"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lpq"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2841,22 +2973,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpq${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2868,22 +3000,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libpq.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2931,9 +3063,23 @@ if test "x$smart_lib" != "x"; then
   SMART_LIBS="$smart_lib $SMART_LIBS"
 fi
 
-        if test "x$ac_cv_lib_pq_PQconnectdb" != "xyes"; then
-         fail="$fail libpq"
-        fi
+       if test "x$ac_cv_lib_pq_PQconnectdb" != "xyes"; then
+               fail="$fail libpq"
+       fi
+       for ac_func in \
+               PQinitOpenSSL \
+               PQinitSSL \
+
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
 
        targetname=rlm_sql_postgresql
 else
@@ -2966,6 +3112,8 @@ mod_cflags=$SMART_CFLAGS
 
 
 
+ac_config_headers="$ac_config_headers config.h"
+
 
   unset ac_cv_env_LIBS_set
   unset ac_cv_env_LIBS_value
@@ -3042,11 +3190,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3062,43 +3210,7 @@ test "x$prefix" = xNONE && prefix=$ac_default_prefix
 # Let make expand exec_prefix.
 test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
 
-# Transform confdefs.h into DEFS.
-# Protect against shell expansion while executing Makefile rules.
-# Protect against Makefile macro expansion.
-#
-# If the first sed substitution is executed (which looks for macros that
-# take arguments), then branch to the quote section.  Otherwise,
-# look for a macro that doesn't take arguments.
-ac_script='
-:mline
-/\\$/{
- N
- s,\\\n,,
- b mline
-}
-t clear
-:clear
-s/^[    ]*#[    ]*define[       ][      ]*\([^  (][^    (]*([^)]*)\)[   ]*\(.*\)/-D\1=\2/g
-t quote
-s/^[    ]*#[    ]*define[       ][      ]*\([^  ][^     ]*\)[   ]*\(.*\)/-D\1=\2/g
-t quote
-b any
-:quote
-s/[     `~#$^&*(){}\\|;'\''"<>?]/\\&/g
-s/\[/\\&/g
-s/\]/\\&/g
-s/\$/$$/g
-H
-:any
-${
-       g
-       s/^\n//
-       s/\n/ /g
-       p
-}
-'
-DEFS=`sed -n "$ac_script" confdefs.h`
-
+DEFS=-DHAVE_CONFIG_H
 
 ac_libobjs=
 ac_ltlibobjs=
@@ -3532,11 +3644,15 @@ case $ac_config_files in *"
 "*) set x $ac_config_files; shift; ac_config_files=$*;;
 esac
 
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
 
 
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 # Files that config.status was made for.
 config_files="$ac_config_files"
+config_headers="$ac_config_headers"
 
 _ACEOF
 
@@ -3552,15 +3668,20 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
 
+Configuration headers:
+$config_headers
+
 Report bugs to the package provider."
 
 _ACEOF
@@ -3621,7 +3742,18 @@ do
     esac
     as_fn_append CONFIG_FILES " '$ac_optarg'"
     ac_need_defaults=false;;
-  --he | --h |  --help | --hel | -h )
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
     $as_echo "$ac_cs_usage"; exit ;;
   -q | -quiet | --quiet | --quie | --qui | --qu | --q \
   | -silent | --silent | --silen | --sile | --sil | --si | --s)
@@ -3677,6 +3809,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 for ac_config_target in $ac_config_targets
 do
   case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
     "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;;
 
   *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@@ -3690,6 +3823,7 @@ done
 # bizarre bug on SunOS 4.1.3.
 if $ac_need_defaults; then
   test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
 fi
 
 # Have a temporary directory for convenience.  Make it in the build tree
@@ -3877,8 +4011,116 @@ fi
 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 fi # test -n "$CONFIG_FILES"
 
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
 
-eval set X "  :F $CONFIG_FILES      "
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
 shift
 for ac_tag
 do
@@ -4086,7 +4328,30 @@ which seems to be undefined.  Please make sure it is defined" >&2;}
   esac \
   || as_fn_error $? "could not create $ac_file" "$LINENO" 5
  ;;
-
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
 
 
   esac
index a5ba479..78905ab 100644 (file)
@@ -44,15 +44,47 @@ if test x$with_[]modname != xno; then
        smart_try_dir="$rlm_sql_postgresql_include_dir /usr/include/postgresql /usr/local/pgsql/include /usr/include/pgsql"
        FR_SMART_CHECK_INCLUDE(libpq-fe.h)
        if test "x$ac_cv_header_libpqmfe_h" != "xyes"; then
-         fail="$fail libpq-fe.h"
+               fail="$fail libpq-fe.h"
+       else
+               CPPFLAGS="$SMART_CPPFLAGS"
+               AC_MSG_CHECKING([for PGRES_SINGLE_TUPLE])
+               AC_COMPILE_IFELSE(
+                 [AC_LANG_PROGRAM([#include <libpq-fe.h>], [[
+                   if (PGRES_SINGLE_TUPLE) return 0;
+                   return 1;
+                 ]])],
+                 [
+                   AC_DEFINE([HAVE_PGRES_SINGLE_TUPLE], [1], [Whether the PGRES_SINGLE_TUPLE constant is defined])
+                   AC_MSG_RESULT(yes)
+                 ],
+                 [
+                   AC_MSG_RESULT(no)
+                 ])
+
+               AC_MSG_CHECKING([for PGRES_COPY_BOTH])
+               AC_COMPILE_IFELSE(
+                 [AC_LANG_PROGRAM([#include <libpq-fe.h>], [[
+                   if (PGRES_COPY_BOTH) return 0;
+                   return 1;
+                 ]])],
+                 [
+                   AC_DEFINE([HAVE_PGRES_COPY_BOTH], [1], [Whether the PGRES_COPY_BOTH constant is defined])
+                   AC_MSG_RESULT(yes)
+                 ],
+                 [
+                   AC_MSG_RESULT(no)
+                 ])
        fi
 
        smart_try_dir="$rlm_sql_postgresql_lib_dir /usr/lib /usr/local/pgsql/lib"
        FR_SMART_CHECK_LIB(pq, PQconnectdb)
-        if test "x$ac_cv_lib_pq_PQconnectdb" != "xyes"; then
-         fail="$fail libpq"
-        fi
-
+       if test "x$ac_cv_lib_pq_PQconnectdb" != "xyes"; then
+               fail="$fail libpq"
+       fi
+       AC_CHECK_FUNCS(\
+               PQinitOpenSSL \
+               PQinitSSL \
+       )
        targetname=modname
 else
        targetname=
@@ -80,4 +112,5 @@ mod_cflags=$SMART_CFLAGS
 AC_SUBST(mod_ldflags)
 AC_SUBST(mod_cflags)
 AC_SUBST(targetname)
+AC_CONFIG_HEADER(config.h)
 AC_OUTPUT(all.mk)
index a5a287c..52737fd 100644 (file)
@@ -44,11 +44,22 @@ RCSID("$Id$")
 #include <sys/stat.h>
 
 #include <libpq-fe.h>
+#include <postgres_ext.h>
+
+#include "config.h"
 #include "rlm_sql.h"
 #include "sql_postgresql.h"
 
+#ifndef NAMEDATALEN
+#  define NAMEDATALEN 64
+#endif
+
+typedef struct rlm_sql_postgres_config {
+       char const      *db_string;
+       bool            send_application_name;
+} rlm_sql_postgres_config_t;
+
 typedef struct rlm_sql_postgres_conn {
-       char const      *dbstring;      //!< String describing parameters for the connection
        PGconn          *db;
        PGresult        *result;
        int             cur_row;
@@ -57,75 +68,145 @@ typedef struct rlm_sql_postgres_conn {
        char            **row;
 } rlm_sql_postgres_conn_t;
 
-/* Internal function. Return true if the postgresql status value
- * indicates successful completion of the query. Return false otherwise
-static int
-status_is_ok(ExecStatusType status)
+static CONF_PARSER driver_config[] = {
+       { "send_application_name", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_postgres_config_t, send_application_name), "yes" },
+
+       { NULL, -1, 0, NULL, NULL }
+};
+
+static int mod_instantiate(CONF_SECTION *conf, UNUSED rlm_sql_config_t *config)
 {
-       return status == PGRES_COMMAND_OK || status == PGRES_TUPLES_OK;
-}
-*/
+#if defined(HAVE_OPENSSL_CRYPTO_H) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
+       static bool                     ssl_init = false;
+#endif
+
+       rlm_sql_postgres_config_t       *driver;
+       char                            application_name[NAMEDATALEN];
+       char                            *db_string;
+
+#if defined(HAVE_OPENSSL_CRYPTO_H) && (defined(HAVE_PQINITOPENSSL) || defined(HAVE_PQINITSSL))
+       if (!ssl_init) {
+#  ifdef HAVE_PQINITOPENSSL
+               PQinitOpenSSL(0, 0);
+#  else
+               PQinitSSL(0);
+#  endif
+               ssl_init = true;
+       }
+#endif
+
+       MEM(driver = config->driver = talloc_zero(config, rlm_sql_postgres_config_t));
+       if (cf_section_parse(conf, driver, driver_config) < 0) {
+               return -1;
+       }
+
+       db_string = strchr(config->sql_db, '=') ?
+               talloc_typed_strdup(driver, config->sql_db) :
+               talloc_typed_asprintf(driver, "dbname='%s'", config->sql_db);
+
+       if (config->sql_server[0] != '\0') {
+               db_string = talloc_asprintf_append(db_string, " host='%s'", config->sql_server);
+       }
+
+       if (config->sql_port[0] != '\0') {
+               db_string = talloc_asprintf_append(db_string, " port=%s", config->sql_port);
+       }
+
+       if (config->sql_login[0] != '\0') {
+               db_string = talloc_asprintf_append(db_string, " user='%s'", config->sql_login);
+       }
+
+       if (config->sql_password[0] != '\0') {
+               db_string = talloc_asprintf_append(db_string, " password='%s'", config->sql_password);
+       }
 
+       /*
+        *      Allow the user to set their own, or disable it
+        */
+       if (driver->send_application_name) {
+               snprintf(application_name, sizeof(application_name),
+                        "FreeRADIUS "  RADIUSD_VERSION_STRING " - %s (%s)", progname, config->xlat_name);
+               db_string = talloc_asprintf_append(db_string, " application_name='%s'", application_name);
+       }
+       driver->db_string = db_string;
+
+       return 0;
+}
 
-/* Internal function. Return the number of affected rows of the result
- * as an int instead of the string that postgresql provides */
+/** Return the number of affected rows of the result as an int instead of the string that postgresql provides
+ *
+ */
 static int affected_rows(PGresult * result)
 {
        return atoi(PQcmdTuples(result));
 }
 
-/* Internal function. Free the row of the current result that's stored
- * in the conn struct. */
+/** Free the row of the current result that's stored in the conn struct
+ *
+ */
 static void free_result_row(rlm_sql_postgres_conn_t *conn)
 {
+       TALLOC_FREE(conn->row);
+       conn->num_fields = 0;
+}
+
+#if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
+static sql_rcode_t sql_classify_error(PGresult const *result)
+{
        int i;
-       if (conn->row != NULL) {
-               for (i = conn->num_fields-1; i >= 0; i--) {
-                       if (conn->row[i] != NULL) {
-                               free(conn->row[i]);
-                       }
-               }
-               free((char*)conn->row);
-               conn->row = NULL;
-               conn->num_fields = 0;
+
+       char *errorcode;
+       char *errormsg;
+
+       /*
+        *      Check the error code to see if we should reconnect or not
+        *      Error Code table taken from:
+        *      http://www.postgresql.org/docs/8.1/interactive/errcodes-appendix.html
+        */
+       errorcode = PQresultErrorField(result, PG_DIAG_SQLSTATE);
+       errormsg = PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY);
+       if (!errorcode) {
+               ERROR("rlm_sql_postgresql: Error occurred, but unable to retrieve error code");
+               return RLM_SQL_ERROR;
        }
-}
 
+       /* SUCCESSFUL COMPLETION */
+       if (strcmp("00000", errorcode) == 0) {
+               return RLM_SQL_OK;
+       }
 
-/*************************************************************************
-*      Function: check_fatal_error
-*
-*      Purpose:  Check error type and behave accordingly
-*
-*************************************************************************/
+       /* WARNING */
+       if (strcmp("01000", errorcode) == 0) {
+               WARN("%s", errormsg);
+               return RLM_SQL_OK;
+       }
 
-static int check_fatal_error (char *errorcode)
-{
-       int x = 0;
+       /* UNIQUE VIOLATION */
+       if (strcmp("23505", errorcode) == 0) {
+               return RLM_SQL_DUPLICATE;
+       }
 
-       /*
-       Check the error code to see if we should reconnect or not
-       Error Code table taken from
-       http://www.postgresql.org/docs/8.1/interactive/errcodes-appendix.html
-       */
-
-       if (!errorcode) return -1;
-
-       while(errorcodes[x].errorcode != NULL){
-               if (strcmp(errorcodes[x].errorcode, errorcode) == 0){
-                       DEBUG("rlm_sql_postgresql: Postgresql Fatal Error: [%s: %s] Occurred!!", errorcode, errorcodes[x].meaning);
-                       if (errorcodes[x].shouldreconnect == 1)
-                               return RLM_SQL_RECONNECT;
-                       else
-                               return -1;
+       /* others */
+       for (i = 0; errorcodes[i].errorcode != NULL; i++) {
+               if (strcmp(errorcodes[i].errorcode, errorcode) == 0) {
+                       ERROR("rlm_sql_postgresql: %s: %s", errorcode, errorcodes[i].meaning);
+
+                       return (errorcodes[i].reconnect == true) ?
+                               RLM_SQL_RECONNECT :
+                               RLM_SQL_ERROR;
                }
-               x++;
        }
 
-       DEBUG("rlm_sql_postgresql: Postgresql Fatal Error: [%s] Occurred!!", errorcode);
-       /*      We don't seem to have a matching error class/code */
-       return -1;
+       ERROR("rlm_sql_postgresql: Can't classify: %s", errorcode);
+       return RLM_SQL_ERROR;
 }
+#  else
+static sql_rcode_t sql_classify_error(UNUSED PGresult const *result)
+{
+       ERROR("rlm_sql_postgresql: Error occurred, no more information available, rebuild with newer libpq");
+       return RLM_SQL_ERROR;
+}
+#endif
 
 static int sql_socket_destructor(void *c)
 {
@@ -139,6 +220,7 @@ static int sql_socket_destructor(void *c)
 
        /* PQfinish also frees the memory used by the PGconn structure */
        PQfinish(conn->db);
+       conn->db = NULL;
 
        return 0;
 }
@@ -150,49 +232,27 @@ static int sql_socket_destructor(void *c)
  *     Purpose: Establish connection to the db
  *
  *************************************************************************/
-static int sql_init_socket(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
-       char *dbstring;
+static int CC_HINT(nonnull) sql_init_socket(rlm_sql_handle_t *handle, rlm_sql_config_t *config)
+{
+       rlm_sql_postgres_config_t *driver = config->driver;
        rlm_sql_postgres_conn_t *conn;
 
-#ifdef HAVE_OPENSSL_CRYPTO_H
-       static bool ssl_init = false;
-
-       if (!ssl_init) {
-               PQinitOpenSSL(0, 0);
-               ssl_init = true;
-       }
-#endif
-
        MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_postgres_conn_t));
        talloc_set_destructor((void *) conn, sql_socket_destructor);
 
-       dbstring = strchr(config->sql_db, '=') ?
-               talloc_strdup(conn, config->sql_db) :
-               talloc_asprintf(conn, "dbname='%s'", config->sql_db);
-
-       if (config->sql_server[0] != '\0') {
-               dbstring = talloc_asprintf_append(dbstring, " host='%s'", config->sql_server);
-       }
-
-       if (config->sql_port[0] != '\0') {
-               dbstring = talloc_asprintf_append(dbstring, " port=%s", config->sql_port);
-       }
-
-       if (config->sql_login[0] != '\0') {
-               dbstring = talloc_asprintf_append(dbstring, " user='%s'", config->sql_login);
-       }
-
-       if (config->sql_password[0] != '\0') {
-               dbstring = talloc_asprintf_append(dbstring, " password='%s'", config->sql_password);
+       DEBUG2("rlm_sql_postgresql: Connecting using parameters: %s", driver->db_string);
+       conn->db = PQconnectdb(driver->db_string);
+       if (!conn->db) {
+               ERROR("rlm_sql_postgresql: Connection failed: Out of memory");
+               return -1;
        }
-
-       conn->dbstring = dbstring;
-       conn->db = PQconnectdb(dbstring);
-       DEBUG2("rlm_sql_postgresql: Connecting using parameters: %s", dbstring);
-       if (!conn->db || (PQstatus(conn->db) != CONNECTION_OK)) {
+       if (PQstatus(conn->db) != CONNECTION_OK) {
                ERROR("rlm_sql_postgresql: Connection failed: %s", PQerrorMessage(conn->db));
+               PQfinish(conn->db);
+               conn->db = NULL;
                return -1;
        }
+
        DEBUG2("Connected to database '%s' on '%s' server version %i, protocol version %i, backend PID %i ",
               PQdb(conn->db), PQhost(conn->db), PQserverVersion(conn->db), PQprotocolVersion(conn->db),
               PQbackendPID(conn->db));
@@ -207,121 +267,95 @@ static int sql_init_socket(rlm_sql_handle_t *handle, rlm_sql_config_t *config) {
  *     Purpose: Issue a query to the database
  *
  *************************************************************************/
-static sql_rcode_t sql_query(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t *config, char const *query)
+static CC_HINT(nonnull) sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config,
+                                             char const *query)
 {
        rlm_sql_postgres_conn_t *conn = handle->conn;
+       ExecStatusType status;
        int numfields = 0;
-       char *errorcode;
-       char *errormsg;
 
        if (!conn->db) {
                ERROR("rlm_sql_postgresql: Socket not connected");
                return RLM_SQL_RECONNECT;
        }
 
+       /*
+        *  Returns a PGresult pointer or possibly a null pointer.
+        *  A non-null pointer will generally be returned except in
+        *  out-of-memory conditions or serious errors such as inability
+        *  to send the command to the server. If a null pointer is
+        *  returned, it should be treated like a PGRES_FATAL_ERROR
+        *  result.
+        */
        conn->result = PQexec(conn->db, query);
-               /*
-                * Returns a PGresult pointer or possibly a null pointer.
-                * A non-null pointer will generally be returned except in
-                * out-of-memory conditions or serious errors such as inability
-                * to send the command to the server. If a null pointer is
-                * returned, it should be treated like a PGRES_FATAL_ERROR
-                * result.
-                */
-       if (!conn->result) {
-               ERROR("rlm_sql_postgresql: PostgreSQL Query failed Error: %s",
-                               PQerrorMessage(conn->db));
-               /* As this error COULD be a connection error OR an out-of-memory
-                * condition return value WILL be wrong SOME of the time regardless!
-                * Pick your poison....
-                */
-               return  RLM_SQL_RECONNECT;
-       } else {
-               ExecStatusType status = PQresultStatus(conn->result);
-               DEBUG("rlm_sql_postgresql: Status: %s", PQresStatus(status));
-
-               switch (status){
-
-                       case PGRES_COMMAND_OK:
-                               /*Successful completion of a command returning no data.*/
-
-                               /*affected_rows function only returns
-                               the number of affected rows of a command
-                               returning no data...
-                               */
-                               conn->affected_rows     = affected_rows(conn->result);
-                               DEBUG("rlm_sql_postgresql: query affected rows = %i", conn->affected_rows);
-                               return 0;
-
-                       break;
-
-                       case PGRES_TUPLES_OK:
-                               /*Successful completion of a command returning data (such as a SELECT or SHOW).*/
-
-                               conn->cur_row = 0;
-                               conn->affected_rows = PQntuples(conn->result);
-                               numfields = PQnfields(conn->result); /*Check row storing functions..*/
-                               DEBUG("rlm_sql_postgresql: query affected rows = %i , fields = %i", conn->affected_rows, numfields);
-                               return 0;
-
-                       break;
 
-                       case PGRES_BAD_RESPONSE:
-                               /*The server's response was not understood.*/
-                               DEBUG("rlm_sql_postgresql: Bad Response From Server!!");
-                               return -1;
-
-                       break;
-
-                       case PGRES_NONFATAL_ERROR:
-                               /*A nonfatal error (a notice or warning) occurred. Possibly never returns*/
-
-                               return -1;
-
-                       break;
+       /*
+        *  As this error COULD be a connection error OR an out-of-memory
+        *  condition return value WILL be wrong SOME of the time
+        *  regardless! Pick your poison...
+        */
+       if (!conn->result) {
+               ERROR("rlm_sql_postgresql: Failed getting query result: %s", PQerrorMessage(conn->db));
+               return RLM_SQL_RECONNECT;
+       }
 
-                       case PGRES_FATAL_ERROR:
-#if defined(PG_DIAG_SQLSTATE) && defined(PG_DIAG_MESSAGE_PRIMARY)
-                               /*A fatal error occurred.*/
+       status = PQresultStatus(conn->result);
+       DEBUG("rlm_sql_postgresql: Status: %s", PQresStatus(status));
 
-                               errorcode = PQresultErrorField(conn->result, PG_DIAG_SQLSTATE);
-                               errormsg  = PQresultErrorField(conn->result, PG_DIAG_MESSAGE_PRIMARY);
-                               DEBUG("rlm_sql_postgresql: Error %s", errormsg);
-                               return check_fatal_error(errorcode);
+       switch (status){
+       /*
+        *  Successful completion of a command returning no data.
+        */
+       case PGRES_COMMAND_OK:
+               /*
+                *  Affected_rows function only returns the number of affected rows of a command
+                *  returning no data...
+                */
+               conn->affected_rows = affected_rows(conn->result);
+               DEBUG("rlm_sql_postgresql: query affected rows = %i", conn->affected_rows);
+               return RLM_SQL_OK;
+       /*
+        *  Successful completion of a command returning data (such as a SELECT or SHOW).
+        */
+#ifdef HAVE_PGRES_SINGLE_TUPLE
+       case PGRES_SINGLE_TUPLE:
 #endif
+       case PGRES_TUPLES_OK:
+               conn->cur_row = 0;
+               conn->affected_rows = PQntuples(conn->result);
+               numfields = PQnfields(conn->result); /*Check row storing functions..*/
+               DEBUG("rlm_sql_postgresql: query affected rows = %i , fields = %i", conn->affected_rows, numfields);
+               return RLM_SQL_OK;
+
+#ifdef HAVE_PGRES_COPY_BOTH
+       case PGRES_COPY_BOTH:
+#endif
+       case PGRES_COPY_OUT:
+       case PGRES_COPY_IN:
+               DEBUG("rlm_sql_postgresql: Data transfer started");
+               return RLM_SQL_OK;
 
-                       break;
-
-                       default:
-                               /* FIXME: An unhandled error occurred.*/
-
-                               /* PGRES_EMPTY_QUERY PGRES_COPY_OUT PGRES_COPY_IN */
-
-                               return -1;
-
-                       break;
+       /*
+        *  Weird.. this shouldn't happen.
+        */
+       case PGRES_EMPTY_QUERY:
+               ERROR("rlm_sql_postgresql: Empty query");
+               return RLM_SQL_QUERY_ERROR;
 
+       /*
+        *  The server's response was not understood.
+        */
+       case PGRES_BAD_RESPONSE:
+               ERROR("rlm_sql_postgresql: Bad Response From Server");
+               return RLM_SQL_RECONNECT;
 
-               }
 
-               /*
-                       Note to self ... sql_store_result returns 0 anyway
-                       after setting the handle->affected_rows..
-                       sql_num_fields returns 0 at worst case which means the check below
-                       has a really small chance to return false..
-                       lets remove it then .. yuck!!
-               */
-               /*
-               } else {
-                       if ((sql_store_result(handle, config) == 0)
-                                       && (sql_num_fields(handle, config) >= 0))
-                               return 0;
-                       else
-                               return -1;
-               }
-               */
+       case PGRES_NONFATAL_ERROR:
+       case PGRES_FATAL_ERROR:
+               return sql_classify_error(conn->result);
        }
-       return -1;
+
+       return RLM_SQL_ERROR;
 }
 
 
@@ -361,14 +395,11 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t * handle, UNUSED rlm_sql_confi
        conn->num_fields = records;
 
        if ((PQntuples(conn->result) > 0) && (records > 0)) {
-               conn->row = (char **)rad_malloc((records+1)*sizeof(char *));
-               memset(conn->row, '\0', (records+1)*sizeof(char *));
-
+               conn->row = talloc_zero_array(conn, char *, records + 1);
                for (i = 0; i < records; i++) {
                        len = PQgetlength(conn->result, conn->cur_row, i);
-                       conn->row[i] = (char *)rad_malloc(len+1);
-                       memset(conn->row[i], '\0', len+1);
-                       strlcpy(conn->row[i], PQgetvalue(conn->result, conn->cur_row,i),len + 1);
+                       conn->row[i] = talloc_array(conn->row, char, len + 1);
+                       strlcpy(conn->row[i], PQgetvalue(conn->result, conn->cur_row, i),len + 1);
                }
                conn->cur_row++;
                handle->row = conn->row;
@@ -410,7 +441,7 @@ static sql_rcode_t sql_free_result(rlm_sql_handle_t * handle, UNUSED rlm_sql_con
 
        rlm_sql_postgres_conn_t *conn = handle->conn;
 
-       if (conn->result) {
+       if (conn->result != NULL) {
                PQclear(conn->result);
                conn->result = NULL;
        }
@@ -439,31 +470,6 @@ static char const *sql_error(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t
 
 /*************************************************************************
  *
- *     Function: sql_finish_query
- *
- *     Purpose: End the query, such as freeing memory
- *
- *************************************************************************/
-static sql_rcode_t sql_finish_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config) {
-
-       return sql_free_result(handle, config);
-}
-
-/*************************************************************************
- *
- *     Function: sql_finish_select_query
- *
- *     Purpose: End the select query, such as freeing memory or result
- *
- *************************************************************************/
-static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t * handle, rlm_sql_config_t *config) {
-
-       return sql_free_result(handle, config);
-}
-
-
-/*************************************************************************
- *
  *     Function: sql_affected_rows
  *
  *     Purpose: Return the number of rows affected by the last query.
@@ -478,7 +484,7 @@ static int sql_affected_rows(rlm_sql_handle_t * handle, UNUSED rlm_sql_config_t
 /* Exported to rlm_sql */
 rlm_sql_module_t rlm_sql_postgresql = {
        "rlm_sql_postgresql",
-       NULL,
+       mod_instantiate,
        sql_init_socket,
        sql_query,
        sql_select_query,
@@ -488,7 +494,7 @@ rlm_sql_module_t rlm_sql_postgresql = {
        sql_fetch_row,
        NULL, /* sql_free_result */
        sql_error,
-       sql_finish_query,
-       sql_finish_select_query,
+       sql_free_result,
+       sql_free_result,
        sql_affected_rows,
 };
index f8885c8..fe75a56 100644 (file)
 
 RCSIDH(sql_postgresql_h, "$Id$")
 
-/**************************************************
-* Error Codes and required information Lookup table
-* Does this shite ever needed? Lets c..
-***************************************************/
+/** Error Codes and required information
+ *
+ */
 typedef struct pgsql_error{
-       char const *errorcode;
-       char const *meaning;
-       int  shouldreconnect;
-}pgerror;
+       char const      *errorcode;     //!< 5 char error code from PG_DIAG_SQLSTATE.
+       char const      *meaning;       //!< Verbose description.
+       bool            reconnect;      //!< Should reconnect socket when receiving this error.
+} pgerror;
 
 pgerror errorcodes[]=
 {
-       { "1000", "WARNING", 0, },
-       { "0100C", "DYNAMIC RESULT SETS RETURNED", 0, },
-       { "1008", "IMPLICIT ZERO BIT PADDING", 0, },
-       { "1003", "NULL VALUE ELIMINATED IN SET FUNCTION", 0, },
-       { "1007", "PRIVILEGE NOT GRANTED", 0, },
-       { "1006", "PRIVILEGE NOT REVOKED", 0, },
-       { "1004", "STRING DATA RIGHT TRUNCATION", 0, },
-       { "01P01", "DEPRECATED FEATURE", 0, },
-
-       { "2000", "NO DATA", 0, },
-       { "2001", "NO ADDITIONAL DYNAMIC RESULT SETS RETURNED", 0, },
-
-       { "3000", "SQL STATEMENT NOT YET COMPLETE", 0, },
-
-       { "8000", "CONNECTION EXCEPTION", 0, },
-       { "8003", "CONNECTION DOES NOT EXIST", 0, },
-       { "8006", "CONNECTION FAILURE", 0, },
-       { "8001", "SQLCLIENT UNABLE TO ESTABLISH SQLCONNECTION", 0, },
-       { "8004", "SQLSERVER REJECTED ESTABLISHMENT OF SQLCONNECTION", 0, },
-       { "8007", "TRANSACTION RESOLUTION UNKNOWN", 0, },
-       { "08P01", "PROTOCOL VIOLATION", 0, },
-
-       { "9000", "TRIGGERED ACTION EXCEPTION", 0, },
-
-       { "0A000", "FEATURE NOT SUPPORTED", 0, },
-
-       { "0B000", "INVALID TRANSACTION INITIATION", 0, },
-
-       { "0F000", "LOCATOR EXCEPTION", 0, },
-       { "0F001", "INVALID LOCATOR SPECIFICATION", 0, },
-
-       { "0L000", "INVALID GRANTOR", 0, },
-       { "0LP01", "INVALID GRANT OPERATION", 0, },
-
-       { "21000", "CARDINALITY VIOLATION", 0, },
-
-       { "22000", "DATA EXCEPTION", 0, },
-       { "2202E", "ARRAY SUBSCRIPT ERROR", 0, },
-       { "22021", "CHARACTER NOT IN REPERTOIRE", 0, },
-       { "22008", "DATETIME FIELD OVERFLOW", 0, },
-       { "22012", "DIVISION BY ZERO", 0, },
-       { "22005", "ERROR IN ASSIGNMENT", 0, },
-       { "2200B", "ESCAPE CHARACTER CONFLICT", 0, },
-       { "22022", "INDICATOR OVERFLOW", 0, },
-       { "22015", "INTERVAL FIELD OVERFLOW", 0, },
-       { "2201E", "INVALID ARGUMENT FOR LOGARITHM", 0, },
-       { "2201F", "INVALID ARGUMENT FOR POWER FUNCTION", 0, },
-       { "2201G", "INVALID ARGUMENT FOR WIDTH BUCKET FUNCTION", 0, },
-       { "22018", "INVALID CHARACTER VALUE FOR CAST", 0, },
-       { "22007", "INVALID DATETIME FORMAT", 0, },
-       { "22019", "INVALID ESCAPE CHARACTER", 0, },
-       { "2200D", "INVALID ESCAPE OCTET", 0, },
-       { "22025", "INVALID ESCAPE SEQUENCE", 0, },
-       { "22P06", "NONSTANDARD USE OF ESCAPE CHARACTER", 0, },
-       { "22010", "INVALID INDICATOR PARAMETER VALUE", 0, },
-       { "22020", "INVALID LIMIT VALUE", 0, },
-       { "22023", "INVALID PARAMETER VALUE", 0, },
-       { "2201B", "INVALID REGULAR EXPRESSION", 0, },
-       { "22009", "INVALID TIME ZONE DISPLACEMENT VALUE", 0, },
-       { "2200C", "INVALID USE OF ESCAPE CHARACTER", 0, },
-       { "2200G", "MOST SPECIFIC TYPE MISMATCH", 0, },
-       { "22004", "NULL VALUE NOT ALLOWED", 0, },
-       { "22002", "NULL VALUE NO INDICATOR PARAMETER", 0, },
-       { "22003", "NUMERIC VALUE OUT OF RANGE", 0, },
-       { "22026", "STRING DATA LENGTH MISMATCH", 0, },
-       { "22001", "STRING DATA RIGHT TRUNCATION", 0, },
-       { "22011", "SUBSTRING ERROR", 0, },
-       { "22027", "TRIM ERROR", 0, },
-       { "22024", "UNTERMINATED C STRING", 0, },
-       { "2200F", "ZERO LENGTH CHARACTER STRING", 0, },
-       { "22P01", "FLOATING POINT EXCEPTION", 0, },
-       { "22P02", "INVALID TEXT REPRESENTATION", 0, },
-       { "22P03", "INVALID BINARY REPRESENTATION", 0, },
-       { "22P04", "BAD COPY FILE FORMAT", 0, },
-       { "22P05", "UNTRANSLATABLE CHARACTER", 0, },
-
-       { "23000", "INTEGRITY CONSTRAINT VIOLATION", 0, },
-       { "23001", "RESTRICT VIOLATION", 0, },
-       { "23502", "NOT NULL VIOLATION", 0, },
-       { "23503", "FOREIGN KEY VIOLATION", 0, },
-       { "23505", "UNIQUE VIOLATION", 0, },
-       { "23514", "CHECK VIOLATION", 0, },
-
-       { "24000", "INVALID CURSOR STATE", 0, },
-
-       { "25000", "INVALID TRANSACTION STATE", 0, },
-       { "25001", "ACTIVE SQL TRANSACTION", 0, },
-       { "25002", "BRANCH TRANSACTION ALREADY ACTIVE", 0, },
-       { "25008", "HELD CURSOR REQUIRES SAME ISOLATION LEVEL", 0, },
-       { "25003", "INAPPROPRIATE ACCESS MODE FOR BRANCH TRANSACTION", 0, },
-       { "25004", "INAPPROPRIATE ISOLATION LEVEL FOR BRANCH TRANSACTION", 0, },
-       { "25005", "NO ACTIVE SQL TRANSACTION FOR BRANCH TRANSACTION", 0, },
-       { "25006", "READ ONLY SQL TRANSACTION", 0, },
-       { "25007", "SCHEMA AND DATA STATEMENT MIXING NOT SUPPORTED", 0, },
-       { "25P01", "NO ACTIVE SQL TRANSACTION", 0, },
-       { "25P02", "IN FAILED SQL TRANSACTION", 0, },
-
-       { "26000", "INVALID SQL STATEMENT NAME", 0, },
-
-       { "27000", "TRIGGERED DATA CHANGE VIOLATION", 0, },
-
-       { "28000", "INVALID AUTHORIZATION SPECIFICATION", 0, },
-
-       { "2B000", "DEPENDENT PRIVILEGE DESCRIPTORS STILL EXIST", 0, },
-       { "2BP01", "DEPENDENT OBJECTS STILL EXIST", 0, },
-
-       { "2D000", "INVALID TRANSACTION TERMINATION", 0, },
-
-       { "2F000", "SQL ROUTINE EXCEPTION", 0, },
-       { "2F005", "FUNCTION EXECUTED NO RETURN STATEMENT", 0, },
-       { "2F002", "MODIFYING SQL DATA NOT PERMITTED", 0, },
-       { "2F003", "PROHIBITED SQL STATEMENT ATTEMPTED", 0, },
-       { "2F004", "READING SQL DATA NOT PERMITTED", 0, },
-
-       { "34000", "INVALID CURSOR NAME", 0, },
-
-       { "38000", "EXTERNAL ROUTINE EXCEPTION", 0, },
-       { "38001", "CONTAINING SQL NOT PERMITTED", 0, },
-       { "38002", "MODIFYING SQL DATA NOT PERMITTED", 0, },
-       { "38003", "PROHIBITED SQL STATEMENT ATTEMPTED", 0, },
-       { "38004", "READING SQL DATA NOT PERMITTED", 0, },
-
-       { "39000", "EXTERNAL ROUTINE INVOCATION EXCEPTION", 0, },
-       { "39001", "INVALID SQLSTATE RETURNED", 0, },
-       { "39004", "NULL VALUE NOT ALLOWED", 0, },
-       { "39P01", "TRIGGER PROTOCOL VIOLATED", 0, },
-       { "39P02", "SRF PROTOCOL VIOLATED", 0, },
-
-       { "3B000", "SAVEPOINT EXCEPTION", 0, },
-       { "3B001", "INVALID SAVEPOINT SPECIFICATION", 0, },
-
-       { "3D000", "INVALID CATALOG NAME", 0, },
-       { "3F000", "INVALID SCHEMA NAME", 0, },
-
-       { "40000", "TRANSACTION ROLLBACK", 0, },
-       { "40002", "TRANSACTION INTEGRITY CONSTRAINT VIOLATION", 0, },
-       { "40001", "SERIALIZATION FAILURE", 0, },
-       { "40003", "STATEMENT COMPLETION UNKNOWN", 0, },
-       { "40P01", "DEADLOCK DETECTED", 0, },
-
-       { "44000", "WITH CHECK OPTION VIOLATION", 0, },
-
-       { "53000", "INSUFFICIENT RESOURCES", 0, },
-       { "53100", "DISK FULL", 0, },
-       { "53200", "OUT OF MEMORY", 0, },
-       { "53300", "TOO MANY CONNECTIONS", 0, },
-
-       { "54000", "PROGRAM LIMIT EXCEEDED", 0, },
-       { "54001", "STATEMENT TOO COMPLEX", 0, },
-       { "54011", "TOO MANY COLUMNS", 0, },
-       { "54023", "TOO MANY ARGUMENTS", 0, },
-
-       { "55000", "OBJECT NOT IN PREREQUISITE STATE", 0, },
-       { "55006", "OBJECT IN USE", 0, },
-       { "55P02", "CANT CHANGE RUNTIME PARAM", 0, },
-       { "55P03", "LOCK NOT AVAILABLE", 0, },
-
-       { "57000", "OPERATOR INTERVENTION", 1, },
-       { "57014", "QUERY CANCELED", 1, },
-       { "57P01", "ADMIN SHUTDOWN", 1, },
-       { "57P02", "CRASH SHUTDOWN", 1, },
-       { "57P03", "CANNOT CONNECT NOW", 1, },
-
-       { "58030", "IO ERROR", 1, },
-       { "58P01", "UNDEFINED FILE", 1, },
-       { "58P02", "DUPLICATE FILE", 1, },
-
-       { "F0000", "CONFIG FILE ERROR", 1, },
-       { "F0001", "LOCK FILE EXISTS", 1, },
-
-       { "P0000", "PLPGSQL ERROR", 0, },
-       { "P0001", "RAISE EXCEPTION", 0, },
-
-       { "42000", "SYNTAX ERROR OR ACCESS RULE VIOLATION", 0, },
-       { "42601", "SYNTAX ERROR", 0, },
-       { "42501", "INSUFFICIENT PRIVILEGE", 0, },
-       { "42846", "CANNOT COERCE", 0, },
-       { "42803", "GROUPING ERROR", 0, },
-       { "42830", "INVALID FOREIGN KEY", 0, },
-       { "42602", "INVALID NAME", 0, },
-       { "42622", "NAME TOO LONG", 0, },
-       { "42939", "RESERVED NAME", 0, },
-       { "42804", "DATATYPE MISMATCH", 0, },
-       { "42P18", "INDETERMINATE DATATYPE", 0, },
-       { "42809", "WRONG OBJECT TYPE", 0, },
-       { "42703", "UNDEFINED COLUMN", 0, },
-       { "42883", "UNDEFINED FUNCTION", 0, },
-       { "42P01", "UNDEFINED TABLE", 0, },
-       { "42P02", "UNDEFINED PARAMETER", 0, },
-       { "42704", "UNDEFINED OBJECT", 0, },
-       { "42701", "DUPLICATE COLUMN", 0, },
-       { "42P03", "DUPLICATE CURSOR", 0, },
-       { "42P04", "DUPLICATE DATABASE", 0, },
-       { "42723", "DUPLICATE FUNCTION", 0, },
-       { "42P05", "DUPLICATE PREPARED STATEMENT", 0, },
-       { "42P06", "DUPLICATE SCHEMA", 0, },
-       { "42P07", "DUPLICATE TABLE", 0, },
-       { "42712", "DUPLICATE ALIAS", 0, },
-       { "42710", "DUPLICATE OBJECT", 0, },
-       { "42702", "AMBIGUOUS COLUMN", 0, },
-       { "42725", "AMBIGUOUS FUNCTION", 0, },
-       { "42P08", "AMBIGUOUS PARAMETER", 0, },
-       { "42P09", "AMBIGUOUS ALIAS", 0, },
-       { "42P10", "INVALID COLUMN REFERENCE", 0, },
-       { "42611", "INVALID COLUMN DEFINITION", 0, },
-       { "42P11", "INVALID CURSOR DEFINITION", 0, },
-       { "42P12", "INVALID DATABASE DEFINITION", 0, },
-       { "42P13", "INVALID FUNCTION DEFINITION", 0, },
-       { "42P14", "INVALID PREPARED STATEMENT DEFINITION", 0, },
-       { "42P15", "INVALID SCHEMA DEFINITION", 0, },
-       { "42P16", "INVALID TABLE DEFINITION", 0, },
-       { "42P17", "INVALID OBJECT DEFINITION", 0, },
-
-       { "XX000", "INTERNAL ERROR", 0, },
-       { "XX001", "DATA CORRUPTED", 0, },
-       { "XX002", "INDEX CORRUPTED", 0, },
+       { "0100C", "DYNAMIC RESULT SETS RETURNED", false },
+       { "01008", "IMPLICIT ZERO BIT PADDING", false },
+       { "01003", "NULL VALUE ELIMINATED IN SET FUNCTION", false },
+       { "01007", "PRIVILEGE NOT GRANTED", false },
+       { "01006", "PRIVILEGE NOT REVOKED", false },
+       { "01004", "STRING DATA RIGHT TRUNCATION", false },
+       { "01P01", "DEPRECATED FEATURE", false },
+
+       { "02000", "NO DATA", false },
+       { "02001", "NO ADDITIONAL DYNAMIC RESULT SETS RETURNED", false },
+
+       { "03000", "SQL STATEMENT NOT YET COMPLETE", false },
+
+       { "08000", "CONNECTION EXCEPTION", false },
+       { "08003", "CONNECTION DOES NOT EXIST", false },
+       { "08006", "CONNECTION FAILURE", false },
+       { "08001", "SQLCLIENT UNABLE TO ESTABLISH SQLCONNECTION", false },
+       { "08004", "SQLSERVER REJECTED ESTABLISHMENT OF SQLCONNECTION", false },
+       { "08007", "TRANSACTION RESOLUTION UNKNOWN", false },
+       { "08P01", "PROTOCOL VIOLATION", false },
+
+       { "9000", "TRIGGERED ACTION EXCEPTION", false },
+
+       { "0A000", "FEATURE NOT SUPPORTED", false },
+
+       { "0B000", "INVALID TRANSACTION INITIATION", false },
+
+       { "0F000", "LOCATOR EXCEPTION", false },
+       { "0F001", "INVALID LOCATOR SPECIFICATION", false },
+
+       { "0L000", "INVALID GRANTOR", false },
+       { "0LP01", "INVALID GRANT OPERATION", false },
+
+       { "21000", "CARDINALITY VIOLATION", false },
+
+       { "22000", "DATA EXCEPTION", false },
+       { "2202E", "ARRAY SUBSCRIPT ERROR", false },
+       { "22021", "CHARACTER NOT IN REPERTOIRE", false },
+       { "22008", "DATETIME FIELD OVERFLOW", false },
+       { "22012", "DIVISION BY ZERO", false },
+       { "22005", "ERROR IN ASSIGNMENT", false },
+       { "2200B", "ESCAPE CHARACTER CONFLICT", false },
+       { "22022", "INDICATOR OVERFLOW", false },
+       { "22015", "INTERVAL FIELD OVERFLOW", false },
+       { "2201E", "INVALID ARGUMENT FOR LOGARITHM", false },
+       { "2201F", "INVALID ARGUMENT FOR POWER FUNCTION", false },
+       { "2201G", "INVALID ARGUMENT FOR WIDTH BUCKET FUNCTION", false },
+       { "22018", "INVALID CHARACTER VALUE FOR CAST", false },
+       { "22007", "INVALID DATETIME FORMAT", false },
+       { "22019", "INVALID ESCAPE CHARACTER", false },
+       { "2200D", "INVALID ESCAPE OCTET", false },
+       { "22025", "INVALID ESCAPE SEQUENCE", false },
+       { "22P06", "NONSTANDARD USE OF ESCAPE CHARACTER", false },
+       { "22010", "INVALID INDICATOR PARAMETER VALUE", false },
+       { "22020", "INVALID LIMIT VALUE", false },
+       { "22023", "INVALID PARAMETER VALUE", false },
+       { "2201B", "INVALID REGULAR EXPRESSION", false },
+       { "22009", "INVALID TIME ZONE DISPLACEMENT VALUE", false },
+       { "2200C", "INVALID USE OF ESCAPE CHARACTER", false },
+       { "2200G", "MOST SPECIFIC TYPE MISMATCH", false },
+       { "22004", "NULL VALUE NOT ALLOWED", false },
+       { "22002", "NULL VALUE NO INDICATOR PARAMETER", false },
+       { "22003", "NUMERIC VALUE OUT OF RANGE", false },
+       { "22026", "STRING DATA LENGTH MISMATCH", false },
+       { "22001", "STRING DATA RIGHT TRUNCATION", false },
+       { "22011", "SUBSTRING ERROR", false },
+       { "22027", "TRIM ERROR", false },
+       { "22024", "UNTERMINATED C STRING", false },
+       { "2200F", "ZERO LENGTH CHARACTER STRING", false },
+       { "22P01", "FLOATING POINT EXCEPTION", false },
+       { "22P02", "INVALID TEXT REPRESENTATION", false },
+       { "22P03", "INVALID BINARY REPRESENTATION", false },
+       { "22P04", "BAD COPY FILE FORMAT", false },
+       { "22P05", "UNTRANSLATABLE CHARACTER", false },
+
+       { "23000", "INTEGRITY CONSTRAINT VIOLATION", false },
+       { "23001", "RESTRICT VIOLATION", false },
+       { "23502", "NOT NULL VIOLATION", false },
+       { "23503", "FOREIGN KEY VIOLATION", false },
+       { "23514", "CHECK VIOLATION", false },
+
+       { "24000", "INVALID CURSOR STATE", false },
+
+       { "25000", "INVALID TRANSACTION STATE", false },
+       { "25001", "ACTIVE SQL TRANSACTION", false },
+       { "25002", "BRANCH TRANSACTION ALREADY ACTIVE", false },
+       { "25008", "HELD CURSOR REQUIRES SAME ISOLATION LEVEL", false },
+       { "25003", "INAPPROPRIATE ACCESS MODE FOR BRANCH TRANSACTION", false },
+       { "25004", "INAPPROPRIATE ISOLATION LEVEL FOR BRANCH TRANSACTION", false },
+       { "25005", "NO ACTIVE SQL TRANSACTION FOR BRANCH TRANSACTION", false },
+       { "25006", "READ ONLY SQL TRANSACTION", false },
+       { "25007", "SCHEMA AND DATA STATEMENT MIXING NOT SUPPORTED", false },
+       { "25P01", "NO ACTIVE SQL TRANSACTION", false },
+       { "25P02", "IN FAILED SQL TRANSACTION", false },
+
+       { "26000", "INVALID SQL STATEMENT NAME", false },
+
+       { "27000", "TRIGGERED DATA CHANGE VIOLATION", false },
+
+       { "28000", "INVALID AUTHORIZATION SPECIFICATION", false },
+
+       { "2B000", "DEPENDENT PRIVILEGE DESCRIPTORS STILL EXIST", false },
+       { "2BP01", "DEPENDENT OBJECTS STILL EXIST", false },
+
+       { "2D000", "INVALID TRANSACTION TERMINATION", false },
+
+       { "2F000", "SQL ROUTINE EXCEPTION", false },
+       { "2F005", "FUNCTION EXECUTED NO RETURN STATEMENT", false },
+       { "2F002", "MODIFYING SQL DATA NOT PERMITTED", false },
+       { "2F003", "PROHIBITED SQL STATEMENT ATTEMPTED", false },
+       { "2F004", "READING SQL DATA NOT PERMITTED", false },
+
+       { "34000", "INVALID CURSOR NAME", false },
+
+       { "38000", "EXTERNAL ROUTINE EXCEPTION", false },
+       { "38001", "CONTAINING SQL NOT PERMITTED", false },
+       { "38002", "MODIFYING SQL DATA NOT PERMITTED", false },
+       { "38003", "PROHIBITED SQL STATEMENT ATTEMPTED", false },
+       { "38004", "READING SQL DATA NOT PERMITTED", false },
+
+       { "39000", "EXTERNAL ROUTINE INVOCATION EXCEPTION", false },
+       { "39001", "INVALID SQLSTATE RETURNED", false },
+       { "39004", "NULL VALUE NOT ALLOWED", false },
+       { "39P01", "TRIGGER PROTOCOL VIOLATED", false },
+       { "39P02", "SRF PROTOCOL VIOLATED", false },
+
+       { "3B000", "SAVEPOINT EXCEPTION", false },
+       { "3B001", "INVALID SAVEPOINT SPECIFICATION", false },
+
+       { "3D000", "INVALID CATALOG NAME", false },
+       { "3F000", "INVALID SCHEMA NAME", false },
+
+       { "40000", "TRANSACTION ROLLBACK", false },
+       { "40002", "TRANSACTION INTEGRITY CONSTRAINT VIOLATION", false },
+       { "40001", "SERIALIZATION FAILURE", false },
+       { "40003", "STATEMENT COMPLETION UNKNOWN", false },
+       { "40P01", "DEADLOCK DETECTED", false },
+
+       { "44000", "WITH CHECK OPTION VIOLATION", false },
+
+       { "53000", "INSUFFICIENT RESOURCES", false },
+       { "53100", "DISK FULL", false },
+       { "53200", "OUT OF MEMORY", false },
+       { "53300", "TOO MANY CONNECTIONS", false },
+
+       { "54000", "PROGRAM LIMIT EXCEEDED", false },
+       { "54001", "STATEMENT TOO COMPLEX", false },
+       { "54011", "TOO MANY COLUMNS", false },
+       { "54023", "TOO MANY ARGUMENTS", false },
+
+       { "55000", "OBJECT NOT IN PREREQUISITE STATE", false },
+       { "55006", "OBJECT IN USE", false },
+       { "55P02", "CANT CHANGE RUNTIME PARAM", false },
+       { "55P03", "LOCK NOT AVAILABLE", false },
+
+       { "57000", "OPERATOR INTERVENTION", true },
+
+       /*
+        *      This is really 'statement_timeout' or the error which is returned when
+        *      'statement_timeout' is hit.
+        *
+        *      It's unlikely that this has been caused by a connection failure, and
+        *      most likely to have been caused by a long running query.
+        *
+        *      If the query is persistently long running then the database/query should
+        *      be optimised, or 'statement_timeout' should be increased.
+        *
+        *      Forcing a reconnect here only eats more resources on the DB so we will
+        *      no longer do so as of 3.0.4.
+        */
+       { "57014", "QUERY CANCELED", false },
+       { "57P01", "ADMIN SHUTDOWN", true },
+       { "57P02", "CRASH SHUTDOWN", true },
+       { "57P03", "CANNOT CONNECT NOW", true },
+
+       { "58030", "IO ERROR", true },
+       { "58P01", "UNDEFINED FILE", true },
+       { "58P02", "DUPLICATE FILE", true },
+
+       { "F0000", "CONFIG FILE ERROR", true },
+       { "F0001", "LOCK FILE EXISTS", true },
+
+       { "P0000", "PLPGSQL ERROR", false },
+       { "P0001", "RAISE EXCEPTION", false },
+
+       { "42000", "SYNTAX ERROR OR ACCESS RULE VIOLATION", false },
+       { "42601", "SYNTAX ERROR", false },
+       { "42501", "INSUFFICIENT PRIVILEGE", false },
+       { "42846", "CANNOT COERCE", false },
+       { "42803", "GROUPING ERROR", false },
+       { "42830", "INVALID FOREIGN KEY", false },
+       { "42602", "INVALID NAME", false },
+       { "42622", "NAME TOO LONG", false },
+       { "42939", "RESERVED NAME", false },
+       { "42804", "DATATYPE MISMATCH", false },
+       { "42P18", "INDETERMINATE DATATYPE", false },
+       { "42809", "WRONG OBJECT TYPE", false },
+       { "42703", "UNDEFINED COLUMN", false },
+       { "42883", "UNDEFINED FUNCTION", false },
+       { "42P01", "UNDEFINED TABLE", false },
+       { "42P02", "UNDEFINED PARAMETER", false },
+       { "42704", "UNDEFINED OBJECT", false },
+       { "42701", "DUPLICATE COLUMN", false },
+       { "42P03", "DUPLICATE CURSOR", false },
+       { "42P04", "DUPLICATE DATABASE", false },
+       { "42723", "DUPLICATE FUNCTION", false },
+       { "42P05", "DUPLICATE PREPARED STATEMENT", false },
+       { "42P06", "DUPLICATE SCHEMA", false },
+       { "42P07", "DUPLICATE TABLE", false },
+       { "42712", "DUPLICATE ALIAS", false },
+       { "42710", "DUPLICATE OBJECT", false },
+       { "42702", "AMBIGUOUS COLUMN", false },
+       { "42725", "AMBIGUOUS FUNCTION", false },
+       { "42P08", "AMBIGUOUS PARAMETER", false },
+       { "42P09", "AMBIGUOUS ALIAS", false },
+       { "42P10", "INVALID COLUMN REFERENCE", false },
+       { "42611", "INVALID COLUMN DEFINITION", false },
+       { "42P11", "INVALID CURSOR DEFINITION", false },
+       { "42P12", "INVALID DATABASE DEFINITION", false },
+       { "42P13", "INVALID FUNCTION DEFINITION", false },
+       { "42P14", "INVALID PREPARED STATEMENT DEFINITION", false },
+       { "42P15", "INVALID SCHEMA DEFINITION", false },
+       { "42P16", "INVALID TABLE DEFINITION", false },
+       { "42P17", "INVALID OBJECT DEFINITION", false },
+
+       { "XX000", "INTERNAL ERROR", false },
+       { "XX001", "DATA CORRUPTED", false },
+       { "XX002", "INDEX CORRUPTED", false },
 
        { NULL, NULL, 0 }
 };
index 59abf27..e466871 100644 (file)
@@ -1,7 +1,22 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
-/* Define if the SQLite library has v2 API functions */
-#undef HAVE_SQLITE_V2_API
+/* Define to 1 if you have the `sqlite3_create_function_v2' function. */
+#undef HAVE_SQLITE3_CREATE_FUNCTION_V2
+
+/* Define to 1 if you have the `sqlite3_errstr' function. */
+#undef HAVE_SQLITE3_ERRSTR
+
+/* Define to 1 if you have the `sqlite3_extended_result_codes' function. */
+#undef HAVE_SQLITE3_EXTENDED_RESULT_CODES
+
+/* Define to 1 if the system has the type `sqlite3_int64'. */
+#undef HAVE_SQLITE3_INT64
+
+/* Define to 1 if you have the `sqlite3_open_v2' function. */
+#undef HAVE_SQLITE3_OPEN_V2
+
+/* Define to 1 if you have the `sqlite3_prepare_v2' function. */
+#undef HAVE_SQLITE3_PREPARE_V2
 
 /* Define to the address where bug reports for this package should be sent. */
 #undef PACKAGE_BUGREPORT
index 8a603af..443854b 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-sqlite-include-dir=DIR
-                          Directory where the sqlite includes may be found
+                         Directory where the sqlite includes may be found
   --with-sqlite-lib-dir=DIR
-                          Directory where the sqlite libraries may be found
+                         Directory where the sqlite libraries may be found
   --with-sqlite-dir=DIR   Base directory where sqlite is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1432,6 +1432,127 @@ fi
   as_fn_set_status $ac_retval
 
 } # ac_fn_c_try_link
+
+# ac_fn_c_check_func LINENO FUNC VAR
+# ----------------------------------
+# Tests whether FUNC exists, setting the cache variable VAR accordingly
+ac_fn_c_check_func ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $2 innocuous_$2
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $2 (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $2
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $2 ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined __stub_$2 || defined __stub___$2
+choke me
+#endif
+
+int
+main ()
+{
+return $2 ();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  eval "$3=yes"
+else
+  eval "$3=no"
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_func
+
+# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
+# -------------------------------------------
+# Tests whether TYPE exists after having included INCLUDES, setting cache
+# variable VAR accordingly.
+ac_fn_c_check_type ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+$as_echo_n "checking for $2... " >&6; }
+if eval \${$3+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  eval "$3=no"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof ($2))
+        return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+$4
+int
+main ()
+{
+if (sizeof (($2)))
+           return 0;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  eval "$3=yes"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$3
+              { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_type
 cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
@@ -1793,7 +1914,7 @@ SMART_CLFAGS=
 if test x$with_rlm_sql_sqlite != xno; then
 
 
-        sqlite_include_dir=
+       sqlite_include_dir=
 
 # Check whether --with-sqlite-include-dir was given.
 if test "${with_sqlite_include_dir+set}" = set; then :
@@ -1810,7 +1931,7 @@ if test "${with_sqlite_include_dir+set}" = set; then :
 fi
 
 
-        sqlite_lib_dir=
+       sqlite_lib_dir=
 
 # Check whether --with-sqlite-lib-dir was given.
 if test "${with_sqlite_lib_dir+set}" = set; then :
@@ -1943,7 +2064,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2165,7 +2286,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2636,7 +2757,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 
 
 
-        smart_try_dir="$sqlite_lib_dir"
+       smart_try_dir="$sqlite_lib_dir"
 
 
 
@@ -2697,8 +2818,8 @@ sqlite3_open()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lsqlite3"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lsqlite3"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2714,22 +2835,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libsqlite3${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2741,22 +2862,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libsqlite3.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2804,184 +2925,31 @@ if test "x$smart_lib" != "x"; then
   SMART_LIBS="$smart_lib $SMART_LIBS"
 fi
 
+       LDFLAGS="$SMART_LIBS"
     if test "x$ac_cv_lib_sqlite3_sqlite3_open" != "xyes"
     then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Sqlite libraries not found. Use --with-sqlite-lib-dir=<path>." >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Sqlite libraries not found. Use --with-sqlite-lib-dir=<path>." >&5
 $as_echo "$as_me: WARNING: Sqlite libraries not found. Use --with-sqlite-lib-dir=<path>." >&2;}
-        fail="$fail libsqlite3"
+       fail="$fail libsqlite3"
     else
-
-
-sm_lib_safe=`echo "sqlite3" | sed 'y%./+-%__p_%'`
-sm_func_safe=`echo "sqlite3_open_v2" | sed 'y%./+-%__p_%'`
-
-old_LIBS="$LIBS"
-smart_lib=
-smart_lib_dir=
-
-if test "x$smart_try_dir" != "x"; then
-  for try in $smart_try_dir; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_open_v2 in -lsqlite3 in $try" >&5
-$as_echo_n "checking for sqlite3_open_v2 in -lsqlite3 in $try... " >&6; }
-    LIBS="-L$try -lsqlite3 $old_LIBS -Wl,-rpath,$try"
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-extern char sqlite3_open_v2();
-int
-main ()
-{
-sqlite3_open_v2()
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-
-                smart_lib="-L$try -lsqlite3 -Wl,-rpath,$try"
-                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-                break
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-  done
-  LIBS="$old_LIBS"
-fi
-
-if test "x$smart_lib" = "x"; then
-  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_open_v2 in -lsqlite3" >&5
-$as_echo_n "checking for sqlite3_open_v2 in -lsqlite3... " >&6; }
-  LIBS="-lsqlite3 $old_LIBS"
-  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-extern char sqlite3_open_v2();
-int
-main ()
-{
-sqlite3_open_v2()
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
-
-               smart_lib="-lsqlite3"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-  LIBS="$old_LIBS"
-fi
-
-if test "x$smart_lib" = "x"; then
-
-
-if test "x$LOCATE" != "x"; then
-        DIRS=
-  file=libsqlite3${libltdl_cv_shlibext}
-
-  for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
-    if test "x$x" = "x$base"; then
-      continue;
-    fi
-
-    dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
-    if test "x$exclude" != "x"; then
-      continue
-    fi
-
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
-    if test "x$already" = "x"; then
-      DIRS="$DIRS $dir"
-    fi
-  done
-fi
-
-eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
-
-
-
-if test "x$LOCATE" != "x"; then
-        DIRS=
-  file=libsqlite3.a
-
-  for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
-    if test "x$x" = "x$base"; then
-      continue;
-    fi
-
-    dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
-    if test "x$exclude" != "x"; then
-      continue
-    fi
-
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
-    if test "x$already" = "x"; then
-      DIRS="$DIRS $dir"
-    fi
-  done
-fi
-
-eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
-
-
-  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
-    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for sqlite3_open_v2 in -lsqlite3 in $try" >&5
-$as_echo_n "checking for sqlite3_open_v2 in -lsqlite3 in $try... " >&6; }
-    LIBS="-L$try -lsqlite3 $old_LIBS -Wl,-rpath,$try"
-    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-extern char sqlite3_open_v2();
-int
-main ()
-{
-sqlite3_open_v2()
-  ;
-  return 0;
-}
+               for ac_func in \
+               sqlite3_prepare_v2 \
+               sqlite3_open_v2 \
+               sqlite3_create_function_v2 \
+               sqlite3_errstr \
+               sqlite3_extended_result_codes \
+
+do :
+  as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
+ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
+  cat >>confdefs.h <<_ACEOF
+#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
 _ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
 
-                 smart_lib="-L$try -lsqlite3 -Wl,-rpath,$try"
-                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-                 break
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-  done
-  LIBS="$old_LIBS"
-fi
-
-if test "x$smart_lib" != "x"; then
-  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
-  LIBS="$smart_lib $old_LIBS"
-  SMART_LIBS="$smart_lib $SMART_LIBS"
 fi
+done
 
-        if test "x$ac_cv_lib_sqlite3_sqlite3_open_v2" == "xyes"
-       then
-
-$as_echo "#define HAVE_SQLITE_V2_API 1" >>confdefs.h
-
-        fi
     fi
 
 
@@ -3065,22 +3033,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=sqlite3.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3131,10 +3099,22 @@ if test "x$smart_include" != "x"; then
 fi
 
     if test "x$ac_cv_header_sqlite3_h" != "xyes"; then
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Sqlite headers not found. Use --with-sqlite-include-dir=<path>." >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Sqlite headers not found. Use --with-sqlite-include-dir=<path>." >&5
 $as_echo "$as_me: WARNING: Sqlite headers not found. Use --with-sqlite-include-dir=<path>." >&2;}
-        fail="$fail sqlite.h"
+       fail="$fail sqlite.h"
     fi
+    CFLAGS="$SMART_CPPFLAGS"
+    ac_fn_c_check_type "$LINENO" "sqlite3_int64" "ac_cv_type_sqlite3_int64" "#include <sqlite3.h>
+"
+if test "x$ac_cv_type_sqlite3_int64" = xyes; then :
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SQLITE3_INT64 1
+_ACEOF
+
+
+fi
+
 
     targetname=rlm_sql_sqlite
 else
@@ -3239,11 +3219,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3717,13 +3697,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index 94ab603..7dad077 100644 (file)
@@ -73,16 +73,21 @@ if test x$with_[]modname != xno; then
     dnl try to link to libsqlite3
     smart_try_dir="$sqlite_lib_dir"
     FR_SMART_CHECK_LIB(sqlite3, sqlite3_open)
+    dnl # Ensure we use the library we just found the rest of the checks
+    LDFLAGS="$SMART_LIBS"
     if test "x$ac_cv_lib_sqlite3_sqlite3_open" != "xyes"
     then
-        AC_MSG_WARN([Sqlite libraries not found. Use --with-sqlite-lib-dir=<path>.])
-        fail="$fail libsqlite3"
+       AC_MSG_WARN([Sqlite libraries not found. Use --with-sqlite-lib-dir=<path>.])
+       fail="$fail libsqlite3"
     else
-        FR_SMART_CHECK_LIB(sqlite3, sqlite3_open_v2)
-        if test "x$ac_cv_lib_sqlite3_sqlite3_open_v2" == "xyes"
-       then
-            AC_DEFINE(HAVE_SQLITE_V2_API, [1], [Define if the SQLite library has v2 API functions])   
-        fi
+       dnl # Add any v2 variants here
+       AC_CHECK_FUNCS(\
+               sqlite3_prepare_v2 \
+               sqlite3_open_v2 \
+               sqlite3_create_function_v2 \
+               sqlite3_errstr \
+               sqlite3_extended_result_codes \
+       )
     fi
 
     dnl ############################################################
@@ -92,9 +97,11 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$sqlite_include_dir"
     FR_SMART_CHECK_INCLUDE(sqlite3.h)
     if test "x$ac_cv_header_sqlite3_h" != "xyes"; then
-        AC_MSG_WARN([Sqlite headers not found. Use --with-sqlite-include-dir=<path>.])
-        fail="$fail sqlite.h"
+       AC_MSG_WARN([Sqlite headers not found. Use --with-sqlite-include-dir=<path>.])
+       fail="$fail sqlite.h"
     fi
+    CFLAGS="$SMART_CPPFLAGS"
+    AC_CHECK_TYPES([sqlite3_int64], [], [], [[#include <sqlite3.h>]])
 
     targetname=modname
 else
index 31fca11..44d1369 100644 (file)
@@ -43,6 +43,10 @@ RCSID("$Id$")
 #  define SQLITE_OPEN_NOMUTEX 0
 #endif
 
+#ifndef HAVE_SQLITE3_INT64
+typedef sqlite3_int64 sqlite_int64
+#endif
+
 typedef struct rlm_sql_sqlite_conn {
        sqlite3 *db;
        sqlite3_stmt *statement;
@@ -55,10 +59,8 @@ typedef struct rlm_sql_sqlite_config {
 } rlm_sql_sqlite_config_t;
 
 static const CONF_PARSER driver_config[] = {
-       {"filename", PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED,
-        offsetof(rlm_sql_sqlite_config_t, filename), NULL, NULL},
-       {"bootstrap", PW_TYPE_FILE_INPUT,
-        offsetof(rlm_sql_sqlite_config_t, bootstrap), NULL, NULL},
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, rlm_sql_sqlite_config_t, filename), NULL },
+       { "bootstrap", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_sql_sqlite_config_t, bootstrap), NULL },
 
        {NULL, -1, 0, NULL, NULL}
 };
@@ -94,7 +96,7 @@ static int sql_check_error(sqlite3 *db)
        }
 }
 
-#ifdef HAVE_SQLITE_V2_API
+#ifdef HAVE_SQLITE3_OPEN_V2
 static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
 {
        ssize_t len;
@@ -113,14 +115,14 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
        f = fopen(filename, "r");
        if (!f) {
                ERROR("rlm_sql_sqlite: Failed opening SQL file \"%s\": %s", filename,
-                      strerror(errno));
+                      fr_syserror(errno));
 
                return -1;
        }
 
        if (fstat(fileno(f), &finfo) < 0) {
                ERROR("rlm_sql_sqlite: Failed stating SQL file \"%s\": %s", filename,
-                      strerror(errno));
+                      fr_syserror(errno));
 
                fclose(f);
 
@@ -146,7 +148,7 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
 
        if (!len) {
                if (ferror(f)) {
-                       ERROR("rlm_sql_sqlite: Error reading SQL file: %s", strerror(errno));
+                       ERROR("rlm_sql_sqlite: Error reading SQL file: %s", fr_syserror(errno));
 
                        fclose(f);
                        talloc_free(buffer);
@@ -196,7 +198,11 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
 
                *q = '\0';
 
+#ifdef HAVE_SQLITE3_PREPARE_V2
                (void) sqlite3_prepare_v2(db, s, len, &statement, &z_tail);
+#else
+               (void) sqlite3_prepare(db, s, len, &>statement, &z_tail);
+#endif
                if (sql_check_error(db)) {
                        talloc_free(buffer);
                        return -1;
@@ -221,8 +227,9 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename)
 
 static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
 {
+       bool exists;
        rlm_sql_sqlite_config_t *driver;
-       int exists;
+       struct stat buf;
 
        if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) {
                DEBUG2("rlm_sql_sqlite: SQLite library version (%s) is different from the version the server was "
@@ -231,25 +238,26 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
        }
 
        MEM(driver = config->driver = talloc_zero(config, rlm_sql_sqlite_config_t));
-
        if (cf_section_parse(conf, driver, driver_config) < 0) {
                return -1;
        }
 
        INFO("rlm_sql_sqlite: SQLite library version: %s", sqlite3_libversion());
        if (!driver->filename) {
-               MEM(driver->filename = talloc_asprintf(driver, "%s/%s", radius_dir, config->sql_db));
+               MEM(driver->filename = talloc_typed_asprintf(driver, "%s/%s", get_radius_dir(), config->sql_db));
        }
 
-       exists = rad_file_exists(driver->filename);
-       if (exists < 0) {
-               ERROR("rlm_sql_sqlite: Database exists, but couldn't be opened: %s", strerror(errno));
-
+       if (stat(driver->filename, &buf) == 0) {
+               exists = true;
+       } else if (errno == ENOENT) {
+               exists = false;
+       } else {
+               ERROR("rlm_sql_sqlite: Database exists, but couldn't be opened: %s", fr_syserror(errno));
                return -1;
        }
 
        if (driver->bootstrap && !exists) {
-#ifdef HAVE_SQLITE_V2_API
+#  ifdef HAVE_SQLITE3_OPEN_V2
                int status;
                int ret;
                char *p;
@@ -265,23 +273,26 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
                        buff = talloc_array(conf, char, len);
                        strlcpy(buff, driver->filename, len);
                } else {
-                       MEM(buff = talloc_strdup(conf, driver->filename));
+                       MEM(buff = talloc_typed_strdup(conf, driver->filename));
                }
 
-               if (rad_mkdir(buff, 0700) < 0) {
-                       ERROR("rlm_sql_sqlite: Failed creating directory for SQLite database");
-
-                       talloc_free(buff);
+               ret = rad_mkdir(buff, 0700);
+               talloc_free(buff);
+               if (ret < 0) {
+                       ERROR("rlm_sql_sqlite: Failed creating directory for SQLite database: %s", fr_syserror(errno));
 
                        return -1;
-               }
-
-               talloc_free(buff);
+               };
 
                status = sqlite3_open_v2(driver->filename, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
                if (!db) {
-                       ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite database, error "
-                              "code (%u)", status);
+#  ifdef HAVE_SQLITE3_ERRSTR
+                       ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite database: %s",
+                             sqlite3_errstr(status));
+#  else
+                       ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite database, got code (%i)",
+                             status);
+#  endif
 
                        goto unlink;
                }
@@ -295,19 +306,27 @@ static int mod_instantiate(CONF_SECTION *conf, rlm_sql_config_t *config)
                ret = sql_loadfile(conf, db, driver->bootstrap);
                status = sqlite3_close(db);
                if (status != SQLITE_OK) {
-                       ERROR("rlm_sql_sqlite: Error closing SQLite handle, error code (%u)", status);
+               /*
+                *      Safer to use sqlite3_errstr here, just in case the handle is in a weird state
+                */
+#  ifdef HAVE_SQLITE3_ERRSTR
+                       ERROR("rlm_sql_sqlite: Error closing SQLite handle: %s", sqlite3_errstr(status));
+#  else
+                       ERROR("rlm_sql_sqlite: Error closing SQLite handle, got code (%i)", status);
+#  endif
+
                        goto unlink;
                }
                if (ret < 0) {
-                       unlink:
-                       if (unlink(driver->filename) < 0) {
+               unlink:
+                       if ((unlink(driver->filename) < 0) && (errno != ENOENT)) {
                                ERROR("rlm_sql_sqlite: Error removing partially initialised database: %s",
-                                      strerror(errno));
+                                     fr_syserror(errno));
                        }
                        return -1;
                }
 #else
-               WDEBUG("rlm_sql_sqlite: sqlite3_open_v2() not available, cannot bootstrap database. "
+               WARN("rlm_sql_sqlite: sqlite3_open_v2() not available, cannot bootstrap database. "
                       "Upgrade to SQLite >= 3.5.1 if you need this functionality");
 #endif
        }
@@ -325,7 +344,7 @@ static int sql_socket_destructor(void *c)
        if (conn->db) {
                status = sqlite3_close(conn->db);
                if (status != SQLITE_OK) {
-                       WDEBUG("rlm_sql_sqlite: Got SQLite error code (%u) when closing socket", status);
+                       WARN("rlm_sql_sqlite: Got SQLite error code (%u) when closing socket", status);
                }
        }
 
@@ -358,15 +377,18 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        talloc_set_destructor((void *) conn, sql_socket_destructor);
 
        INFO("rlm_sql_sqlite: Opening SQLite database \"%s\"", driver->filename);
-
-#ifdef HAVE_SQLITE_V2_API
+#ifdef HAVE_SQLITE3_OPEN_V2
        status = sqlite3_open_v2(driver->filename, &(conn->db), SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX, NULL);
 #else
        status = sqlite3_open(driver->filename, &(conn->db));
 #endif
        if (!conn->db) {
-               ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite database error code (%u)",
-                      status);
+#ifdef HAVE_SQLITE3_ERRSTR
+               ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite: %s", sqlite3_errstr(status));
+#else
+               ERROR("rlm_sql_sqlite: Failed creating opening/creating SQLite database error code (%i)",
+                     status);
+#endif
 
                return -1;
        }
@@ -378,13 +400,14 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        /*
         *      Enable extended return codes for extra debugging info.
         */
-       status = sqlite3_extended_result_codes(conn->db, 1);
-
+#ifdef HAVE_SQLITE3_EXTENDED_RESULT_CODES
+       (void) sqlite3_extended_result_codes(conn->db, 1);
+#endif
        if (sql_check_error(conn->db)) {
                return -1;
        }
 
-#ifdef HAVE_SQLITE_V2_API
+#ifdef HAVE_SQLITE3_CREATE_FUNCTION_V2
        status = sqlite3_create_function_v2(conn->db, "GREATEST", -1, SQLITE_ANY, NULL,
                                            _sql_greatest, NULL, NULL, NULL);
 #else
@@ -403,7 +426,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_con
        rlm_sql_sqlite_conn_t *conn = handle->conn;
        char const *z_tail;
 
-#ifdef HAVE_SQLITE_V2_API
+#ifdef HAVE_SQLITE3_PREPARE_V2
        (void) sqlite3_prepare_v2(conn->db, query, strlen(query), &conn->statement, &z_tail);
 #else
        (void) sqlite3_prepare(conn->db, query, strlen(query), &conn->statement, &z_tail);
@@ -421,7 +444,7 @@ static sql_rcode_t sql_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *
        rlm_sql_sqlite_conn_t *conn = handle->conn;
        char const *z_tail;
 
-#ifdef HAVE_SQLITE_V2_API
+#ifdef HAVE_SQLITE3_PREPARE_V2
        status = sqlite3_prepare_v2(conn->db, query, strlen(query), &conn->statement, &z_tail);
 #else
        status = sqlite3_prepare(conn->db, query, strlen(query), &conn->statement, &z_tail);
@@ -511,11 +534,11 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
        for (i = 0; i < conn->col_count; i++) {
                switch (sqlite3_column_type(conn->statement, i)) {
                case SQLITE_INTEGER:
-                       MEM(row[i] = talloc_asprintf(row, "%d", sqlite3_column_int(conn->statement, i)));
+                       MEM(row[i] = talloc_typed_asprintf(row, "%d", sqlite3_column_int(conn->statement, i)));
                        break;
 
                case SQLITE_FLOAT:
-                       MEM(row[i] = talloc_asprintf(row, "%f", sqlite3_column_double(conn->statement, i)));
+                       MEM(row[i] = talloc_typed_asprintf(row, "%f", sqlite3_column_double(conn->statement, i)));
                        break;
 
                case SQLITE_TEXT:
@@ -524,7 +547,7 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
                                p = (char const *) sqlite3_column_text(conn->statement, i);
 
                                if (p) {
-                                       MEM(row[i] = talloc_strdup(row, p));
+                                       MEM(row[i] = talloc_typed_strdup(row, p));
                                }
                        }
                        break;
index 558e9c3..0e09f60 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1210,9 +1210,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1255,19 +1255,19 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-unixodbc-include-dir=DIR
-                          Directory where the unixODBC includes may be found
+                         Directory where the unixODBC includes may be found
   --with-unixodbc-lib-dir=DIR
-                          Directory where the unixODBC libraries may be found
+                         Directory where the unixODBC libraries may be found
   --with-unixodbc-dir=DIR Base directory where unixODBC is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1942,7 +1942,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2164,7 +2164,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2693,8 +2693,8 @@ SQLConnect()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lodbc"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lodbc"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2710,22 +2710,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libodbc${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2737,22 +2737,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libodbc.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2884,22 +2884,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=sql.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3052,11 +3052,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3562,11 +3562,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 13676c5..3f929fd 100644 (file)
@@ -35,6 +35,7 @@ typedef struct rlm_sql_unixodbc_conn {
 } rlm_sql_unixodbc_conn_t;
 
 
+USES_APPLE_DEPRECATED_API
 #include <sql.h>
 #include <sqlext.h>
 
@@ -102,7 +103,7 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c
        if (sql_state(err_handle, handle, config)) {
                ERROR("rlm_sql_unixodbc: Can't allocate connection handle");
                return -1;
-       }
+       }
 
        /* 3. Connect to the datasource */
        {
@@ -271,8 +272,8 @@ static sql_rcode_t sql_fetch_row(rlm_sql_handle_t *handle, rlm_sql_config_t *con
 
        if ((state = sql_state(err_handle, handle, config))) {
                if(state == RLM_SQL_RECONNECT) {
-                       DEBUG("rlm_sql_unixodbc: rlm_sql will attempt to reconnect");
-               }
+                       DEBUG("rlm_sql_unixodbc: rlm_sql will attempt to reconnect");
+               }
 
                return state;
        }
@@ -415,7 +416,7 @@ static sql_rcode_t sql_state(long err_handle, rlm_sql_handle_t *handle, UNUSED r
                default:
                        ERROR("rlm_sql_unixodbc: %s %s", state, error);
                        res = -1;
-                       break;
+                       break;
                }
        }
 
index 15ed23a..a6d5639 100644 (file)
@@ -37,75 +37,46 @@ RCSID("$Id$")
 #include "rlm_sql.h"
 
 static const CONF_PARSER acct_section_config[] = {
-       {"reference", PW_TYPE_STRING_PTR,
-         offsetof(sql_acct_section_t, reference), NULL, ".query"},
-       {"logfile", PW_TYPE_STRING_PTR,
-        offsetof(sql_acct_section_t, logfile), NULL, NULL},
+       { "reference", FR_CONF_OFFSET(PW_TYPE_STRING, sql_acct_section_t, reference), ".query" },
+       { "logfile", FR_CONF_OFFSET(PW_TYPE_STRING, sql_acct_section_t, logfile), NULL },
 
        {NULL, -1, 0, NULL, NULL}
 };
 
 static const CONF_PARSER module_config[] = {
-       {"driver", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,sql_driver_name), NULL, "rlm_sql_null"},
-       {"server", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,sql_server), NULL, "localhost"},
-       {"port", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,sql_port), NULL, ""},
-       {"login", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,sql_login), NULL, ""},
-       {"password", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,sql_password), NULL, ""},
-       {"radius_db", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,sql_db), NULL, "radius"},
-       {"read_groups", PW_TYPE_BOOLEAN,
-        offsetof(rlm_sql_config_t,read_groups), NULL, "yes"},
-       {"readclients", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED,
-        offsetof(rlm_sql_config_t,do_clients), NULL, NULL},
-       {"read_clients", PW_TYPE_BOOLEAN,
-        offsetof(rlm_sql_config_t,do_clients), NULL, "no"},
-       {"deletestalesessions", PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED,
-        offsetof(rlm_sql_config_t,deletestalesessions), NULL, NULL},
-       {"delete_stale_sessions", PW_TYPE_BOOLEAN,
-        offsetof(rlm_sql_config_t,deletestalesessions), NULL, "yes"},
-       {"sql_user_name", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,query_user), NULL, ""},
-       {"logfile", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,logfile), NULL, NULL},
-       {"default_user_profile", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,default_profile), NULL, ""},
-       {"nas_query", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-        offsetof(rlm_sql_config_t,client_query), NULL, NULL},
-       {"client_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,client_query), NULL,
-        "SELECT id,nasname,shortname,type,secret FROM nas"},
-       {"authorize_check_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,authorize_check_query), NULL, ""},
-       {"authorize_reply_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,authorize_reply_query), NULL, NULL},
-       {"authorize_group_check_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,authorize_group_check_query), NULL, ""},
-       {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,authorize_group_reply_query), NULL, ""},
-       {"group_membership_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,groupmemb_query), NULL, NULL},
+       { "driver", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_driver_name), "rlm_sql_null" },
+       { "server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_server), "localhost" },
+       { "port", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_port), "" },
+       { "login", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_login), "" },
+       { "password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_sql_config_t, sql_password), "" },
+       { "radius_db", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, sql_db), "radius" },
+       { "read_groups", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, read_groups), "yes" },
+       { "readclients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_sql_config_t, do_clients), NULL },
+       { "read_clients", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, do_clients), "no" },
+       { "deletestalesessions", FR_CONF_OFFSET(PW_TYPE_BOOLEAN | PW_TYPE_DEPRECATED, rlm_sql_config_t, deletestalesessions), NULL },
+       { "delete_stale_sessions", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sql_config_t, deletestalesessions), "yes" },
+       { "sql_user_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, query_user), "" },
+       { "logfile", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, logfile), NULL },
+       { "default_user_profile", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, default_profile), "" },
+       { "nas_query", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sql_config_t, client_query), NULL },
+       { "client_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, client_query), "SELECT id,nasname,shortname,type,secret FROM nas" },
+       { "authorize_check_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_check_query), "" },
+       { "open_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, open_query), NULL },
+       { "authorize_reply_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_reply_query), NULL },
+       { "authorize_group_check_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_group_check_query), "" },
+       { "authorize_group_reply_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, authorize_group_reply_query), "" },
+       { "group_membership_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, groupmemb_query), NULL },
 #ifdef WITH_SESSION_MGMT
-       {"simul_count_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,simul_count_query), NULL, ""},
-       {"simul_verify_query", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,simul_verify_query), NULL, ""},
+       { "simul_count_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, simul_count_query), "" },
+       { "simul_verify_query", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, simul_verify_query), "" },
 #endif
-       {"safe-characters", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-        offsetof(rlm_sql_config_t,allowed_chars), NULL, NULL},
-       {"safe_characters", PW_TYPE_STRING_PTR,
-        offsetof(rlm_sql_config_t,allowed_chars), NULL,
-       "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
+       { "safe-characters", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sql_config_t, allowed_chars), NULL },
+       { "safe_characters", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sql_config_t, allowed_chars), "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /" },
 
        /*
         *      This only works for a few drivers.
         */
-       {"query_timeout", PW_TYPE_INTEGER,
-        offsetof(rlm_sql_config_t,query_timeout), NULL, NULL},
+       { "query_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sql_config_t, query_timeout), NULL },
 
        {NULL, -1, 0, NULL, NULL}
 };
@@ -166,7 +137,11 @@ static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, cha
                int numaffected;
                char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
 
-               if (rlm_sql_query(&handle, inst, query)) {
+               if (rlm_sql_query(&handle, inst, query) != RLM_SQL_OK) {
+                       char const *error = (inst->module->sql_error)(handle, inst->config);
+                       REDEBUG("SQL query failed: %s", error);
+
+                       ret = -1;
                        goto finish;
                }
 
@@ -205,7 +180,9 @@ static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, cha
                goto finish;
        } /* else it's a SELECT statement */
 
-       if (rlm_sql_select_query(&handle, inst, query)){
+       if (rlm_sql_select_query(&handle, inst, query) != RLM_SQL_OK){
+               char const *error = (inst->module->sql_error)(handle, inst->config);
+               REDEBUG("SQL query failed: %s", error);
                ret = -1;
 
                goto finish;
@@ -213,7 +190,7 @@ static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, cha
 
        ret = rlm_sql_fetch_row(&handle, inst);
        if (ret) {
-               RDEBUG("SQL query failed");
+               REDEBUG("SQL query failed");
                (inst->module->sql_finish_select_query)(handle, inst->config);
                ret = -1;
 
@@ -230,7 +207,7 @@ static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, cha
        }
 
        if (!row[0]){
-               RDEBUG("Null value in first column");
+               RDEBUG("NULL value in first column of result");
                (inst->module->sql_finish_select_query)(handle, inst->config);
                ret = -1;
 
@@ -249,11 +226,9 @@ static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, cha
        strlcpy(out, row[0], freespace);
        ret = len;
 
-       RDEBUG("sql_xlat finished");
-
        (inst->module->sql_finish_select_query)(handle, inst->config);
 
-       finish:
+finish:
        sql_release_socket(inst, handle);
 
        return ret;
@@ -277,11 +252,11 @@ static int generate_sql_clients(rlm_sql_t *inst)
                return -1;
        }
 
-       if (rlm_sql_select_query(&handle, inst, inst->config->client_query)){
+       if (rlm_sql_select_query(&handle, inst, inst->config->client_query) != RLM_SQL_OK){
                return -1;
        }
 
-       while((rlm_sql_fetch_row(&handle, inst) == 0) && (row = handle->row)) {
+       while ((rlm_sql_fetch_row(&handle, inst) == 0) && (row = handle->row)) {
                char *server = NULL;
                i++;
 
@@ -416,6 +391,8 @@ int sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username)
        char const *sqluser;
        ssize_t len;
 
+       rad_assert(request->packet != NULL);
+
        if (username != NULL) {
                sqluser = username;
        } else if (inst->config->query_user[0] != '\0') {
@@ -438,13 +415,13 @@ int sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username)
        pairstrsteal(vp, expanded);
        RDEBUG2("SQL-User-Name set to '%s'", vp->vp_strvalue);
        vp->op = T_OP_SET;
-       pairmove(request, &request->packet->vps, &vp);  /* needs to be pair move else op is not respected */
+       radius_pairmove(request, &request->packet->vps, vp, false);     /* needs to be pair move else op is not respected */
 
        return 0;
 }
 
 
-static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t *handle, REQUEST *request,
+static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t **handle, REQUEST *request,
                             rlm_sql_grouplist_t **phead)
 {
        char    *expanded = NULL;
@@ -465,35 +442,38 @@ static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t *handle, REQUEST
                return -1;
        }
 
-       ret = rlm_sql_select_query(&handle, inst, expanded);
+       ret = rlm_sql_select_query(handle, inst, expanded);
        talloc_free(expanded);
-       if (ret < 0) {
+       if (ret != RLM_SQL_OK) {
                return -1;
        }
 
-       while (rlm_sql_fetch_row(&handle, inst) == 0) {
-               row = handle->row;
+       while (rlm_sql_fetch_row(handle, inst) == 0) {
+               row = (*handle)->row;
                if (!row)
                        break;
+
                if (!row[0]){
                        RDEBUG("row[0] returned NULL");
-                       (inst->module->sql_finish_select_query)(handle, inst->config);
+                       (inst->module->sql_finish_select_query)(*handle, inst->config);
                        talloc_free(entry);
                        return -1;
                }
 
                if (!*phead) {
-                       *phead = talloc_zero(handle, rlm_sql_grouplist_t);
+                       *phead = talloc_zero(*handle, rlm_sql_grouplist_t);
                        entry = *phead;
                } else {
                        entry->next = talloc_zero(*phead, rlm_sql_grouplist_t);
                        entry = entry->next;
                }
                entry->next = NULL;
-               entry->name = talloc_strdup(entry, row[0]);
+               entry->name = talloc_typed_strdup(entry, row[0]);
+
+               num_groups++;
        }
 
-       (inst->module->sql_finish_select_query)(handle, inst->config);
+       (inst->module->sql_finish_select_query)(*handle, inst->config);
 
        return num_groups;
 }
@@ -506,22 +486,21 @@ static int sql_get_grouplist(rlm_sql_t *inst, rlm_sql_handle_t *handle, REQUEST
  * username will then be checked with the passed check string.
  */
 
-static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp, VALUE_PAIR *check,
-                       UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs)
+static int CC_HINT(nonnull (1 ,2, 4)) sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp,
+                                                  VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs,
+                                                  UNUSED VALUE_PAIR **reply_pairs)
 {
        rlm_sql_handle_t *handle;
        rlm_sql_t *inst = instance;
        rlm_sql_grouplist_t *head, *entry;
 
        RDEBUG("sql_groupcmp");
-       if (!check || !check->length){
+
+       if (check->length == 0){
                RDEBUG("sql_groupcmp: Illegal group name");
                return 1;
        }
-       if (!request){
-               RDEBUG("sql_groupcmp: NULL request");
-               return 1;
-       }
+
        /*
         *      Set, escape, and check the user attr here
         */
@@ -539,7 +518,7 @@ static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *req
        /*
         *      Get the list of groups this user is a member of
         */
-       if (sql_get_grouplist(inst, handle, request, &head) < 0) {
+       if (sql_get_grouplist(inst, &handle, request, &head) < 0) {
                REDEBUG("Error getting group membership");
                sql_release_socket(inst, handle);
                return 1;
@@ -557,15 +536,14 @@ static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *req
 
        /* Free the grouplist */
        talloc_free(head);
-       sql_release_socket(inst,handle);
+       sql_release_socket(inst, handle);
 
-       RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
-              check->vp_strvalue);
+       RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue);
 
        return 1;
 }
 
-static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t *handle,
+static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle,
                                          bool *dofallthrough)
 {
        rlm_rcode_t             rcode = RLM_MODULE_NOOP;
@@ -575,6 +553,8 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
        char                    *expanded = NULL;
        int                     rows;
 
+       rad_assert(request->packet != NULL);
+
        /*
         *      Get the list of groups this user is a member of
         */
@@ -585,6 +565,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                return RLM_MODULE_FAIL;
        }
        if (rows == 0) {
+               RDEBUG2("User not found in any groups");
                rcode = RLM_MODULE_NOTFOUND;
                goto finish;
        }
@@ -614,7 +595,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                                goto finish;
                        }
 
-                       rows = sql_getvpdata(inst, &handle, request, &check_tmp, expanded);
+                       rows = sql_getvpdata(inst, handle, request, &check_tmp, expanded);
                        TALLOC_FREE(expanded);
                        if (rows < 0) {
                                REDEBUG("Error retrieving check pairs for group %s", entry->name);
@@ -636,7 +617,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                        RDEBUG2("Group \"%s\" check items matched", entry->name);
                        rcode = RLM_MODULE_OK;
 
-                       radius_xlat_move(request, &request->config_items, &check_tmp);
+                       radius_pairmove(request, &request->config_items, check_tmp, true);
                        check_tmp = NULL;
                }
 
@@ -651,7 +632,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                                goto finish;
                        }
 
-                       rows = sql_getvpdata(inst, &handle, request->reply, &reply_tmp, expanded);
+                       rows = sql_getvpdata(inst, handle, request->reply, &reply_tmp, expanded);
                        TALLOC_FREE(expanded);
                        if (rows < 0) {
                                REDEBUG("Error retrieving reply pairs for group %s", entry->name);
@@ -664,7 +645,7 @@ static rlm_rcode_t rlm_sql_process_groups(rlm_sql_t *inst, REQUEST *request, rlm
                        RDEBUG2("Group \"%s\" reply items processed", entry->name);
                        rcode = RLM_MODULE_OK;
 
-                       radius_xlat_move(request, &request->reply->vps, &reply_tmp);
+                       radius_pairmove(request, &request->reply->vps, reply_tmp, true);
                        reply_tmp = NULL;
                }
 
@@ -701,9 +682,9 @@ static int mod_detach(void *instance)
 }
 
 static int parse_sub_section(CONF_SECTION *parent,
-                            rlm_sql_t *inst,
-                            sql_acct_section_t **config,
-                            rlm_components_t comp)
+                            rlm_sql_t *inst,
+                            sql_acct_section_t **config,
+                            rlm_components_t comp)
 {
        CONF_SECTION *cs;
 
@@ -746,13 +727,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                inst->config->xlat_name = cf_section_name1(conf);
        } else {
                char *group_name;
-               DICT_ATTR const *dattr;
+               DICT_ATTR const *da;
                ATTR_FLAGS flags;
 
                /*
                 *      Allocate room for <instance>-SQL-Group
                 */
-               group_name = talloc_asprintf(inst, "%s-SQL-Group", inst->config->xlat_name);
+               group_name = talloc_typed_asprintf(inst, "%s-SQL-Group", inst->config->xlat_name);
                DEBUG("rlm_sql (%s): Creating new attribute %s",
                      inst->config->xlat_name, group_name);
 
@@ -764,8 +745,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                        return -1;
                }
 
-               dattr = dict_attrbyname(group_name);
-               if (!dattr) {
+               da = dict_attrbyname(group_name);
+               if (!da) {
                        ERROR("rlm_sql (%s): Failed to create "
                               "attribute %s", inst->config->xlat_name, group_name);
                        return -1;
@@ -775,7 +756,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                    inst->config->groupmemb_query[0]) {
                        DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s",
                              inst->config->xlat_name, group_name);
-                       paircompare_register(dattr, dict_attrbyvalue(PW_USER_NAME, 0),
+                       paircompare_register(da, dict_attrbyvalue(PW_USER_NAME, 0),
                                             false, sql_groupcmp, inst);
                }
        }
@@ -785,12 +766,8 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        /*
         *      If the configuration parameters can't be parsed, then fail.
         */
-       if ((parse_sub_section(conf, inst,
-                              &inst->config->accounting,
-                              RLM_COMPONENT_ACCT) < 0) ||
-           (parse_sub_section(conf, inst,
-                              &inst->config->postauth,
-                              RLM_COMPONENT_POST_AUTH) < 0)) {
+       if ((parse_sub_section(conf, inst, &inst->config->accounting, RLM_COMPONENT_ACCT) < 0) ||
+           (parse_sub_section(conf, inst, &inst->config->postauth, RLM_COMPONENT_POST_AUTH) < 0)) {
                cf_log_err_cs(conf, "Invalid configuration");
                return -1;
        }
@@ -838,7 +815,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                       inst->config->sql_driver_name,
                       dlerror());
                ERROR("Make sure it (and all its dependent libraries!)"
-                      "are in the search path of your system's ld.");
+                      "are in the search path of your system's ld");
                return -1;
        }
 
@@ -878,6 +855,12 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                }
        }
 
+       inst->lf = fr_logfile_init(inst);
+       if (!inst->lf) {
+               cf_log_err_cs(conf, "Failed creating log file context");
+               return -1;
+       }
+
        INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked",
               inst->config->xlat_name, inst->config->sql_driver_name,
               inst->module->name);
@@ -898,7 +881,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
        if (inst->config->do_clients) {
                if (generate_sql_clients(inst) == -1){
-                       ERROR("Failed to load clients from SQL.");
+                       ERROR("Failed to load clients from SQL");
                        return -1;
                }
        }
@@ -907,7 +890,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 }
 
 
-static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST * request)
 {
        rlm_rcode_t rcode = RLM_MODULE_NOOP;
 
@@ -924,6 +907,9 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
 
        char    *expanded = NULL;
 
+       rad_assert(request->packet != NULL);
+       rad_assert(request->reply != NULL);
+
        /*
         *  Set, escape, and check the user attr here
         */
@@ -962,9 +948,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                        goto error;
                }
 
-               if (rows == 0) {
-                       goto skipreply;
-               }
+               if (rows == 0) goto skipreply;  /* Don't need to free VPs we don't have */
 
                /*
                 *      Only do this if *some* check pairs were returned
@@ -972,12 +956,15 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                RDEBUG2("User found in radcheck table");
                user_found = true;
                if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) {
+                       pairfree(&check_tmp);
+                       check_tmp = NULL;
                        goto skipreply;
                }
 
                RDEBUG2("Check items matched");
-               radius_xlat_move(request, &request->config_items, &check_tmp);
+               radius_pairmove(request, &request->config_items, check_tmp, true);
                rcode = RLM_MODULE_OK;
+               check_tmp = NULL;
        }
 
        if (inst->config->authorize_reply_query && (inst->config->authorize_reply_query[0] != '\0')) {
@@ -985,7 +972,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                 *      Now get the reply pairs since the paircompare matched
                 */
                if (radius_axlat(&expanded, request, inst->config->authorize_reply_query,
-                               sql_escape_func, inst) < 0) {
+                                sql_escape_func, inst) < 0) {
                        REDEBUG("Error generating query");
                        rcode = RLM_MODULE_FAIL;
                        goto error;
@@ -999,9 +986,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                        goto error;
                }
 
-               if (rows == 0) {
-                       goto skipreply;
-               }
+               if (rows == 0) goto skipreply;
 
                if (!inst->config->read_groups) {
                        dofallthrough = fallthrough(reply_tmp);
@@ -1009,18 +994,12 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
 
                RDEBUG2("User found in radreply table");
                user_found = true;
-               radius_xlat_move(request, &request->reply->vps, &reply_tmp);
+               radius_pairmove(request, &request->reply->vps, reply_tmp, true);
                rcode = RLM_MODULE_OK;
+               reply_tmp = NULL;
        }
 
-       skipreply:
-
-       /*
-        *      Clear out the pairlists
-        */
-       pairfree(&check_tmp);
-       pairfree(&reply_tmp);
-
+skipreply:
        /*
         *      dofallthrough is set to 1 by default so that if the user information
         *      is not found, we will still process groups.  If the user information,
@@ -1031,7 +1010,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                rlm_rcode_t ret;
 
                RDEBUG3("... falling-through to group processing");
-               ret = rlm_sql_process_groups(inst, request, handle, &dofallthrough);
+               ret = rlm_sql_process_groups(inst, request, &handle, &dofallthrough);
                switch (ret) {
                        /*
                         *      Nothing bad happened, continue...
@@ -1064,7 +1043,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                rlm_rcode_t ret;
 
                /*
-                *  Check for a default_profile or for a User-Profile.
+                *  Check for a default_profile or for a User-Profile.
                 */
                RDEBUG3("... falling-through to profile processing");
                user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY);
@@ -1085,7 +1064,7 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
                        goto error;
                }
 
-               ret = rlm_sql_process_groups(inst, request, handle, &dofallthrough);
+               ret = rlm_sql_process_groups(inst, request, &handle, &dofallthrough);
                switch (ret) {
                        /*
                         *      Nothing bad happened, continue...
@@ -1115,17 +1094,21 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST * request)
         *      At this point the key (user) hasn't be found in the check table, the reply table
         *      or the group mapping table, and there was no matching profile.
         */
-       release:
+release:
        if (!user_found) {
                rcode = RLM_MODULE_NOTFOUND;
        }
 
-       error:
        sql_release_socket(inst, handle);
 
+       return rcode;
+
+error:
        pairfree(&check_tmp);
        pairfree(&reply_tmp);
 
+       sql_release_socket(inst, handle);
+
        return rcode;
 }
 
@@ -1232,10 +1215,8 @@ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t
                 */
                sql_ret = rlm_sql_query(&handle, inst, expanded);
                TALLOC_FREE(expanded);
-
                if (sql_ret == RLM_SQL_RECONNECT) {
                        rcode = RLM_MODULE_FAIL;
-
                        goto finish;
                }
                rad_assert(handle);
@@ -1243,11 +1224,14 @@ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t
                /*
                 *  Assume all other errors are incidental, and just meant our
                 *  operation failed and its not a client or SQL syntax error.
+                *
+                *  @fixme We should actually be able to distinguish between key
+                *  constraint violations (which we expect) and other errors.
                 */
-               if (sql_ret == 0) {
+               if (sql_ret == RLM_SQL_OK) {
                        numaffected = (inst->module->sql_affected_rows)(handle, inst->config);
                        if (numaffected > 0) {
-                               break;
+                               break;  /* A query succeeded, were done! */
                        }
 
                        RDEBUG("No records updated");
@@ -1273,7 +1257,7 @@ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t
 
        (inst->module->sql_finish_query)(handle, inst->config);
 
-       finish:
+finish:
        talloc_free(expanded);
        sql_release_socket(inst, handle);
 
@@ -1285,7 +1269,7 @@ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t
 /*
  *     Accounting: Insert or update session data in our sql table
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST * request) {
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST * request) {
        rlm_sql_t *inst = instance;
 
        if (inst->config->accounting) {
@@ -1307,7 +1291,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST * request) {
  *     logins by querying the terminal server (using eg. SNMP).
  */
 
-static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
+static rlm_rcode_t CC_HINT(nonnull) mod_checksimul(void *instance, REQUEST * request) {
        rlm_rcode_t             rcode = RLM_MODULE_OK;
        rlm_sql_handle_t        *handle = NULL;
        rlm_sql_t               *inst = instance;
@@ -1318,7 +1302,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
        VALUE_PAIR              *vp;
        int                     ret;
        uint32_t                nas_addr = 0;
-       int                     nas_port = 0;
+       uint32_t                nas_port = 0;
 
        char                    *expanded = NULL;
 
@@ -1349,7 +1333,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
                return RLM_MODULE_FAIL;
        }
 
-       if (rlm_sql_select_query(&handle, inst, expanded)) {
+       if (rlm_sql_select_query(&handle, inst, expanded) != RLM_SQL_OK) {
                rcode = RLM_MODULE_FAIL;
                goto finish;
        }
@@ -1371,7 +1355,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
        (inst->module->sql_finish_select_query)(handle, inst->config);
        TALLOC_FREE(expanded);
 
-       if(request->simul_count < request->simul_max) {
+       if (request->simul_count < request->simul_max) {
                rcode = RLM_MODULE_OK;
                goto finish;
        }
@@ -1392,7 +1376,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
                goto finish;
        }
 
-       if(rlm_sql_select_query(&handle, inst, expanded)) {
+       if (rlm_sql_select_query(&handle, inst, expanded) != RLM_SQL_OK) {
                goto finish;
        }
 
@@ -1416,14 +1400,14 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
                }
 
                if (!row[2]){
-                       RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
+                       RDEBUG("Cannot zap stale entry. No username present in entry");
                        rcode = RLM_MODULE_FAIL;
 
                        goto finish;
                }
 
                if (!row[1]){
-                       RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
+                       RDEBUG("Cannot zap stale entry. No session id in entry");
                        rcode = RLM_MODULE_FAIL;
 
                        goto finish;
@@ -1506,7 +1490,7 @@ static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) {
 /*
  *     Postauth: Write a record of the authentication attempt
  */
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST * request) {
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST * request) {
        rlm_sql_t *inst = instance;
 
        if (inst->config->postauth) {
index 6db982f..69de680 100644 (file)
@@ -14,18 +14,17 @@ RCSIDH(rlm_sql_h, "$Id$")
 #include       <freeradius-devel/connection.h>
 #include       <freeradius-devel/modpriv.h>
 
-#define MAX_QUERY_LEN          4096
-
 #define PW_ITEM_CHECK          0
 #define PW_ITEM_REPLY          1
 
 
 /* SQL Errors */
 typedef enum {
-       RLM_SQL_QUERY_ERROR = -3,
-       RLM_SQL_ERROR = -2,
-       RLM_SQL_OK = 0,
-       RLM_SQL_RECONNECT = 1
+       RLM_SQL_QUERY_ERROR = -3,       //!< Query syntax error
+       RLM_SQL_ERROR = -2,             //!< General connection/server error
+       RLM_SQL_OK = 0,                 //!< Success
+       RLM_SQL_RECONNECT = 1,          //!< Stale connection, should reconnect
+       RLM_SQL_DUPLICATE = 2           //!< Key constraint violation
 } sql_rcode_t;
 
 typedef char **rlm_sql_row_t;
@@ -56,6 +55,8 @@ typedef struct sql_config {
        char const      *default_profile;
 
        char const      *client_query;
+
+       char const      *open_query;
        char const      *authorize_check_query;
        char const      *authorize_reply_query;
        char const      *authorize_group_check_query;
@@ -64,13 +65,13 @@ typedef struct sql_config {
        char const      *simul_verify_query;
        char const      *groupmemb_query;
 
-       bool const      do_clients;
-       bool const      read_groups;
+       bool            do_clients;
+       bool            read_groups;
        char const      *logfile;
 
-       bool const      deletestalesessions;
+       bool            deletestalesessions;
        char const      *allowed_chars;
-       int const       query_timeout;
+       uint32_t        query_timeout;
 
        void            *driver;        //!< Where drivers should write a
                                        //!< pointer to their configurations.
@@ -90,9 +91,9 @@ typedef struct sql_config {
 typedef struct sql_inst rlm_sql_t;
 
 typedef struct rlm_sql_handle {
-       void    *conn;
-       rlm_sql_row_t row;
-       rlm_sql_t *inst;
+       void            *conn;  //!< Database specific connection handle.
+       rlm_sql_row_t   row;    //!< Row data from the last query.
+       rlm_sql_t       *inst;  //!< The rlm_sql instance this connection belongs to.
 } rlm_sql_handle_t;
 
 typedef struct rlm_sql_module_t {
@@ -121,6 +122,7 @@ struct sql_inst {
 
        DICT_ATTR const         *sql_user;      //!< Cached pointer to SQL-User-Name
                                                //!< dictionary attribute.
+       fr_logfile_t            *lf;
 
        void *handle;
        rlm_sql_module_t *module;
@@ -139,21 +141,20 @@ typedef struct sql_grouplist {
        struct sql_grouplist    *next;
 } rlm_sql_grouplist_t;
 
-int     sql_socket_pool_init(rlm_sql_t *inst);
-void    sql_poolfree(rlm_sql_t *inst);
-int     sql_close_socket(rlm_sql_t *inst, rlm_sql_handle_t *handle);
+int            sql_socket_pool_init(rlm_sql_t *inst);
+void           sql_poolfree(rlm_sql_t *inst);
+int            sql_close_socket(rlm_sql_t *inst, rlm_sql_handle_t *handle);
 rlm_sql_handle_t *sql_get_socket(rlm_sql_t *inst);
-int     sql_release_socket(rlm_sql_t *inst, rlm_sql_handle_t *handle);
-int     sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **first_pair, rlm_sql_row_t row);
-int     sql_read_realms(rlm_sql_handle_t *handle);
-int     sql_getvpdata(rlm_sql_t *inst, rlm_sql_handle_t **handle, TALLOC_CTX *ctx, VALUE_PAIR **pair, char const *query);
-int     sql_read_naslist(rlm_sql_handle_t *handle);
-int     sql_read_clients(rlm_sql_handle_t *handle);
-int     sql_dict_init(rlm_sql_handle_t *handle);
-void   rlm_sql_query_log(rlm_sql_t *inst, REQUEST *request,
-                         sql_acct_section_t *section, char const *query);
-int    rlm_sql_select_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query);
-int    rlm_sql_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query);
-int    rlm_sql_fetch_row(rlm_sql_handle_t **handle, rlm_sql_t *inst);
-int    sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username);
+int            sql_release_socket(rlm_sql_t *inst, rlm_sql_handle_t *handle);
+int            sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **first_pair, rlm_sql_row_t row);
+int            sql_read_realms(rlm_sql_handle_t *handle);
+int            sql_getvpdata(rlm_sql_t *inst, rlm_sql_handle_t **handle, TALLOC_CTX *ctx, VALUE_PAIR **pair, char const *query);
+int            sql_read_naslist(rlm_sql_handle_t *handle);
+int            sql_read_clients(rlm_sql_handle_t *handle);
+int            sql_dict_init(rlm_sql_handle_t *handle);
+void           CC_HINT(nonnull (1, 2, 4)) rlm_sql_query_log(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section, char const *query);
+sql_rcode_t    CC_HINT(nonnull) rlm_sql_select_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query);
+sql_rcode_t    CC_HINT(nonnull) rlm_sql_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query);
+int            rlm_sql_fetch_row(rlm_sql_handle_t **handle, rlm_sql_t *inst);
+int            sql_set_user(rlm_sql_t *inst, REQUEST *request, char const *username);
 #endif
index 8dc6d5d..a64bd1e 100644 (file)
@@ -74,19 +74,26 @@ static void *mod_conn_create(void *instance)
        talloc_set_destructor((void *) handle, sql_conn_destructor);
 
        rcode = (inst->module->sql_socket_init)(handle, inst->config);
-       if (rcode == 0) {
-               exec_trigger(NULL, inst->cs, "modules.sql.open", false);
+       if (rcode != 0) {
+       fail:
+               exec_trigger(NULL, inst->cs, "modules.sql.fail", true);
 
-               return handle;
+               /*
+                *      Destroy any half opened connections.
+                */
+               talloc_free(handle);
+               return NULL;
        }
 
-       exec_trigger(NULL, inst->cs, "modules.sql.fail", true);
+       if (inst->config->open_query && *inst->config->open_query) {
+               if (rlm_sql_select_query(&handle, inst, inst->config->open_query)) {
+                       goto fail;
+               }
+               (inst->module->sql_finish_select_query)(handle, inst->config);
+       }
 
-       /*
-        *      Destroy any half opened connections.
-        */
-       talloc_free(handle);
-       return NULL;
+       exec_trigger(NULL, inst->cs, "modules.sql.open", false);
+       return handle;
 }
 
 /*
@@ -172,7 +179,7 @@ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row)
         *      Verify the 'Attribute' field
         */
        if (!row[2] || row[2][0] == '\0') {
-               ERROR("rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row.");
+               ERROR("rlm_sql: The 'Attribute' field is empty or NULL, skipping the entire row");
                return -1;
        }
 
@@ -181,7 +188,7 @@ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row)
         */
        if (row[4] != NULL && row[4][0] != '\0') {
                ptr = row[4];
-               operator = gettoken(&ptr, buf, sizeof(buf));
+               operator = gettoken(&ptr, buf, sizeof(buf), false);
                if ((operator < T_OP_ADD) ||
                    (operator > T_OP_CMP_EQ)) {
                        ERROR("rlm_sql: Invalid operator \"%s\" for attribute %s", row[4], row[2]);
@@ -194,7 +201,7 @@ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row)
                 */
                operator = T_OP_CMP_EQ;
                ERROR("rlm_sql: The 'op' field for attribute '%s = %s' is NULL, or non-existent.", row[2], row[3]);
-               ERROR("rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect.");
+               ERROR("rlm_sql: You MUST FIX THIS if you want the configuration to behave as you expect");
        }
 
        /*
@@ -209,7 +216,7 @@ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row)
           ((row[3][0] == '\'') || (row[3][0] == '`') || (row[3][0] == '"')) &&
           (row[3][0] == row[3][strlen(row[3])-1])) {
 
-               token = gettoken(&value, buf, sizeof(buf));
+               token = gettoken(&value, buf, sizeof(buf), false);
                switch (token) {
                /*
                 *      Take the unquoted string.
@@ -254,8 +261,8 @@ int sql_userparse(TALLOC_CTX *ctx, VALUE_PAIR **head, rlm_sql_row_t row)
                        return -1;
                }
        } else {
-               if (!pairparsevalue(vp, value)) {
-                       ERROR("rlm_sql: Error parsing value");
+               if (pairparsevalue(vp, value, 0) < 0) {
+                       ERROR("rlm_sql: Error parsing value: %s", fr_strerror());
 
                        talloc_free(vp);
                        return -1;
@@ -291,112 +298,181 @@ int rlm_sql_fetch_row(rlm_sql_handle_t **handle, rlm_sql_t *inst)
         * that connection.
         */
        ret = (inst->module->sql_fetch_row)(*handle, inst->config);
-
        if (ret < 0) {
                char const *error = (inst->module->sql_error)(*handle, inst->config);
-               EDEBUG("rlm_sql (%s): Error fetching row: %s",
+               ERROR("rlm_sql (%s): Error fetching row: %s",
                       inst->config->xlat_name, error ? error : "<UNKNOWN>");
        }
 
        return ret;
 }
 
-/*************************************************************************
- *
- *     Function: rlm_sql_query
- *
- *     Purpose: call the module's sql_query and implement re-connect
- *
- *************************************************************************/
-int rlm_sql_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query)
+static void rlm_sql_query_error(rlm_sql_handle_t *handle, rlm_sql_t *inst)
 {
-       int ret = -1;
+       char const *p, *q;
+
+       p = (inst->module->sql_error)(handle, inst->config);
+       if (!p) {
+               ERROR("rlm_sql (%s): Unknown query error", inst->config->xlat_name);
+               return;
+       }
 
        /*
-        *      If there's no query, return an error.
+        *      Some drivers are nice and provide us with a ^ pointer to
+        *      the place in the query string where the error occurred.
+        *
+        *      For this to be useful we need to split log messages on
+        *      \n and output each of the lines individually.
         */
-       if (!query || !*query) {
-               return -1;
+       while ((q = strchr(p, '\n'))) {
+               ERROR("rlm_sql (%s): %.*s", inst->config->xlat_name, (int) (q - p), p);
+               p = q + 1;
        }
 
-       if (!*handle || !(*handle)->conn) {
-               goto sql_down;
+       if (*p != '\0') {
+               ERROR("rlm_sql (%s): %s", inst->config->xlat_name, p);
        }
+}
+
+static void rlm_sql_query_debug(rlm_sql_handle_t *handle, rlm_sql_t *inst)
+{
+       char const *p, *q;
+
+       p = (inst->module->sql_error)(handle, inst->config);
+       if (!p) {
+               return;
+       }
+
+       /*
+        *      Some drivers are nice and provide us with a ^ pointer to
+        *      the place in the query string where the error occurred.
+        *
+        *      For this to be useful we need to split log messages on
+        *      \n and output each of the lines individually.
+        */
+       while ((q = strchr(p, '\n'))) {
+               DEBUG2("rlm_sql (%s): %.*s", inst->config->xlat_name, (int) (q - p), p);
+               p = q + 1;
+       }
+
+       if (*p != '\0') {
+               DEBUG2("rlm_sql (%s): %s", inst->config->xlat_name, p);
+       }
+}
+
+/** Call the driver's sql_query method, reconnecting if necessary.
+ *
+ * @param handle to query the database with. *handle should not be NULL, as this indicates
+ *       previous reconnection attempt has failed.
+ * @param inst rlm_sql instance data.
+ * @param query to execute. Should not be zero length.
+ * @return RLM_SQL_OK on success, RLM_SQL_RECONNECT if a new handle is required (also sets *handle = NULL),
+ *         RLM_SQL_QUERY_ERROR/RLM_SQL_ERROR on invalid query or connection error, RLM_SQL_DUPLICATE on constraints
+ *         violation.
+ */
+sql_rcode_t rlm_sql_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query)
+{
+       int ret = RLM_SQL_ERROR;
+       int i;
 
-       while (1) {
-               DEBUG("rlm_sql (%s): Executing query: '%s'",
-                     inst->config->xlat_name, query);
+       /* There's no query to run, return an error */
+       if (query[0] == '\0') return RLM_SQL_QUERY_ERROR;
+
+       /* There's no handle, we need a new one */
+       if (!*handle) return RLM_SQL_RECONNECT;
+
+       /* For sanity, for when no connections are viable, and we can't make a new one */
+       for (i = fr_connection_get_num(inst->pool); i >= 0; i--) {
+               DEBUG("rlm_sql (%s): Executing query: '%s'", inst->config->xlat_name, query);
 
                ret = (inst->module->sql_query)(*handle, inst->config, query);
+               switch (ret) {
+               case RLM_SQL_OK:
+                       break;
+
                /*
-                * Run through all available sockets until we exhaust all existing
-                * sockets in the pool and fail to establish a *new* connection.
+                *      Run through all available sockets until we exhaust all existing
+                *      sockets in the pool and fail to establish a *new* connection.
                 */
-               if (ret == RLM_SQL_RECONNECT) {
-                       sql_down:
+               case RLM_SQL_RECONNECT:
                        *handle = fr_connection_reconnect(inst->pool, *handle);
+                       /* Reconnection failed */
                        if (!*handle) return RLM_SQL_RECONNECT;
-
+                       /* Reconnection succeeded, try again with the new handle */
                        continue;
-               }
 
-               if (ret < 0) {
-                       char const *error = (inst->module->sql_error)(*handle, inst->config);
-                       ERROR("rlm_sql (%s): Database query error: %s",
-                             inst->config->xlat_name, error ? error : "<UNKNOWN>");
+               case RLM_SQL_QUERY_ERROR:
+               case RLM_SQL_ERROR:
+                       rlm_sql_query_error(*handle, inst);
+                       break;
+
+               case RLM_SQL_DUPLICATE:
+                       rlm_sql_query_debug(*handle, inst);
+                       break;
+
                }
 
                return ret;
        }
+
+       ERROR("rlm_sql (%s): Hit reconnection limit", inst->config->xlat_name);
+
+       return RLM_SQL_ERROR;
 }
 
-/*************************************************************************
+/** Call the driver's sql_select_query method, reconnecting if necessary.
  *
- *     Function: rlm_sql_select_query
- *
- *     Purpose: call the module's sql_select_query and implement re-connect
- *
- *************************************************************************/
-int rlm_sql_select_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query)
+ * @param handle to query the database with. *handle should not be NULL, as this indicates
+ *       previous reconnection attempt has failed.
+ * @param inst rlm_sql instance data.
+ * @param query to execute. Should not be zero length.
+ * @return RLM_SQL_OK on success, RLM_SQL_RECONNECT if a new handle is required (also sets *handle = NULL),
+ *         RLM_SQL_QUERY_ERROR/RLM_SQL_ERROR on invalid query or connection error.
+ */
+sql_rcode_t rlm_sql_select_query(rlm_sql_handle_t **handle, rlm_sql_t *inst, char const *query)
 {
-       int ret = -1;
+       int ret = RLM_SQL_ERROR;
+       int i;
 
-       /*
-        *      If there's no query, return an error.
-        */
-       if (!query || !*query) {
-               return -1;
-       }
+       /* There's no query to run, return an error */
+       if (query[0] == '\0') return RLM_SQL_QUERY_ERROR;
 
-       if (!*handle || !(*handle)->conn) {
-               goto sql_down;
-       }
+       /* There's no handle, we need a new one */
+       if (!*handle) return RLM_SQL_RECONNECT;
 
-       while (1) {
-               DEBUG("rlm_sql (%s): Executing query: '%s'",
-                     inst->config->xlat_name, query);
+       /* For sanity, for when no connections are viable, and we can't make a new one */
+       for (i = fr_connection_get_num(inst->pool); i >= 0; i--) {
+               DEBUG("rlm_sql (%s): Executing query: '%s'", inst->config->xlat_name, query);
 
                ret = (inst->module->sql_select_query)(*handle, inst->config, query);
+               switch (ret) {
+               case RLM_SQL_OK:
+                       break;
+
                /*
-                * Run through all available sockets until we exhaust all existing
-                * sockets in the pool and fail to establish a *new* connection.
+                *      Run through all available sockets until we exhaust all existing
+                *      sockets in the pool and fail to establish a *new* connection.
                 */
-               if (ret == RLM_SQL_RECONNECT) {
-                       sql_down:
+               case RLM_SQL_RECONNECT:
                        *handle = fr_connection_reconnect(inst->pool, *handle);
+                       /* Reconnection failed */
                        if (!*handle) return RLM_SQL_RECONNECT;
-
+                       /* Reconnection succeeded, try again with the new handle */
                        continue;
-               }
 
-               if (ret < 0) {
-                       char const *error = (inst->module->sql_error)(*handle, inst->config);
-                       ERROR("rlm_sql (%s): Database query error '%s'",
-                             inst->config->xlat_name, error ? error : "<UNKNOWN>");
+               case RLM_SQL_QUERY_ERROR:
+               case RLM_SQL_ERROR:
+               default:
+                       rlm_sql_query_error(*handle, inst);
+                       break;
                }
 
                return ret;
        }
+
+       ERROR("rlm_sql (%s): Hit reconnection limit", inst->config->xlat_name);
+
+       return RLM_SQL_ERROR;
 }
 
 
@@ -422,7 +498,7 @@ int sql_getvpdata(rlm_sql_t * inst, rlm_sql_handle_t **handle,
                if (!row)
                        break;
                if (sql_userparse(ctx, pair, row) != 0) {
-                       ERROR("rlm_sql (%s): Error getting data from database", inst->config->xlat_name);
+                       ERROR("rlm_sql (%s): Error parsing user data from database result", inst->config->xlat_name);
 
                        (inst->module->sql_finish_select_query)(*handle, inst->config);
 
@@ -444,6 +520,8 @@ void rlm_sql_query_log(rlm_sql_t *inst, REQUEST *request,
        int fd;
        char const *filename = NULL;
        char *expanded = NULL;
+       size_t len;
+       bool failed = false;    /* Write the log message outside of the critical region */
 
        if (section) {
                filename = section->logfile;
@@ -459,20 +537,25 @@ void rlm_sql_query_log(rlm_sql_t *inst, REQUEST *request,
                return;
        }
 
-       fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, 0666);
+       fd = fr_logfile_open(inst->lf, filename, 0640);
        if (fd < 0) {
                ERROR("rlm_sql (%s): Couldn't open logfile '%s': %s", inst->config->xlat_name,
-                      expanded, strerror(errno));
+                     expanded, fr_syserror(errno));
 
                talloc_free(expanded);
                return;
        }
 
-       if ((rad_lockfd(fd, MAX_QUERY_LEN) < 0) || (write(fd, query, strlen(query)) < 0) || (write(fd, ";\n", 2) < 0)) {
+       len = strlen(query);
+       if ((write(fd, query, len) < 0) || (write(fd, ";\n", 2) < 0)) {
+               failed = true;
+       }
+
+       if (failed) {
                ERROR("rlm_sql (%s): Failed writing to logfile '%s': %s", inst->config->xlat_name, expanded,
-                      strerror(errno));
+                     fr_syserror(errno));
        }
 
        talloc_free(expanded);
-       close(fd);              /* and release the lock */
+       fr_logfile_close(inst->lf, fd);
 }
index 51e6f78..cdf630d 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,10 +1254,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1869,7 +1869,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2091,7 +2091,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2797,11 +2797,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3307,11 +3307,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 72f8aaa..20cabb3 100644 (file)
@@ -57,15 +57,15 @@ RCSID("$Id$")
  *     be used as the instance handle.
  */
 typedef struct rlm_sqlcounter_t {
-       char            *counter_name;  //!< Daily-Session-Time.
-       char            *check_name;    //!< Max-Daily-Session.
-       char            *reply_name;    //!< Session-Timeout.
-       char            *key_name;      //!< User-Name.
-       char            *sqlmod_inst;   //!< Instance of SQL module to use,
+       char const      *counter_name;  //!< Daily-Session-Time.
+       char const      *limit_name;    //!< Max-Daily-Session.
+       char const      *reply_name;    //!< Session-Timeout.
+       char const      *key_name;      //!< User-Name.
+       char const      *sqlmod_inst;   //!< Instance of SQL module to use,
                                        //!< usually just 'sql'.
-       char            *query;         //!< SQL query to retrieve current
+       char const      *query;         //!< SQL query to retrieve current
                                        //!< session time.
-       char            *reset;         //!< Daily, weekly, monthly,
+       char const      *reset;         //!< Daily, weekly, monthly,
                                        //!< never or user defined.
        time_t          reset_time;
        time_t          last_reset;
@@ -84,32 +84,21 @@ typedef struct rlm_sqlcounter_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-       { "sql-module-instance", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlcounter_t,sqlmod_inst), NULL, NULL },
-       { "sql_module_instance", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlcounter_t,sqlmod_inst), NULL, NULL },
-
-       { "key", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,
-         offsetof(rlm_sqlcounter_t,key_name), NULL, NULL },
-       { "query", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlcounter_t,query), NULL, NULL },
-       { "reset", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlcounter_t,reset), NULL,  NULL },
-
-       { "counter-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlcounter_t,counter_name), NULL,  NULL },
-       { "counter_name", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlcounter_t,counter_name), NULL,  NULL },
-
-       { "check-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlcounter_t,check_name), NULL, NULL },
-       { "check_name", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlcounter_t,check_name), NULL, NULL },
-
-       { "reply-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlcounter_t,reply_name), NULL, NULL },
-       { "reply_name", PW_TYPE_STRING_PTR | PW_TYPE_ATTRIBUTE,
-         offsetof(rlm_sqlcounter_t,reply_name), NULL, "Session-Timeout" },
+       { "sql-module-instance", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlcounter_t, sqlmod_inst), NULL },
+       { "sql_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlcounter_t, sqlmod_inst), NULL },
+
+       { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_ATTRIBUTE, rlm_sqlcounter_t, key_name), NULL },
+       { "query", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlcounter_t, query), NULL },
+       { "reset", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlcounter_t, reset), NULL },
+
+       { "counter-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlcounter_t, counter_name), NULL },
+       { "counter_name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlcounter_t, counter_name), NULL },
+
+       { "check-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlcounter_t, limit_name), NULL },
+       { "check_name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlcounter_t, limit_name), NULL },
+
+       { "reply-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlcounter_t, reply_name), NULL },
+       { "reply_name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_ATTRIBUTE, rlm_sqlcounter_t, reply_name), "Session-Timeout" },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -261,43 +250,25 @@ static int find_prev_reset(rlm_sqlcounter_t *inst, time_t timeval)
 
 static size_t sqlcounter_expand(char *out, int outlen, char const *fmt, rlm_sqlcounter_t *inst)
 {
-       int c,freespace;
+       int c, freespace;
        char const *p;
        char *q;
        char tmpdt[40]; /* For temporary storing of dates */
 
        q = out;
        for (p = fmt; *p ; p++) {
-       /* Calculate freespace in output */
-       freespace = outlen - (q - out);
-               if (freespace <= 1)
+               /* Calculate freespace in output */
+               freespace = outlen - (q - out);
+               if (freespace <= 1) {
                        return -1;
+               }
                c = *p;
-               if ((c != '%') && (c != '\\')) {
+               if (c != '%') {
                        *q++ = *p;
                        continue;
                }
                if (*++p == '\0') break;
-               if (c == '\\') switch(*p) {
-                       case '\\':
-                               *q++ = *p;
-                               break;
-                       case 't':
-                               *q++ = '\t';
-                               break;
-                       case 'n':
-                               *q++ = '\n';
-                               break;
-                       default:
-                               *q++ = c;
-                               *q++ = *p;
-                               break;
-
-               } else if (c == '%') switch(*p) {
-
-                       case '%':
-                               *q++ = *p;
-                               break;
+               if (c == '%') switch(*p) {
                        case 'b': /* last_reset */
                                snprintf(tmpdt, sizeof(tmpdt), "%" PRId64, (int64_t) inst->last_reset);
                                strlcpy(q, tmpdt, freespace);
@@ -309,7 +280,7 @@ static size_t sqlcounter_expand(char *out, int outlen, char const *fmt, rlm_sqlc
                                q += strlen(q);
                                break;
                        case 'k': /* Key Name */
-                               WDEBUG2("Please replace '%%k' with '${key}'");
+                               WARN("Please replace '%%k' with '${key}'");
                                strlcpy(q, inst->key_name, freespace);
                                q += strlen(q);
                                break;
@@ -321,7 +292,7 @@ static size_t sqlcounter_expand(char *out, int outlen, char const *fmt, rlm_sqlc
        }
        *q = '\0';
 
-       DEBUG2("sqlcounter_expand:  '%s'", out);
+       DEBUG2("sqlcounter_expand: '%s'", out);
 
        return strlen(out);
 }
@@ -336,46 +307,33 @@ static int sqlcounter_cmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *r
        rlm_sqlcounter_t *inst = instance;
        uint64_t counter;
 
-       char *p;
-       char query[MAX_QUERY_LEN];
+       char query[MAX_QUERY_LEN], subst[MAX_QUERY_LEN];
        char *expanded = NULL;
-
        size_t len;
 
-       len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, inst->query);
-       if (len >= sizeof(query) - 1) {
+       /* First, expand %k, %b and %e in query */
+       if (sqlcounter_expand(subst, sizeof(subst), inst->query, inst) <= 0) {
                REDEBUG("Insufficient query buffer space");
 
                return RLM_MODULE_FAIL;
        }
 
-       p = query + len;
-
-       /* first, expand %k, %b and %e in query */
-       len = sqlcounter_expand(p, p - query, inst->query, inst);
-       if (len <= 0) {
-               REDEBUG("Insufficient query buffer space");
-
-               return RLM_MODULE_FAIL;
-       }
-
-       p += len;
-
-       if ((p - query) < 2) {
+       /* Then combine that with the name of the module were using to do the query */
+       len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, subst);
+       if (len >= sizeof(query) - 1) {
                REDEBUG("Insufficient query buffer space");
 
                return RLM_MODULE_FAIL;
        }
 
-       p[0] = '}';
-       p[1] = '\0';
-
        /* Finally, xlat resulting SQL query */
        if (radius_axlat(&expanded, request, query, NULL, NULL) < 0) {
                return RLM_MODULE_FAIL;
        }
 
-       counter = strtoull(expanded, NULL, 10);
+       if (sscanf(expanded, "%" PRIu64, &counter) != 1) {
+               RDEBUG2("No integer found in string \"%s\"", expanded);
+       }
        talloc_free(expanded);
 
        if (counter < check->vp_integer64) {
@@ -426,7 +384,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
         */
        rad_assert(inst->counter_name && *inst->counter_name);
        memset(&flags, 0, sizeof(flags));
-       dict_addattr(inst->counter_name, -1, 0, PW_TYPE_INTEGER, flags);
+       dict_addattr(inst->counter_name, -1, 0, PW_TYPE_INTEGER64, flags);
        da = dict_attrbyname(inst->counter_name);
        if (!da) {
                cf_log_err_cs(conf, "Failed to create counter attribute %s", inst->counter_name);
@@ -435,13 +393,13 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        inst->dict_attr = da;
 
        /*
-        * Create a new attribute for the check item.
+        *  Create a new attribute for the check item.
         */
-       rad_assert(inst->check_name && *inst->check_name);
-       dict_addattr(inst->check_name, 0, PW_TYPE_INTEGER, -1, flags);
-       da = dict_attrbyname(inst->check_name);
+       rad_assert(inst->limit_name && *inst->limit_name);
+       dict_addattr(inst->limit_name, -1, 0, PW_TYPE_INTEGER64, flags);
+       da = dict_attrbyname(inst->limit_name);
        if (!da) {
-               cf_log_err_cs(conf, "Failed to create check attribute %s", inst->check_name);
+               cf_log_err_cs(conf, "Failed to create check attribute %s", inst->limit_name);
                return -1;
        }
 
@@ -464,7 +422,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        }
 
        /*
-        *      Register the counter comparison operation.
+        *  Register the counter comparison operation.
         */
        paircompare_register(inst->dict_attr, NULL, true, sqlcounter_cmp, inst);
 
@@ -477,18 +435,17 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_sqlcounter_t *inst = instance;
        int rcode = RLM_MODULE_NOOP;
        uint64_t counter, res;
        DICT_ATTR const *da;
-       VALUE_PAIR *key_vp, *check_vp;
+       VALUE_PAIR *key_vp, *limit;
        VALUE_PAIR *reply_item;
        char msg[128];
 
-       char *p;
-       char query[MAX_QUERY_LEN];
+       char query[MAX_QUERY_LEN], subst[MAX_QUERY_LEN];
        char *expanded = NULL;
 
        size_t len;
@@ -509,91 +466,74 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
         *      Look for the key.  User-Name is special.  It means
         *      The REAL username, after stripping.
         */
-       RDEBUG2("Entering module authorize code");
-       key_vp = ((inst->key_attr->vendor == 0) && (inst->key_attr->attr == PW_USER_NAME)) ?
-                       request->username :
-                       pairfind(request->packet->vps, inst->key_attr->attr, inst->key_attr->vendor, TAG_ANY);
+       if ((inst->key_attr->vendor == 0) && (inst->key_attr->attr == PW_USER_NAME)) {
+               key_vp = request->username;
+       } else {
+               key_vp = pairfind(request->packet->vps, inst->key_attr->attr, inst->key_attr->vendor, TAG_ANY);
+       }
        if (!key_vp) {
-               RDEBUG2("Could not find Key value pair");
+               RWDEBUG2("Couldn't find key attribute 'request:%s'", inst->key_attr->name);
                return rcode;
        }
 
        /*
         *      Look for the check item
         */
-       if ((da = dict_attrbyname(inst->check_name)) == NULL) {
+       if ((da = dict_attrbyname(inst->limit_name)) == NULL) {
                return rcode;
        }
-       /* DEBUG2("rlm_sqlcounter: Found Check item attribute %d", da->attr); */
-       if ((check_vp = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY)) == NULL) {
-               RDEBUG2("Could not find Check item value pair");
-               return rcode;
-       }
-
-       len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, inst->query);
-       if (len >= sizeof(query) - 1) {
-               REDEBUG("Insufficient query buffer space");
 
-               return RLM_MODULE_FAIL;
+       limit = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY);
+       if (limit == NULL) {
+               RWDEBUG2("Couldn't find control attribute 'control:%s'", inst->limit_name);
+               return rcode;
        }
 
-       p = query + len;
-
-       /* first, expand %k, %b and %e in query */
-       len = sqlcounter_expand(p, p - query, inst->query, inst);
-       if (len <= 0) {
+       /* First, expand %k, %b and %e in query */
+       if (sqlcounter_expand(subst, sizeof(subst), inst->query, inst) <= 0) {
                REDEBUG("Insufficient query buffer space");
 
                return RLM_MODULE_FAIL;
        }
 
-       p += len;
-
-       if ((p - query) < 2) {
+       /* Then combine that with the name of the module were using to do the query */
+       len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, subst);
+       if (len >= (sizeof(query) - 1)) {
                REDEBUG("Insufficient query buffer space");
 
                return RLM_MODULE_FAIL;
        }
 
-       p[0] = '}';
-       p[1] = '\0';
-
        /* Finally, xlat resulting SQL query */
        if (radius_axlat(&expanded, request, query, NULL, NULL) < 0) {
                return RLM_MODULE_FAIL;
        }
+       talloc_free(expanded);
 
        if (sscanf(expanded, "%" PRIu64, &counter) != 1) {
-               RDEBUG2("No integer found in string \"%s\"", expanded);
-               return RLM_MODULE_NOOP;
+               RDEBUG2("No integer found in result string \"%s\".  May be first session, setting counter to 0",
+                       expanded);
+               counter = 0;
        }
 
-       talloc_free(expanded);
-
        /*
         *      Check if check item > counter
         */
-       if (check_vp->vp_integer64 <= counter) {
-               RDEBUG2("(Check item - counter) is less than zero");
-
-               /*
-                * User is denied access, send back a reply message
-                */
+       if (limit->vp_integer64 <= counter) {
+               /* User is denied access, send back a reply message */
                snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", inst->reset);
                pairmake_reply("Reply-Message", msg, T_OP_EQ);
 
-               REDEBUG("Maximum %s usage time reached", inst->reset);
-               rcode = RLM_MODULE_REJECT;
-
-               RDEBUG2("Rejected user %s, check_item=%" PRIu64 ", counter=%" PRIu64,
-                       key_vp->vp_strvalue, check_vp->vp_integer64, counter);
+               REDEBUG2("Maximum %s usage time reached", inst->reset);
+               REDEBUG2("Rejecting user, control:%s value (%" PRIu64 ") is less than counter value (%" PRIu64 ")",
+                        inst->limit_name, limit->vp_integer64, counter);
 
                return RLM_MODULE_REJECT;
-
        }
 
-       res = check_vp->vp_integer64 - counter;
-       RDEBUG2("Check item is greater than query result");
+       res = limit->vp_integer64 - counter;
+       RDEBUG2("Allowing user, control:%s value (%" PRIu64 ") is greater than counter value (%" PRIu64 ")",
+               inst->limit_name, limit->vp_integer64, counter);
        /*
         *      We are assuming that simultaneous-use=1. But
         *      even if that does not happen then our user
@@ -608,29 +548,28 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
         */
        if (((inst->reply_attr->vendor == 0) && (inst->reply_attr->attr == PW_SESSION_TIMEOUT)) &&
            inst->reset_time && ((int) res >= (inst->reset_time - request->timestamp))) {
-               res = inst->reset_time - request->timestamp;
-               res += check_vp->vp_integer;
+               res = (inst->reset_time - request->timestamp);
+               res += limit->vp_integer;
        }
 
        /*
-        *      Limit the reply attribute to the minimum of
-        *      the existing value, or this new one.
+        *      Limit the reply attribute to the minimum of the existing value, or this new one.
         */
        reply_item = pairfind(request->reply->vps, inst->reply_attr->attr, inst->reply_attr->vendor, TAG_ANY);
        if (reply_item) {
-               if (reply_item->vp_integer64 > res) {
-                       reply_item->vp_integer64 = res;
+               if (reply_item->vp_integer64 <= res) {
+                       RDEBUG2("Leaving existing reply:%s value of %" PRIu64, inst->reply_attr->name,
+                               reply_item->vp_integer64);
+
+                       return RLM_MODULE_OK;
                }
        } else {
-               reply_item = radius_paircreate(request, &request->reply->vps, inst->reply_attr->attr,
+               reply_item = radius_paircreate(request->reply, &request->reply->vps, inst->reply_attr->attr,
                                               inst->reply_attr->vendor);
-               reply_item->vp_integer64 = res;
        }
+       reply_item->vp_integer64 = res;
 
-       RDEBUG2("Authorized user %s, check_item=%" PRIu64 ", counter=%" PRIu64 ,
-               key_vp->vp_strvalue, check_vp->vp_integer64, counter);
-       RDEBUG2("Sent Reply-Item for user %s, Type=%s, value=%" PRIu64,
-               key_vp->vp_strvalue, inst->reply_name, reply_item->vp_integer64);
+       RDEBUG2("Setting reply:%s value to %" PRIu64, inst->reply_name, reply_item->vp_integer64);
 
        return RLM_MODULE_OK;
 }
@@ -646,7 +585,7 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
  */
 module_t rlm_sqlcounter = {
        RLM_MODULE_INIT,
-       "SQL Counter",
+       "rlm_sqlcounter",
        RLM_TYPE_THREAD_SAFE,           /* type */
        sizeof(rlm_sqlcounter_t),
        module_config,
index 7970ff2..b5a5b39 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1209,9 +1209,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1254,10 +1254,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -1869,7 +1869,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2091,7 +2091,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2799,11 +2799,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3277,13 +3277,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index c05ad7e..3d65897 100644 (file)
 #define RLM_NETVIM_MAX_ROWS 1000000
 #define RLM_NETVIM_TMP_PREFIX "auth-tmp-"
 
-static char const rcsid[] = "$Id$";
+#define MAX_QUERY_LEN 4096
+
+RCSID("$Id$");
 
 typedef struct rlm_sqlhpwippool_t {
-       char const *myname;             //!< Name of this instance
+       char const *myname;                     //!< Name of this instance
        rlm_sql_t *sqlinst;
        rlm_sql_module_t *db;
 #ifdef HAVE_PTHREAD_D
-       pthread_mutex_t mutex;          //!< Used "with" sync_after
+       pthread_mutex_t mutex;                  //!< Used "with" sync_after
 #endif
-       int     sincesync;              //!< req. done so far since last free IP sync.
+       uint32_t        sincesync;              //!< req. done so far since last free IP sync.
 
        /* from config */
-       char    *sql_module_instance;   //!< rlm_sql instance to use.
-       char    *db_name;               //!< Netvim database.
-       bool    no_free_fail;           //!< Fail if no free IP addresses found.
-       int     free_after;             //!< How many seconds an IP should not be used after freeing.
-       int     sync_after;             //!< How often to sync with radacct.
+       char const      *sql_module_instance;   //!< rlm_sql instance to use.
+       char const      *db_name;               //!< Netvim database.
+       bool            no_free_fail;           //!< Fail if no free IP addresses found.
+       uint32_t        free_after;             //!< How many seconds an IP should not be used after freeing.
+       uint32_t        sync_after;             //!< How often to sync with radacct.
 } rlm_sqlhpwippool_t;
 
 /* char *name, int type,
  * size_t offset, void *data, char *dflt */
 static CONF_PARSER module_config[] = {
-       { "sql_module_instance",       PW_TYPE_STRING_PTR,
-         offsetof(rlm_sqlhpwippool_t, sql_module_instance),       NULL, "sql" },
-       { "db_name",        PW_TYPE_STRING_PTR,
-         offsetof(rlm_sqlhpwippool_t, db_name),            NULL, "netvim" },
-       { "no_free_fail",        PW_TYPE_BOOLEAN,
-         offsetof(rlm_sqlhpwippool_t, no_free_fail),    NULL, "yes" },
-       { "free_after",   PW_TYPE_INTEGER,
-         offsetof(rlm_sqlhpwippool_t, free_after),       NULL, "300" },
-       { "sync_after",   PW_TYPE_INTEGER,
-         offsetof(rlm_sqlhpwippool_t, sync_after),       NULL, "25" },
+       { "sql_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlhpwippool_t, sql_module_instance), "sql" },
+       { "db_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlhpwippool_t, db_name), "netvim" },
+       { "no_free_fail", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sqlhpwippool_t, no_free_fail), "yes" },
+       { "free_after", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sqlhpwippool_t, free_after), "300" },
+       { "sync_after", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sqlhpwippool_t, sync_after), "25" },
        { NULL, -1, 0, NULL, NULL } /* end */
 };
 
+int nvp_log(unsigned int line, rlm_sqlhpwippool_t *data, int lvl, char const *fmt, ...) CC_HINT(format (printf, 4, 5));
+
+DIAG_OFF(format-nonliteral)
 /* wrapper around radlog which adds prefix with module and instance name */
-static int nvp_log(unsigned int line, rlm_sqlhpwippool_t *data, int lvl,
-                  char const *fmt, ...)
+int nvp_log(unsigned int line, rlm_sqlhpwippool_t *data, int lvl, char const *fmt, ...)
 {
        va_list ap;
        int r;
@@ -100,8 +99,8 @@ static int nvp_log(unsigned int line, rlm_sqlhpwippool_t *data, int lvl,
 
        return r;
 }
+
 /* handy SQL query tool */
-DIAG_OFF(format-nonliteral)
 static int nvp_vquery(unsigned int line, rlm_sqlhpwippool_t *data,
                      rlm_sql_handle_t *sqlsock, char const *fmt, va_list ap)
 {
@@ -187,15 +186,15 @@ static int nvp_freeclosed(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock)
 {
        if (!nvp_query(__LINE__, data, sqlsock,
            "UPDATE `%s`.`ips`, `radacct` "
-               "SET "
-                       "`ips`.`rsv_until` = `radacct`.`acctstoptime` + INTERVAL %u SECOND "
-               "WHERE "
-                       "`radacct`.`acctstoptime` IS NOT NULL AND "   /* session is closed */
-                       "("                                 /* address is being used */
-                               "`ips`.`pid` IS NOT NULL AND "
-                               "(`rsv_until` = 0 OR `rsv_until` > NOW())"
-                       ") AND "
-                       "`radacct`.`acctuniqueid` = `ips`.`rsv_by`",
+               "SET "
+                       "`ips`.`rsv_until` = `radacct`.`acctstoptime` + INTERVAL %u SECOND "
+               "WHERE "
+                       "`radacct`.`acctstoptime` IS NOT NULL AND "   /* session is closed */
+                       "("                                 /* address is being used */
+                               "`ips`.`pid` IS NOT NULL AND "
+                               "(`rsv_until` = 0 OR `rsv_until` > NOW())"
+                       ") AND "
+                       "`radacct`.`acctuniqueid` = `ips`.`rsv_by`",
            data->db_name, data->free_after)) {
                return 0;
        }
@@ -209,16 +208,16 @@ static int nvp_syncfree(rlm_sqlhpwippool_t *data, rlm_sql_handle_t *sqlsock)
 {
        if (!nvp_query(__LINE__, data, sqlsock,
            "UPDATE `%s`.`ip_pools` "
-               "SET `ip_pools`.`free` = "
-                       "(SELECT COUNT(*) "
-                               "FROM `%1$s`.`ips` "
-                               "WHERE "
-                                       "`ips`.`ip` BETWEEN "
-                                               "`ip_pools`.`ip_start` AND `ip_pools`.`ip_stop` AND "
-                                       "("
-                                               "`ips`.`pid` IS NULL OR "
-                                               "(`ips`.`rsv_until` > 0 AND `ips`.`rsv_until` < NOW())"
-                                       "))",
+               "SET `ip_pools`.`free` = "
+                       "(SELECT COUNT(*) "
+                               "FROM `%1$s`.`ips` "
+                               "WHERE "
+                                       "`ips`.`ip` BETWEEN "
+                                               "`ip_pools`.`ip_start` AND `ip_pools`.`ip_stop` AND "
+                                       "("
+                                               "`ips`.`pid` IS NULL OR "
+                                               "(`ips`.`rsv_until` > 0 AND `ips`.`rsv_until` < NOW())"
+                                       "))",
            data->db_name)) {
                return 0;
        }
@@ -249,19 +248,19 @@ static int nvp_cleanup(rlm_sqlhpwippool_t *data)
        /* add sessions opened in the meantime */
        if (!nvp_query(__LINE__, data, sqlsock,
            "UPDATE `%s`.`ips`, `radacct` "
-               "SET "
-                       "`ips`.`pid` = 0, "
-                       "`ips`.`rsv_by` = `radacct`.`acctuniqueid`, "
-                       "`ips`.`rsv_since` = `radacct`.`acctstarttime`, "
-                       "`ips`.`rsv_until` = 0 "
-               "WHERE "
-                       "`radacct`.`acctstoptime` IS NULL AND "     /* session is opened */
-                       "`ips`.`ip` = INET_ATON(`radacct`.`framedipaddress`) AND "
-                       "("
-                               "`ips`.`pid` IS NULL OR "
+               "SET "
+                       "`ips`.`pid` = 0, "
+                       "`ips`.`rsv_by` = `radacct`.`acctuniqueid`, "
+                       "`ips`.`rsv_since` = `radacct`.`acctstarttime`, "
+                       "`ips`.`rsv_until` = 0 "
+               "WHERE "
+                       "`radacct`.`acctstoptime` IS NULL AND "     /* session is opened */
+                       "`ips`.`ip` = INET_ATON(`radacct`.`framedipaddress`) AND "
+                       "("
+                               "`ips`.`pid` IS NULL OR "
 /*                             "(`ips`.`rsv_until` > 0 AND `ips.`rsv_until` < NOW()) " */
-                               "`ips`.`rsv_until` != 0"   /* no acct pkt received yet */
-                       ")",
+                               "`ips`.`rsv_until` != 0"   /* no acct pkt received yet */
+                       ")",
            data->db_name)) {
                sql_release_socket(data->sqlinst, sqlsock);
                return 0;
@@ -320,7 +319,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 }
 
 /* assign new IP address, if required */
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        VALUE_PAIR *vp;
        char const *pname;       /* name of requested IP pool */
@@ -426,17 +425,17 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                /* find the most specific group which NAS belongs to */
                switch (nvp_select(__LINE__, inst, sqlsock,
                       "SELECT `host_groups`.`gid` "
-                       "FROM "
-                               "`%s`.`host_groups`, "
-                               "`%1$s`.`gid_ip`, "
-                               "`%1$s`.`ids` "
-                       "WHERE "
-                               "`host_groups`.`gid` = `ids`.`id` AND "
-                               "`ids`.`enabled` = 1 AND "
-                               "`host_groups`.`gid` = `gid_ip`.`gid` AND "
-                               "%lu BETWEEN `gid_ip`.`ip_start` AND `gid_ip`.`ip_stop` "
-                       "ORDER BY (`gid_ip`.`ip_stop` - `gid_ip`.`ip_start`) ASC "
-                       "LIMIT %lu, 1",
+                       "FROM "
+                               "`%s`.`host_groups`, "
+                               "`%1$s`.`gid_ip`, "
+                               "`%1$s`.`ids` "
+                       "WHERE "
+                               "`host_groups`.`gid` = `ids`.`id` AND "
+                               "`ids`.`enabled` = 1 AND "
+                               "`host_groups`.`gid` = `gid_ip`.`gid` AND "
+                               "%lu BETWEEN `gid_ip`.`ip_start` AND `gid_ip`.`ip_stop` "
+                       "ORDER BY (`gid_ip`.`ip_stop` - `gid_ip`.`ip_start`) ASC "
+                       "LIMIT %lu, 1",
                       inst->db_name, nasip, s_gid)) {
                        case -1:
                                nvp_log(__LINE__, inst, L_ERR,
@@ -467,7 +466,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                                        "FROM "
                                                "`%s`.`ip_pools`, "
                                                "`%1$s`.`ids`, "
-                                               "`%1$s`.`pool_names` "
+                                               "`%1$s`.`pool_names` "
                                        "WHERE "
                                                "`ip_pools`.`gid` = %lu AND "
                                                "`ids`.`id` = `ip_pools`.`pid` AND "
@@ -482,7 +481,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                                case -1:
                                        nvp_log(__LINE__, inst, L_DBG,
                                                "mod_post_auth(): couldn't find "
-                                               "any more matching pools for gid = %u",
+                                               "any more matching pools for gid = %lu",
                                                gid);
                                        goto end_prio;         /* select next gid */
                                case 0:
@@ -545,19 +544,19 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                                /* reserve an IP address */
                                if (!nvp_query(__LINE__, inst, sqlsock,
                                    "UPDATE `%s`.`ips` "
-                                       "SET "
-                                               "`pid` = %lu, "
-                                               "`rsv_since` = NOW(), "
-                                               "`rsv_by` = '" RLM_NETVIM_TMP_PREFIX "%lu', "
-                                               "`rsv_until` = NOW() + INTERVAL %d SECOND "
-                                       "WHERE "
-                                               "`ip` BETWEEN %lu AND %lu AND "
-                                               "("
-                                                       "`pid` IS NULL OR "
-                                                       "(`rsv_until` > 0 AND `rsv_until` < NOW())"
-                                               ") "
-                                       "ORDER BY RAND() "
-                                       "LIMIT 1",
+                                       "SET "
+                                               "`pid` = %lu, "
+                                               "`rsv_since` = NOW(), "
+                                               "`rsv_by` = '" RLM_NETVIM_TMP_PREFIX "%lu', "
+                                               "`rsv_until` = NOW() + INTERVAL %d SECOND "
+                                       "WHERE "
+                                               "`ip` BETWEEN %lu AND %lu AND "
+                                               "("
+                                                       "`pid` IS NULL OR "
+                                                       "(`rsv_until` > 0 AND `rsv_until` < NOW())"
+                                               ") "
+                                       "ORDER BY RAND() "
+                                       "LIMIT 1",
                                    inst->db_name, pid, connid, inst->free_after, ip_start, ip_stop)) {
                                        sql_release_socket(inst->sqlinst, sqlsock);
                                        return RLM_MODULE_FAIL;
@@ -588,11 +587,11 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                                /* update free IPs count */
                                if (!nvp_query(__LINE__, inst, sqlsock,
                                    "UPDATE `%s`.`ip_pools` "
-                                       "SET "
-                                               "`free` = `free` - 1 "
-                                       "WHERE "
-                                               "`pid` = %lu "
-                               "LIMIT 1",
+                                       "SET "
+                                               "`free` = `free` - 1 "
+                                       "WHERE "
+                                               "`pid` = %lu "
+                               "LIMIT 1",
                                    inst->db_name, pid)) {
                                        sql_release_socket(inst->sqlinst, sqlsock);
                                        return RLM_MODULE_FAIL;
@@ -630,7 +629,7 @@ end_gid:
        }
 
        /* add IP address to reply packet */
-       vp = radius_paircreate(request, &request->reply->vps,
+       vp = radius_paircreate(request->reply, &request->reply->vps,
                               PW_FRAMED_IP_ADDRESS, 0);
        vp->vp_ipaddr = ip.s_addr;
 
@@ -639,7 +638,7 @@ end_gid:
        return RLM_MODULE_OK;
 }
 
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        VALUE_PAIR *vp;
        rlm_sql_handle_t *sqlsock;
@@ -703,10 +702,10 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 
                        if (!nvp_query(__LINE__, inst, sqlsock,
                            "UPDATE `%s`.`ips` "
-                               "SET "
-                                       "`rsv_until` = 0, "
-                                       "`rsv_by` = '%s' "
-                               "WHERE `ip` = %lu",
+                               "SET "
+                                       "`rsv_until` = 0, "
+                                       "`rsv_by` = '%s' "
+                               "WHERE `ip` = %lu",
                            inst->db_name, sessid, framedip)) {
                                sql_release_socket(inst->sqlinst, sqlsock);
                                return RLM_MODULE_FAIL;
@@ -717,12 +716,12 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                case PW_STATUS_STOP:
                        if (!nvp_query(__LINE__, inst, sqlsock,
                            "UPDATE `%s`.`ips`, `%1$s`.`ip_pools` "
-                               "SET "
-                                       "`ips`.`rsv_until` = NOW() + INTERVAL %u SECOND, "
-                                       "`ip_pools`.`free` = `ip_pools`.`free` + 1 "
-                               "WHERE "
-                                       "`ips`.`rsv_by` = '%s' AND "
-                                       "`ips`.`ip` BETWEEN `ip_pools`.`ip_start` AND `ip_pools`.`ip_stop`",
+                               "SET "
+                                       "`ips`.`rsv_until` = NOW() + INTERVAL %u SECOND, "
+                                       "`ip_pools`.`free` = `ip_pools`.`free` + 1 "
+                               "WHERE "
+                                       "`ips`.`rsv_by` = '%s' AND "
+                                       "`ips`.`ip` BETWEEN `ip_pools`.`ip_start` AND `ip_pools`.`ip_stop`",
                            inst->db_name, inst->free_after, sessid)) {
                                sql_release_socket(inst->sqlinst, sqlsock);
                                return RLM_MODULE_FAIL;
@@ -744,10 +743,10 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 
                        if (!nvp_query(__LINE__, inst, sqlsock,
                            "UPDATE `%s`.`ips`, `radacct` "
-                               "SET `ips`.`rsv_until` = NOW() + INTERVAL %u SECOND "
-                               "WHERE "
-                                       "`radacct`.`nasipaddress` = '%s' AND "
-                                       "`ips`.`rsv_by` = `radacct`.`acctuniqueid`",
+                               "SET `ips`.`rsv_until` = NOW() + INTERVAL %u SECOND "
+                               "WHERE "
+                                       "`radacct`.`nasipaddress` = '%s' AND "
+                                       "`ips`.`rsv_by` = `radacct`.`acctuniqueid`",
                            inst->db_name, inst->free_after, nasipstr)) {
                                sql_release_socket(inst->sqlinst, sqlsock);
                                return RLM_MODULE_FAIL;
index 019699f..12487f9 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1195,9 +1195,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1757,11 +1757,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -2267,11 +2267,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 407c7ec..00ee376 100644 (file)
@@ -31,76 +31,72 @@ RCSID("$Id$")
 
 #include <rlm_sql.h>
 
+#define MAX_QUERY_LEN 4096
+
 /*
  *     Define a structure for our module configuration.
  */
 typedef struct rlm_sqlippool_t {
-       char *sql_instance_name;
+       char const *sql_instance_name;
 
-       int lease_duration;
+       uint32_t lease_duration;
 
        rlm_sql_t *sql_inst;
 
-       char *pool_name;
-
-       /* We ended up removing the init
-          queries so that its up to user
-          to create the db structure and put the required
-          information in there
-       */
-                               /* Allocation sequence */
-       time_t last_clear;      /* so we only do it once a second */
-       char *allocate_begin;   /* SQL query to begin */
-       char *allocate_clear;   /* SQL query to clear an IP */
-       char *allocate_find;    /* SQL query to find an unused IP */
-       char *allocate_update;  /* SQL query to mark an IP as used */
-       char *allocate_commit;  /* SQL query to commit */
-
-       char *pool_check;       /* Query to check for the existence of the pool */
-
-                               /* Start sequence */
-       char *start_begin;      /* SQL query to begin */
-       char *start_update;     /* SQL query to update an IP entry */
-       char *start_commit;     /* SQL query to commit */
-
-                               /* Alive sequence */
-       char *alive_begin;      /* SQL query to begin */
-       char *alive_update;     /* SQL query to update an IP entry */
-       char *alive_commit;     /* SQL query to commit */
-
-                               /* Stop sequence */
-       char *stop_begin;       /* SQL query to begin */
-       char *stop_clear;       /* SQL query to clear an IP */
-       char *stop_commit;      /* SQL query to commit */
-
-                               /* On sequence */
-       char *on_begin;         /* SQL query to begin */
-       char *on_clear;         /* SQL query to clear an entire NAS */
-       char *on_commit;        /* SQL query to commit */
-
-                               /* Off sequence */
-       char *off_begin;        /* SQL query to begin */
-       char *off_clear;        /* SQL query to clear an entire NAS */
-       char *off_commit;       /* SQL query to commit */
-
-                               /* Logging Section */
-       char *log_exists;       /* There was an ip address already assigned */
-       char *log_success;      /* We successfully allocated ip address from pool */
-       char *log_clear;        /* We successfully deallocated ip address from pool */
-       char *log_failed;       /* Failed to allocate ip from the pool */
-       char *log_nopool;       /* There was no Framed-IP-Address but also no Pool-Name */
-
-                               /* Reserved to handle 255.255.255.254 Requests */
-       char *defaultpool;      /* Default Pool-Name if there is none in the check items */
+       char const *pool_name;
+
+       time_t last_clear;              //!< So we only do it once a second.
+       char const *allocate_begin;     //!< SQL query to begin.
+       char const *allocate_clear;     //!< SQL query to clear an IP.
+       char const *allocate_find;      //!< SQL query to find an unused IP.
+       char const *allocate_update;    //!< SQL query to mark an IP as used.
+       char const *allocate_commit;    //!< SQL query to commit.
+
+       char const *pool_check;         //!< Query to check for the existence of the pool.
+
+                                       //!< Start sequence.
+       char const *start_begin;        //!< SQL query to begin.
+       char const *start_update;       //!< SQL query to update an IP entry.
+       char const *start_commit;       //!< SQL query to commit.
+
+                                       //!< Alive sequence.
+       char const *alive_begin;        //!< SQL query to begin.
+       char const *alive_update;       //!< SQL query to update an IP entry.
+       char const *alive_commit;       //!< SQL query to commit.
+
+                                       //!< Stop sequence.
+       char const *stop_begin;         //!< SQL query to begin.
+       char const *stop_clear;         //!< SQL query to clear an IP.
+       char const *stop_commit;        //!< SQL query to commit.
+
+                                       //!< On sequence.
+       char const *on_begin;           //!< SQL query to begin.
+       char const *on_clear;           //!< SQL query to clear an entire NAS.
+       char const *on_commit;          //!< SQL query to commit.
+
+                                       //!< Off sequence.
+       char const *off_begin;          //!< SQL query to begin.
+       char const *off_clear;          //!< SQL query to clear an entire NAS.
+       char const *off_commit;         //!< SQL query to commit.
+
+                                       //!< Logging Section.
+       char const *log_exists;         //!< There was an ip address already assigned.
+       char const *log_success;        //!< We successfully allocated ip address from pool.
+       char const *log_clear;          //!< We successfully deallocated ip address from pool.
+       char const *log_failed;         //!< Failed to allocate ip from the pool.
+       char const *log_nopool;         //!< There was no Framed-IP-Address but also no Pool-Name.
+
+                                       //!< Reserved to handle 255.255.255.254 Requests.
+       char const *defaultpool;        //!< Default Pool-Name if there is none in the check items.
 
 } rlm_sqlippool_t;
 
 static CONF_PARSER message_config[] = {
-       { "exists", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_exists), NULL, NULL },
-       { "success", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_success), NULL, NULL },
-       { "clear", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_clear), NULL, NULL },
-       { "failed", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_failed), NULL, NULL },
-       { "nopool", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, log_nopool), NULL, NULL },
+       { "exists", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, log_exists), NULL },
+       { "success", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, log_success), NULL },
+       { "clear", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, log_clear), NULL },
+       { "failed", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, log_failed), NULL },
+       { "nopool", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, log_nopool), NULL },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -115,101 +111,89 @@ static CONF_PARSER message_config[] = {
  *     buffer over-flows.
  */
 static CONF_PARSER module_config[] = {
-       {"sql-instance-name",PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-        offsetof(rlm_sqlippool_t,sql_instance_name), NULL, NULL},
-       {"sql_module_instance",PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-        offsetof(rlm_sqlippool_t,sql_instance_name), NULL, "sql"},
+       { "sql-instance-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, sql_instance_name), NULL },
+       { "sql_module_instance", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, sql_instance_name), "sql" },
 
-       { "lease-duration", PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,lease_duration), NULL, NULL},
-       { "lease_duration", PW_TYPE_INTEGER, offsetof(rlm_sqlippool_t,lease_duration), NULL, "86400"},
+       { "lease-duration", FR_CONF_OFFSET(PW_TYPE_INTEGER | PW_TYPE_DEPRECATED, rlm_sqlippool_t, lease_duration), NULL },
+       { "lease_duration", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_sqlippool_t, lease_duration), "86400" },
 
-       { "pool-name", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t, pool_name), NULL, NULL},
-       { "pool_name", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, pool_name), NULL, ""},
+       { "pool-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, pool_name), NULL },
+       { "pool_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, pool_name), "" },
 
-       { "default-pool", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t, defaultpool), NULL, NULL },
-       { "default_pool", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t, defaultpool), NULL, "main_pool" },
+       { "default-pool", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, defaultpool), NULL },
+       { "default_pool", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, defaultpool), "main_pool" },
 
 
-       { "allocate-begin", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlippool_t,allocate_begin), NULL, NULL},
-       { "allocate_begin", PW_TYPE_STRING_PTR,
-         offsetof(rlm_sqlippool_t,allocate_begin), NULL, "START TRANSACTION" },
+       { "allocate-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_begin), NULL },
+       { "allocate_begin", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, allocate_begin), "START TRANSACTION" },
 
-       { "allocate-clear", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlippool_t,allocate_clear), NULL, NULL},
-       { "allocate_clear", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlippool_t,allocate_clear), NULL, "" },
+       { "allocate-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_clear), NULL },
+       { "allocate_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_clear), ""  },
 
-       { "allocate-find", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlippool_t,allocate_find), NULL, NULL},
-       { "allocate_find", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlippool_t,allocate_find), NULL, "" },
+       { "allocate-find", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_find), NULL },
+       { "allocate_find", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_find), ""  },
 
-       { "allocate-update", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlippool_t,allocate_update), NULL, NULL },
-       { "allocate_update", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED,
-         offsetof(rlm_sqlippool_t,allocate_update), NULL, "" },
+       { "allocate-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_update), NULL },
+       { "allocate_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, allocate_update), ""  },
 
-       { "allocate-commit", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED,
-         offsetof(rlm_sqlippool_t,allocate_commit), NULL, NULL },
-       { "allocate_commit", PW_TYPE_STRING_PTR,
-         offsetof(rlm_sqlippool_t,allocate_commit), NULL, "COMMIT" },
+       { "allocate-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, allocate_commit), NULL },
+       { "allocate_commit", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, allocate_commit), "COMMIT" },
 
 
-       { "pool-check", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,pool_check), NULL, NULL },
-       { "pool_check", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,pool_check), NULL, "" },
+       { "pool-check", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, pool_check), NULL },
+       { "pool_check", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, pool_check), ""  },
 
 
-       { "start-begin", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,start_begin), NULL, NULL },
-       { "start_begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_begin), NULL, "START TRANSACTION" },
+       { "start-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_begin), NULL },
+       { "start_begin", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, start_begin), "START TRANSACTION" },
 
-       { "start-update", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,start_update), NULL, NULL },
-       { "start_update", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(rlm_sqlippool_t,start_update), NULL, "" },
+       { "start-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_update), NULL },
+       { "start_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, start_update), ""  },
 
-       { "start-commit", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,start_commit), NULL, NULL },
-       { "start_commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,start_commit), NULL, "COMMIT" },
+       { "start-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_commit), NULL },
+       { "start_commit", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, start_commit), "COMMIT" },
 
 
-       { "alive-begin", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,alive_begin), NULL, NULL },
-       { "alive_begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_begin), NULL, "START TRANSACTION" },
+       { "alive-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_begin), NULL },
+       { "alive_begin", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, alive_begin), "START TRANSACTION" },
 
-       { "alive-update", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,alive_update), NULL, NULL },
-       { "alive_update", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(rlm_sqlippool_t,alive_update), NULL, "" },
+       { "alive-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_update), NULL },
+       { "alive_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, alive_update), ""  },
 
-       { "alive-commit", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,alive_commit), NULL, NULL },
-       { "alive_commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,alive_commit), NULL, "COMMIT" },
+       { "alive-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_commit), NULL },
+       { "alive_commit", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, alive_commit), "COMMIT" },
 
 
-       { "stop-begin", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,stop_begin), NULL, NULL },
-       { "stop_begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_begin), NULL, "START TRANSACTION" },
+       { "stop-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_begin), NULL },
+       { "stop_begin", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, stop_begin), "START TRANSACTION" },
 
-       { "stop-clear", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,stop_clear), NULL, NULL },
-       { "stop_clear", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(rlm_sqlippool_t,stop_clear), NULL, "" },
+       { "stop-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_clear), NULL },
+       { "stop_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, stop_clear), ""  },
 
-       { "stop-commit", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,stop_commit), NULL, NULL },
-       { "stop_commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,stop_commit), NULL, "COMMIT" },
+       { "stop-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_commit), NULL },
+       { "stop_commit", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, stop_commit), "COMMIT" },
 
 
-       { "on-begin", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,on_begin), NULL, NULL },
-       { "on_begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_begin), NULL, "START TRANSACTION" },
+       { "on-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_begin), NULL },
+       { "on_begin", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, on_begin), "START TRANSACTION" },
 
-       { "on-clear", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,on_clear), NULL, NULL },
-       { "on_clear", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(rlm_sqlippool_t,on_clear), NULL, "" },
+       { "on-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_clear), NULL },
+       { "on_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, on_clear), ""  },
 
-       { "on-commit", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,on_commit), NULL, NULL },
-       { "on_commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,on_commit), NULL, "COMMIT" },
+       { "on-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_commit), NULL },
+       { "on_commit", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, on_commit), "COMMIT" },
 
 
-       { "off-begin", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,off_begin), NULL, NULL },
-       { "off_begin", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_begin), NULL, "START TRANSACTION" },
+       { "off-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_begin), NULL },
+       { "off_begin", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, off_begin), "START TRANSACTION" },
 
-       { "off-clear", PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,off_clear), NULL, NULL },
-       { "off_clear", PW_TYPE_STRING_PTR | PW_TYPE_REQUIRED, offsetof(rlm_sqlippool_t,off_clear), NULL, "" },
+       { "off-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_clear), NULL },
+       { "off_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_sqlippool_t, off_clear), ""  },
 
-       { "off-commit",PW_TYPE_STRING_PTR | PW_TYPE_DEPRECATED, offsetof(rlm_sqlippool_t,off_commit), NULL, NULL },
-       { "off_commit", PW_TYPE_STRING_PTR, offsetof(rlm_sqlippool_t,off_commit), NULL, "COMMIT" },
+       { "off-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_commit), NULL },
+       { "off_commit", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, off_commit), "COMMIT" },
 
-       { "messages", PW_TYPE_SUBSECTION, 0, NULL, (void const *) message_config },
+       { "messages", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) message_config },
 
        { NULL, -1, 0, NULL, NULL }
 };
@@ -240,36 +224,17 @@ static int sqlippool_expand(char * out, int outlen, char const * fmt,
                        break;
 
                c = *p;
-               if (c != '%' && c != '$' && c != '\\') {
+               if (c != '%') {
                        *q++ = *p;
                        continue;
                }
 
-               if (*++p == '\0')
+               if (*++p == '\0') {
                        break;
-
-               if (c == '\\') {
-                       switch(*p) {
-                       case '\\':
-                               *q++ = '\\';
-                               break;
-                       case 't':
-                               *q++ = '\t';
-                               break;
-                       case 'n':
-                               *q++ = '\n';
-                               break;
-                       default:
-                               *q++ = c;
-                               *q++ = *p;
-                               break;
-                       }
                }
-               else if (c == '%') {
+
+               if (c == '%') {
                        switch(*p) {
-                       case '%':
-                               *q++ = *p;
-                               break;
                        case 'P': /* pool name */
                                strlcpy(q, data->pool_name, freespace);
                                q += strlen(q);
@@ -291,6 +256,7 @@ static int sqlippool_expand(char * out, int outlen, char const * fmt,
                                strlcpy(q, tmp, freespace);
                                q += strlen(q);
                                break;
+
                        default:
                                *q++ = '%';
                                *q++ = *p;
@@ -362,9 +328,9 @@ static int sqlippool_command(char const * fmt, rlm_sql_handle_t * handle, rlm_sq
 /*
  * Query the database expecting a single result row
  */
-static int sqlippool_query1(char *out, int outlen, char const *fmt,
-                           rlm_sql_handle_t *handle, rlm_sqlippool_t *data,
-                           REQUEST *request, char *param, int param_len)
+static int CC_HINT(nonnull (1, 3, 4, 5)) sqlippool_query1(char *out, int outlen, char const *fmt,
+                                                         rlm_sql_handle_t *handle, rlm_sqlippool_t *data,
+                                                         REQUEST *request, char *param, int param_len)
 {
        char query[MAX_QUERY_LEN];
        char *expanded = NULL;
@@ -376,8 +342,6 @@ static int sqlippool_query1(char *out, int outlen, char const *fmt,
         */
        sqlippool_expand(query, sizeof(query), fmt, data, param, param_len);
 
-       rad_assert(request != NULL);
-
        *out = '\0';
 
        /*
@@ -440,11 +404,11 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
        char const *pool_name = NULL;
 
        pool_name = cf_section_name2(conf);
-       if (pool_name != NULL)
-               inst->pool_name = talloc_strdup(inst, pool_name);
-       else
-               inst->pool_name = talloc_strdup(inst, "ippool");
-
+       if (pool_name != NULL) {
+               inst->pool_name = talloc_typed_strdup(inst, pool_name);
+       } else {
+               inst->pool_name = talloc_typed_strdup(inst, "ippool");
+       }
        sqlinst = find_module_instance(cf_section_find("modules"),
                                       inst->sql_instance_name, 1);
        if (!sqlinst) {
@@ -469,7 +433,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
  *     If we have something to log, then we log it.
  *     Otherwise we return the retcode as soon as possible
  */
-static int do_logging(REQUEST *request, char *str, int rcode)
+static int do_logging(REQUEST *request, char const *str, int rcode)
 {
        char *expanded = NULL;
 
@@ -490,7 +454,7 @@ static int do_logging(REQUEST *request, char *str, int rcode)
 /*
  *     Allocate an IP number from the pool.
  */
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        rlm_sqlippool_t *inst = (rlm_sqlippool_t *) instance;
        char allocation[MAX_STRING_LEN];
@@ -511,7 +475,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
        }
 
        if (pairfind(request->config_items, PW_POOL_NAME, 0, TAG_ANY) == NULL) {
-               RDEBUG("No Pool-Name defined.");
+               RDEBUG("No Pool-Name defined");
 
                return do_logging(request, inst->log_nopool, RLM_MODULE_NOOP);
        }
@@ -590,21 +554,21 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                         *      sqlippool, so we should just ignore this
                         *      allocation failure and return NOOP
                         */
-                       RDEBUG("IP address could not be allocated as no pool exists with that name.");
+                       RDEBUG("IP address could not be allocated as no pool exists with that name");
                        return RLM_MODULE_NOOP;
 
                }
 
                inst->sql_inst->sql_release_socket(inst->sql_inst, handle);
 
-               RDEBUG("IP address could not be allocated.");
+               RDEBUG("IP address could not be allocated");
                return do_logging(request, inst->log_failed, RLM_MODULE_NOOP);
        }
 
        /*
         *      FIXME: Make it work with the ipv6 addresses
         */
-       if ((ip_hton(allocation, AF_INET, &ipaddr) < 0) ||
+       if ((ip_hton(&ipaddr, AF_INET, allocation, false) < 0) ||
            ((ip_allocation = ipaddr.ipaddr.ip4addr.s_addr) == INADDR_NONE)) {
                DO(allocate_commit);
 
@@ -621,7 +585,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
 
        RDEBUG("Allocated IP %s [%08x]", allocation, ip_allocation);
 
-       vp = radius_paircreate(request, &request->reply->vps,
+       vp = radius_paircreate(request->reply, &request->reply->vps,
                               PW_FRAMED_IP_ADDRESS, 0);
        vp->vp_ipaddr = ip_allocation;
        vp->length = 4;
@@ -687,7 +651,7 @@ static int mod_accounting_off(rlm_sql_handle_t *handle,
  *     If we find one and we have allocated an IP to this nas/port
  *     combination, then deallocate it.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        int rcode = RLM_MODULE_NOOP;
        VALUE_PAIR *vp;
@@ -697,7 +661,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 
        vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY);
        if (!vp) {
-               RDEBUG("Could not find account status type in packet.");
+               RDEBUG("Could not find account status type in packet");
                return RLM_MODULE_NOOP;
        }
        acct_status_type = vp->vp_integer;
diff --git a/src/modules/rlm_unbound/.gitignore b/src/modules/rlm_unbound/.gitignore
new file mode 100644 (file)
index 0000000..01a5daa
--- /dev/null
@@ -0,0 +1 @@
+all.mk
diff --git a/src/modules/rlm_unbound/all.mk.in b/src/modules/rlm_unbound/all.mk.in
new file mode 100644 (file)
index 0000000..2f6988e
--- /dev/null
@@ -0,0 +1,13 @@
+TARGETNAME     := @targetname@
+
+ifneq "$(TARGETNAME)" ""
+TARGET         := $(TARGETNAME).a
+endif
+
+SOURCES                := $(TARGETNAME).c
+
+SRC_CFLAGS     := @mod_cflags@
+TGT_LDLIBS     := @mod_ldflags@
+
+MAN            := rlm_unbound.5
+
diff --git a/src/modules/rlm_unbound/config.h.in b/src/modules/rlm_unbound/config.h.in
new file mode 100644 (file)
index 0000000..f80de9c
--- /dev/null
@@ -0,0 +1,19 @@
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
diff --git a/src/modules/rlm_unbound/configure b/src/modules/rlm_unbound/configure
new file mode 100755 (executable)
index 0000000..d84e606
--- /dev/null
@@ -0,0 +1,4519 @@
+#! /bin/sh
+# From configure.ac Revision.
+# Guess values for system-dependent variables and create Makefiles.
+# Generated by GNU Autoconf 2.69.
+#
+#
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+#
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Use a proper internal environment variable to ensure we don't fall
+  # into an infinite loop, continuously re-executing ourselves.
+  if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+    _as_can_reexec=no; export _as_can_reexec;
+    # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+  fi
+  # We don't want this to propagate to other subprocesses.
+         { _as_can_reexec=; unset _as_can_reexec;}
+if test "x$CONFIG_SHELL" = x; then
+  as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '\${1+\"\$@\"}'='\"\$@\"'
+  setopt NO_GLOB_SUBST
+else
+  case \`(set -o) 2>/dev/null\` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+"
+  as_required="as_fn_return () { (exit \$1); }
+as_fn_success () { as_fn_return 0; }
+as_fn_failure () { as_fn_return 1; }
+as_fn_ret_success () { return 0; }
+as_fn_ret_failure () { return 1; }
+
+exitcode=0
+as_fn_success || { exitcode=1; echo as_fn_success failed.; }
+as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
+as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
+as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
+if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+
+else
+  exitcode=1; echo positional parameters were not saved.
+fi
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
+  as_suggested="  as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
+  as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
+  eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
+  test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1"
+  if (eval "$as_required") 2>/dev/null; then :
+  as_have_required=yes
+else
+  as_have_required=no
+fi
+  if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+
+else
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  as_found=:
+  case $as_dir in #(
+        /*)
+          for as_base in sh bash ksh sh5; do
+            # Try only shells that exist, to save several forks.
+            as_shell=$as_dir/$as_base
+            if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
+                   { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  CONFIG_SHELL=$as_shell as_have_required=yes
+                  if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+  break 2
+fi
+fi
+          done;;
+       esac
+  as_found=false
+done
+$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+             { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
+  CONFIG_SHELL=$SHELL as_have_required=yes
+fi; }
+IFS=$as_save_IFS
+
+
+      if test "x$CONFIG_SHELL" != x; then :
+  export CONFIG_SHELL
+            # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+  *v*x* | *x*v* ) as_opts=-vx ;;
+  *v* ) as_opts=-v ;;
+  *x* ) as_opts=-x ;;
+  * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
+fi
+
+    if test x$as_have_required = xno; then :
+  $as_echo "$0: This script requires a shell more modern than all"
+  $as_echo "$0: the shells that I found on your system."
+  if test x${ZSH_VERSION+set} = xset ; then
+    $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+    $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+  else
+    $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+$0: including any error possibly output before this
+$0: message. Then install a modern shell, or manually run
+$0: the script under such a shell if you do have one."
+  fi
+  exit 1
+fi
+fi
+fi
+SHELL=${CONFIG_SHELL-/bin/sh}
+export SHELL
+# Unset more variables known to interfere with behavior of common tools.
+CLICOLOR_FORCE= GREP_OPTIONS=
+unset CLICOLOR_FORCE GREP_OPTIONS
+
+## --------------------- ##
+## M4sh Shell Functions. ##
+## --------------------- ##
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+
+  as_lineno_1=$LINENO as_lineno_1a=$LINENO
+  as_lineno_2=$LINENO as_lineno_2a=$LINENO
+  eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" &&
+  test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || {
+  # Blame Lee E. McMahon (1931-1989) for sed's syntax.  :-)
+  sed -n '
+    p
+    /[$]LINENO/=
+  ' <$as_myself |
+    sed '
+      s/[$]LINENO.*/&-/
+      t lineno
+      b
+      :lineno
+      N
+      :loop
+      s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/
+      t loop
+      s/-\n.*//
+    ' >$as_me.lineno &&
+  chmod +x "$as_me.lineno" ||
+    { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+
+  # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+  # already done that, so ensure we don't try to do so again and fall
+  # in an infinite loop.  This has already happened in practice.
+  _as_can_reexec=no; export _as_can_reexec
+  # Don't try to exec as it changes $[0], causing all sort of problems
+  # (the dirname of $[0] is not the place where we might find the
+  # original and so on.  Autoconf is especially sensitive to this).
+  . "./$as_me.lineno"
+  # Exit status is that of the last command.
+  exit
+}
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+test -n "$DJDIR" || exec 7<&0 </dev/null
+exec 6>&1
+
+# Name of the host.
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
+# so uname gets run too.
+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
+
+#
+# Initializations.
+#
+ac_default_prefix=/usr/local
+ac_clean_files=
+ac_config_libobj_dir=.
+LIBOBJS=
+cross_compiling=no
+subdirs=
+MFLAGS=
+MAKEFLAGS=
+
+# Identity of this package.
+PACKAGE_NAME=
+PACKAGE_TARNAME=
+PACKAGE_VERSION=
+PACKAGE_STRING=
+PACKAGE_BUGREPORT=
+PACKAGE_URL=
+
+ac_unique_file="rlm_unbound.c"
+ac_subst_vars='LTLIBOBJS
+LIBOBJS
+targetname
+mod_ldflags
+mod_cflags
+CPP
+OBJEXT
+EXEEXT
+ac_ct_CC
+CPPFLAGS
+LDFLAGS
+CFLAGS
+CC
+target_alias
+host_alias
+build_alias
+LIBS
+ECHO_T
+ECHO_N
+ECHO_C
+DEFS
+mandir
+localedir
+libdir
+psdir
+pdfdir
+dvidir
+htmldir
+infodir
+docdir
+oldincludedir
+includedir
+localstatedir
+sharedstatedir
+sysconfdir
+datadir
+datarootdir
+libexecdir
+sbindir
+bindir
+program_transform_name
+prefix
+exec_prefix
+PACKAGE_URL
+PACKAGE_BUGREPORT
+PACKAGE_STRING
+PACKAGE_VERSION
+PACKAGE_TARNAME
+PACKAGE_NAME
+PATH_SEPARATOR
+SHELL'
+ac_subst_files=''
+ac_user_opts='
+enable_option_checking
+'
+      ac_precious_vars='build_alias
+host_alias
+target_alias
+CC
+CFLAGS
+LDFLAGS
+LIBS
+CPPFLAGS
+CPP'
+
+
+# Initialize some variables set by options.
+ac_init_help=
+ac_init_version=false
+ac_unrecognized_opts=
+ac_unrecognized_sep=
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+cache_file=/dev/null
+exec_prefix=NONE
+no_create=
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+verbose=
+x_includes=NONE
+x_libraries=NONE
+
+# Installation directory options.
+# These are left unexpanded so users can "make install exec_prefix=/foo"
+# and all the variables that are supposed to be based on exec_prefix
+# by default will actually change.
+# Use braces instead of parens because sh, perl, etc. also accept them.
+# (The list follows the same order as the GNU Coding Standards.)
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datarootdir='${prefix}/share'
+datadir='${datarootdir}'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+docdir='${datarootdir}/doc/${PACKAGE}'
+infodir='${datarootdir}/info'
+htmldir='${docdir}'
+dvidir='${docdir}'
+pdfdir='${docdir}'
+psdir='${docdir}'
+libdir='${exec_prefix}/lib'
+localedir='${datarootdir}/locale'
+mandir='${datarootdir}/man'
+
+ac_prev=
+ac_dashdash=
+for ac_option
+do
+  # If the previous option needs an argument, assign it.
+  if test -n "$ac_prev"; then
+    eval $ac_prev=\$ac_option
+    ac_prev=
+    continue
+  fi
+
+  case $ac_option in
+  *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+  *=)   ac_optarg= ;;
+  *)    ac_optarg=yes ;;
+  esac
+
+  # Accept the important Cygnus configure options, so we can diagnose typos.
+
+  case $ac_dashdash$ac_option in
+  --)
+    ac_dashdash=yes ;;
+
+  -bindir | --bindir | --bindi | --bind | --bin | --bi)
+    ac_prev=bindir ;;
+  -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+    bindir=$ac_optarg ;;
+
+  -build | --build | --buil | --bui | --bu)
+    ac_prev=build_alias ;;
+  -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+    build_alias=$ac_optarg ;;
+
+  -cache-file | --cache-file | --cache-fil | --cache-fi \
+  | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+    ac_prev=cache_file ;;
+  -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+  | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+    cache_file=$ac_optarg ;;
+
+  --config-cache | -C)
+    cache_file=config.cache ;;
+
+  -datadir | --datadir | --datadi | --datad)
+    ac_prev=datadir ;;
+  -datadir=* | --datadir=* | --datadi=* | --datad=*)
+    datadir=$ac_optarg ;;
+
+  -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \
+  | --dataroo | --dataro | --datar)
+    ac_prev=datarootdir ;;
+  -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \
+  | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)
+    datarootdir=$ac_optarg ;;
+
+  -disable-* | --disable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=no ;;
+
+  -docdir | --docdir | --docdi | --doc | --do)
+    ac_prev=docdir ;;
+  -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)
+    docdir=$ac_optarg ;;
+
+  -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)
+    ac_prev=dvidir ;;
+  -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)
+    dvidir=$ac_optarg ;;
+
+  -enable-* | --enable-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid feature name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"enable_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval enable_$ac_useropt=\$ac_optarg ;;
+
+  -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+  | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+  | --exec | --exe | --ex)
+    ac_prev=exec_prefix ;;
+  -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+  | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+  | --exec=* | --exe=* | --ex=*)
+    exec_prefix=$ac_optarg ;;
+
+  -gas | --gas | --ga | --g)
+    # Obsolete; use --with-gas.
+    with_gas=yes ;;
+
+  -help | --help | --hel | --he | -h)
+    ac_init_help=long ;;
+  -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)
+    ac_init_help=recursive ;;
+  -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)
+    ac_init_help=short ;;
+
+  -host | --host | --hos | --ho)
+    ac_prev=host_alias ;;
+  -host=* | --host=* | --hos=* | --ho=*)
+    host_alias=$ac_optarg ;;
+
+  -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)
+    ac_prev=htmldir ;;
+  -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \
+  | --ht=*)
+    htmldir=$ac_optarg ;;
+
+  -includedir | --includedir | --includedi | --included | --include \
+  | --includ | --inclu | --incl | --inc)
+    ac_prev=includedir ;;
+  -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+  | --includ=* | --inclu=* | --incl=* | --inc=*)
+    includedir=$ac_optarg ;;
+
+  -infodir | --infodir | --infodi | --infod | --info | --inf)
+    ac_prev=infodir ;;
+  -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+    infodir=$ac_optarg ;;
+
+  -libdir | --libdir | --libdi | --libd)
+    ac_prev=libdir ;;
+  -libdir=* | --libdir=* | --libdi=* | --libd=*)
+    libdir=$ac_optarg ;;
+
+  -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+  | --libexe | --libex | --libe)
+    ac_prev=libexecdir ;;
+  -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+  | --libexe=* | --libex=* | --libe=*)
+    libexecdir=$ac_optarg ;;
+
+  -localedir | --localedir | --localedi | --localed | --locale)
+    ac_prev=localedir ;;
+  -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)
+    localedir=$ac_optarg ;;
+
+  -localstatedir | --localstatedir | --localstatedi | --localstated \
+  | --localstate | --localstat | --localsta | --localst | --locals)
+    ac_prev=localstatedir ;;
+  -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+  | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)
+    localstatedir=$ac_optarg ;;
+
+  -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+    ac_prev=mandir ;;
+  -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+    mandir=$ac_optarg ;;
+
+  -nfp | --nfp | --nf)
+    # Obsolete; use --without-fp.
+    with_fp=no ;;
+
+  -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+  | --no-cr | --no-c | -n)
+    no_create=yes ;;
+
+  -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+  | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+    no_recursion=yes ;;
+
+  -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+  | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+  | --oldin | --oldi | --old | --ol | --o)
+    ac_prev=oldincludedir ;;
+  -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+  | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+  | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+    oldincludedir=$ac_optarg ;;
+
+  -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+    ac_prev=prefix ;;
+  -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+    prefix=$ac_optarg ;;
+
+  -program-prefix | --program-prefix | --program-prefi | --program-pref \
+  | --program-pre | --program-pr | --program-p)
+    ac_prev=program_prefix ;;
+  -program-prefix=* | --program-prefix=* | --program-prefi=* \
+  | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+    program_prefix=$ac_optarg ;;
+
+  -program-suffix | --program-suffix | --program-suffi | --program-suff \
+  | --program-suf | --program-su | --program-s)
+    ac_prev=program_suffix ;;
+  -program-suffix=* | --program-suffix=* | --program-suffi=* \
+  | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+    program_suffix=$ac_optarg ;;
+
+  -program-transform-name | --program-transform-name \
+  | --program-transform-nam | --program-transform-na \
+  | --program-transform-n | --program-transform- \
+  | --program-transform | --program-transfor \
+  | --program-transfo | --program-transf \
+  | --program-trans | --program-tran \
+  | --progr-tra | --program-tr | --program-t)
+    ac_prev=program_transform_name ;;
+  -program-transform-name=* | --program-transform-name=* \
+  | --program-transform-nam=* | --program-transform-na=* \
+  | --program-transform-n=* | --program-transform-=* \
+  | --program-transform=* | --program-transfor=* \
+  | --program-transfo=* | --program-transf=* \
+  | --program-trans=* | --program-tran=* \
+  | --progr-tra=* | --program-tr=* | --program-t=*)
+    program_transform_name=$ac_optarg ;;
+
+  -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)
+    ac_prev=pdfdir ;;
+  -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)
+    pdfdir=$ac_optarg ;;
+
+  -psdir | --psdir | --psdi | --psd | --ps)
+    ac_prev=psdir ;;
+  -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)
+    psdir=$ac_optarg ;;
+
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil)
+    silent=yes ;;
+
+  -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+    ac_prev=sbindir ;;
+  -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+  | --sbi=* | --sb=*)
+    sbindir=$ac_optarg ;;
+
+  -sharedstatedir | --sharedstatedir | --sharedstatedi \
+  | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+  | --sharedst | --shareds | --shared | --share | --shar \
+  | --sha | --sh)
+    ac_prev=sharedstatedir ;;
+  -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+  | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+  | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+  | --sha=* | --sh=*)
+    sharedstatedir=$ac_optarg ;;
+
+  -site | --site | --sit)
+    ac_prev=site ;;
+  -site=* | --site=* | --sit=*)
+    site=$ac_optarg ;;
+
+  -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+    ac_prev=srcdir ;;
+  -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+    srcdir=$ac_optarg ;;
+
+  -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+  | --syscon | --sysco | --sysc | --sys | --sy)
+    ac_prev=sysconfdir ;;
+  -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+  | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+    sysconfdir=$ac_optarg ;;
+
+  -target | --target | --targe | --targ | --tar | --ta | --t)
+    ac_prev=target_alias ;;
+  -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+    target_alias=$ac_optarg ;;
+
+  -v | -verbose | --verbose | --verbos | --verbo | --verb)
+    verbose=yes ;;
+
+  -version | --version | --versio | --versi | --vers | -V)
+    ac_init_version=: ;;
+
+  -with-* | --with-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=\$ac_optarg ;;
+
+  -without-* | --without-*)
+    ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
+    # Reject names that are not valid shell variable names.
+    expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
+      as_fn_error $? "invalid package name: $ac_useropt"
+    ac_useropt_orig=$ac_useropt
+    ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+    case $ac_user_opts in
+      *"
+"with_$ac_useropt"
+"*) ;;
+      *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig"
+        ac_unrecognized_sep=', ';;
+    esac
+    eval with_$ac_useropt=no ;;
+
+  --x)
+    # Obsolete; use --with-x.
+    with_x=yes ;;
+
+  -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+  | --x-incl | --x-inc | --x-in | --x-i)
+    ac_prev=x_includes ;;
+  -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+  | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+    x_includes=$ac_optarg ;;
+
+  -x-libraries | --x-libraries | --x-librarie | --x-librari \
+  | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+    ac_prev=x_libraries ;;
+  -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+  | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+    x_libraries=$ac_optarg ;;
+
+  -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
+    ;;
+
+  *=*)
+    ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`
+    # Reject names that are not valid shell variable names.
+    case $ac_envvar in #(
+      '' | [0-9]* | *[!_$as_cr_alnum]* )
+      as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+    esac
+    eval $ac_envvar=\$ac_optarg
+    export $ac_envvar ;;
+
+  *)
+    # FIXME: should be removed in autoconf 3.0.
+    $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+    expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
+      $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+    : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
+    ;;
+
+  esac
+done
+
+if test -n "$ac_prev"; then
+  ac_option=--`echo $ac_prev | sed 's/_/-/g'`
+  as_fn_error $? "missing argument to $ac_option"
+fi
+
+if test -n "$ac_unrecognized_opts"; then
+  case $enable_option_checking in
+    no) ;;
+    fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
+    *)     $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+  esac
+fi
+
+# Check all directory arguments for consistency.
+for ac_var in  exec_prefix prefix bindir sbindir libexecdir datarootdir \
+               datadir sysconfdir sharedstatedir localstatedir includedir \
+               oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
+               libdir localedir mandir
+do
+  eval ac_val=\$$ac_var
+  # Remove trailing slashes.
+  case $ac_val in
+    */ )
+      ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'`
+      eval $ac_var=\$ac_val;;
+  esac
+  # Be sure to have absolute directory names.
+  case $ac_val in
+    [\\/$]* | ?:[\\/]* )  continue;;
+    NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
+  esac
+  as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
+done
+
+# There might be people who depend on the old broken behavior: `$host'
+# used to hold the argument of --host etc.
+# FIXME: To remove some day.
+build=$build_alias
+host=$host_alias
+target=$target_alias
+
+# FIXME: To remove some day.
+if test "x$host_alias" != x; then
+  if test "x$build_alias" = x; then
+    cross_compiling=maybe
+  elif test "x$build_alias" != "x$host_alias"; then
+    cross_compiling=yes
+  fi
+fi
+
+ac_tool_prefix=
+test -n "$host_alias" && ac_tool_prefix=$host_alias-
+
+test "$silent" = yes && exec 6>/dev/null
+
+
+ac_pwd=`pwd` && test -n "$ac_pwd" &&
+ac_ls_di=`ls -di .` &&
+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
+  as_fn_error $? "working directory cannot be determined"
+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
+  as_fn_error $? "pwd does not report name of working directory"
+
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+  ac_srcdir_defaulted=yes
+  # Try the directory containing this script, then the parent directory.
+  ac_confdir=`$as_dirname -- "$as_myself" ||
+$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_myself" : 'X\(//\)[^/]' \| \
+        X"$as_myself" : 'X\(//\)$' \| \
+        X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_myself" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  srcdir=$ac_confdir
+  if test ! -r "$srcdir/$ac_unique_file"; then
+    srcdir=..
+  fi
+else
+  ac_srcdir_defaulted=no
+fi
+if test ! -r "$srcdir/$ac_unique_file"; then
+  test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
+  as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
+fi
+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_abs_confdir=`(
+       cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
+       pwd)`
+# When building in place, set srcdir=.
+if test "$ac_abs_confdir" = "$ac_pwd"; then
+  srcdir=.
+fi
+# Remove unnecessary trailing slashes from srcdir.
+# Double slashes in file names in object file debugging info
+# mess up M-x gdb in Emacs.
+case $srcdir in
+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;
+esac
+for ac_var in $ac_precious_vars; do
+  eval ac_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_env_${ac_var}_value=\$${ac_var}
+  eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}
+  eval ac_cv_env_${ac_var}_value=\$${ac_var}
+done
+
+#
+# Report the --help message.
+#
+if test "$ac_init_help" = "long"; then
+  # Omit some internal or obsolete options to make the list less imposing.
+  # This message is too long to be a string in the A/UX 3.1 sh.
+  cat <<_ACEOF
+\`configure' configures this package to adapt to many kinds of systems.
+
+Usage: $0 [OPTION]... [VAR=VALUE]...
+
+To assign environment variables (e.g., CC, CFLAGS...), specify them as
+VAR=VALUE.  See below for descriptions of some of the useful variables.
+
+Defaults for the options are specified in brackets.
+
+Configuration:
+  -h, --help              display this help and exit
+      --help=short        display options specific to this package
+      --help=recursive    display the short help of all the included packages
+  -V, --version           display version information and exit
+  -q, --quiet, --silent   do not print \`checking ...' messages
+      --cache-file=FILE   cache test results in FILE [disabled]
+  -C, --config-cache      alias for \`--cache-file=config.cache'
+  -n, --no-create         do not create output files
+      --srcdir=DIR        find the sources in DIR [configure dir or \`..']
+
+Installation directories:
+  --prefix=PREFIX         install architecture-independent files in PREFIX
+                         [$ac_default_prefix]
+  --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
+                         [PREFIX]
+
+By default, \`make install' will install all the files in
+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
+an installation prefix other than \`$ac_default_prefix' using \`--prefix',
+for instance \`--prefix=\$HOME'.
+
+For better control, use the options below.
+
+Fine tuning of the installation directories:
+  --bindir=DIR            user executables [EPREFIX/bin]
+  --sbindir=DIR           system admin executables [EPREFIX/sbin]
+  --libexecdir=DIR        program executables [EPREFIX/libexec]
+  --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
+  --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
+  --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
+  --libdir=DIR            object code libraries [EPREFIX/lib]
+  --includedir=DIR        C header files [PREFIX/include]
+  --oldincludedir=DIR     C header files for non-gcc [/usr/include]
+  --datarootdir=DIR       read-only arch.-independent data root [PREFIX/share]
+  --datadir=DIR           read-only architecture-independent data [DATAROOTDIR]
+  --infodir=DIR           info documentation [DATAROOTDIR/info]
+  --localedir=DIR         locale-dependent data [DATAROOTDIR/locale]
+  --mandir=DIR            man documentation [DATAROOTDIR/man]
+  --docdir=DIR            documentation root [DATAROOTDIR/doc/PACKAGE]
+  --htmldir=DIR           html documentation [DOCDIR]
+  --dvidir=DIR            dvi documentation [DOCDIR]
+  --pdfdir=DIR            pdf documentation [DOCDIR]
+  --psdir=DIR             ps documentation [DOCDIR]
+_ACEOF
+
+  cat <<\_ACEOF
+_ACEOF
+fi
+
+if test -n "$ac_init_help"; then
+
+  cat <<\_ACEOF
+
+Some influential environment variables:
+  CC          C compiler command
+  CFLAGS      C compiler flags
+  LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
+             nonstandard directory <lib dir>
+  LIBS        libraries to pass to the linker, e.g. -l<library>
+  CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
+             you have headers in a nonstandard directory <include dir>
+  CPP         C preprocessor
+
+Use these variables to override the choices made by `configure' or to help
+it to find libraries and programs with nonstandard names/locations.
+
+Report bugs to the package provider.
+_ACEOF
+ac_status=$?
+fi
+
+if test "$ac_init_help" = "recursive"; then
+  # If there are subdirs, report their specific --help.
+  for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue
+    test -d "$ac_dir" ||
+      { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } ||
+      continue
+    ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+    cd "$ac_dir" || { ac_status=$?; continue; }
+    # Check for guested configure.
+    if test -f "$ac_srcdir/configure.gnu"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure.gnu" --help=recursive
+    elif test -f "$ac_srcdir/configure"; then
+      echo &&
+      $SHELL "$ac_srcdir/configure" --help=recursive
+    else
+      $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+    fi || ac_status=$?
+    cd "$ac_pwd" || { ac_status=$?; break; }
+  done
+fi
+
+test -n "$ac_init_help" && exit $ac_status
+if $ac_init_version; then
+  cat <<\_ACEOF
+configure
+generated by GNU Autoconf 2.69
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This configure script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it.
+_ACEOF
+  exit
+fi
+
+## ------------------------ ##
+## Autoconf initialization. ##
+## ------------------------ ##
+
+# ac_fn_c_try_compile LINENO
+# --------------------------
+# Try to compile conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_compile ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext
+  if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest.$ac_objext; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_compile
+
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } > conftest.i && {
+        test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+    ac_retval=1
+fi
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
+# ac_fn_c_try_link LINENO
+# -----------------------
+# Try to link conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_link ()
+{
+  as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+  rm -f conftest.$ac_objext conftest$ac_exeext
+  if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    grep -v '^ *+' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+    mv -f conftest.er1 conftest.err
+  fi
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; } && {
+        test -z "$ac_c_werror_flag" ||
+        test ! -s conftest.err
+       } && test -s conftest$ac_exeext && {
+        test "$cross_compiling" = yes ||
+        test -x conftest$ac_exeext
+       }; then :
+  ac_retval=0
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+       ac_retval=1
+fi
+  # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
+  # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
+  # interfere with the next link command; also delete a directory that is
+  # left behind by Apple's compiler.  We do this before executing the actions.
+  rm -rf conftest.dSYM conftest_ipa8_conftest.oo
+  eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+  as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_link
+cat >config.log <<_ACEOF
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+
+It was created by $as_me, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  $ $0 $@
+
+_ACEOF
+exec 5>>config.log
+{
+cat <<_ASUNAME
+## --------- ##
+## Platform. ##
+## --------- ##
+
+hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null     || echo unknown`
+
+/bin/arch              = `(/bin/arch) 2>/dev/null              || echo unknown`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null       || echo unknown`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`
+/usr/bin/hostinfo      = `(/usr/bin/hostinfo) 2>/dev/null      || echo unknown`
+/bin/machine           = `(/bin/machine) 2>/dev/null           || echo unknown`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null       || echo unknown`
+/bin/universe          = `(/bin/universe) 2>/dev/null          || echo unknown`
+
+_ASUNAME
+
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    $as_echo "PATH: $as_dir"
+  done
+IFS=$as_save_IFS
+
+} >&5
+
+cat >&5 <<_ACEOF
+
+
+## ----------- ##
+## Core tests. ##
+## ----------- ##
+
+_ACEOF
+
+
+# Keep a trace of the command line.
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Strip out --silent because we don't want to record it for future runs.
+# Also quote any args containing shell meta-characters.
+# Make two passes to allow for proper duplicate-argument suppression.
+ac_configure_args=
+ac_configure_args0=
+ac_configure_args1=
+ac_must_keep_next=false
+for ac_pass in 1 2
+do
+  for ac_arg
+  do
+    case $ac_arg in
+    -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;
+    -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+    | -silent | --silent | --silen | --sile | --sil)
+      continue ;;
+    *\'*)
+      ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    case $ac_pass in
+    1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
+    2)
+      as_fn_append ac_configure_args1 " '$ac_arg'"
+      if test $ac_must_keep_next = true; then
+       ac_must_keep_next=false # Got value, back to normal.
+      else
+       case $ac_arg in
+         *=* | --config-cache | -C | -disable-* | --disable-* \
+         | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \
+         | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \
+         | -with-* | --with-* | -without-* | --without-* | --x)
+           case "$ac_configure_args0 " in
+             "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;
+           esac
+           ;;
+         -* ) ac_must_keep_next=true ;;
+       esac
+      fi
+      as_fn_append ac_configure_args " '$ac_arg'"
+      ;;
+    esac
+  done
+done
+{ ac_configure_args0=; unset ac_configure_args0;}
+{ ac_configure_args1=; unset ac_configure_args1;}
+
+# When interrupted or exit'd, cleanup temporary files, and complete
+# config.log.  We remove comments because anyway the quotes in there
+# would cause problems or look ugly.
+# WARNING: Use '\'' to represent an apostrophe within the trap.
+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
+trap 'exit_status=$?
+  # Save into config.log some information that might help in debugging.
+  {
+    echo
+
+    $as_echo "## ---------------- ##
+## Cache variables. ##
+## ---------------- ##"
+    echo
+    # The following way of writing the cache mishandles newlines in values,
+(
+  for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+  (set) 2>&1 |
+    case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      sed -n \
+       "s/'\''/'\''\\\\'\'''\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"
+      ;; #(
+    *)
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+)
+    echo
+
+    $as_echo "## ----------------- ##
+## Output variables. ##
+## ----------------- ##"
+    echo
+    for ac_var in $ac_subst_vars
+    do
+      eval ac_val=\$$ac_var
+      case $ac_val in
+      *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+      esac
+      $as_echo "$ac_var='\''$ac_val'\''"
+    done | sort
+    echo
+
+    if test -n "$ac_subst_files"; then
+      $as_echo "## ------------------- ##
+## File substitutions. ##
+## ------------------- ##"
+      echo
+      for ac_var in $ac_subst_files
+      do
+       eval ac_val=\$$ac_var
+       case $ac_val in
+       *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+       esac
+       $as_echo "$ac_var='\''$ac_val'\''"
+      done | sort
+      echo
+    fi
+
+    if test -s confdefs.h; then
+      $as_echo "## ----------- ##
+## confdefs.h. ##
+## ----------- ##"
+      echo
+      cat confdefs.h
+      echo
+    fi
+    test "$ac_signal" != 0 &&
+      $as_echo "$as_me: caught signal $ac_signal"
+    $as_echo "$as_me: exit $exit_status"
+  } >&5
+  rm -f core *.core core.conftest.* &&
+    rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
+    exit $exit_status
+' 0
+for ac_signal in 1 2 13 15; do
+  trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal
+done
+ac_signal=0
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -f -r conftest* confdefs.h
+
+$as_echo "/* confdefs.h */" > confdefs.h
+
+# Predefined preprocessor variables.
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_NAME "$PACKAGE_NAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_VERSION "$PACKAGE_VERSION"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_STRING "$PACKAGE_STRING"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
+_ACEOF
+
+cat >>confdefs.h <<_ACEOF
+#define PACKAGE_URL "$PACKAGE_URL"
+_ACEOF
+
+
+# Let the site file select an alternate cache file if it wants to.
+# Prefer an explicitly selected file to automatically selected ones.
+ac_site_file1=NONE
+ac_site_file2=NONE
+if test -n "$CONFIG_SITE"; then
+  # We do not want a PATH search for config.site.
+  case $CONFIG_SITE in #((
+    -*)  ac_site_file1=./$CONFIG_SITE;;
+    */*) ac_site_file1=$CONFIG_SITE;;
+    *)   ac_site_file1=./$CONFIG_SITE;;
+  esac
+elif test "x$prefix" != xNONE; then
+  ac_site_file1=$prefix/share/config.site
+  ac_site_file2=$prefix/etc/config.site
+else
+  ac_site_file1=$ac_default_prefix/share/config.site
+  ac_site_file2=$ac_default_prefix/etc/config.site
+fi
+for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+do
+  test "x$ac_site_file" = xNONE && continue
+  if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+    sed 's/^/| /' "$ac_site_file" >&5
+    . "$ac_site_file" \
+      || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
+  fi
+done
+
+if test -r "$cache_file"; then
+  # Some versions of bash will fail to source /dev/null (special files
+  # actually), so we avoid doing that.  DJGPP emulates it as a regular file.
+  if test /dev/null != "$cache_file" && test -f "$cache_file"; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+$as_echo "$as_me: loading cache $cache_file" >&6;}
+    case $cache_file in
+      [\\/]* | ?:[\\/]* ) . "$cache_file";;
+      *)                      . "./$cache_file";;
+    esac
+  fi
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+$as_echo "$as_me: creating cache $cache_file" >&6;}
+  >$cache_file
+fi
+
+# Check that the precious variables saved in the cache have kept the same
+# value.
+ac_cache_corrupted=false
+for ac_var in $ac_precious_vars; do
+  eval ac_old_set=\$ac_cv_env_${ac_var}_set
+  eval ac_new_set=\$ac_env_${ac_var}_set
+  eval ac_old_val=\$ac_cv_env_${ac_var}_value
+  eval ac_new_val=\$ac_env_${ac_var}_value
+  case $ac_old_set,$ac_new_set in
+    set,)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,set)
+      { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
+$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+      ac_cache_corrupted=: ;;
+    ,);;
+    *)
+      if test "x$ac_old_val" != "x$ac_new_val"; then
+       # differences in whitespace do not lead to failure.
+       ac_old_val_w=`echo x $ac_old_val`
+       ac_new_val_w=`echo x $ac_new_val`
+       if test "$ac_old_val_w" != "$ac_new_val_w"; then
+         { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
+$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+         ac_cache_corrupted=:
+       else
+         { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
+$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+         eval $ac_var=\$ac_old_val
+       fi
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   former value:  \`$ac_old_val'" >&5
+$as_echo "$as_me:   former value:  \`$ac_old_val'" >&2;}
+       { $as_echo "$as_me:${as_lineno-$LINENO}:   current value: \`$ac_new_val'" >&5
+$as_echo "$as_me:   current value: \`$ac_new_val'" >&2;}
+      fi;;
+  esac
+  # Pass precious variables to config.status.
+  if test "$ac_new_set" = set; then
+    case $ac_new_val in
+    *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+    *) ac_arg=$ac_var=$ac_new_val ;;
+    esac
+    case " $ac_configure_args " in
+      *" '$ac_arg' "*) ;; # Avoid dups.  Use of quotes ensures accuracy.
+      *) as_fn_append ac_configure_args " '$ac_arg'" ;;
+    esac
+  fi
+done
+if $ac_cache_corrupted; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+  { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
+$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
+  as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+fi
+## -------------------- ##
+## Main body of script. ##
+## -------------------- ##
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+if test x$with_rlm_unbound != xno; then
+       ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+if test -n "$ac_tool_prefix"; then
+  # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+  ac_ct_CC=$CC
+  # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="gcc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+else
+  CC="$ac_cv_prog_CC"
+fi
+
+if test -z "$CC"; then
+         if test -n "$ac_tool_prefix"; then
+    # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="${ac_tool_prefix}cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  fi
+fi
+if test -z "$CC"; then
+  # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+  ac_prog_rejected=no
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+       ac_prog_rejected=yes
+       continue
+     fi
+    ac_cv_prog_CC="cc"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+if test $ac_prog_rejected = yes; then
+  # We found a bogon in the path, so make sure we never use it.
+  set dummy $ac_cv_prog_CC
+  shift
+  if test $# != 0; then
+    # We chose a different compiler from the bogus one.
+    # However, it has the same basename, so the bogon will be chosen
+    # first if we set CC to just the basename; use the full file name.
+    shift
+    ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+  fi
+fi
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test -z "$CC"; then
+  if test -n "$ac_tool_prefix"; then
+  for ac_prog in cl.exe
+  do
+    # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
+set dummy $ac_tool_prefix$ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$CC"; then
+  ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+$as_echo "$CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+    test -n "$CC" && break
+  done
+fi
+if test -z "$CC"; then
+  ac_ct_CC=$CC
+  for ac_prog in cl.exe
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$ac_ct_CC"; then
+  ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_ac_ct_CC="$ac_prog"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+$as_echo "$ac_ct_CC" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$ac_ct_CC" && break
+done
+
+  if test "x$ac_ct_CC" = x; then
+    CC=""
+  else
+    case $cross_compiling:$ac_tool_warned in
+yes:)
+{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+    CC=$ac_ct_CC
+  fi
+fi
+
+fi
+
+
+test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
+
+# Provide some information about the compiler.
+$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+set X $ac_compile
+ac_compiler=$2
+for ac_option in --version -v -V -qversion; do
+  { { ac_try="$ac_compiler $ac_option >&5"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compiler $ac_option >&5") 2>conftest.err
+  ac_status=$?
+  if test -s conftest.err; then
+    sed '10a\
+... rest of stderr output deleted ...
+        10q' conftest.err >conftest.er1
+    cat conftest.er1 >&5
+  fi
+  rm -f conftest.er1 conftest.err
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+done
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
+# Try to create an executable without -o first, disregard a.out.
+# It will help us diagnose broken compilers, and finding out an intuition
+# of exeext.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+$as_echo_n "checking whether the C compiler works... " >&6; }
+ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+
+# The possible output files:
+ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
+
+ac_rmfiles=
+for ac_file in $ac_files
+do
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    * ) ac_rmfiles="$ac_rmfiles $ac_file";;
+  esac
+done
+rm -f $ac_rmfiles
+
+if { { ac_try="$ac_link_default"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link_default") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+# in a Makefile.  We should not override ac_cv_exeext if it was cached,
+# so that the user can short-circuit this test for compilers unknown to
+# Autoconf.
+for ac_file in $ac_files ''
+do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj )
+       ;;
+    [ab].out )
+       # We found the default executable, but exeext='' is most
+       # certainly right.
+       break;;
+    *.* )
+       if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+       then :; else
+          ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+       fi
+       # We set ac_cv_exeext here because the later test for it is not
+       # safe: cross compilers may not add the suffix if given an `-o'
+       # argument, so we may need to know it at that point already.
+       # Even if this section looks crufty: it has the advantage of
+       # actually working.
+       break;;
+    * )
+       break;;
+  esac
+done
+test "$ac_cv_exeext" = no && ac_cv_exeext=
+
+else
+  ac_file=''
+fi
+if test -z "$ac_file"; then :
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+$as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
+$as_echo_n "checking for C compiler default output file name... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+$as_echo "$ac_file" >&6; }
+ac_exeext=$ac_cv_exeext
+
+rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+$as_echo_n "checking for suffix of executables... " >&6; }
+if { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  # If both `conftest.exe' and `conftest' are `present' (well, observable)
+# catch `conftest.exe'.  For instance with Cygwin, `ls conftest' will
+# work properly (i.e., refer to `conftest.exe'), while it won't with
+# `rm'.
+for ac_file in conftest.exe conftest conftest.*; do
+  test -f "$ac_file" || continue
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;;
+    *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
+         break;;
+    * ) break;;
+  esac
+done
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest conftest$ac_cv_exeext
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+$as_echo "$ac_cv_exeext" >&6; }
+
+rm -f conftest.$ac_ext
+EXEEXT=$ac_cv_exeext
+ac_exeext=$EXEEXT
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdio.h>
+int
+main ()
+{
+FILE *f = fopen ("conftest.out", "w");
+ return ferror (f) || fclose (f) != 0;
+
+  ;
+  return 0;
+}
+_ACEOF
+ac_clean_files="$ac_clean_files conftest.out"
+# Check that the compiler produces executables we can run.  If not, either
+# the compiler is broken, or we cross compile.
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+$as_echo_n "checking whether we are cross compiling... " >&6; }
+if test "$cross_compiling" != yes; then
+  { { ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_link") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }
+  if { ac_try='./conftest$ac_cv_exeext'
+  { { case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_try") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }; then
+    cross_compiling=no
+  else
+    if test "$cross_compiling" = maybe; then
+       cross_compiling=yes
+    else
+       { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot run C compiled programs.
+If you meant to cross compile, use \`--host'.
+See \`config.log' for more details" "$LINENO" 5; }
+    fi
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
+$as_echo "$cross_compiling" >&6; }
+
+rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+ac_clean_files=$ac_clean_files_save
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
+$as_echo_n "checking for suffix of object files... " >&6; }
+if ${ac_cv_objext+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.o conftest.obj
+if { { ac_try="$ac_compile"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+$as_echo "$ac_try_echo"; } >&5
+  (eval "$ac_compile") 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; then :
+  for ac_file in conftest.o conftest.obj conftest.*; do
+  test -f "$ac_file" || continue;
+  case $ac_file in
+    *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;;
+    *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`
+       break;;
+  esac
+done
+else
+  $as_echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+rm -f conftest.$ac_cv_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+$as_echo "$ac_cv_objext" >&6; }
+OBJEXT=$ac_cv_objext
+ac_objext=$OBJEXT
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
+$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
+if ${ac_cv_c_compiler_gnu+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+#ifndef __GNUC__
+       choke me
+#endif
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_compiler_gnu=yes
+else
+  ac_compiler_gnu=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ac_cv_c_compiler_gnu=$ac_compiler_gnu
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+if test $ac_compiler_gnu = yes; then
+  GCC=yes
+else
+  GCC=
+fi
+ac_test_CFLAGS=${CFLAGS+set}
+ac_save_CFLAGS=$CFLAGS
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+$as_echo_n "checking whether $CC accepts -g... " >&6; }
+if ${ac_cv_prog_cc_g+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_c_werror_flag=$ac_c_werror_flag
+   ac_c_werror_flag=yes
+   ac_cv_prog_cc_g=no
+   CFLAGS="-g"
+   cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+else
+  CFLAGS=""
+      cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+  ac_c_werror_flag=$ac_save_c_werror_flag
+        CFLAGS="-g"
+        cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+int
+main ()
+{
+
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_g=yes
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+   ac_c_werror_flag=$ac_save_c_werror_flag
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+$as_echo "$ac_cv_prog_cc_g" >&6; }
+if test "$ac_test_CFLAGS" = set; then
+  CFLAGS=$ac_save_CFLAGS
+elif test $ac_cv_prog_cc_g = yes; then
+  if test "$GCC" = yes; then
+    CFLAGS="-g -O2"
+  else
+    CFLAGS="-g"
+  fi
+else
+  if test "$GCC" = yes; then
+    CFLAGS="-O2"
+  else
+    CFLAGS=
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
+$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
+if ${ac_cv_prog_cc_c89+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_cv_prog_cc_c89=no
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <stdarg.h>
+#include <stdio.h>
+struct stat;
+/* Most of the following tests are stolen from RCS 5.7's src/conf.sh.  */
+struct buf { int x; };
+FILE * (*rcsopen) (struct buf *, struct stat *, int);
+static char *e (p, i)
+     char **p;
+     int i;
+{
+  return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+  char *s;
+  va_list v;
+  va_start (v,p);
+  s = g (p, va_arg (v,int));
+  va_end (v);
+  return s;
+}
+
+/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default.  It has
+   function prototypes and stuff, but not '\xHH' hex character constants.
+   These don't provoke an error unfortunately, instead are silently treated
+   as 'x'.  The following induces an error, until -std is added to get
+   proper ANSI mode.  Curiously '\x00'!='x' always comes out true, for an
+   array size at least.  It's necessary to write '\x00'==0 to get something
+   that's true only with -std.  */
+int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+   inside strings and character constants.  */
+#define FOO(x) 'x'
+int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
+int argc;
+char **argv;
+int
+main ()
+{
+return f (e, argv, 0) != argv[0]  ||  f (e, argv, 1) != argv[1];
+  ;
+  return 0;
+}
+_ACEOF
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
+       -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+do
+  CC="$ac_save_CC $ac_arg"
+  if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_prog_cc_c89=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext
+  test "x$ac_cv_prog_cc_c89" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CC=$ac_save_CC
+
+fi
+# AC_CACHE_VAL
+case "x$ac_cv_prog_cc_c89" in
+  x)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
+$as_echo "none needed" >&6; } ;;
+  xno)
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
+$as_echo "unsupported" >&6; } ;;
+  *)
+    CC="$CC $ac_cv_prog_cc_c89"
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
+esac
+if test "x$ac_cv_prog_cc_c89" != xno; then :
+
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+       ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+$as_echo_n "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+  CPP=
+fi
+if test -z "$CPP"; then
+  if ${ac_cv_prog_CPP+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+      # Double quotes because CPP needs to be expanded
+    for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+    do
+      ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+  break
+fi
+
+    done
+    ac_cv_prog_CPP=$CPP
+
+fi
+  CPP=$ac_cv_prog_CPP
+else
+  ac_cv_prog_CPP=$CPP
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+$as_echo "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+  # Use a header file that comes with gcc, so configuring glibc
+  # with a fresh cross-compiler works.
+  # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+  # <limits.h> exists even on freestanding compilers.
+  # On the NeXT, cc -E runs the code through the compiler's parser,
+  # not just through cpp. "Syntax error" is here to catch this case.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+                    Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+
+else
+  # Broken: fails on valid input.
+continue
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+  # OK, works on sane cases.  Now check whether nonexistent headers
+  # can be detected and how.
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  # Broken: success on invalid input.
+continue
+else
+  # Passes both tests.
+ac_preproc_ok=:
+break
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok; then :
+
+else
+  { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+
+
+
+sm_lib_safe=`echo "unbound" | sed 'y%./+-%__p_%'`
+sm_func_safe=`echo "ub_ctx_create" | sed 'y%./+-%__p_%'`
+
+old_LIBS="$LIBS"
+smart_lib=
+smart_lib_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ub_ctx_create in -lunbound in $try" >&5
+$as_echo_n "checking for ub_ctx_create in -lunbound in $try... " >&6; }
+    LIBS="-L$try -lunbound $old_LIBS -Wl,-rpath,$try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char ub_ctx_create();
+int
+main ()
+{
+ub_ctx_create()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                smart_lib="-L$try -lunbound -Wl,-rpath,$try"
+                { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ub_ctx_create in -lunbound" >&5
+$as_echo_n "checking for ub_ctx_create in -lunbound... " >&6; }
+  LIBS="-lunbound $old_LIBS"
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char ub_ctx_create();
+int
+main ()
+{
+ub_ctx_create()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+               smart_lib="-lunbound"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libunbound${libltdl_cv_shlibext}
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=libunbound.a
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
+
+
+  for try in $smart_lib_dir /usr/local/lib /opt/lib; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ub_ctx_create in -lunbound in $try" >&5
+$as_echo_n "checking for ub_ctx_create in -lunbound in $try... " >&6; }
+    LIBS="-L$try -lunbound $old_LIBS -Wl,-rpath,$try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+extern char ub_ctx_create();
+int
+main ()
+{
+ub_ctx_create()
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+                 smart_lib="-L$try -lunbound -Wl,-rpath,$try"
+                 { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                 break
+
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+  done
+  LIBS="$old_LIBS"
+fi
+
+if test "x$smart_lib" != "x"; then
+  eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes"
+  LIBS="$smart_lib $old_LIBS"
+  SMART_LIBS="$smart_lib $SMART_LIBS"
+fi
+
+               if test "x$ac_cv_lib_unbound_ub_ctx_create" != "xyes"; then
+               fail="$fail libunbound"
+       fi
+
+
+
+ac_safe=`echo "unbound.h" | sed 'y%./+-%__pm%'`
+old_CFLAGS="$CFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unbound.h in $try" >&5
+$as_echo_n "checking for unbound.h in $try... " >&6; }
+    CFLAGS="$old_CFLAGS -isystem $try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <unbound.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CFLAGS="$old_CFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unbound.h" >&5
+$as_echo_n "checking for unbound.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <unbound.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=unbound.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unbound.h in $try" >&5
+$as_echo_n "checking for unbound.h in $try... " >&6; }
+    CFLAGS="$old_CFLAGS -isystem $try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <unbound.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CFLAGS="$old_CFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CFLAGS="$old_CFLAGS $smart_include"
+  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+fi
+
+       if test "$ac_cv_header_unbound_h" != "yes"; then
+               fail="$fail unbound.h"
+       fi
+
+
+
+ac_safe=`echo "openssl/crypto.h" | sed 'y%./+-%__pm%'`
+old_CFLAGS="$CFLAGS"
+smart_include=
+smart_include_dir=
+
+if test "x$smart_try_dir" != "x"; then
+  for try in $smart_try_dir; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/crypto.h in $try" >&5
+$as_echo_n "checking for openssl/crypto.h in $try... " >&6; }
+    CFLAGS="$old_CFLAGS -isystem $try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <openssl/crypto.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CFLAGS="$old_CFLAGS"
+fi
+
+if test "x$smart_include" = "x"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/crypto.h" >&5
+$as_echo_n "checking for openssl/crypto.h... " >&6; }
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                 #include <openssl/crypto.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                  smart_include=" "
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                  break
+
+else
+
+                  smart_include=
+                  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+if test "x$smart_include" = "x"; then
+
+
+if test "x$LOCATE" != "x"; then
+       DIRS=
+  file=openssl/crypto.h
+
+  for x in `${LOCATE} $file 2>/dev/null`; do
+                                       base=`echo $x | sed "s%/${file}%%"`
+    if test "x$x" = "x$base"; then
+      continue;
+    fi
+
+    dir=`${DIRNAME} $x 2>/dev/null`
+               exclude=`echo ${dir} | ${GREP} /home`
+    if test "x$exclude" != "x"; then
+      continue
+    fi
+
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+    if test "x$already" = "x"; then
+      DIRS="$DIRS $dir"
+    fi
+  done
+fi
+
+eval "smart_include_dir=\"\$smart_include_dir $DIRS\""
+
+  for try in $smart_include_dir /usr/local/include /opt/include; do
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/crypto.h in $try" >&5
+$as_echo_n "checking for openssl/crypto.h in $try... " >&6; }
+    CFLAGS="$old_CFLAGS -isystem $try"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+                   #include <openssl/crypto.h>
+int
+main ()
+{
+int a = 1;
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+                    smart_include="-isystem $try"
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+$as_echo "yes" >&6; }
+                    break
+
+else
+
+                    smart_include=
+                    { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+  done
+  CFLAGS="$old_CFLAGS"
+fi
+
+if test "x$smart_include" != "x"; then
+  eval "ac_cv_header_$ac_safe=yes"
+  CFLAGS="$old_CFLAGS $smart_include"
+  SMART_CFLAGS="$SMART_CFLAGS $smart_include"
+fi
+
+       if test "$ac_cv_header_openssl_crypto_h" != "yes"; then
+               fail="$fail openssl/crypto.h"
+       fi
+
+       targetname=rlm_unbound
+else
+       targetname=
+       echo \*\*\* module rlm_unbound is disabled.
+fi
+
+if test x"$fail" != x""; then
+       if test x"${enable_strict_dependencies}" = x"yes"; then
+               as_fn_error $? "set --without-rlm_unbound to disable it explicitly." "$LINENO" 5
+       else
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building rlm_unbound." >&5
+$as_echo "$as_me: WARNING: silently not building rlm_unbound." >&2;}
+               { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: rlm_unbound requires: $fail." >&5
+$as_echo "$as_me: WARNING: FAILURE: rlm_unbound requires: $fail." >&2;};
+               targetname=""
+       fi
+fi
+
+mod_ldflags="${SMART_LIBS}"
+mod_cflags="${SMART_CFLAGS}"
+
+
+
+
+ac_config_headers="$ac_config_headers config.h"
+
+
+
+
+  unset ac_cv_env_LIBS_set
+  unset ac_cv_env_LIBS_value
+
+  ac_config_files="$ac_config_files all.mk"
+
+cat >confcache <<\_ACEOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs, see configure's option --config-cache.
+# It is not useful on other systems.  If it contains results you don't
+# want to keep, you may remove or edit it.
+#
+# config.status only pays attention to the cache file if you give it
+# the --recheck option to rerun configure.
+#
+# `ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# following values.
+
+_ACEOF
+
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, we kill variables containing newlines.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(
+  for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do
+    eval ac_val=\$$ac_var
+    case $ac_val in #(
+    *${as_nl}*)
+      case $ac_var in #(
+      *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+      esac
+      case $ac_var in #(
+      _ | IFS | as_nl) ;; #(
+      BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #(
+      *) { eval $ac_var=; unset $ac_var;} ;;
+      esac ;;
+    esac
+  done
+
+  (set) 2>&1 |
+    case $as_nl`(ac_space=' '; set) 2>&1` in #(
+    *${as_nl}ac_space=\ *)
+      # `set' does not quote correctly, so add quotes: double-quote
+      # substitution turns \\\\ into \\, and sed turns \\ into \.
+      sed -n \
+       "s/'/'\\\\''/g;
+         s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
+      ;; #(
+    *)
+      # `set' quotes correctly as required by POSIX, so do not add quotes.
+      sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
+      ;;
+    esac |
+    sort
+) |
+  sed '
+     /^ac_cv_env_/b end
+     t clear
+     :clear
+     s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+     t end
+     s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
+     :end' >>confcache
+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
+  if test -w "$cache_file"; then
+    if test "x$cache_file" != "x/dev/null"; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+$as_echo "$as_me: updating cache $cache_file" >&6;}
+      if test ! -f "$cache_file" || test -h "$cache_file"; then
+       cat confcache >"$cache_file"
+      else
+       case $cache_file in #(
+       */* | ?:*)
+         mv -f confcache "$cache_file"$$ &&
+         mv -f "$cache_file"$$ "$cache_file" ;; #(
+       *)
+         mv -f confcache "$cache_file" ;;
+       esac
+      fi
+    fi
+  else
+    { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+  fi
+fi
+rm -f confcache
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+DEFS=-DHAVE_CONFIG_H
+
+ac_libobjs=
+ac_ltlibobjs=
+U=
+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
+  # 1. Remove the extension, and $U if already installed.
+  ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
+  ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+  # 2. Prepend LIBOBJDIR.  When used with automake>=1.10 LIBOBJDIR
+  #    will be set to the directory where LIBOBJS objects are built.
+  as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
+  as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo'
+done
+LIBOBJS=$ac_libobjs
+
+LTLIBOBJS=$ac_ltlibobjs
+
+
+
+: "${CONFIG_STATUS=./config.status}"
+ac_write_fail=0
+ac_clean_files_save=$ac_clean_files
+ac_clean_files="$ac_clean_files $CONFIG_STATUS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+as_write_fail=0
+cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
+#! $SHELL
+# Generated by $as_me.
+# Run this file to recreate the current configuration.
+# Compiler output produced by configure, useful for debugging
+# configure, is in config.log if it exists.
+
+debug=false
+ac_cs_recheck=false
+ac_cs_silent=false
+
+SHELL=\${CONFIG_SHELL-$SHELL}
+export SHELL
+_ASEOF
+cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+  emulate sh
+  NULLCMD=:
+  # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+  # is contrary to our usage.  Disable this feature.
+  alias -g '${1+"$@"}'='"$@"'
+  setopt NO_GLOB_SUBST
+else
+  case `(set -o) 2>/dev/null` in #(
+  *posix*) :
+    set -o posix ;; #(
+  *) :
+     ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+    && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='print -r --'
+  as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+  as_echo='printf %s\n'
+  as_echo_n='printf %s'
+else
+  if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+    as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+    as_echo_n='/usr/ucb/echo -n'
+  else
+    as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+    as_echo_n_body='eval
+      arg=$1;
+      case $arg in #(
+      *"$as_nl"*)
+       expr "X$arg" : "X\\(.*\\)$as_nl";
+       arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+      esac;
+      expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+    '
+    export as_echo_n_body
+    as_echo_n='sh -c $as_echo_n_body as_echo'
+  fi
+  export as_echo_body
+  as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+  PATH_SEPARATOR=:
+  (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+    (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+      PATH_SEPARATOR=';'
+  }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order.  Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" ""       $as_nl"
+
+# Find who we are.  Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+  *[\\/]* ) as_myself=$0 ;;
+  *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+  done
+IFS=$as_save_IFS
+
+     ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+  as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+  $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+  exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh).  But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there.  '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+  && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+  as_status=$1; test $as_status -eq 0 && as_status=1
+  if test "$4"; then
+    as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+    $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+  fi
+  $as_echo "$as_me: error: $2" >&2
+  as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+  return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+  set +e
+  as_fn_set_status $1
+  exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+  { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+  eval 'as_fn_append ()
+  {
+    eval $1+=\$2
+  }'
+else
+  as_fn_append ()
+  {
+    eval $1=\$$1\$2
+  }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+  eval 'as_fn_arith ()
+  {
+    as_val=$(( $* ))
+  }'
+else
+  as_fn_arith ()
+  {
+    as_val=`expr "$@" || test $? -eq 1`
+  }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+   test "X`expr 00001 : '.*\(...\)'`" = X001; then
+  as_expr=expr
+else
+  as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+  as_basename=basename
+else
+  as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+  as_dirname=dirname
+else
+  as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+        X"$0" : 'X\(//\)$' \| \
+        X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+    sed '/^.*\/\([^/][^/]*\)\/*$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\/\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+  case `echo 'xy\c'` in
+  *c*) ECHO_T='        ';;     # ECHO_T is single tab character.
+  xy)  ECHO_C='\c';;
+  *)   echo `echo ksh88 bug on AIX 6.1` > /dev/null
+       ECHO_T='        ';;
+  esac;;
+*)
+  ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+  rm -f conf$$.dir/conf$$.file
+else
+  rm -f conf$$.dir
+  mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+  if ln -s conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s='ln -s'
+    # ... but there are two gotchas:
+    # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+    # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+    # In both cases, we have to default to `cp -pR'.
+    ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+      as_ln_s='cp -pR'
+  elif ln conf$$.file conf$$ 2>/dev/null; then
+    as_ln_s=ln
+  else
+    as_ln_s='cp -pR'
+  fi
+else
+  as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+  case $as_dir in #(
+  -*) as_dir=./$as_dir;;
+  esac
+  test -d "$as_dir" || eval $as_mkdir_p || {
+    as_dirs=
+    while :; do
+      case $as_dir in #(
+      *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+      *) as_qdir=$as_dir;;
+      esac
+      as_dirs="'$as_qdir' $as_dirs"
+      as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$as_dir" : 'X\(//\)[^/]' \| \
+        X"$as_dir" : 'X\(//\)$' \| \
+        X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+      test -d "$as_dir" && break
+    done
+    test -z "$as_dirs" || eval "mkdir $as_dirs"
+  } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+  as_mkdir_p='mkdir -p "$as_dir"'
+else
+  test -d ./-p && rmdir ./-p
+  as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+  test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## ----------------------------------- ##
+## Main body of $CONFIG_STATUS script. ##
+## ----------------------------------- ##
+_ASEOF
+test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# Save the log message, to keep $0 and so on meaningful, and to
+# report actual input values of CONFIG_FILES etc. instead of their
+# values after options handling.
+ac_log="
+This file was extended by $as_me, which was
+generated by GNU Autoconf 2.69.  Invocation command line was
+
+  CONFIG_FILES    = $CONFIG_FILES
+  CONFIG_HEADERS  = $CONFIG_HEADERS
+  CONFIG_LINKS    = $CONFIG_LINKS
+  CONFIG_COMMANDS = $CONFIG_COMMANDS
+  $ $0 $@
+
+on `(hostname || uname -n) 2>/dev/null | sed 1q`
+"
+
+_ACEOF
+
+case $ac_config_files in *"
+"*) set x $ac_config_files; shift; ac_config_files=$*;;
+esac
+
+case $ac_config_headers in *"
+"*) set x $ac_config_headers; shift; ac_config_headers=$*;;
+esac
+
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+# Files that config.status was made for.
+config_files="$ac_config_files"
+config_headers="$ac_config_headers"
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+ac_cs_usage="\
+\`$as_me' instantiates files and other configuration actions
+from templates according to the current configuration.  Unless the files
+and actions are specified as TAGs, all are instantiated by default.
+
+Usage: $0 [OPTION]... [TAG]...
+
+  -h, --help       print this help, then exit
+  -V, --version    print version number and configuration settings, then exit
+      --config     print configuration, then exit
+  -q, --quiet, --silent
+                  do not print progress messages
+  -d, --debug      don't remove temporary files
+      --recheck    update $as_me by reconfiguring in the same conditions
+      --file=FILE[:TEMPLATE]
+                  instantiate the configuration file FILE
+      --header=FILE[:TEMPLATE]
+                  instantiate the configuration header FILE
+
+Configuration files:
+$config_files
+
+Configuration headers:
+$config_headers
+
+Report bugs to the package provider."
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_version="\\
+config.status
+configured by $0, generated by GNU Autoconf 2.69,
+  with options \\"\$ac_cs_config\\"
+
+Copyright (C) 2012 Free Software Foundation, Inc.
+This config.status script is free software; the Free Software Foundation
+gives unlimited permission to copy, distribute and modify it."
+
+ac_pwd='$ac_pwd'
+srcdir='$srcdir'
+test -n "\$AWK" || AWK=awk
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# The default lists apply if the user does not specify any file.
+ac_need_defaults=:
+while test $# != 0
+do
+  case $1 in
+  --*=?*)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
+    ac_shift=:
+    ;;
+  --*=)
+    ac_option=`expr "X$1" : 'X\([^=]*\)='`
+    ac_optarg=
+    ac_shift=:
+    ;;
+  *)
+    ac_option=$1
+    ac_optarg=$2
+    ac_shift=shift
+    ;;
+  esac
+
+  case $ac_option in
+  # Handling of the options.
+  -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+    ac_cs_recheck=: ;;
+  --version | --versio | --versi | --vers | --ver | --ve | --v | -V )
+    $as_echo "$ac_cs_version"; exit ;;
+  --config | --confi | --conf | --con | --co | --c )
+    $as_echo "$ac_cs_config"; exit ;;
+  --debug | --debu | --deb | --de | --d | -d )
+    debug=: ;;
+  --file | --fil | --fi | --f )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    '') as_fn_error $? "missing file argument" ;;
+    esac
+    as_fn_append CONFIG_FILES " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --header | --heade | --head | --hea )
+    $ac_shift
+    case $ac_optarg in
+    *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+    esac
+    as_fn_append CONFIG_HEADERS " '$ac_optarg'"
+    ac_need_defaults=false;;
+  --he | --h)
+    # Conflict between --help and --header
+    as_fn_error $? "ambiguous option: \`$1'
+Try \`$0 --help' for more information.";;
+  --help | --hel | -h )
+    $as_echo "$ac_cs_usage"; exit ;;
+  -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+  | -silent | --silent | --silen | --sile | --sil | --si | --s)
+    ac_cs_silent=: ;;
+
+  # This is an error.
+  -*) as_fn_error $? "unrecognized option: \`$1'
+Try \`$0 --help' for more information." ;;
+
+  *) as_fn_append ac_config_targets " $1"
+     ac_need_defaults=false ;;
+
+  esac
+  shift
+done
+
+ac_configure_extra_args=
+
+if $ac_cs_silent; then
+  exec 6>/dev/null
+  ac_configure_extra_args="$ac_configure_extra_args --silent"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+if \$ac_cs_recheck; then
+  set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+  shift
+  \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+  CONFIG_SHELL='$SHELL'
+  export CONFIG_SHELL
+  exec "\$@"
+fi
+
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+exec 5>>config.log
+{
+  echo
+  sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+  $as_echo "$ac_log"
+} >&5
+
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+
+# Handling of arguments.
+for ac_config_target in $ac_config_targets
+do
+  case $ac_config_target in
+    "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
+    "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;;
+
+  *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+  esac
+done
+
+
+# If the user did not use the arguments to specify the items to instantiate,
+# then the envvar interface is used.  Set only those that are not.
+# We use the long form for the default assignment because of an extremely
+# bizarre bug on SunOS 4.1.3.
+if $ac_need_defaults; then
+  test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
+  test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
+fi
+
+# Have a temporary directory for convenience.  Make it in the build tree
+# simply because there is no reason against having it here, and in addition,
+# creating and moving files from /tmp can sometimes cause problems.
+# Hook for its removal unless debugging.
+# Note that there is a small window in which the directory will not be cleaned:
+# after its creation but before its name has been assigned to `$tmp'.
+$debug ||
+{
+  tmp= ac_tmp=
+  trap 'exit_status=$?
+  : "${ac_tmp:=$tmp}"
+  { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
+' 0
+  trap 'as_fn_exit 1' 1 2 13 15
+}
+# Create a (secure) tmp directory for tmp files.
+
+{
+  tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
+  test -d "$tmp"
+}  ||
+{
+  tmp=./conf$$-$RANDOM
+  (umask 077 && mkdir "$tmp")
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
+
+# Set up the scripts for CONFIG_FILES section.
+# No need to generate them if there are no CONFIG_FILES.
+# This happens for instance with `./config.status config.h'.
+if test -n "$CONFIG_FILES"; then
+
+
+ac_cr=`echo X | tr X '\015'`
+# On cygwin, bash can eat \r inside `` if the user requested igncr.
+# But we know of no other shell where ac_cr would be empty at this
+# point, so we can use a bashism as a fallback.
+if test "x$ac_cr" = x; then
+  eval ac_cr=\$\'\\r\'
+fi
+ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
+if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
+  ac_cs_awk_cr='\\r'
+else
+  ac_cs_awk_cr=$ac_cr
+fi
+
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
+_ACEOF
+
+
+{
+  echo "cat >conf$$subs.awk <<_ACEOF" &&
+  echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
+  echo "_ACEOF"
+} >conf$$subs.sh ||
+  as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
+ac_delim='%!_!# '
+for ac_last_try in false false false false false :; do
+  . ./conf$$subs.sh ||
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+
+  ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
+  if test $ac_delim_n = $ac_delim_num; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+rm -f conf$$subs.sh
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
+_ACEOF
+sed -n '
+h
+s/^/S["/; s/!.*/"]=/
+p
+g
+s/^[^!]*!//
+:repl
+t repl
+s/'"$ac_delim"'$//
+t delim
+:nl
+h
+s/\(.\{148\}\)..*/\1/
+t more1
+s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/
+p
+n
+b repl
+:more1
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t nl
+:delim
+h
+s/\(.\{148\}\)..*/\1/
+t more2
+s/["\\]/\\&/g; s/^/"/; s/$/"/
+p
+b
+:more2
+s/["\\]/\\&/g; s/^/"/; s/$/"\\/
+p
+g
+s/.\{148\}//
+t delim
+' <conf$$subs.awk | sed '
+/^[^""]/{
+  N
+  s/\n//
+}
+' >>$CONFIG_STATUS || ac_write_fail=1
+rm -f conf$$subs.awk
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+_ACAWK
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
+  for (key in S) S_is_set[key] = 1
+  FS = "\a"
+
+}
+{
+  line = $ 0
+  nfields = split(line, field, "@")
+  substed = 0
+  len = length(field[1])
+  for (i = 2; i < nfields; i++) {
+    key = field[i]
+    keylen = length(key)
+    if (S_is_set[key]) {
+      value = S[key]
+      line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3)
+      len += length(value) + length(field[++i])
+      substed = 1
+    } else
+      len += 1 + keylen
+  }
+
+  print line
+}
+
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
+  sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
+else
+  cat
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+  || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
+_ACEOF
+
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
+# trailing colons and then remove the whole line if VPATH becomes empty
+# (actually we leave an empty line to preserve line numbers).
+if test "x$srcdir" = x.; then
+  ac_vpsub='/^[         ]*VPATH[        ]*=[    ]*/{
+h
+s///
+s/^/:/
+s/[     ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
+s/:*$//
+x
+s/\(=[  ]*\).*/\1/
+G
+s/\n//
+s/^[^=]*=[      ]*$//
+}'
+fi
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+fi # test -n "$CONFIG_FILES"
+
+# Set up the scripts for CONFIG_HEADERS section.
+# No need to generate them if there are no CONFIG_HEADERS.
+# This happens for instance with `./config.status Makefile'.
+if test -n "$CONFIG_HEADERS"; then
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
+BEGIN {
+_ACEOF
+
+# Transform confdefs.h into an awk script `defines.awk', embedded as
+# here-document in config.status, that substitutes the proper values into
+# config.h.in to produce config.h.
+
+# Create a delimiter string that does not exist in confdefs.h, to ease
+# handling of long lines.
+ac_delim='%!_!# '
+for ac_last_try in false false :; do
+  ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+  if test -z "$ac_tt"; then
+    break
+  elif $ac_last_try; then
+    as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
+  else
+    ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
+  fi
+done
+
+# For the awk script, D is an array of macro values keyed by name,
+# likewise P contains macro parameters if any.  Preserve backslash
+# newline sequences.
+
+ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]*
+sed -n '
+s/.\{148\}/&'"$ac_delim"'/g
+t rset
+:rset
+s/^[    ]*#[    ]*define[       ][      ]*/ /
+t def
+d
+:def
+s/\\$//
+t bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3"/p
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2"/p
+d
+:bsnl
+s/["\\]/\\&/g
+s/^ \('"$ac_word_re"'\)\(([^()]*)\)[    ]*\(.*\)/P["\1"]="\2"\
+D["\1"]=" \3\\\\\\n"\\/p
+t cont
+s/^ \('"$ac_word_re"'\)[        ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p
+t cont
+d
+:cont
+n
+s/.\{148\}/&'"$ac_delim"'/g
+t clear
+:clear
+s/\\$//
+t bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/"/p
+d
+:bsnlc
+s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p
+b cont
+' <confdefs.h | sed '
+s/'"$ac_delim"'/"\\\
+"/g' >>$CONFIG_STATUS || ac_write_fail=1
+
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  for (key in D) D_is_set[key] = 1
+  FS = "\a"
+}
+/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ {
+  line = \$ 0
+  split(line, arg, " ")
+  if (arg[1] == "#") {
+    defundef = arg[2]
+    mac1 = arg[3]
+  } else {
+    defundef = substr(arg[1], 2)
+    mac1 = arg[2]
+  }
+  split(mac1, mac2, "(") #)
+  macro = mac2[1]
+  prefix = substr(line, 1, index(line, defundef) - 1)
+  if (D_is_set[macro]) {
+    # Preserve the white space surrounding the "#".
+    print prefix "define", macro P[macro] D[macro]
+    next
+  } else {
+    # Replace #undef with comments.  This is necessary, for example,
+    # in the case of _POSIX_SOURCE, which is predefined and required
+    # on some systems where configure will not decide to define it.
+    if (defundef == "undef") {
+      print "/*", prefix defundef, macro, "*/"
+      next
+    }
+  }
+}
+{ print }
+_ACAWK
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+  as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
+fi # test -n "$CONFIG_HEADERS"
+
+
+eval set X "  :F $CONFIG_FILES  :H $CONFIG_HEADERS    "
+shift
+for ac_tag
+do
+  case $ac_tag in
+  :[FHLC]) ac_mode=$ac_tag; continue;;
+  esac
+  case $ac_mode$ac_tag in
+  :[FHL]*:*);;
+  :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+  :[FH]-) ac_tag=-:-;;
+  :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
+  esac
+  ac_save_IFS=$IFS
+  IFS=:
+  set x $ac_tag
+  IFS=$ac_save_IFS
+  shift
+  ac_file=$1
+  shift
+
+  case $ac_mode in
+  :L) ac_source=$1;;
+  :[FH])
+    ac_file_inputs=
+    for ac_f
+    do
+      case $ac_f in
+      -) ac_f="$ac_tmp/stdin";;
+      *) # Look for the file first in the build tree, then in the source tree
+        # (if the path is not absolute).  The absolute path cannot be DOS-style,
+        # because $ac_f cannot contain `:'.
+        test -f "$ac_f" ||
+          case $ac_f in
+          [\\/$]*) false;;
+          *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
+          esac ||
+          as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+      esac
+      case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
+      as_fn_append ac_file_inputs " '$ac_f'"
+    done
+
+    # Let's still pretend it is `configure' which instantiates (i.e., don't
+    # use $as_me), people would be surprised to read:
+    #    /* config.h.  Generated by config.status.  */
+    configure_input='Generated from '`
+         $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+       `' by configure.'
+    if test x"$ac_file" != x-; then
+      configure_input="$ac_file.  $configure_input"
+      { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+$as_echo "$as_me: creating $ac_file" >&6;}
+    fi
+    # Neutralize special characters interpreted by sed in replacement strings.
+    case $configure_input in #(
+    *\&* | *\|* | *\\* )
+       ac_sed_conf_input=`$as_echo "$configure_input" |
+       sed 's/[\\\\&|]/\\\\&/g'`;; #(
+    *) ac_sed_conf_input=$configure_input;;
+    esac
+
+    case $ac_tag in
+    *:-:* | *:-) cat >"$ac_tmp/stdin" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
+    esac
+    ;;
+  esac
+
+  ac_dir=`$as_dirname -- "$ac_file" ||
+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+        X"$ac_file" : 'X\(//\)[^/]' \| \
+        X"$ac_file" : 'X\(//\)$' \| \
+        X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$ac_file" |
+    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)[^/].*/{
+           s//\1/
+           q
+         }
+         /^X\(\/\/\)$/{
+           s//\1/
+           q
+         }
+         /^X\(\/\).*/{
+           s//\1/
+           q
+         }
+         s/.*/./; q'`
+  as_dir="$ac_dir"; as_fn_mkdir_p
+  ac_builddir=.
+
+case "$ac_dir" in
+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
+*)
+  ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+  # A ".." for each directory in $ac_dir_suffix.
+  ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+  case $ac_top_builddir_sub in
+  "") ac_top_builddir_sub=. ac_top_build_prefix= ;;
+  *)  ac_top_build_prefix=$ac_top_builddir_sub/ ;;
+  esac ;;
+esac
+ac_abs_top_builddir=$ac_pwd
+ac_abs_builddir=$ac_pwd$ac_dir_suffix
+# for backward compatibility:
+ac_top_builddir=$ac_top_build_prefix
+
+case $srcdir in
+  .)  # We are building in place.
+    ac_srcdir=.
+    ac_top_srcdir=$ac_top_builddir_sub
+    ac_abs_top_srcdir=$ac_pwd ;;
+  [\\/]* | ?:[\\/]* )  # Absolute name.
+    ac_srcdir=$srcdir$ac_dir_suffix;
+    ac_top_srcdir=$srcdir
+    ac_abs_top_srcdir=$srcdir ;;
+  *) # Relative name.
+    ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix
+    ac_top_srcdir=$ac_top_build_prefix$srcdir
+    ac_abs_top_srcdir=$ac_pwd/$srcdir ;;
+esac
+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
+
+
+  case $ac_mode in
+  :F)
+  #
+  # CONFIG_FILE
+  #
+
+_ACEOF
+
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+# If the template does not know about datarootdir, expand it.
+# FIXME: This hack should be removed a few years after 2.60.
+ac_datarootdir_hack=; ac_datarootdir_seen=
+ac_sed_dataroot='
+/datarootdir/ {
+  p
+  q
+}
+/@datadir@/p
+/@docdir@/p
+/@infodir@/p
+/@localedir@/p
+/@mandir@/p'
+case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
+*datarootdir*) ac_datarootdir_seen=yes;;
+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+_ACEOF
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+  ac_datarootdir_hack='
+  s&@datadir@&$datadir&g
+  s&@docdir@&$docdir&g
+  s&@infodir@&$infodir&g
+  s&@localedir@&$localedir&g
+  s&@mandir@&$mandir&g
+  s&\\\${datarootdir}&$datarootdir&g' ;;
+esac
+_ACEOF
+
+# Neutralize VPATH when `$srcdir' = `.'.
+# Shell code in configure.ac might set extrasub.
+# FIXME: do we really want to maintain this feature?
+cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
+ac_sed_extra="$ac_vpsub
+$extrasub
+_ACEOF
+cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
+:t
+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b
+s|@configure_input@|$ac_sed_conf_input|;t t
+s&@top_builddir@&$ac_top_builddir_sub&;t t
+s&@top_build_prefix@&$ac_top_build_prefix&;t t
+s&@srcdir@&$ac_srcdir&;t t
+s&@abs_srcdir@&$ac_abs_srcdir&;t t
+s&@top_srcdir@&$ac_top_srcdir&;t t
+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t
+s&@builddir@&$ac_builddir&;t t
+s&@abs_builddir@&$ac_abs_builddir&;t t
+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t
+$ac_datarootdir_hack
+"
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+  >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+
+test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
+  { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+  { ac_out=`sed -n '/^[         ]*datarootdir[  ]*:*=/p' \
+      "$ac_tmp/out"`; test -z "$ac_out"; } &&
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&5
+$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+which seems to be undefined.  Please make sure it is defined" >&2;}
+
+  rm -f "$ac_tmp/stdin"
+  case $ac_file in
+  -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+  *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
+  esac \
+  || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ ;;
+  :H)
+  #
+  # CONFIG_HEADER
+  #
+  if test x"$ac_file" != x-; then
+    {
+      $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+    } >"$ac_tmp/config.h" \
+      || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
+      { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+$as_echo "$as_me: $ac_file is unchanged" >&6;}
+    else
+      rm -f "$ac_file"
+      mv "$ac_tmp/config.h" "$ac_file" \
+       || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+    fi
+  else
+    $as_echo "/* $configure_input  */" \
+      && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+      || as_fn_error $? "could not create -" "$LINENO" 5
+  fi
+ ;;
+
+
+  esac
+
+done # for ac_tag
+
+
+as_fn_exit 0
+_ACEOF
+ac_clean_files=$ac_clean_files_save
+
+test $ac_write_fail = 0 ||
+  as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
+
+
+# configure is writing to config.log, and then calls config.status.
+# config.status does its own redirection, appending to config.log.
+# Unfortunately, on DOS this fails, as config.log is still kept open
+# by configure, so config.status won't be able to write to it; its
+# output is simply discarded.  So we exec the FD to /dev/null,
+# effectively closing config.log, so it can be properly (re)opened and
+# appended to by config.status.  When coming back to configure, we
+# need to make the FD available again.
+if test "$no_create" != yes; then
+  ac_cs_success=:
+  ac_config_status_args=
+  test "$silent" = yes &&
+    ac_config_status_args="$ac_config_status_args --quiet"
+  exec 5>/dev/null
+  $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false
+  exec 5>>config.log
+  # Use ||, not &&, to avoid exiting from the if with $? = 1, which
+  # would make configure fail if this is the last instruction.
+  $ac_cs_success || as_fn_exit 1
+fi
+if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
diff --git a/src/modules/rlm_unbound/configure.ac b/src/modules/rlm_unbound/configure.ac
new file mode 100644 (file)
index 0000000..0768e76
--- /dev/null
@@ -0,0 +1,54 @@
+AC_PREREQ([2.53])
+AC_INIT(rlm_unbound.c)
+AC_REVISION($Revision$)
+AC_DEFUN(modname,[rlm_unbound])
+
+if test x$with_[]modname != xno; then
+       AC_PROG_CC
+       AC_PROG_CPP
+
+       FR_SMART_CHECK_LIB(unbound, ub_ctx_create)
+               if test "x$ac_cv_lib_unbound_ub_ctx_create" != "xyes"; then
+               fail="$fail libunbound"
+       fi
+
+       FR_SMART_CHECK_INCLUDE(unbound.h)
+       if test "$ac_cv_header_unbound_h" != "yes"; then
+               fail="$fail unbound.h"
+       fi
+
+dnl # This needs work as libunbound could be using NSS or various other
+dnl # mixes of incompatible options and header/lib availability may occur.
+dnl # Since libunbound needs openssl locking set up, and may be
+dnl # linked against openssl even when we are not, play it safe.
+       FR_SMART_CHECK_INCLUDE(openssl/crypto.h)
+       if test "$ac_cv_header_openssl_crypto_h" != "yes"; then
+               fail="$fail openssl/crypto.h"
+       fi
+
+       targetname=modname
+else
+       targetname=
+       echo \*\*\* module modname is disabled.
+fi
+
+if test x"$fail" != x""; then
+       if test x"${enable_strict_dependencies}" = x"yes"; then
+               AC_MSG_ERROR([set --without-]modname[ to disable it explicitly.])
+       else
+               AC_MSG_WARN([silently not building ]modname[.])
+               AC_MSG_WARN([FAILURE: ]modname[ requires: $fail.]);
+               targetname=""
+       fi
+fi
+
+mod_ldflags="${SMART_LIBS}"
+mod_cflags="${SMART_CFLAGS}"
+
+AC_SUBST(mod_cflags)
+AC_SUBST(mod_ldflags)
+
+AC_CONFIG_HEADER(config.h)
+
+AC_SUBST(targetname)
+AC_OUTPUT(all.mk)
diff --git a/src/modules/rlm_unbound/rlm_unbound.5 b/src/modules/rlm_unbound/rlm_unbound.5
new file mode 100644 (file)
index 0000000..e269af4
--- /dev/null
@@ -0,0 +1,75 @@
+.\"     # DS - begin display
+.de DS
+.RS
+.nf
+.sp
+..
+.\"     # DE - end display
+.de DE
+.fi
+.RE
+.sp
+..
+.TH rlm_unbound 5 "8 July 2013" "" "FreeRADIUS Module"
+.SH NAME
+rlm_unbound \- FreeRADIUS Module
+.SH DESCRIPTION
+Each instance of \fIrlm_unbound\fP provides an embedded DNS client
+for performing DNS lookups.  Each instance may be configured separately
+to query different DNS horizons, change DNSSEC options, etc.
+.PP
+The module is primarily intended for use by other modules through
+internal APIs, and so, instances should be initialized earlier than
+those modules which use them.  Each instance does also provide some
+xlat functionalities for general use and for troubleshooting.
+.PP
+Each instance of rlm_unbound may take the following parameters:
+.IP filename
+This file must exist and must point to a valid libunbound configuration file.
+The default is ${raddbdir}/mods-config/unbound/default.conf.
+.IP timeout
+While libunbound provides an asyncronous API for internal use, using any xlat
+is done syncronously from the perspective of unlang.  This value limits the
+amount of time a request will wait for DNS to respond, after which the xlat
+will fail.  The default is 3000 milliseconds.  This setting is independent of
+any libunbound configuration values.
+.PP
+An instance named, for example, "dns" will provide the following xlat
+functionalities:
+.IP %{dns-a:<owner>}
+Performs an A lookup for the owner name, returning a stringified IPv4
+address.  Only the first A record in the RRSET will be returned.
+.IP %{dns-aaaa:<owner>}
+Performs an AAAA lookup for the owner name, returning a stringified IPv6
+address.  Only the first AAAA record in the RRSET will be returned.
+.IP %{dns-ptr:<owner>}
+Performs a PTR lookup for the owner.
+.PP
+.SH CAVEATS
+Logging from rlm_unbound can be problematic, especialy if more than one
+instantiation of the module is used.  This is due to the need for additional
+features in the underlying libunbound which hopefully will be enhanced over
+time.
+.PP
+There is a potential for a FreeRADIUS server using rlm_unbound to either
+fail to terminate cleanly (leaving zombie processes, failing to clean up
+other modules, and hanging after a SIGTERM until a SIGKILL is sent) or
+to fail valgrind checks during termination when run with -m.  Likewise this
+problem will rely on upstream enhancements before it can be fixed, and the
+exact behavior may change in interim releases until then.
+.PP
+The logging behavior of rlm_unbound may vary depending on whether
+FreeRADIUS is compiled with support for threads.
+.PP
+.SH FILES
+.I /etc/raddb/modules-available/rlm_unbound
+.I /etc/raddb/modules-config/unbound/
+.PP
+.SH "SEE ALSO"
+.BR radiusd (8),
+.BR radiusd.conf (5)
+.BR libunbound (3)
+.BR unbound.conf (5)
+.SH AUTHOR
+Brian S. Julin, bjulin@clarku.edu
+
diff --git a/src/modules/rlm_unbound/rlm_unbound.c b/src/modules/rlm_unbound/rlm_unbound.c
new file mode 100644 (file)
index 0000000..927d1b8
--- /dev/null
@@ -0,0 +1,766 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file rlm_unbound.c
+ * @brief DNS services via libunbound.
+ *
+ * @copyright 2013 The FreeRADIUS server project
+ * @copyright 2013 Brian S. Julin <bjulin@clarku.edu>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <freeradius-devel/log.h>
+#include <fcntl.h>
+#include <unbound.h>
+
+typedef struct rlm_unbound_t {
+       struct ub_ctx   *ub;   /* This must come first.  Do not move */
+       fr_event_list_t *el; /* This must come second.  Do not move. */
+
+       char const      *name;
+       char const      *xlat_a_name;
+       char const      *xlat_aaaa_name;
+       char const      *xlat_ptr_name;
+
+       uint32_t        timeout;
+
+       char const      *filename;
+
+       int             log_fd;
+       FILE            *log_stream;
+
+       int             log_pipe[2];
+       FILE            *log_pipe_stream[2];
+       bool            log_pipe_in_use;
+} rlm_unbound_t;
+
+/*
+ *     A mapping of configuration file names to internal variables.
+ */
+static const CONF_PARSER module_config[] = {
+       { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT | PW_TYPE_REQUIRED, rlm_unbound_t, filename), "${modconfdir}/unbound/default.conf"  },
+       { "timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_unbound_t, timeout), "3000" },
+       { NULL, -1, 0, NULL, NULL }             /* end the list */
+};
+
+/*
+ *     Callback sent to libunbound for xlat functions.  Simply links the
+ *     new ub_result via a pointer that has been allocated from the heap.
+ *     This pointer has been pre-initialized to a magic value.
+ */
+static void link_ubres(void* my_arg, int err, struct ub_result* result)
+{
+       struct ub_result **ubres = (struct ub_result **)my_arg;
+
+       /*
+        *      Note that while result will be NULL on error, we are explicit
+        *      here because that is actually a behavior that is suboptimal
+        *      and only documented in the examples.  It could change.
+        */
+       if (err) {
+               ERROR("rlm_unbound: %s", ub_strerror(err));
+               *ubres = NULL;
+       } else {
+               *ubres = result;
+       }
+}
+
+/*
+ *     Convert labels as found in a DNS result to a NULL terminated string.
+ *
+ *     Result is written to memory pointed to by "out" but no result will
+ *     be written unless it and its terminating NULL character fit in "left"
+ *     bytes.  Returns the number of bytes written excluding the terminating
+ *     NULL, or -1 if nothing was written because it would not fit or due
+ *     to a violation in the labels format.
+ */
+static int rrlabels_tostr(char *out, char *rr, size_t left)
+{
+       int offset = 0;
+
+       /*
+        * TODO: verify that unbound results (will) always use this label
+        * format, and review the specs on this label format for nuances.
+        */
+
+       if (!left) {
+               return -1;
+       }
+       if (left > 253) {
+               left = 253; /* DNS length limit */
+       }
+       /* As a whole this should be "NULL terminated" by the 0-length label */
+       if (strnlen(rr, left) > left - 1) {
+               return -1;
+       }
+
+       /* It will fit, but does it it look well formed? */
+       while (1) {
+               size_t count;
+
+               count = *((unsigned char *)(rr + offset));
+               if (!count) break;
+
+               offset++;
+               if (count > 63 || strlen(rr + offset) < count) {
+                       return -1;
+               }
+               offset += count;
+       }
+
+       /* Data is valid and fits.  Copy it. */
+       offset = 0;
+       while (1) {
+               int count;
+
+               count = *((unsigned char *)(rr));
+               if (!count) break;
+
+               if (offset) {
+                       *(out + offset) = '.';
+                       offset++;
+               }
+
+               rr++;
+               memcpy(out + offset, rr, count);
+               rr += count;
+               offset += count;
+       }
+
+       *(out + offset) = '\0';
+       return offset;
+}
+
+static int ub_common_wait(rlm_unbound_t *inst, REQUEST *request, char const *tag, struct ub_result **ub, int async_id)
+{
+       useconds_t iv, waited;
+
+       iv = inst->timeout > 64 ? 64000 : inst->timeout * 1000;
+       ub_process(inst->ub);
+
+       for (waited = 0; (void*)*ub == (void *)inst; waited += iv, iv += iv) {
+
+               if (waited + iv > (useconds_t)inst->timeout * 1000) {
+                       usleep(inst->timeout * 1000 - waited);
+                       ub_process(inst->ub);
+                       break;
+               }
+
+               usleep(iv);
+
+               /* Check if already handled by event loop */
+               if ((void *)*ub != (void *)inst) {
+                       break;
+               }
+
+               /* In case we are running single threaded */
+               ub_process(inst->ub);
+       }
+
+       if ((void *)*ub == (void *)inst) {
+               int res;
+
+               RDEBUG("rlm_unbound (%s): DNS took too long", tag);
+
+               res = ub_cancel(inst->ub, async_id);
+               if (res) {
+                       REDEBUG("rlm_unbound (%s): ub_cancel: %s",
+                               tag, ub_strerror(res));
+               }
+               return -1;
+       }
+
+       return 0;
+}
+
+static int ub_common_fail(REQUEST *request, char const *tag, struct ub_result *ub)
+{
+       if (ub->bogus) {
+               RWDEBUG("rlm_unbound (%s): Bogus DNS response", tag);
+               return -1;
+       }
+
+       if (ub->nxdomain) {
+               RDEBUG("rlm_unbound (%s): NXDOMAIN", tag);
+               return -1;
+       }
+
+       if (!ub->havedata) {
+               RDEBUG("rlm_unbound (%s): empty result", tag);
+               return -1;
+       }
+
+       return 0;
+}
+
+static ssize_t xlat_a(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
+{
+       rlm_unbound_t *inst = instance;
+       struct ub_result **ubres;
+       int async_id;
+       char *fmt2; /* For const warnings.  Keep till new libunbound ships. */
+
+       /* This has to be on the heap, because threads. */
+       ubres = talloc(inst, struct ub_result *);
+
+       /* Used and thus impossible value from heap to designate incomplete */
+       *ubres = (void *)instance;
+
+       fmt2 = talloc_typed_strdup(inst, fmt);
+       ub_resolve_async(inst->ub, fmt2, 1, 1, ubres, link_ubres, &async_id);
+       talloc_free(fmt2);
+
+       if (ub_common_wait(inst, request, inst->xlat_a_name, ubres, async_id)) {
+               goto error0;
+       }
+
+       if (*ubres) {
+               if (ub_common_fail(request, inst->xlat_a_name, *ubres)) {
+                       goto error1;
+               }
+
+               if (!inet_ntop(AF_INET, (*ubres)->data[0], out, freespace)) {
+                       goto error1;
+               };
+
+               ub_resolve_free(*ubres);
+               talloc_free(ubres);
+               return strlen(out);
+       }
+
+       RWDEBUG("rlm_unbound (%s): no result", inst->xlat_a_name);
+
+ error1:
+       ub_resolve_free(*ubres); /* Handles NULL gracefully */
+
+ error0:
+       talloc_free(ubres);
+       return -1;
+}
+
+static ssize_t xlat_aaaa(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
+{
+       rlm_unbound_t *inst = instance;
+       struct ub_result **ubres;
+       int async_id;
+       char *fmt2; /* For const warnings.  Keep till new libunbound ships. */
+
+       /* This has to be on the heap, because threads. */
+       ubres = talloc(inst, struct ub_result *);
+
+       /* Used and thus impossible value from heap to designate incomplete */
+       *ubres = (void *)instance;
+
+       fmt2 = talloc_typed_strdup(inst, fmt);
+       ub_resolve_async(inst->ub, fmt2, 28, 1, ubres, link_ubres, &async_id);
+       talloc_free(fmt2);
+
+       if (ub_common_wait(inst, request, inst->xlat_aaaa_name, ubres, async_id)) {
+               goto error0;
+       }
+
+       if (*ubres) {
+               if (ub_common_fail(request, inst->xlat_aaaa_name, *ubres)) {
+                       goto error1;
+               }
+               if (!inet_ntop(AF_INET6, (*ubres)->data[0], out, freespace)) {
+                       goto error1;
+               };
+               ub_resolve_free(*ubres);
+               talloc_free(ubres);
+               return strlen(out);
+       }
+
+       RWDEBUG("rlm_unbound (%s): no result", inst->xlat_aaaa_name);
+
+error1:
+       ub_resolve_free(*ubres); /* Handles NULL gracefully */
+
+error0:
+       talloc_free(ubres);
+       return -1;
+}
+
+static ssize_t xlat_ptr(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace)
+{
+       rlm_unbound_t *inst = instance;
+       struct ub_result **ubres;
+       int async_id;
+       char *fmt2; /* For const warnings.  Keep till new libunbound ships. */
+
+       /* This has to be on the heap, because threads. */
+       ubres = talloc(inst, struct ub_result *);
+
+       /* Used and thus impossible value from heap to designate incomplete */
+       *ubres = (void *)instance;
+
+       fmt2 = talloc_typed_strdup(inst, fmt);
+       ub_resolve_async(inst->ub, fmt2, 12, 1, ubres, link_ubres, &async_id);
+       talloc_free(fmt2);
+
+       if (ub_common_wait(inst, request, inst->xlat_ptr_name,
+                          ubres, async_id)) {
+               goto error0;
+       }
+
+       if (*ubres) {
+               if (ub_common_fail(request, inst->xlat_ptr_name, *ubres)) {
+                       goto error1;
+               }
+               if (rrlabels_tostr(out, (*ubres)->data[0], freespace) < 0) {
+                       goto error1;
+               }
+               ub_resolve_free(*ubres);
+               talloc_free(ubres);
+               return strlen(out);
+       }
+
+       RWDEBUG("rlm_unbound (%s): no result", inst->xlat_ptr_name);
+
+error1:
+       ub_resolve_free(*ubres);  /* Handles NULL gracefully */
+
+error0:
+       talloc_free(ubres);
+       return -1;
+}
+
+/*
+ *     Even when run in asyncronous mode, callbacks sent to libunbound still
+ *     must be run in an application-side thread (via ub_process.)  This is
+ *     probably to keep the API usage consistent across threaded and forked
+ *     embedded client modes.  This callback function lets an event loop call
+ *     ub_process when the instance's file descriptor becomes ready.
+ */
+static void ub_fd_handler(UNUSED fr_event_list_t *el, UNUSED int sock, void *ctx)
+{
+       rlm_unbound_t *inst = ctx;
+       int err;
+
+       err = ub_process(inst->ub);
+       if (err) {
+               ERROR("rlm_unbound (%s) async ub_process: %s",
+                     inst->name, ub_strerror(err));
+       }
+}
+
+#ifndef HAVE_PTHREAD_H
+
+/* If we have to use a pipe to redirect logging, this does the work. */
+static void log_spew(UNUSED fr_event_list_t *el, UNUSED int sock, void *ctx)
+{
+       rlm_unbound_t *inst = ctx;
+       char line[1024];
+
+       /*
+        *  This works for pipes from processes, but not from threads
+        *  right now.  The latter is hinky and will require some fancy
+        *  blocking/nonblocking trickery which is not figured out yet,
+        *  since selecting on a pipe from a thread in the same process
+        *  seems to behave differently.  It will likely preclude the use
+        *  of fgets and streams.  Left for now since some unbound logging
+        *  infrastructure is still global across multiple contexts.  Maybe
+        *  we can get unbound folks to provide a ub_ctx_debugout_async that
+        *  takes a function hook instead to just bypass the piping when
+        *  used in threaded mode.
+        */
+       while (fgets(line, 1024, inst->log_pipe_stream[0])) {
+               DEBUG("rlm_unbound (%s): %s", inst->name, line);
+       }
+}
+
+#endif
+
+static int mod_instantiate(CONF_SECTION *conf, void *instance)
+{
+       rlm_unbound_t *inst = instance;
+       int res;
+       char *optval;
+
+       log_dst_t log_dst;
+       int log_level;
+       int log_fd = -1;
+
+       char k[64]; /* To silence const warns until newer unbound in distros */
+
+       inst->el = radius_event_list_corral(EVENT_CORRAL_AUX);
+       inst->log_pipe_stream[0] = NULL;
+       inst->log_pipe_stream[1] = NULL;
+       inst->log_fd = -1;
+       inst->log_pipe_in_use = false;
+
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) {
+               inst->name = cf_section_name1(conf);
+       }
+
+       if (inst->timeout > 10000) {
+               ERROR("rlm_unbound (%s): timeout must be 0 to 10000", inst->name);
+               return -1;
+       }
+
+       inst->ub = ub_ctx_create();
+       if (!inst->ub) {
+               ERROR("rlm_unbound (%s): ub_ctx_create failed", inst->name);
+               return -1;
+       }
+
+#ifdef HAVE_PTHREAD_H
+       /*
+        *      Note unbound threads WILL happen with -s option, if it matters.
+        *      We cannot tell from here whether that option is in effect.
+        */
+       res = ub_ctx_async(inst->ub, 1);
+#else
+       /*
+        *      Uses forked subprocesses instead.
+        */
+       res = ub_ctx_async(inst->ub, 0);
+#endif
+
+       if (res) goto error;
+
+       /*      Glean some default settings to match the main server.   */
+       /*      TODO: debug_level can be changed at runtime. */
+       /*      TODO: log until fork when stdout or stderr and !debug_flag. */
+       log_level = 0;
+
+       if (debug_flag > 0) {
+               log_level = debug_flag;
+
+       } else if (main_config.debug_level > 0) {
+               log_level = main_config.debug_level;
+       }
+
+       switch (log_level) {
+       /* TODO: This will need some tweaking */
+       case 0:
+       case 1:
+               break;
+
+       case 2:
+               log_level = 1;
+               break;
+
+       case 3:
+       case 4:
+               log_level = 2; /* mid-to-heavy levels of output */
+               break;
+
+       case 5:
+       case 6:
+       case 7:
+       case 8:
+               log_level = 3; /* Pretty crazy amounts of output */
+               break;
+
+       default:
+               log_level = 4; /* Insane amounts of output including crypts */
+               break;
+       }
+
+       res = ub_ctx_debuglevel(inst->ub, log_level);
+       if (res) goto error;
+
+       switch(default_log.dst) {
+       case L_DST_STDOUT:
+               if (!debug_flag) {
+                       log_dst = L_DST_NULL;
+                       break;
+               }
+               log_dst = L_DST_STDOUT;
+               log_fd = dup(STDOUT_FILENO);
+               break;
+
+       case L_DST_STDERR:
+               if (!debug_flag) {
+                       log_dst = L_DST_NULL;
+                       break;
+               }
+               log_dst = L_DST_STDOUT;
+               log_fd = dup(STDERR_FILENO);
+               break;
+
+       case L_DST_FILES:
+               if (main_config.log_file) {
+                       char *log_file;
+
+                       strcpy(k, "logfile:");
+                       /* 3rd argument isn't const'd in libunbounds API */
+                       memcpy(&log_file, &main_config.log_file, sizeof(log_file));
+                       res = ub_ctx_set_option(inst->ub, k, log_file);
+                       if (res) {
+                               goto error;
+                       }
+                       log_dst = L_DST_FILES;
+                       break;
+               }
+               /* FALL-THROUGH */
+
+       case L_DST_NULL:
+               log_dst = L_DST_NULL;
+               break;
+
+       default:
+               log_dst = L_DST_SYSLOG;
+               break;
+       }
+
+       /* Now load the config file, which can override gleaned settings. */
+       {
+               char *file;
+
+               memcpy(&file, &inst->filename, sizeof(file));
+               res = ub_ctx_config(inst->ub, file);
+               if (res) goto error;
+       }
+
+       /*
+        *      Check if the config file tried to use syslog.  Unbound
+        *      does not share syslog gracefully.
+        */
+       strcpy(k, "use-syslog");
+       res = ub_ctx_get_option(inst->ub, k, &optval);
+       if (res || !optval) goto error;
+
+       if (!strcmp(optval, "yes")) {
+               char v[3];
+
+               free(optval);
+
+               WARN("rlm_unbound (%s): Overriding syslog settings.", inst->name);
+               strcpy(k, "use-syslog:");
+               strcpy(v, "no");
+               res = ub_ctx_set_option(inst->ub, k, v);
+               if (res) goto error;
+
+               if (log_dst == L_DST_FILES) {
+                       char *log_file;
+
+                       /* Reinstate the log file name JIC */
+                       strcpy(k, "logfile:");
+                       /* 3rd argument isn't const'd in libunbounds API */
+                       memcpy(&log_file, &main_config.log_file, sizeof(log_file));
+                       res = ub_ctx_set_option(inst->ub, k, log_file);
+                       if (res) goto error;
+               }
+
+       } else {
+               if (optval) free(optval);
+               strcpy(k, "logfile");
+
+               res = ub_ctx_get_option(inst->ub, k, &optval);
+               if (res) goto error;
+
+               if (optval && strlen(optval)) {
+                       log_dst = L_DST_FILES;
+
+               } else if (!debug_flag) {
+                       log_dst = L_DST_NULL;
+               }
+
+               if (optval) free(optval);
+       }
+
+       switch (log_dst) {
+       case L_DST_STDOUT:
+               /*
+                * We have an fd to log to.  And we've already attempted to
+                * dup it so libunbound doesn't close it on us.
+                */
+               if (log_fd == -1) {
+                       ERROR("rlm_unbound (%s): Could not dup fd", inst->name);
+                       goto error_nores;
+               }
+
+               inst->log_stream = fdopen(log_fd, "w");
+               if (!inst->log_stream) {
+                       ERROR("rlm_unbound (%s): error setting up log stream", inst->name);
+                       goto error_nores;
+               }
+
+               res = ub_ctx_debugout(inst->ub, inst->log_stream);
+               if (res) goto error;
+               break;
+
+       case L_DST_FILES:
+               /* We gave libunbound a filename.  It is on its own now. */
+               break;
+
+       case L_DST_NULL:
+               /* We tell libunbound not to log at all. */
+               res = ub_ctx_debugout(inst->ub, NULL);
+               if (res) goto error;
+               break;
+
+       case L_DST_SYSLOG:
+#ifdef HAVE_PTHREAD_H
+               /*
+                *  Currently this wreaks havoc when running threaded, so just
+                *  turn logging off until that gets figured out.
+                */
+               res = ub_ctx_debugout(inst->ub, NULL);
+               if (res) goto error;
+               break;
+#else
+               /*
+                *  We need to create a pipe, because libunbound does not
+                *  share syslog nicely.  Or the core added some new logsink.
+                */
+               if (pipe(inst->log_pipe)) {
+               error_pipe:
+                       ERROR("rlm_unbound (%s): Error setting up log pipes", inst->name);
+                       goto error_nores;
+               }
+
+               if ((fcntl(inst->log_pipe[0], F_SETFL, O_NONBLOCK) < 0) ||
+                   (fcntl(inst->log_pipe[0], F_SETFD, FD_CLOEXEC) < 0)) {
+                       goto error_pipe;
+               }
+
+               /* Opaque to us when this can be closed, so we do not. */
+               if (fcntl(inst->log_pipe[1], F_SETFL, O_NONBLOCK) < 0) {
+                       goto error_pipe;
+               }
+
+               inst->log_pipe_stream[0] = fdopen(inst->log_pipe[0], "r");
+               inst->log_pipe_stream[1] = fdopen(inst->log_pipe[1], "w");
+
+               if (!inst->log_pipe_stream[0] || !inst->log_pipe_stream[1]) {
+                       if (!inst->log_pipe_stream[1]) {
+                               close(inst->log_pipe[1]);
+                       }
+
+                       if (!inst->log_pipe_stream[0]) {
+                               close(inst->log_pipe[0]);
+                       }
+                       ERROR("rlm_unbound (%s): Error setting up log stream", inst->name);
+                       goto error_nores;
+               }
+
+               res = ub_ctx_debugout(inst->ub, inst->log_pipe_stream[1]);
+               if (res) goto error;
+
+               if (!fr_event_fd_insert(inst->el, 0, inst->log_pipe[0], log_spew, inst)) {
+                       ERROR("rlm_unbound (%s): could not insert log fd", inst->name);
+                       goto error_nores;
+               }
+
+               inst->log_pipe_in_use = true;
+#endif
+       default:
+               break;
+       }
+
+       /*
+        *  Now we need to finalize the context.
+        *
+        *  There's no clean API to just finalize the context made public
+        *  in libunbound.  But we can trick it by trying to delete data
+        *  which as it happens fails quickly and quietly even though the
+        *  data did not exist.
+        */
+       strcpy(k, "notar33lsite.foo123.nottld A 127.0.0.1");
+       ub_ctx_data_remove(inst->ub, k);
+
+       inst->log_fd = ub_fd(inst->ub);
+       if (inst->log_fd >= 0) {
+               if (!fr_event_fd_insert(inst->el, 0, inst->log_fd, ub_fd_handler, inst)) {
+                       ERROR("rlm_unbound (%s): could not insert async fd", inst->name);
+                       inst->log_fd = -1;
+                       goto error_nores;
+               }
+
+       }
+
+       MEM(inst->xlat_a_name = talloc_typed_asprintf(inst, "%s-a", inst->name));
+       MEM(inst->xlat_aaaa_name = talloc_typed_asprintf(inst, "%s-aaaa", inst->name));
+       MEM(inst->xlat_ptr_name = talloc_typed_asprintf(inst, "%s-ptr", inst->name));
+
+       if (xlat_register(inst->xlat_a_name, xlat_a, NULL, inst) ||
+           xlat_register(inst->xlat_aaaa_name, xlat_aaaa, NULL, inst) ||
+           xlat_register(inst->xlat_ptr_name, xlat_ptr, NULL, inst)) {
+               ERROR("rlm_unbound (%s): Failed registering xlats", inst->name);
+               xlat_unregister(inst->xlat_a_name, xlat_a, inst);
+               xlat_unregister(inst->xlat_aaaa_name, xlat_aaaa, inst);
+               xlat_unregister(inst->xlat_ptr_name, xlat_ptr, inst);
+               goto error_nores;
+       }
+       return 0;
+
+ error:
+       ERROR("rlm_unbound (%s): %s", inst->name, ub_strerror(res));
+
+ error_nores:
+       if (log_fd > -1) close(log_fd);
+
+       return -1;
+}
+
+static int mod_detach(UNUSED void *instance)
+{
+       rlm_unbound_t *inst = instance;
+
+       xlat_unregister(inst->xlat_a_name, xlat_a, inst);
+       xlat_unregister(inst->xlat_aaaa_name, xlat_aaaa, inst);
+       xlat_unregister(inst->xlat_ptr_name, xlat_ptr, inst);
+
+       if (inst->log_fd >= 0) {
+               fr_event_fd_delete(inst->el, 0, inst->log_fd);
+               if (inst->ub) {
+                       ub_process(inst->ub);
+                       /* This can hang/leave zombies currently
+                        * see upstream bug #519
+                        * ...so expect valgrind to complain with -m
+                        */
+#if 0
+                       ub_ctx_delete(inst->ub);
+#endif
+               }
+       }
+
+       if (inst->log_pipe_stream[1]) {
+               fclose(inst->log_pipe_stream[1]);
+       }
+
+       if (inst->log_pipe_stream[0]) {
+               if (inst->log_pipe_in_use) {
+                       fr_event_fd_delete(inst->el, 0, inst->log_pipe[0]);
+               }
+               fclose(inst->log_pipe_stream[0]);
+       }
+
+       if (inst->log_stream) {
+               fclose(inst->log_stream);
+       }
+
+       return 0;
+}
+
+module_t rlm_unbound = {
+       RLM_MODULE_INIT,
+       "unbound",
+       RLM_TYPE_THREAD_SAFE,           /* type */
+       sizeof(rlm_unbound_t),
+       module_config,
+       mod_instantiate,                /* instantiation */
+       mod_detach,                     /* detach */
+       /* This module does not directly interact with requests */
+       { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
+};
index 159d3c5..85dbc34 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1248,9 +1248,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1293,10 +1293,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2181,7 +2181,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2403,7 +2403,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3322,7 +3322,7 @@ done
                { $as_echo "$as_me:${as_lineno-$LINENO}: result: no getpwnam" >&5
 $as_echo "no getpwnam" >&6; }
                 fail=$fail" getpwnam"
-        fi
+       fi
 
        if test "$ac_cv_header_pwd_h" != "yes"; then
                { $as_echo "$as_me:${as_lineno-$LINENO}: result: no pwd.h" >&5
@@ -3476,11 +3476,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3954,13 +3954,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index dbd1633..25f4736 100644 (file)
@@ -30,7 +30,7 @@ if test x$with_[]modname != xno; then
        if test "$ac_cv_func_getpwnam" != "yes"; then
                AC_MSG_RESULT(no getpwnam)
                [ fail=$fail" getpwnam" ]
-        fi
+       fi
 
        if test "$ac_cv_header_pwd_h" != "yes"; then
                AC_MSG_RESULT(no pwd.h)
index 1a68ce5..0539109 100644 (file)
@@ -59,12 +59,12 @@ static char trans[64] =
 #define ENC(c) trans[c]
 
 struct unix_instance {
+       char const *name;       //!< Instance name.
        char const *radwtmp;
 };
 
 static const CONF_PARSER module_config[] = {
-       { "radwtmp",  PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED,
-         offsetof(struct unix_instance,radwtmp), NULL,   "NULL" },
+       { "radwtmp", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED, struct unix_instance, radwtmp), "NULL" },
 
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -115,12 +115,38 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
 {
        struct unix_instance *inst = instance;
 
+       DICT_ATTR const *group_da, *user_name_da;
+
+       inst->name = cf_section_name2(conf);
+       if (!inst->name) {
+               inst->name = cf_section_name1(conf);
+       }
+
+       group_da = dict_attrbyvalue(PW_GROUP, 0);
+       if (!group_da) {
+               ERROR("rlm_unix (%s): 'Group' attribute not found in dictionary", inst->name);
+               return -1;
+       }
+
+       user_name_da = dict_attrbyvalue(PW_USER_NAME, 0);
+       if (!user_name_da) {
+               ERROR("rlm_unix (%s): 'User-Name' attribute not found in dictionary", inst->name);
+               return -1;
+       }
        /* FIXME - delay these until a group file has been read so we know
         * groupcmp can actually do something */
-       paircompare_register(dict_attrbyvalue(PW_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0), false, groupcmp, inst);
+       paircompare_register(group_da, user_name_da, false, groupcmp, inst);
 #ifdef PW_GROUP_NAME /* compat */
-       paircompare_register(dict_attrbyvalue(PW_GROUP_NAME, 0), dict_attrbyvalue(PW_USER_NAME, 0),
-                       true, groupcmp, inst);
+       {
+               DICT_ATTR const *group_name_da;
+
+               group_name_da = dict_attrbyvalue(PW_GROUP_NAME, 0);
+               if (!group_name_da) {
+                       ERROR("rlm_unix (%s): 'Group-Name' attribute not found in dictionary", inst->name);
+                       return -1;
+               }
+               paircompare_register(group_name_da, user_name_da, true, groupcmp, inst);
+       }
 #endif
 
        return 0;
@@ -131,7 +157,7 @@ static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
  *     Pull the users password from where-ever, and add it to
  *     the given vp list.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, REQUEST *request)
 {
        char const      *name;
        char const      *encrypted_pass;
@@ -307,7 +333,7 @@ static char *uue(void *in)
 /*
  *     Unix accounting - write a wtmp file.
  */
-static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *request)
 {
        VALUE_PAIR      *vp;
        vp_cursor_t     cursor;
@@ -323,15 +349,15 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 #ifdef USER_PROCESS
        int             protocol = -1;
 #endif
-       int             nas_port = 0;
-       int             port_seen = 0;
+       uint32_t        nas_port = 0;
+       bool            port_seen = true;
        struct unix_instance *inst = (struct unix_instance *) instance;
 
        /*
         *      No radwtmp.  Don't do anything.
         */
        if (!inst->radwtmp) {
-               RDEBUG2("No radwtmp file configured.  Ignoring accounting request.");
+               RDEBUG2("No radwtmp file configured.  Ignoring accounting request");
                return RLM_MODULE_NOOP;
        }
 
@@ -344,7 +370,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
         *      Which type is this.
         */
        if ((vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY))==NULL) {
-               RDEBUG("no Accounting-Status-Type attribute in request.");
+               RDEBUG("no Accounting-Status-Type attribute in request");
                return RLM_MODULE_NOOP;
        }
        status = vp->vp_integer;
@@ -369,9 +395,9 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
        /*
         *      First, find the interesting attributes.
         */
-       for (vp = paircursor(&cursor, &request->packet->vps);
+       for (vp = fr_cursor_init(&cursor, &request->packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                if (!vp->da->vendor) switch (vp->da->attr) {
                        case PW_USER_NAME:
                                if (vp->length >= sizeof(ut.ut_name)) {
@@ -394,7 +420,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
                                break;
                        case PW_NAS_PORT:
                                nas_port = vp->vp_integer;
-                               port_seen = 1;
+                               port_seen = true;
                                break;
                        case PW_ACCT_DELAY_TIME:
                                delay = vp->vp_ipaddr;
@@ -485,7 +511,7 @@ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
 module_t rlm_unix = {
        RLM_MODULE_INIT,
        "System",
-       RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_CHECK_CONFIG_SAFE,
+       RLM_TYPE_THREAD_UNSAFE,
        sizeof(struct unix_instance),
        module_config,
        mod_instantiate,                /* instantiation */
diff --git a/src/modules/rlm_unpack/all.mk b/src/modules/rlm_unpack/all.mk
new file mode 100644 (file)
index 0000000..e569fbe
--- /dev/null
@@ -0,0 +1,2 @@
+TARGET         := rlm_unpack.a
+SOURCES                := rlm_unpack.c
diff --git a/src/modules/rlm_unpack/rlm_unpack.c b/src/modules/rlm_unpack/rlm_unpack.c
new file mode 100644 (file)
index 0000000..3aeb7c2
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ *   This program is is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License, version 2 if the
+ *   License as published by the Free Software Foundation.
+ *
+ *   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
+ */
+
+/**
+ * $Id$
+ * @file rlm_unpack.c
+ * @brief Unpack binary data
+ *
+ * @copyright 2014 The FreeRADIUS server project
+ * @copyright 2014 Alan DeKok <aland@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+#include <ctype.h>
+
+#define PW_CAST_BASE (1850)
+
+#define GOTO_ERROR do { REDEBUG("Unexpected text at '%s'", p); goto error;} while (0)
+
+/** Unpack data
+ *
+ *  Example: %{unpack:&Class 0 integer}
+ *
+ *  Expands Class, treating octet at offset 0 (bytes 0-3) as an "integer".
+ */
+static ssize_t unpack_xlat(UNUSED void *instance, REQUEST *request, char const *fmt,
+                          char *out, size_t outlen)
+{
+       char *data_name, *data_size, *data_type;
+       char *p;
+       size_t len, input_len;
+       int offset;
+       PW_TYPE type;
+       DICT_ATTR const *da;
+       VALUE_PAIR *vp, *cast;
+       uint8_t const *input;
+       char buffer[256];
+       uint8_t blob[256];
+
+       /*
+        *      FIXME: copy only the fields here, as we parse them.
+        */
+       strlcpy(buffer, fmt, sizeof(buffer));
+
+       p = buffer;
+       while (isspace((int) *p)) p++; /* skip leading spaces */
+
+       data_name = p;
+
+       while (*p && !isspace((int) *p)) p++;
+
+       if (!*p) {
+       error:
+               REDEBUG("Format string should be '<data> <offset> <type>' e.g. '&Class 1 integer'");
+       nothing:
+               *out = '\0';
+               return -1;
+       }
+
+       while (isspace((int) *p)) *(p++) = '\0';
+       if (!*p) GOTO_ERROR;
+
+       data_size = p;
+
+       while (*p && !isspace((int) *p)) p++;
+       if (!*p) GOTO_ERROR;
+
+       while (isspace((int) *p)) *(p++) = '\0';
+       if (!*p) GOTO_ERROR;
+
+       data_type = p;
+
+       while (*p && !isspace((int) *p)) p++;
+       if (*p) GOTO_ERROR;     /* anything after the type is an error */
+
+       /*
+        *      Attribute reference
+        */
+       if (*data_name == '&') {
+               if (radius_get_vp(&vp, request, data_name) < 0) goto nothing;
+
+               if ((vp->da->type != PW_TYPE_OCTETS) &&
+                   (vp->da->type != PW_TYPE_STRING)) {
+                       REDEBUG("unpack requires the input attribute to be 'string' or 'octets'");
+                       goto nothing;
+               }
+               input = vp->vp_octets;
+               input_len = vp->length;
+
+       } else if ((data_name[0] == '0') && (data_name[1] == 'x')) {
+               /*
+                *      Hex data.
+                */
+               len = strlen(data_name + 2);
+               if ((len & 0x01) != 0) {
+                       RDEBUG("Invalid hex string in '%s'", data_name);
+                       goto nothing;
+               }
+               input = blob;
+               input_len = fr_hex2bin(blob, data_name + 2, sizeof(blob));
+
+       } else {
+               GOTO_ERROR;
+       }
+
+       offset = (int) strtoul(data_size, &p, 10);
+       if (*p) {
+               REDEBUG("unpack requires a decimal number, not '%s'", data_size);
+               goto nothing;
+       }
+
+       type = fr_str2int(dict_attr_types, data_type, PW_TYPE_INVALID);
+       if (type == PW_TYPE_INVALID) {
+               REDEBUG("Invalid data type '%s'", data_type);
+               goto nothing;
+       }
+
+       /*
+        *      Output must be a non-zero limited size.
+        */
+       if ((dict_attr_sizes[type][0] ==  0) ||
+           (dict_attr_sizes[type][0] != dict_attr_sizes[type][1])) {
+               REDEBUG("unpack requires fixed-size output type, not '%s'", data_type);
+               goto nothing;
+       }
+
+       if (input_len < (offset + dict_attr_sizes[type][0])) {
+               REDEBUG("Insufficient data to unpack '%s' from '%s'", data_type, data_name);
+               goto nothing;
+       }
+
+       da = dict_attrbyvalue(PW_CAST_BASE + type, 0);
+       if (!da) {
+               REDEBUG("Cannot decode type '%s'", data_type);
+               goto nothing;
+       }
+
+       cast = pairalloc(request, da);
+       if (!cast) goto nothing;
+
+       memcpy(&(cast->data), input + offset, dict_attr_sizes[type][0]);
+       cast->length = dict_attr_sizes[type][0];
+
+       /*
+        *      Hacks
+        */
+       switch (type) {
+       case PW_TYPE_SIGNED:
+       case PW_TYPE_INTEGER:
+       case PW_TYPE_DATE:
+               cast->vp_integer = ntohl(cast->vp_integer);
+               break;
+
+       case PW_TYPE_SHORT:
+               cast->vp_short = ((input[offset] << 8) | input[offset + 1]);
+               break;
+
+       case PW_TYPE_INTEGER64:
+               cast->vp_integer64 = ntohll(cast->vp_integer64);
+               break;
+
+       default:
+               break;
+       }
+
+       len = vp_prints_value(out, outlen, cast, 0);
+       talloc_free(cast);
+       if (is_truncated(len, outlen)) {
+               REDEBUG("Insufficient buffer space to unpack data");
+               goto nothing;
+       }
+
+       return len;
+}
+
+
+/*
+ *     Register the xlats
+ */
+static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
+{
+       xlat_register("unpack", unpack_xlat, NULL, instance);
+
+       return 0;
+}
+
+/*
+ *     The module name should be the only globally exported symbol.
+ *     That is, everything else should be 'static'.
+ *
+ *     If the module needs to temporarily modify it's instantiation
+ *     data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
+ *     The server will then take care of ensuring that the module
+ *     is single-threaded.
+ */
+module_t rlm_unpack = {
+       RLM_MODULE_INIT,
+       "unpack",
+       RLM_TYPE_THREAD_SAFE,           /* type */
+       0,
+       NULL,
+       mod_instantiate,                /* instantiation */
+       NULL,                           /* detach */
+       {
+               NULL,                   /* authentication */
+               NULL,                   /* authorization */
+               NULL, NULL, NULL,
+               NULL,                   /* pre-proxy */
+               NULL,                   /* post-proxy */
+               NULL                    /* post-auth */
+       },
+};
index e4938f7..082c694 100644 (file)
@@ -28,15 +28,15 @@ RCSID("$Id$")
 /*
  *     Reject any non-UTF8 data.
  */
-static rlm_rcode_t utf8_clean(UNUSED void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_utf8_clean(UNUSED void *instance, REQUEST *request)
 {
        size_t i, len;
        VALUE_PAIR *vp;
        vp_cursor_t cursor;
 
-       for (vp = paircursor(&cursor, &request->packet->vps);
+       for (vp = fr_cursor_init(&cursor, &request->packet->vps);
             vp;
-            vp = pairnext(&cursor)) {
+            vp = fr_cursor_next(&cursor)) {
                if (vp->da->type != PW_TYPE_STRING) continue;
 
                for (i = 0; i < vp->length; i += len) {
@@ -67,15 +67,15 @@ module_t rlm_utf8 = {
        NULL,                           /* detach */
        {
                NULL,                   /* authentication */
-               utf8_clean,             /* authorization */
-               utf8_clean,             /* preaccounting */
+               mod_utf8_clean,         /* authorization */
+               mod_utf8_clean,         /* preaccounting */
                NULL,                   /* accounting */
                NULL,                   /* checksimul */
                NULL,                   /* pre-proxy */
                NULL,                   /* post-proxy */
                NULL                    /* post-auth */
 #ifdef WITH_COA
-               , utf8_clean,
+               , mod_utf8_clean,
                NULL
 #endif
        },
index f8f7889..e869d92 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -238,7 +238,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1248,9 +1248,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1293,10 +1293,10 @@ Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
   CPP         C preprocessor
 
 Use these variables to override the choices made by `configure' or to help
@@ -2068,7 +2068,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2290,7 +2290,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -3267,11 +3267,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -3777,11 +3777,11 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
 
 Configuration files:
 $config_files
index 0e240ff..93ecfb7 100644 (file)
@@ -44,8 +44,7 @@ typedef struct rlm_wimax_t {
  *     buffer over-flows.
  */
 static const CONF_PARSER module_config[] = {
-  { "delete_mppe_keys", PW_TYPE_BOOLEAN,
-    offsetof(rlm_wimax_t,delete_mppe_keys), NULL,   "no" },
+  { "delete_mppe_keys", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_wimax_t, delete_mppe_keys), "no" },
 
   { NULL, -1, 0, NULL, NULL }          /* end the list */
 };
@@ -56,7 +55,7 @@ static const CONF_PARSER module_config[] = {
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
 {
        VALUE_PAIR *vp;
 
@@ -88,16 +87,17 @@ static rlm_rcode_t mod_authorize(UNUSED void *instance, UNUSED REQUEST *request)
 
                DEBUG2("rlm_wimax: Fixing WiMAX binary Calling-Station-Id to %s",
                       vp->vp_strvalue);
+               return RLM_MODULE_OK;
        }
 
-       return RLM_MODULE_OK;
+       return RLM_MODULE_NOOP;
 }
 
 
 /*
  *     Massage the request before recording it or proxying it
  */
-static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_preacct(void *instance, REQUEST *request)
 {
        return mod_authorize(instance, request);
 }
@@ -105,7 +105,7 @@ static rlm_rcode_t mod_preacct(void *instance, REQUEST *request)
 /*
  *     Write accounting information to this modules database.
  */
-static rlm_rcode_t mod_accounting(UNUSED void *instance, UNUSED REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_accounting(UNUSED void *instance, UNUSED REQUEST *request)
 {
        return RLM_MODULE_OK;
 }
@@ -113,7 +113,7 @@ static rlm_rcode_t mod_accounting(UNUSED void *instance, UNUSED REQUEST *request
 /*
  *     Generate the keys after the user has been authenticated.
  */
-static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *request)
 {
        rlm_wimax_t *inst = instance;
        VALUE_PAIR *msk, *emsk, *vp;
@@ -128,7 +128,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
        msk = pairfind(request->reply->vps, 1129, 0, TAG_ANY);
        emsk = pairfind(request->reply->vps, 1130, 0, TAG_ANY);
        if (!msk || !emsk) {
-               RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys.");
+               RDEBUG("No EAP-MSK or EAP-EMSK.  Cannot create WiMAX keys");
                return RLM_MODULE_NOOP;
        }
 
@@ -213,8 +213,8 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
        mn_nai = pairfind(request->packet->vps, 1900, 0, TAG_ANY);
        if (!mn_nai) mn_nai = pairfind(request->reply->vps, 1900, 0, TAG_ANY);
        if (!mn_nai) {
-               RWDEBUG("WiMAX-MN-NAI was not found in the request or in the reply.");
-               RWDEBUG("We cannot calculate MN-HA keys.");
+               RWDEBUG("WiMAX-MN-NAI was not found in the request or in the reply");
+               RWDEBUG("We cannot calculate MN-HA keys");
        }
 
        /*
@@ -223,7 +223,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
        vp = NULL;
        if (mn_nai) vp = pairfind(request->reply->vps, 23, VENDORPEC_WIMAX, TAG_ANY);
        if (!vp) {
-               RWDEBUG("WiMAX-IP-Technology not found in reply.");
+               RWDEBUG("WiMAX-IP-Technology not found in reply");
                RWDEBUG("Not calculating MN-HA keys");
        }
 
@@ -254,7 +254,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               10, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -268,7 +268,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               11, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -304,7 +304,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, 10, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               10, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -318,7 +318,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, 11, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               11, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -354,7 +354,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, 12, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               12, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -368,7 +368,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->reply->vps, 13, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               13, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -405,7 +405,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
        if (fa_rk) {
                vp = pairfind(request->reply->vps, 61, VENDORPEC_WIMAX, TAG_ANY);
                if (!vp) {
-                       vp = radius_paircreate(request, &request->reply->vps,
+                       vp = radius_paircreate(request->reply, &request->reply->vps,
                                               61, VENDORPEC_WIMAX);
                }
                if (!vp) {
@@ -422,7 +422,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
         */
        vp = pairfind(request->packet->vps, 20, VENDORPEC_WIMAX, TAG_ANY);
        if (vp) {
-               RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage.");
+               RDEBUG("Client requested MN-HA key: Should use SPI to look up key from storage");
                if (!mn_nai) {
                        RWDEBUG("MN-NAI was not found!");
                }
@@ -440,7 +440,7 @@ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request)
                 */
                vp = pairfind(request->packet->vps, 58, VENDORPEC_WIMAX, TAG_ANY);
                if (vp && (vp->vp_integer == 1)) {
-                       RDEBUG("Client requested HA-RK: Should use IP to look it up from storage.");
+                       RDEBUG("Client requested HA-RK: Should use IP to look it up from storage");
                }
        }
 
index 55228df..2f41e75 100755 (executable)
@@ -157,7 +157,7 @@ $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
 as_fn_exit 255
   fi
   # We don't want this to propagate to other subprocesses.
-          { _as_can_reexec=; unset _as_can_reexec;}
+         { _as_can_reexec=; unset _as_can_reexec;}
 if test "x$CONFIG_SHELL" = x; then
   as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
   emulate sh
@@ -237,7 +237,7 @@ IFS=$as_save_IFS
 
       if test "x$CONFIG_SHELL" != x; then :
   export CONFIG_SHELL
-             # We cannot yet assume a decent shell, so we have to provide a
+            # We cannot yet assume a decent shell, so we have to provide a
 # neutralization value for shells without unset; and this also
 # works around shells that cannot unset nonexistent variables.
 # Preserve -v and -x to the replacement shell.
@@ -1213,9 +1213,9 @@ Configuration:
 
 Installation directories:
   --prefix=PREFIX         install architecture-independent files in PREFIX
-                          [$ac_default_prefix]
+                         [$ac_default_prefix]
   --exec-prefix=EPREFIX   install architecture-dependent files in EPREFIX
-                          [PREFIX]
+                         [PREFIX]
 
 By default, \`make install' will install all the files in
 \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc.  You can specify
@@ -1258,24 +1258,24 @@ Optional Packages:
   --with-PACKAGE[=ARG]    use PACKAGE [ARG=yes]
   --without-PACKAGE       do not use PACKAGE (same as --with-PACKAGE=no)
   --with-yubikey-include-dir=DIR
-                          Directory where the yubikey includes may be found
+                         Directory where the yubikey includes may be found
   --with-yubikey-lib-dir=DIR
-                          Directory where the yubikey libraries may be found
+                         Directory where the yubikey libraries may be found
   --with-yubikey-dir=DIR  Base directory where yubikey is installed
   --with-ykclient-include-dir=DIR
-                          Directory where the ykclient includes may be found
+                         Directory where the ykclient includes may be found
   --with-ykclient-lib-dir=DIR
-                          Directory where the ykclient libraries may be found
+                         Directory where the ykclient libraries may be found
   --with-ykclient-dir=DIR Base directory where ykclient is installed
 
 Some influential environment variables:
   CC          C compiler command
   CFLAGS      C compiler flags
   LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
-              nonstandard directory <lib dir>
+             nonstandard directory <lib dir>
   LIBS        libraries to pass to the linker, e.g. -l<library>
   CPPFLAGS    (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
-              you have headers in a nonstandard directory <include dir>
+             you have headers in a nonstandard directory <include dir>
 
 Use these variables to override the choices made by `configure' or to help
 it to find libraries and programs with nonstandard names/locations.
@@ -1798,7 +1798,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
 if test x$with_rlm_yubikey != xno; then
 
 
-        yubikey_include_dir=
+       yubikey_include_dir=
 
 # Check whether --with-yubikey-include-dir was given.
 if test "${with_yubikey_include_dir+set}" = set; then :
@@ -1815,7 +1815,7 @@ if test "${with_yubikey_include_dir+set}" = set; then :
 fi
 
 
-        yubikey_lib_dir=
+       yubikey_lib_dir=
 
 # Check whether --with-yubikey-lib-dir was given.
 if test "${with_yubikey_lib_dir+set}" = set; then :
@@ -1850,7 +1850,7 @@ fi
 
 
 
-        ykclient_include_dir=
+       ykclient_include_dir=
 
 # Check whether --with-ykclient-include-dir was given.
 if test "${with_ykclient_include_dir+set}" = set; then :
@@ -1867,7 +1867,7 @@ if test "${with_ykclient_include_dir+set}" = set; then :
 fi
 
 
-        ykclient_lib_dir=
+       ykclient_lib_dir=
 
 # Check whether --with-ykclient-lib-dir was given.
 if test "${with_ykclient_lib_dir+set}" = set; then :
@@ -2002,7 +2002,7 @@ else
 fi
 
 if test -z "$CC"; then
-          if test -n "$ac_tool_prefix"; then
+         if test -n "$ac_tool_prefix"; then
     # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
 set dummy ${ac_tool_prefix}cc; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -2224,7 +2224,7 @@ $as_echo "$ac_try_echo"; } >&5
   if test -s conftest.err; then
     sed '10a\
 ... rest of stderr output deleted ...
-         10q' conftest.err >conftest.er1
+        10q' conftest.err >conftest.er1
     cat conftest.er1 >&5
   fi
   rm -f conftest.er1 conftest.err
@@ -2773,22 +2773,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=yubikey.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2839,13 +2839,13 @@ if test "x$smart_include" != "x"; then
 fi
 
     if test "x$ac_cv_header_yubikey_h" != "xyes"; then
-        have_ykclient="no"
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: yubikey headers not found. Use --with-yubikey-include-dir=<path>." >&5
+       have_ykclient="no"
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: yubikey headers not found. Use --with-yubikey-include-dir=<path>." >&5
 $as_echo "$as_me: WARNING: yubikey headers not found. Use --with-yubikey-include-dir=<path>." >&2;}
     fi
 
 
-        smart_try_dir="$yubikey_lib_dir"
+       smart_try_dir="$yubikey_lib_dir"
 
 
 sm_lib_safe=`echo "yubikey" | sed 'y%./+-%__p_%'`
@@ -2905,8 +2905,8 @@ yubikey_aes_decrypt()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lyubikey"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lyubikey"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -2922,22 +2922,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libyubikey${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -2949,22 +2949,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libyubikey.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3013,8 +3013,8 @@ if test "x$smart_lib" != "x"; then
 fi
 
     if test "x$ac_cv_lib_yubikey_yubikey_aes_decrypt" != "xyes"; then
-        have_yubikey="no"
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: yubikey libraries not found. Use --with-yubikey-lib-dir=<path>." >&5
+       have_yubikey="no"
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: yubikey libraries not found. Use --with-yubikey-lib-dir=<path>." >&5
 $as_echo "$as_me: WARNING: yubikey libraries not found. Use --with-yubikey-lib-dir=<path>." >&2;}
     fi
 
@@ -3023,7 +3023,7 @@ $as_echo "$as_me: WARNING: yubikey libraries not found. Use --with-yubikey-lib-d
 $as_echo "#define HAVE_YUBIKEY 1" >>confdefs.h
 
     else
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently building without yubikey token decryption support. requires: yubikey" >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently building without yubikey token decryption support. requires: yubikey" >&5
 $as_echo "$as_me: WARNING: silently building without yubikey token decryption support. requires: yubikey" >&2;}
     fi
 
@@ -3109,22 +3109,22 @@ if test "x$smart_include" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=ykclient.h
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_include_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3175,8 +3175,8 @@ if test "x$smart_include" != "x"; then
 fi
 
     if test "x$ac_cv_header_ykclient_h" != "xyes"; then
-        have_ykclient="no"
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ykclient headers not found. Use --with-ykclient-include-dir=<path>." >&5
+       have_ykclient="no"
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ykclient headers not found. Use --with-ykclient-include-dir=<path>." >&5
 $as_echo "$as_me: WARNING: ykclient headers not found. Use --with-ykclient-include-dir=<path>." >&2;}
     fi
 
@@ -3241,8 +3241,8 @@ ykclient_request_process()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lykclient"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lykclient"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3258,22 +3258,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libykclient${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3285,22 +3285,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libykclient.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3349,7 +3349,7 @@ if test "x$smart_lib" != "x"; then
 fi
 
     if test "x$ac_cv_lib_ykclient_ykclient_request_process" != "xyes"; then
-        have_ykclient="no"
+       have_ykclient="no"
 
 
 sm_lib_safe=`echo "ykclient" | sed 'y%./+-%__p_%'`
@@ -3409,8 +3409,8 @@ ykclient_request()
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
 
-               smart_lib="-lykclient"
-               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+               smart_lib="-lykclient"
+               { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
 
 else
@@ -3426,22 +3426,22 @@ if test "x$smart_lib" = "x"; then
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libykclient${libltdl_cv_shlibext}
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3453,22 +3453,22 @@ eval "smart_lib_dir=\"\$smart_lib_dir $DIRS\""
 
 
 if test "x$LOCATE" != "x"; then
-        DIRS=
+       DIRS=
   file=libykclient.a
 
   for x in `${LOCATE} $file 2>/dev/null`; do
-                                        base=`echo $x | sed "s%/${file}%%"`
+                                       base=`echo $x | sed "s%/${file}%%"`
     if test "x$x" = "x$base"; then
       continue;
     fi
 
     dir=`${DIRNAME} $x 2>/dev/null`
-                exclude=`echo ${dir} | ${GREP} /home`
+               exclude=`echo ${dir} | ${GREP} /home`
     if test "x$exclude" != "x"; then
       continue
     fi
 
-                    already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
+                   already=`echo \$smart_lib_dir ${DIRS} | ${GREP} ${dir}`
     if test "x$already" = "x"; then
       DIRS="$DIRS $dir"
     fi
@@ -3516,13 +3516,13 @@ if test "x$smart_lib" != "x"; then
   SMART_LIBS="$smart_lib $SMART_LIBS"
 fi
 
-        if test "x$ac_cv_lib_ykclient_ykclient_request" == "xyes"; then
-            { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libykclient missing ykclient_request_process. A later version of libykclient is required." >&5
+       if test "x$ac_cv_lib_ykclient_ykclient_request" == "xyes"; then
+           { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libykclient missing ykclient_request_process. A later version of libykclient is required." >&5
 $as_echo "$as_me: WARNING: libykclient missing ykclient_request_process. A later version of libykclient is required." >&2;}
-        else
-            { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ykclient libraries not found. Use --with-ykclient-lib-dir=<path>." >&5
+       else
+           { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ykclient libraries not found. Use --with-ykclient-lib-dir=<path>." >&5
 $as_echo "$as_me: WARNING: ykclient libraries not found. Use --with-ykclient-lib-dir=<path>." >&2;}
-        fi
+       fi
     fi
 
     if test "x$have_ykclient" = "xyes"; then
@@ -3542,13 +3542,13 @@ fi
 
 if test x"$fail" != x""; then
     if test x"${enable_strict_dependencies}" = x"yes"; then
-        as_fn_error $? "set --without-rlm_yubikey to disable it explicitly." "$LINENO" 5
+       as_fn_error $? "set --without-rlm_yubikey to disable it explicitly." "$LINENO" 5
     else
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building rlm_yubikey." >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building rlm_yubikey." >&5
 $as_echo "$as_me: WARNING: silently not building rlm_yubikey." >&2;}
-        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: rlm_yubikey requires: $fail." >&5
+       { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: rlm_yubikey requires: $fail." >&5
 $as_echo "$as_me: WARNING: FAILURE: rlm_yubikey requires: $fail." >&2;};
-        targetname=""
+       targetname=""
     fi
 fi
 
@@ -3636,11 +3636,11 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
       if test ! -f "$cache_file" || test -h "$cache_file"; then
        cat confcache >"$cache_file"
       else
-        case $cache_file in #(
-        */* | ?:*)
+       case $cache_file in #(
+       */* | ?:*)
          mv -f confcache "$cache_file"$$ &&
          mv -f "$cache_file"$$ "$cache_file" ;; #(
-        *)
+       *)
          mv -f confcache "$cache_file" ;;
        esac
       fi
@@ -4114,13 +4114,13 @@ Usage: $0 [OPTION]... [TAG]...
   -V, --version    print version number and configuration settings, then exit
       --config     print configuration, then exit
   -q, --quiet, --silent
-                   do not print progress messages
+                  do not print progress messages
   -d, --debug      don't remove temporary files
       --recheck    update $as_me by reconfiguring in the same conditions
       --file=FILE[:TEMPLATE]
-                   instantiate the configuration file FILE
+                  instantiate the configuration file FILE
       --header=FILE[:TEMPLATE]
-                   instantiate the configuration header FILE
+                  instantiate the configuration header FILE
 
 Configuration files:
 $config_files
index d01dcdc..96d5f52 100644 (file)
@@ -120,8 +120,8 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$yubikey_include_dir"
     FR_SMART_CHECK_INCLUDE(yubikey.h)
     if test "x$ac_cv_header_yubikey_h" != "xyes"; then
-        have_ykclient="no"
-        AC_MSG_WARN([yubikey headers not found. Use --with-yubikey-include-dir=<path>.])
+       have_ykclient="no"
+       AC_MSG_WARN([yubikey headers not found. Use --with-yubikey-include-dir=<path>.])
     fi
 
     dnl ############################################################
@@ -132,14 +132,14 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$yubikey_lib_dir"
     FR_SMART_CHECK_LIB(yubikey, yubikey_aes_decrypt)
     if test "x$ac_cv_lib_yubikey_yubikey_aes_decrypt" != "xyes"; then
-        have_yubikey="no"
-        AC_MSG_WARN([yubikey libraries not found. Use --with-yubikey-lib-dir=<path>.])
+       have_yubikey="no"
+       AC_MSG_WARN([yubikey libraries not found. Use --with-yubikey-lib-dir=<path>.])
     fi
     
     if test "x$have_yubikey" = "xyes"; then
-        AC_DEFINE([HAVE_YUBIKEY],[1],[Build with yubikey token decryption support support from yubikey])
+       AC_DEFINE([HAVE_YUBIKEY],[1],[Build with yubikey token decryption support support from yubikey])
     else
-        AC_MSG_WARN([silently building without yubikey token decryption support. requires: yubikey])
+       AC_MSG_WARN([silently building without yubikey token decryption support. requires: yubikey])
     fi
     
     dnl ############################################################
@@ -150,8 +150,8 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$ykclient_include_dir"
     FR_SMART_CHECK_INCLUDE([ykclient.h])
     if test "x$ac_cv_header_ykclient_h" != "xyes"; then
-        have_ykclient="no"
-        AC_MSG_WARN([ykclient headers not found. Use --with-ykclient-include-dir=<path>.])
+       have_ykclient="no"
+       AC_MSG_WARN([ykclient headers not found. Use --with-ykclient-include-dir=<path>.])
     fi
        
     dnl ############################################################
@@ -161,17 +161,17 @@ if test x$with_[]modname != xno; then
     smart_try_dir="$ykclient_lib_dir"
     FR_SMART_CHECK_LIB([ykclient], [ykclient_request_process])
     if test "x$ac_cv_lib_ykclient_ykclient_request_process" != "xyes"; then
-        have_ykclient="no"
-        FR_SMART_CHECK_LIB([ykclient], [ykclient_request])
-        if test "x$ac_cv_lib_ykclient_ykclient_request" == "xyes"; then
-            AC_MSG_WARN([libykclient missing ykclient_request_process. A later version of libykclient is required.])
-        else
-            AC_MSG_WARN([ykclient libraries not found. Use --with-ykclient-lib-dir=<path>.])
-        fi
+       have_ykclient="no"
+       FR_SMART_CHECK_LIB([ykclient], [ykclient_request])
+       if test "x$ac_cv_lib_ykclient_ykclient_request" == "xyes"; then
+           AC_MSG_WARN([libykclient missing ykclient_request_process. A later version of libykclient is required.])
+       else
+           AC_MSG_WARN([ykclient libraries not found. Use --with-ykclient-lib-dir=<path>.])
+       fi
     fi
        
     if test "x$have_ykclient" = "xyes"; then
-        AC_DEFINE([HAVE_YKCLIENT],[1],[Build with yubicloud support from ykclient])
+       AC_DEFINE([HAVE_YKCLIENT],[1],[Build with yubicloud support from ykclient])
     else
        AC_MSG_WARN([silently building without yubicloud support. requires: ykclient])
     fi
@@ -185,11 +185,11 @@ fi
 dnl  Don't change this section.
 if test x"$fail" != x""; then
     if test x"${enable_strict_dependencies}" = x"yes"; then
-        AC_MSG_ERROR([set --without-]modname[ to disable it explicitly.])
+       AC_MSG_ERROR([set --without-]modname[ to disable it explicitly.])
     else
-        AC_MSG_WARN([silently not building ]modname[.])
-        AC_MSG_WARN([FAILURE: ]modname[ requires: $fail.]);
-        targetname=""
+       AC_MSG_WARN([silently not building ]modname[.])
+       AC_MSG_WARN([FAILURE: ]modname[ requires: $fail.]);
+       targetname=""
     fi
 fi
 
index e6c4106..59cbd44 100644 (file)
 /** Decrypt a Yubikey OTP AES block
  *
  * @param inst Module configuration.
- * @param otp string to decrypt.
+ * @param passcode string to decrypt.
  * @return one of the RLM_RCODE_* constants.
  */
-rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, VALUE_PAIR *otp)
+rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char const *passcode)
 {
        uint32_t counter;
        yubikey_token_st token;
@@ -43,7 +43,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, VALUE_PAI
                return RLM_MODULE_INVALID;
        }
 
-       yubikey_parse(otp->vp_octets + inst->id_len, key->vp_octets, &token);
+       yubikey_parse((uint8_t const *) passcode + inst->id_len, key->vp_octets, &token);
 
        /*
         *      Apparently this just uses byte offsets...
@@ -55,7 +55,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, VALUE_PAI
 
        RDEBUG("Token data decrypted successfully");
 
-       if (request->options && request->radlog) {
+       if (request->log.lvl && request->log.func) {
                (void) fr_bin2hex((char *) &private_id, (uint8_t*) &token.uid, YUBIKEY_UID_SIZE);
                RDEBUG2("Private ID     : 0x%s", private_id);
                RDEBUG2("Session counter   : %u", yubikey_counter(token.ctr));
index c21f727..01308df 100644 (file)
@@ -38,18 +38,19 @@ RCSID("$Id$")
 
 #ifdef HAVE_YKCLIENT
 static const CONF_PARSER validation_config[] = {
-       { "client_id", PW_TYPE_INTEGER, offsetof(rlm_yubikey_t, client_id), NULL, 0},
-       { "api_key", PW_TYPE_STRING_PTR, offsetof(rlm_yubikey_t, api_key), NULL, NULL},
+       { "client_id", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_yubikey_t, client_id), 0 },
+       { "api_key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, rlm_yubikey_t, api_key), NULL },
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
 #endif
 
 static const CONF_PARSER module_config[] = {
-       { "id_length", PW_TYPE_INTEGER, offsetof(rlm_yubikey_t, id_len), NULL, "12" },
-       { "decrypt", PW_TYPE_BOOLEAN, offsetof(rlm_yubikey_t, decrypt), NULL, "no" },
-       { "validate", PW_TYPE_BOOLEAN, offsetof(rlm_yubikey_t, validate), NULL, "no" },
+       { "id_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_yubikey_t, id_len), "12" },
+       { "split", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_yubikey_t, split), "yes" },
+       { "decrypt", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_yubikey_t, decrypt), "no" },
+       { "validate", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_yubikey_t, validate), "no" },
 #ifdef HAVE_YKCLIENT
-       { "validation", PW_TYPE_SUBSECTION, 0, NULL, (void const *) validation_config },
+       { "validation", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) validation_config },
 #endif
        { NULL, -1, 0, NULL, NULL }             /* end the list */
 };
@@ -146,7 +147,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
 #ifndef HAVE_YUBIKEY
        if (inst->decrypt) {
-               EDEBUG("rlm_yubikey (%s): Requires libyubikey for OTP decryption", inst->name);
+               ERROR("rlm_yubikey (%s): Requires libyubikey for OTP decryption", inst->name);
                return -1;
        }
 #endif
@@ -157,7 +158,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
 
                cs = cf_section_sub_find(conf, "validation");
                if (!cs) {
-                       EDEBUG("rlm_yubikey (%s): Missing validation section", inst->name);
+                       ERROR("rlm_yubikey (%s): Missing validation section", inst->name);
                        return -1;
                }
 
@@ -165,7 +166,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance)
                        return -1;
                }
 #else
-               EDEBUG("rlm_yubikey (%s): Requires libykclient for OTP validation against Yubicloud servers", inst->name);
+               ERROR("rlm_yubikey (%s): Requires libykclient for OTP validation against Yubicloud servers", inst->name);
                return -1;
 #endif
        }
@@ -187,19 +188,31 @@ static int mod_detach(void *instance)
 }
 #endif
 
+static int CC_HINT(nonnull) otp_string_valid(rlm_yubikey_t *inst, char const *otp, size_t len)
+{
+       size_t i;
+
+       for (i = inst->id_len; i < len; i++) {
+               if (!is_modhex(otp[i])) return -i;
+       }
+
+       return 1;
+}
+
+
 /*
  *     Find the named user in this modules database.  Create the set
  *     of attribute-value pairs to check and reply with for this user
  *     from the database. The authentication code only needs to check
  *     the password, the rest is done here.
  */
-static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
 {
        rlm_yubikey_t *inst = instance;
 
        DICT_VALUE *dval;
        char const *passcode;
-       size_t i, len;
+       size_t len;
        VALUE_PAIR *vp;
 
        /*
@@ -210,17 +223,18 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                 *      Don't print out debugging messages if we know
                 *      they're useless.
                 */
-               if (request->packet->code == PW_ACCESS_CHALLENGE) {
+               if (request->packet->code == PW_CODE_ACCESS_CHALLENGE) {
                        return RLM_MODULE_NOOP;
                }
 
-               RDEBUG2("No Clear-Text password in the request. Can't do Yubikey authentication.");
+               RDEBUG2("No cleartext password in the request. Can't do Yubikey authentication");
 
                return RLM_MODULE_NOOP;
        }
 
        passcode = request->password->vp_strvalue;
        len = request->password->length;
+
        /*
         *      Now see if the passcode is the correct length (in its raw
         *      modhex encoded form).
@@ -228,15 +242,66 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
         *      <public_id (6-16 bytes)> + <aes-block (32 bytes)>
         *
         */
-       if (len != (inst->id_len + 32)) {
-               RDEBUG2("User-Password value is not the correct length, expected %u, got %zu", inst->id_len + 32, len);
+       if (len > (inst->id_len + YUBIKEY_TOKEN_LEN)) {
+               /* May be a concatenation, check the last 32 bytes are modhex */
+               if (inst->split) {
+                       char const *otp;
+                       char *password;
+                       size_t password_len;
+                       int ret;
+
+                       password_len = (len - (inst->id_len + YUBIKEY_TOKEN_LEN));
+                       otp = passcode + password_len;
+                       ret = otp_string_valid(inst, otp, (inst->id_len + YUBIKEY_TOKEN_LEN));
+                       if (ret <= 0) {
+                               if (RDEBUG_ENABLED3) {
+                                       RDMARKER(otp, -ret, "User-Password (aes-block) value contains non modhex chars");
+                               } else {
+                                       RDEBUG("User-Password (aes-block) value contains non modhex chars");
+                               }
+                               return RLM_MODULE_NOOP;
+                       }
+
+                       /*
+                        *      Insert a new request attribute just containing the OTP
+                        *      portion.
+                        */
+                       vp = pairmake_packet("Yubikey-OTP", otp, T_OP_SET);
+                       if (!vp) {
+                               REDEBUG("Failed creating 'Yubikey-OTP' attribute");
+                               return RLM_MODULE_FAIL;
+                       }
+
+                       /*
+                        *      Replace the existing string buffer for the password
+                        *      attribute with one just containing the password portion.
+                        */
+                       MEM(password = talloc_array(request->password, char, password_len + 1));
+                       strlcpy(password, passcode, password_len + 1);
+                       pairstrsteal(request->password, password);
+
+                       RDEBUG3("request:Yubikey-OTP := '%s'", vp->vp_strvalue);
+                       RDEBUG3("request:User-Password := '%s'", request->password->vp_strvalue);
+
+                       /*
+                        *      So the ID split code works on the non password portion.
+                        */
+                       passcode = vp->vp_strvalue;
+               }
+       } else if (len < (inst->id_len + YUBIKEY_TOKEN_LEN)) {
+               RDEBUG2("User-Password value is not the correct length, expected at least %u bytes, got %zu bytes",
+                       inst->id_len + YUBIKEY_TOKEN_LEN, len);
                return RLM_MODULE_NOOP;
-       }
-
-       for (i = inst->id_len; i < len; i++) {
-               if (!is_modhex(*passcode)) {
-                       RDEBUG2("User-Password (aes-block) value contains non modhex chars");
-
+       } else {
+               int ret;
+
+               ret = otp_string_valid(inst, passcode, (inst->id_len + YUBIKEY_TOKEN_LEN));
+               if (ret <= 0) {
+                       if (RDEBUG_ENABLED3) {
+                               RDMARKER(passcode, -ret, "User-Password (aes-block) value contains non modhex chars");
+                       } else {
+                               RDEBUG("User-Password (aes-block) value contains non modhex chars");
+                       }
                        return RLM_MODULE_NOOP;
                }
        }
@@ -252,9 +317,6 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
         *      needs to verify it's associated with the user.
         *
         *      It's left up to the user if they want to decode it or not.
-        *
-        *      @todo: is this even right?  Is there anything in passcode other than
-        *      the public ID?  Why pairmake(...NULL) instead of pairmake(..., passcode) ?
         */
        if (inst->id_len) {
                vp = pairmake(request, &request->packet->vps, "Yubikey-Public-ID", NULL, T_OP_SET);
@@ -264,53 +326,77 @@ static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
                        return RLM_MODULE_FAIL;
                }
 
-               pairstrcpy(vp, passcode);
+               pairstrncpy(vp, passcode, inst->id_len);
        }
 
        return RLM_MODULE_OK;
 }
 
+
 /*
  *     Authenticate the user with the given password.
  */
-static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
+static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
 {
        rlm_rcode_t rcode = RLM_MODULE_NOOP;
        rlm_yubikey_t *inst = instance;
-       char const *passcode;
-       size_t i, len;
+       char const *passcode = NULL;
+       DICT_ATTR const *da;
+       VALUE_PAIR const *vp;
+       size_t len;
+       int ret;
+
+       da = dict_attrbyname("Yubikey-OTP");
+       if (!da) {
+               RDEBUG2("No Yubikey-OTP attribute defined, falling back to User-Password");
+               goto user_password;
+       }
 
-       /*
-        *      Can't do yubikey auth if there's no password.
-        */
-       if (!request->password || (request->password->da->attr != PW_USER_PASSWORD)) {
-               REDEBUG("No Clear-Text password in the request. Can't do Yubikey authentication.");
-               return RLM_MODULE_INVALID;
+       vp = pairfind_da(request->packet->vps, da, TAG_ANY);
+       if (vp) {
+               passcode = vp->vp_strvalue;
+               len = vp->length;
+       } else {
+               RDEBUG2("No Yubikey-OTP attribute found, falling back to User-Password");
+       user_password:
+               /*
+                *      Can't do yubikey auth if there's no password.
+                */
+               if (!request->password || (request->password->da->attr != PW_USER_PASSWORD)) {
+                       REDEBUG("No User-Password in the request. Can't do Yubikey authentication");
+                       return RLM_MODULE_INVALID;
+               }
+
+               vp = request->password;
+               passcode = request->password->vp_strvalue;
+               len = request->password->length;
        }
 
-       passcode = request->password->vp_strvalue;
-       len = request->password->length;
        /*
         *      Verify the passcode is the correct length (in its raw
         *      modhex encoded form).
         *
         *      <public_id (6-16 bytes)> + <aes-block (32 bytes)>
         */
-       if (len != (inst->id_len + 32)) {
-               REDEBUG("User-Password value is not the correct length, expected %u, got %zu", inst->id_len + 32, len);
+       if (len != (inst->id_len + YUBIKEY_TOKEN_LEN)) {
+               REDEBUG("%s value is not the correct length, expected bytes %u, got bytes %zu",
+                       vp->da->name, inst->id_len + YUBIKEY_TOKEN_LEN, len);
                return RLM_MODULE_INVALID;
        }
 
-       for (i = inst->id_len; i < len; i++) {
-               if (!is_modhex(*passcode)) {
-                       RDEBUG2("User-Password (aes-block) value contains non modhex chars");
-                       return RLM_MODULE_INVALID;
+       ret = otp_string_valid(inst, passcode, (inst->id_len + YUBIKEY_TOKEN_LEN));
+       if (ret <= 0) {
+               if (RDEBUG_ENABLED3) {
+                       REMARKER(passcode, -ret, "Passcode (aes-block) value contains non modhex chars");
+               } else {
+                       RERROR("Passcode (aes-block) value contains non modhex chars");
                }
+               return RLM_MODULE_INVALID;
        }
 
 #ifdef HAVE_YUBIKEY
        if (inst->decrypt) {
-               rcode = rlm_yubikey_decrypt(inst, request, request->password);
+               rcode = rlm_yubikey_decrypt(inst, request, passcode);
                if (rcode != RLM_MODULE_OK) {
                        return rcode;
                }
@@ -320,7 +406,7 @@ static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request)
 
 #ifdef HAVE_YKCLIENT
        if (inst->validate) {
-               return rlm_yubikey_validate(inst, request, request->password);
+               return rlm_yubikey_validate(inst, request, passcode);
        }
 #endif
        return rcode;
index 5b4dcbd..5827c59 100644 (file)
@@ -12,6 +12,8 @@
 #include <yubikey.h>
 #endif
 
+#define YUBIKEY_TOKEN_LEN 32
+
 /*
  *     Define a structure for our module configuration.
  *
@@ -23,13 +25,14 @@ typedef struct rlm_yubikey_t {
        char const              *name;                  //!< Instance name.
        int                     auth_type;              //!< Our Auth-Type.
        unsigned int            id_len;                 //!< The length of the Public ID portion of the OTP string.
+       bool                    split;                  //!< Split password string into components.
        bool                    decrypt;                //!< Decrypt the OTP string using the yubikey library.
        bool                    validate;               //!< Validate the OTP string using the ykclient library.
        char const              **uris;                 //!< Yubicloud URLs to validate the token against.
 
 #ifdef HAVE_YKCLIENT
        unsigned int            client_id;              //!< Validation API client ID.
-       char                    *api_key;               //!< Validation API signing key.
+       char const              *api_key;               //!< Validation API signing key.
        ykclient_t              *ykc;                   //!< ykclient configuration.
        fr_connection_pool_t    *conn_pool;             //!< Connection pool instance.
 #endif
@@ -39,7 +42,7 @@ typedef struct rlm_yubikey_t {
 /*
  *     decrypt.c - Decryption functions
  */
-rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, VALUE_PAIR *otp);
+rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char const *passcode);
 
 /*
  *     validate.c - Connection pool and validation functions
@@ -48,4 +51,4 @@ int rlm_yubikey_ykclient_init(CONF_SECTION *conf, rlm_yubikey_t *inst);
 
 int rlm_yubikey_ykclient_detach(rlm_yubikey_t *inst);
 
-rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request, VALUE_PAIR *otp);
+rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request, char const *passcode);
index 629c59c..67b0d7d 100644 (file)
@@ -35,7 +35,7 @@ static void *mod_socket_create(void *instance)
 
        status = ykclient_handle_init(inst->ykc, &yandle);
        if (status != YKCLIENT_OK) {
-               EDEBUG("rlm_yubikey (%s): %s", inst->name, ykclient_strerror(status));
+               ERROR("rlm_yubikey (%s): %s", inst->name, ykclient_strerror(status));
 
                return NULL;
        }
@@ -68,13 +68,15 @@ int rlm_yubikey_ykclient_init(CONF_SECTION *conf, rlm_yubikey_t *inst)
        int count = 0;
 
        if (!inst->client_id) {
-               EDEBUG("rlm_yubikey (%s): client_id must be set when validation is enabled", inst->name);
+               ERROR("rlm_yubikey (%s): validation.client_id must be set (to a valid id) when validation is enabled",
+                     inst->name);
 
                return -1;
        }
 
-       if (!inst->api_key) {
-               EDEBUG("rlm_yubikey (%s): api_key must be set when validation is enabled", inst->name);
+       if (!inst->api_key || !*inst->api_key || is_zero(inst->api_key)) {
+               ERROR("rlm_yubikey (%s): validation.api_key must be set (to a valid key) when validation is enabled",
+                     inst->name);
 
                return -1;
        }
@@ -84,7 +86,7 @@ int rlm_yubikey_ykclient_init(CONF_SECTION *conf, rlm_yubikey_t *inst)
        status = ykclient_global_init();
        if (status != YKCLIENT_OK) {
 yk_error:
-               EDEBUG("rlm_yubikey (%s): %s", ykclient_strerror(status), inst->name);
+               ERROR("rlm_yubikey (%s): %s", ykclient_strerror(status), inst->name);
 
                return -1;
        }
@@ -129,7 +131,7 @@ yk_error:
 init:
        status = ykclient_set_client_b64(inst->ykc, inst->client_id, inst->api_key);
        if (status != YKCLIENT_OK) {
-               EDEBUG("rlm_yubikey (%s): Failed setting API credentials: %s", ykclient_strerror(status), inst->name);
+               ERROR("rlm_yubikey (%s): Failed setting API credentials: %s", ykclient_strerror(status), inst->name);
 
                return -1;
        }
@@ -154,16 +156,14 @@ int rlm_yubikey_ykclient_detach(rlm_yubikey_t *inst)
        return 0;
 }
 
-rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request,  VALUE_PAIR *otp)
+rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request,  char const *passcode)
 {
        rlm_rcode_t rcode = RLM_MODULE_OK;
        ykclient_rc status;
        ykclient_handle_t *yandle;
 
        yandle = fr_connection_get(inst->conn_pool);
-       if (!yandle) {
-               return RLM_MODULE_FAIL;
-       }
+       if (!yandle) return RLM_MODULE_FAIL;
 
        /*
         *      The libcurl multi-handle interface will tear down the TCP sockets for any partially completed
@@ -181,22 +181,21 @@ rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request,  VALUE_P
         */
        ykclient_handle_cleanup(yandle);
 
-       status = ykclient_request_process(inst->ykc, yandle, otp->vp_strvalue);
+       status = ykclient_request_process(inst->ykc, yandle, passcode);
        if (status != YKCLIENT_OK) {
                REDEBUG("%s", ykclient_strerror(status));
-
                switch (status) {
-                       case YKCLIENT_BAD_OTP:
-                       case YKCLIENT_REPLAYED_OTP:
-                               rcode = RLM_MODULE_REJECT;
-                               break;
+               case YKCLIENT_BAD_OTP:
+               case YKCLIENT_REPLAYED_OTP:
+                       rcode = RLM_MODULE_REJECT;
+                       break;
 
-                       case YKCLIENT_NO_SUCH_CLIENT:
-                               rcode = RLM_MODULE_NOTFOUND;
-                               break;
+               case YKCLIENT_NO_SUCH_CLIENT:
+                       rcode = RLM_MODULE_NOTFOUND;
+                       break;
 
-                       default:
-                               rcode = RLM_MODULE_FAIL;
+               default:
+                       rcode = RLM_MODULE_FAIL;
                }
        }
 
index ad43ac5..d16772a 100644 (file)
@@ -27,6 +27,7 @@ rlm_preprocess
 rlm_python
 rlm_radutmp
 rlm_realm
+rlm_rest
 rlm_replicate
 rlm_soh
 rlm_sql
index 7c099a0..3d34894 100644 (file)
@@ -1,6 +1,7 @@
 .cache
 .foo
 .bar
+.request
 dictionary
 test.conf
 radius.log
index c4d2b34..3ec9575 100644 (file)
@@ -19,7 +19,7 @@ export DYLD_LIBRARY_PATH
 RADDB_PATH := $(top_builddir)/raddb
 
 TESTS  = mschapv1 digest-01/digest* \
-       test.example.com 
+       test.example.com
 
 PORT    = 12340
 #PORT   = 1812
@@ -42,28 +42,43 @@ SECRET      = testing123
 all: parse tests
 
 clean:
-       @rm -f $(RADDB_PATH)/test.conf test.conf dictionary
+       @rm -f test.conf dictionary
 
 dictionary:
        @echo "# test dictionary not install.  Delete at any time." > dictionary
        @echo '$$INCLUDE ' $(top_builddir)/share/dictionary >> dictionary
        @echo '$$INCLUDE ' $(top_builddir)/src/tests/dictionary.test >> dictionary
        @echo '$$INCLUDE ' $(top_builddir)/share/dictionary.dhcp >> dictionary
+       @echo '$$INCLUDE ' $(top_builddir)/share/dictionary.vqp >> dictionary
 
 test.conf: dictionary
        @echo "# test configuration file.  Do not install.  Delete at any time." > test.conf
        @echo "libdir =" $(LIB_PATH) >> test.conf
        @echo "testdir =" $(TEST_PATH) >> test.conf
        @echo 'logdir = $${testdir}' >> test.conf
+       @echo 'maindir = ${top_builddir}/raddb/' >> test.conf
        @echo 'radacctdir = $${testdir}' >> test.conf
        @echo 'pidfile = $${testdir}/radiusd.pid' >> test.conf
-       @echo '$$INCLUDE radiusd.conf' >> test.conf
+       @echo 'panic_action = "gdb -batch -x ${testdir}/panic.gdb %e %p > ${testdir}/gdb.log 2>&1; cat ${testdir}/gdb.log"' >> test.conf
+       @echo 'security {' >> $@
+       @echo '        allow_vulnerable_openssl = yes' >> $@
+       @echo '}' >> $@
+       @echo >> $@
+       @echo 'modconfdir = $${maindir}mods-config' >> $@
+       @echo 'certdir = $${maindir}/certs' >> $@
+       @echo 'cadir   = $${maindir}/certs' >> $@
+       @echo '$$INCLUDE $${maindir}/radiusd.conf' >> test.conf
        @echo '$$INCLUDE $${testdir}/config/' >> test.conf
 
-radiusd.pid: $(RADDB_PATH)/test.conf test.conf
+radiusd.pid: test.conf
        @rm -f $(TEST_PATH)/gdb.log $(TEST_PATH)/radius.log
-       @gdb -silent -x $(TEST_PATH)/gdb.conf --args \
-       $(BIN_PATH)/radiusd -fPtxxxxml $(TEST_PATH)/radius.log -d $(RADDB_PATH) -n test -i 127.0.0.1 -p $(PORT) -D $(TEST_PATH) > $(TEST_PATH)/gdb.log 2>&1 &
+       @printf "Starting server... "
+       @if ! $(BIN_PATH)/radiusd -Pxxxxml $(TEST_PATH)/radius.log -d ${top_builddir}/src/tests -n test -i 127.0.0.1 -p $(PORT) -D $(TEST_PATH); then\
+               echo "failed"; \
+               echo "Last log entries were:"; \
+               tail -n 20 "$(TEST_PATH)/radius.log"; \
+       fi
+       @echo "ok"
 
 # We can't make this depend on radiusd.pid, because then make will create
 # radiusd.pid when we make radiusd.kill, which we don't want.
@@ -73,12 +88,12 @@ radiusd.kill:
            ret=0; \
            if ! ps `cat $(TEST_PATH)/radiusd.pid` >/dev/null 2>&1; then \
                rm -f radiusd.pid; \
-               echo "FreeRADIUS terminated during test"; \
-               echo "GDB output was:"; \
-               cat "$(TEST_PATH)/gdb.log"; \
-               echo "Last log entries were:"; \
-               tail -n 20 $(TEST_PATH)/radius.log; \
-               ret=1; \
+               echo "FreeRADIUS terminated during test"; \
+               echo "GDB output was:"; \
+               cat "$(TEST_PATH)/gdb.log"; \
+               echo "Last log entries were:"; \
+               tail -n 20 $(TEST_PATH)/radius.log; \
+               ret=1; \
            fi; \
                if ! kill -TERM `cat $(TEST_PATH)/radiusd.pid` >/dev/null 2>&1; then \
                    ret=1; \
@@ -87,48 +102,22 @@ radiusd.kill:
        fi
        @rm -f radiusd.pid
 
-#  Link from the main database directory to here
-$(RADDB_PATH)/test.conf: test.conf
-       @[ -f $(RADDB_PATH)/test.conf ] || ln -s ../src/tests/test.conf $(RADDB_PATH)/
-
 # kill the server (if it's running)
 # start the server
 # run the tests (ignoring any failures)
 # kill the server
 # remove the changes to raddb/
-tests: $(RADDB_PATH)/test.conf radiusd.kill
+tests: test.conf | radiusd.kill radiusd.pid
        @chmod a+x runtests.sh
-       @$(MAKE) radiusd.pid
-       @echo "Waiting for server to start..."; \
-       for i in 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0; do \
-               printf "$$i "; \
-               if [ -f '$(TEST_PATH)/radiusd.pid' ]; then \
-                       if ps `cat $(TEST_PATH)/radiusd.pid > /dev/null` > /dev/null 2>&1; then \
-                               echo; echo "Server started with PID" `cat $(TEST_PATH)/radiusd.pid`; \
-                               exit 0; \
-                       else \
-                               break; \
-                       fi; \
-               fi; \
-               sleep 1; \
-       done; echo; \
-       echo "FreeRADIUS daemon failed to start"; \
-       echo "Last log entries were:"; \
-       tail -n 20 "$(TEST_PATH)/radius.log"; \
-       echo "GDB output was:"; \
-       cat "$(TEST_PATH)/gdb.log"; \
-       exit 1;
        @BIN_PATH="$(BIN_PATH)" PORT="$(PORT)" ./runtests.sh $(TESTS)
        @$(MAKE) radiusd.kill
-       @rm -f $(RADDB_PATH)/test.conf
 
-tests.eap: $(RADDB_PATH)/test.conf radiusd.kill
+tests.eap: test.conf radiusd.kill
        @chmod a+x runtests.sh
        @rm -f $(TEST_PATH)/radius.log $(TEST_PATH)/gdb.log
        @$(MAKE) radiusd.pid
        @$(MAKE) eap
        @$(MAKE) radiusd.kill
-       @rm -f $(RADDB_PATH)/test.conf
 
 eap: $(EAP_TLS_TESTS)
        for x in $(EAP_TLS_TESTS); do \
@@ -136,7 +125,7 @@ eap: $(EAP_TLS_TESTS)
        done
 
 md5:
-       $(EAPOL_TEST) -c eap-md5.conf -s $(SECRET) 
+       $(EAPOL_TEST) -c eap-md5.conf -s $(SECRET)
 
 tls:
        $(EAPOL_TEST) -c eap-ttls-tls.conf -s $(SECRET)
index 463f9b6..f226e05 100644 (file)
@@ -7,7 +7,7 @@
 #  The list is unordered.  The order is added in the next step by looking
 #  at precursors.
 #
-AUTH_FILES := $(filter-out %.conf %.md %.attrs %.mk %~,$(subst $(DIR)/,,$(wildcard $(DIR)/*)))
+AUTH_FILES := $(filter-out %.conf %.md %.attrs %.mk %~ %.rej,$(subst $(DIR)/,,$(wildcard $(DIR)/*)))
 
 #
 #  Create the output directory
@@ -38,8 +38,11 @@ $(BUILD_DIR)/tests/auth/depends.mk: $(addprefix $(DIR)/,$(AUTH_FILES)) | $(BUILD
        @rm -f $@
        @for x in $^; do \
                y=`grep 'PRE: ' $$x | sed 's/.*://;s/  / /g;s, , $(BUILD_DIR)/tests/auth/,g'`; \
-               echo "$$x: $$y" >> $@; \
-               echo "" >> $@; \
+               if [ "$$y" != "" ]; then \
+                       z=`echo $$x | sed 's,src/,$(BUILD_DIR)/',`; \
+                       echo "$$z: $$y" >> $@; \
+                       echo "" >> $@; \
+               fi \
        done
 #
 #  These ones get copied over from the default input
@@ -81,7 +84,7 @@ AUTH_LIBS     := $(addsuffix .la,$(addprefix rlm_,$(AUTH_MODULES)))
 #
 $(BUILD_DIR)/tests/auth/%: $(DIR)/% $(BUILD_DIR)/tests/auth/%.attrs $(TESTBINDIR)/unittest | $(BUILD_DIR)/tests/auth $(AUTH_RADDB) $(AUTH_LIBS) build.raddb
        @echo UNIT-TEST $(notdir $@)
-       @if ! TESTDIR=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/auth/ -i $@.attrs -f $@.attrs -xx > $@.log 2>&1; then \
+       @if ! TESTDIR=$(notdir $@) $(TESTBIN)/unittest -D share -d src/tests/auth/ -i $@.attrs -f $@.attrs -xxx > $@.log 2>&1; then \
                if ! grep ERROR $< 2>&1 > /dev/null; then \
                        cat $@.log; \
                        echo "# $@.log"; \
diff --git a/src/tests/auth/chap_header b/src/tests/auth/chap_header
new file mode 100644 (file)
index 0000000..13c3c9b
--- /dev/null
@@ -0,0 +1,7 @@
+#
+#  over-ride password set in radiusd.conf
+#
+update control {
+       Cleartext-Password -= 'hello'
+       Password-With-Header := 'oracle01'
+}
diff --git a/src/tests/auth/chap_header.attrs b/src/tests/auth/chap_header.attrs
new file mode 100644 (file)
index 0000000..9f815c7
--- /dev/null
@@ -0,0 +1,4 @@
+User-Name = "bob"
+CHAP-Password = 'oracle01'
+
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/auth/md5_password b/src/tests/auth/md5_password
new file mode 100644 (file)
index 0000000..4376056
--- /dev/null
@@ -0,0 +1,7 @@
+#
+#  over-ride password set in radiusd.conf
+#
+update control {
+       Cleartext-Password -= ANY
+       Password-With-Header := '{md5}5d41402abc4b2a76b9719d911017c592'
+}
diff --git a/src/tests/auth/md5_password.attrs b/src/tests/auth/md5_password.attrs
new file mode 100644 (file)
index 0000000..65b967b
--- /dev/null
@@ -0,0 +1,4 @@
+User-Name = "bob"
+User-Password = "hello"
+
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/auth/password_with_header b/src/tests/auth/password_with_header
new file mode 100644 (file)
index 0000000..08993ab
--- /dev/null
@@ -0,0 +1,7 @@
+#
+#  over-ride password set in radiusd.conf
+#
+update control {
+       Cleartext-Password -= 'hello'
+       Password-With-Header := '{clear}hello'
+}
diff --git a/src/tests/auth/password_with_header.attrs b/src/tests/auth/password_with_header.attrs
new file mode 100644 (file)
index 0000000..65b967b
--- /dev/null
@@ -0,0 +1,4 @@
+User-Name = "bob"
+User-Password = "hello"
+
+Response-Packet-Type == Access-Accept
diff --git a/src/tests/auth/password_without_header b/src/tests/auth/password_without_header
new file mode 100644 (file)
index 0000000..6fab6b3
--- /dev/null
@@ -0,0 +1,7 @@
+#
+#  over-ride password set in radiusd.conf
+#
+update control {
+       Cleartext-Password -= 'hello'
+       Password-With-Header := 'hello'
+}
diff --git a/src/tests/auth/password_without_header.attrs b/src/tests/auth/password_without_header.attrs
new file mode 100644 (file)
index 0000000..65b967b
--- /dev/null
@@ -0,0 +1,4 @@
+User-Name = "bob"
+User-Password = "hello"
+
+Response-Packet-Type == Access-Accept
index ceb0cfe..2d822e7 100644 (file)
@@ -7,6 +7,12 @@ testdir                = src/tests/auth
 
 modconfdir = ${raddb}/mods-config
 
+#  Only for testing!
+#  Setting this on a production system is a BAD IDEA.
+security {
+       allow_vulnerable_openssl = yes
+}
+
 modules {
        $INCLUDE ${raddb}/mods-enabled/always
 
@@ -38,7 +44,7 @@ server default {
 
        authenticate {
                digest
-               pap
+               pap
                chap
        }
 }
index b08c323..a72f9b1 100644 (file)
@@ -7,6 +7,12 @@
 
 test_port      = 10000
 
+#  Only for testing!
+#  Setting this on a production system is a BAD IDEA.
+security {
+       allow_vulnerable_openssl = yes
+}
+
 realm test.example.com {
       authhost = 127.0.0.1:${test_port}
       secret = testing123
@@ -24,7 +30,7 @@ server test {
              load_factor = 10
        }
 
-               listen {
+       listen {
                ipaddr = 127.0.0.1
                port = ${test_port}
                type = auth
index a11c1b4..edbc020 100644 (file)
@@ -2,8 +2,8 @@
 #   eapol_test -c eap-md5.conf -s testing123 -n
 #
 network={
-        key_mgmt=NONE
-        eap=MD5
-        identity="bob"
-        password="hello"
+       key_mgmt=NONE
+       eap=MD5
+       identity="bob"
+       password="hello"
 }
index d9e4182..f738bc6 100644 (file)
@@ -2,9 +2,9 @@
 #   eapol_test -c eap-mschapv2.conf -s testing123
 #
 network={
-        ssid="example"
-        key_mgmt=WPA-EAP
-        eap=MSCHAPV2
-        identity="bob"
-        password="bob"
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=MSCHAPV2
+       identity="bob"
+       password="bob"
 }
index 20f27f0..a1ef3a9 100644 (file)
@@ -2,11 +2,11 @@
 #   eapol_test -c eap-ttls-mschapv2.conf -s testing123
 #
 network={
-        ssid="example"
-        key_mgmt=WPA-EAP
-        eap=TTLS
-        identity="bob"
-        anonymous_identity="anonymous"
-        password="bob"
-        phase2="autheap=MSCHAPV2"
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="bob"
+       anonymous_identity="anonymous"
+       password="bob"
+       phase2="autheap=MSCHAPV2"
 }
index f811169..8e7c005 100644 (file)
@@ -2,11 +2,11 @@
 #   eapol_test -c eap-ttls-mschapv2.conf -s testing123
 #
 network={
-        ssid="example"
-        key_mgmt=WPA-EAP
-        eap=TTLS
-        identity="bob"
-        anonymous_identity="anonymous"
-        password="bob"
-        phase2="auth=MSCHAPV2"
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="bob"
+       anonymous_identity="anonymous"
+       password="bob"
+       phase2="auth=MSCHAPV2"
 }
index a3a0a61..633c800 100644 (file)
@@ -2,10 +2,10 @@
 #   eapol_test -c eap-ttls-pap.conf -s testing123
 #
 network={
-        key_mgmt=WPA-EAP
-        eap=TTLS
-        identity="bob"
-        anonymous_identity="anonymous"
-        password="hello"
-        phase2="auth=PAP"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="bob"
+       anonymous_identity="anonymous"
+       password="hello"
+       phase2="auth=PAP"
 }
index c9fe766..3b12512 100644 (file)
@@ -1,6 +1,6 @@
 SHA1buffer was: 65617073_696d0011_22334455_66771021_32435465_
-                76873041_52637485_96a74d6c_40de483a_dd995090_
-                2c4024ce_765e0002_00010001
+               76873041_52637485_96a74d6c_40de483a_dd995090_
+               2c4024ce_765e0002_00010001
 Input was: 
    identity: (len=6)65617073696d
    nonce_mt: 4d6c40de483add9950902c4024ce765e
@@ -22,17 +22,17 @@ mk:         d1cdd6d3_574ef82e_c1e83879_559e89f8_de8e6e90
 K_aut:      54323970_481b5159_48d00a34_422bbe3c
 K_encr:     72469fd8_bb6c2a4a_93ac42e5_b4668acb
 msk:        0a572a3f_2baeea10_640598c9_41901995_f842097a
-            cbb13272_bc949b66_8fb4f5a3_deefed09_3947fe64
-            c88f7df8_dadcab5f_8d003913_8e9bcff7_1a810316
-            11eeb959
+           cbb13272_bc949b66_8fb4f5a3_deefed09_3947fe64
+           c88f7df8_dadcab5f_8d003913_8e9bcff7_1a810316
+           11eeb959
 emsk:       207256b3_9ada49ef_c1850c12_30461921_d700f9f7
-            83a0f5a9_cff155bd_6c8f1aa5_072b7530_af44f515
-            3cb983b0_8343effa_2eb161e2_7d3543bc_5bd6db22
-            993af27b
+           83a0f5a9_cff155bd_6c8f1aa5_072b7530_af44f515
+           3cb983b0_8343effa_2eb161e2_7d3543bc_5bd6db22
+           993af27b
 SHA1buffer was: 31323434_30373031_30303030_30303031_40656170_
-                73696d2e_666f6fa0_a1a2a3a4_a5a6a7b0_b1b2b3b4_
-                b5b6b7c0_c1c2c3c4_c5c6c701_23456789_abcdeffe_
-                dcba9876_54321000_010001
+               73696d2e_666f6fa0_a1a2a3a4_a5a6a7b0_b1b2b3b4_
+               b5b6b7c0_c1c2c3c4_c5c6c701_23456789_abcdeffe_
+               dcba9876_54321000_010001
 Input was: 
    identity: (len=27)313234343037303130303030303030314065617073696d2e666f6f
    nonce_mt: 0123456789abcdeffedcba9876543210
@@ -54,10 +54,10 @@ mk:         e576d5ca_332e9930_018bf1ba_ee2763c7_95b3c712
 K_aut:      25af1942_efcbf4bc_72b39434_21f2a974
 K_encr:     536e5ebc_4465582a_a6a8ec99_86ebb620
 msk:        39d45aea_f4e30601_983e972b_6cfd46d1_c3637733
-            65690d09_cd44976b_525f47d3_a60a985e_955c53b0
-            90b2e4b7_3719196a_40254296_8fd14a88_8f46b9a7
-            886e4488
+           65690d09_cd44976b_525f47d3_a60a985e_955c53b0
+           90b2e4b7_3719196a_40254296_8fd14a88_8f46b9a7
+           886e4488
 emsk:       5949eab0_fff69d52_315c6c63_4fd14a7f_0d52023d
-            56f79698_fa6596ab_eed4f93f_bb48eb53_4d985414
-            ceed0d9a_8ed33c38_7c9dfdab_92ffbdf2_40fcecf6
-            5a2c93b9
+           56f79698_fa6596ab_eed4f93f_bb48eb53_4d985414
+           ceed0d9a_8ed33c38_7c9dfdab_92ffbdf2_40fcecf6
+           5a2c93b9
index fb5dafd..caed4d8 100644 (file)
@@ -115,13 +115,13 @@ mk:         85153a7d_7dfe0a4f_f3bf72f3_3521ff76_b048dbb2
 K_aut:      72cd7e8c_f2086e24_a98c1780_bc3d745b
 K_encr:     be789668_329769c3_73c0b64b_beffd665
 msk:        9be9fbc9_5415fa9e_f9d52563_bddd9758_65a3fadb
-            47a5815a_7310cf3f_10123d4e_ccaf9d4b_30b13c80
-            4dd130e5_117f35ae_a0e50b43_9a08b80d_dd15922c
-            f7fd9956
+           47a5815a_7310cf3f_10123d4e_ccaf9d4b_30b13c80
+           4dd130e5_117f35ae_a0e50b43_9a08b80d_dd15922c
+           f7fd9956
 emsk:       5dd5a779_65415b21_69aa1300_09dc6ba4_96433d1e
-            72065983_cbe8bc1d_6d744c99_dc76f16f_24324709
-            cb731af2_fbe69c6a_dd302662_a083d7e2_7c05c7cd
-            279c3f66
+           72065983_cbe8bc1d_6d744c99_dc76f16f_24324709
+           cb731af2_fbe69c6a_dd302662_a083d7e2_7c05c7cd
+           279c3f66
 MAC check succeed
 
 +++> About to send encoded packet:
index fb5dafd..caed4d8 100644 (file)
@@ -115,13 +115,13 @@ mk:         85153a7d_7dfe0a4f_f3bf72f3_3521ff76_b048dbb2
 K_aut:      72cd7e8c_f2086e24_a98c1780_bc3d745b
 K_encr:     be789668_329769c3_73c0b64b_beffd665
 msk:        9be9fbc9_5415fa9e_f9d52563_bddd9758_65a3fadb
-            47a5815a_7310cf3f_10123d4e_ccaf9d4b_30b13c80
-            4dd130e5_117f35ae_a0e50b43_9a08b80d_dd15922c
-            f7fd9956
+           47a5815a_7310cf3f_10123d4e_ccaf9d4b_30b13c80
+           4dd130e5_117f35ae_a0e50b43_9a08b80d_dd15922c
+           f7fd9956
 emsk:       5dd5a779_65415b21_69aa1300_09dc6ba4_96433d1e
-            72065983_cbe8bc1d_6d744c99_dc76f16f_24324709
-            cb731af2_fbe69c6a_dd302662_a083d7e2_7c05c7cd
-            279c3f66
+           72065983_cbe8bc1d_6d744c99_dc76f16f_24324709
+           cb731af2_fbe69c6a_dd302662_a083d7e2_7c05c7cd
+           279c3f66
 MAC check succeed
 
 +++> About to send encoded packet:
index d66babc..a0f0258 100644 (file)
@@ -246,9 +246,9 @@ log_auth_goodpass = no
 #
 usercollide = no
 
-# lower_user / lower_pass:  
+# lower_user / lower_pass:
 # Lower case the username/password "before" or "after"
-# attempting to authenticate.  
+# attempting to authenticate.
 #
 #  If "before", the server will first modify the request and then try
 #  to auth the user.  If "after", the server will first auth using the
@@ -324,7 +324,7 @@ security {
        #  Normally this should be set to "no", because they're useless.
        #  See: http://www.freeradius.org/rfc/rfc2865.html#Keep-Alives
        #
-       #  However, certain NAS boxes may require them. 
+       #  However, certain NAS boxes may require them.
        #
        #  When sent a Status-Server message, the server responds with
        #  and Access-Accept packet, containing a Reply-Message attribute,
@@ -356,7 +356,7 @@ $INCLUDE  ${confdir}/proxy.conf
 
 # CLIENTS CONFIGURATION
 #
-#  Client configuration is defined in "clients.conf".  
+#  Client configuration is defined in "clients.conf".
 #
 
 #  The 'clients.conf' file contains all of the information from the old
@@ -576,7 +576,7 @@ modules {
 
        #  Extensible Authentication Protocol
        #
-       #  For all EAP related authentications 
+       #  For all EAP related authentications
        eap {
                #  Invoke the default supported EAP type when
                #  EAP-Identity response is received.
@@ -597,8 +597,8 @@ modules {
                md5 {
                }
 
-               sim {
-               }
+               sim {
+               }
 
                # Cisco LEAP
                #
@@ -612,7 +612,7 @@ modules {
                leap {
                }
 
-               ## EAP-TLS is highly experimental EAP-Type at the moment.  
+               ## EAP-TLS is highly experimental EAP-Type at the moment.
                #       Please give feedback on the mailing list.
                #tls {
                #       private_key_password = password
@@ -665,7 +665,7 @@ modules {
                # to overwrite (or add) Auth-Type during
                # authorization. Normally should be MS-CHAP
                authtype = MS-CHAP
-               
+
                # if use_mppe is not set to no mschap will
                # add MS-CHAP-MPPE-Keys for MS-CHAPv1 and
                # MS-MPPE-Recv-Key/MS-MPPE-Send-Key for MS-CHAPv2
@@ -685,14 +685,14 @@ modules {
        #  This module definition allows you to use LDAP for
        #  authorization and authentication (Auth-Type := LDAP)
        #
-       #  See doc/rlm_ldap for description of configuration options 
-       #  and sample authorize{} and authenticate{} blocks 
+       #  See doc/rlm_ldap for description of configuration options
+       #  and sample authorize{} and authenticate{} blocks
        ldap {
                server = "ldap.your.domain"
                # identity = "cn=admin,o=My Org,c=UA"
                # password = mypass
                basedn = "o=My Org,c=UA"
-               filter = "(uid=%{Stripped-User-Name:-%{User-Name}})"
+               filter = "(uid=%{%{Stripped-User-Name}:-%{User-Name}})"
 
                # set this to 'yes' to use TLS encrypted connections
                # to the LDAP database by using the StartTLS extended
@@ -748,7 +748,7 @@ modules {
        #   ignore_nislike - ignore NIS-related records
        #   delimiter - symbol to use as a field separator in passwd file,
        #            for format ':' symbol is always used. '\0', '\n' are
-        #           not allowed 
+       #            not allowed
        #
 
        #  An example configuration for using /etc/smbpasswd.
@@ -806,7 +806,7 @@ modules {
                format = suffix
                delimiter = "%"
        }
-       
+
        # Preprocess the incoming RADIUS request, before handing it off
        # to other modules.
        #
@@ -957,7 +957,7 @@ modules {
                #  If we want to believe the 'utmp' file, then this
                #  configuration entry can be set to 'no'.
                #
-               check_with_nas = yes            
+               check_with_nas = yes
 
                # Set the file permissions, as the contents of this file
                # are usually private.
@@ -1117,8 +1117,8 @@ modules {
        #  If you wish to execute an external program in more than
        #  one section (e.g. 'authorize', 'pre_proxy', etc), then it
        #  is probably best to define a different instance of the
-       #  'exec' module for every section.     
-       #       
+       #  'exec' module for every section.
+       #
        exec echo {
                #
                #  Wait for the program to finish.
@@ -1273,7 +1273,7 @@ instantiate {
 #  The order of the realm modules will determine the order that
 #  we try to find a matching realm.
 #
-#  Make *sure* that 'preprocess' comes before any realm if you 
+#  Make *sure* that 'preprocess' comes before any realm if you
 #  need to setup hints for the remote radius server
 authorize {
        #
@@ -1286,7 +1286,7 @@ authorize {
        #
        #  It also adds a Client-IP-Address attribute to the request.
        preprocess
-       
+
        #
        #  The chap module will set 'Auth-Type := CHAP' if we are
        #  handling a CHAP request and Auth-Type has not already been set
@@ -1390,7 +1390,7 @@ authenticate {
        #  module checks the users password.  Note that packets
        #  containing CHAP-Password attributes CANNOT be authenticated
        #  against /etc/passwd!  See the FAQ for details.
-       #  
+       #
        unix
 
        # Uncomment it if you want to use ldap for authentication
@@ -1457,7 +1457,7 @@ accounting {
 }
 
 
-#  Session database, used for checking Simultaneous-Use. Either the radutmp 
+#  Session database, used for checking Simultaneous-Use. Either the radutmp
 #  or rlm_sql module can handle this.
 #  The rlm_sql module is *much* faster
 session {
index 9519065..7bcfc89 100644 (file)
        EAP-Sim-KC3 = 0xc0c1c2c3c4c5c6c7,
 
 1232420100000015        Auth-Type := EAP, Autz-Type:=EAP, EAP-Type := SIM
-        EAP-Sim-Rand1 = 0x30000000000000000000000000000000,
-        EAP-Sim-SRES1 = 0x30112233,
-        EAP-Sim-KC1 = 0x445566778899AABB,
-        EAP-Sim-Rand2 = 0x31000000000000000000000000000000,
-        EAP-Sim-SRES2 = 0x31112233,
-        EAP-Sim-KC2 = 0x445566778899AABB,
-        EAP-Sim-Rand3 = 0x32000000000000000000000000000000,
-        EAP-Sim-SRES3 = 0x32112233,
-        EAP-Sim-KC3 = 0x445566778899AABB,
+       EAP-Sim-Rand1 = 0x30000000000000000000000000000000,
+       EAP-Sim-SRES1 = 0x30112233,
+       EAP-Sim-KC1 = 0x445566778899AABB,
+       EAP-Sim-Rand2 = 0x31000000000000000000000000000000,
+       EAP-Sim-SRES2 = 0x31112233,
+       EAP-Sim-KC2 = 0x445566778899AABB,
+       EAP-Sim-Rand3 = 0x32000000000000000000000000000000,
+       EAP-Sim-SRES3 = 0x32112233,
+       EAP-Sim-KC3 = 0x445566778899AABB,
 
 eapsim         Auth-Type := EAP, Autz-Type:=EAP, EAP-Type := SIM
-        EAP-Sim-Rand1 = 0xabcd1234abcd1234abcd1234abcd1234,
-        EAP-Sim-SRES1 = 0x1234abcd,
-        EAP-Sim-KC1 = 0x0011223344556677,
-        EAP-Sim-Rand2 = 0xbcd1234abcd1234abcd1234abcd1234a,
-        EAP-Sim-SRES2 = 0x234abcd1,
-        EAP-Sim-KC2 = 0x1021324354657687,
-        EAP-Sim-Rand3 = 0xcd1234abcd1234abcd1234abcd1234ab,
-        EAP-Sim-SRES3 = 0x34abcd12,
-        EAP-Sim-KC3 = 0x30415263748596a7
+       EAP-Sim-Rand1 = 0xabcd1234abcd1234abcd1234abcd1234,
+       EAP-Sim-SRES1 = 0x1234abcd,
+       EAP-Sim-KC1 = 0x0011223344556677,
+       EAP-Sim-Rand2 = 0xbcd1234abcd1234abcd1234abcd1234a,
+       EAP-Sim-SRES2 = 0x234abcd1,
+       EAP-Sim-KC2 = 0x1021324354657687,
+       EAP-Sim-Rand3 = 0xcd1234abcd1234abcd1234abcd1234ab,
+       EAP-Sim-SRES3 = 0x34abcd12,
+       EAP-Sim-KC3 = 0x30415263748596a7
 
 
index fc386f1..7c6d97f 100644 (file)
@@ -115,13 +115,13 @@ mk:         2a56fd95_adac4bf7_645c2e60_7296a8af_9e1214a1
 K_aut:      2853a70a_4ca089cc_0cf8a24a_45ecec93
 K_encr:     77987afb_1cfd251d_749d2f16_0611338e
 msk:        e8adff17_1d82d5e6_9a78d526_1e86ee56_93cbe646
-            59332585_1f1f58f0_598c3a0c_1640339b_c3407fb4
-            56a14ada_a4791445_e8a3cf40_49b4628f_8e9f597a
-            7891e9d2
+           59332585_1f1f58f0_598c3a0c_1640339b_c3407fb4
+           56a14ada_a4791445_e8a3cf40_49b4628f_8e9f597a
+           7891e9d2
 emsk:       b33c4a19_c1df9108_17196271_7c4b7f98_e53a64ba
-            a67d4e23_5ff142cb_6e427434_8a71358a_3c2b1313
-            4cec6be3_a99e60c8_ae543fdd_52ecd7b3_0542e1df
-            5d10c5f7
+           a67d4e23_5ff142cb_6e427434_8a71358a_3c2b1313
+           4cec6be3_a99e60c8_ae543fdd_52ecd7b3_0542e1df
+           5d10c5f7
 MAC check succeed
 
 +++> About to send encoded packet:
index 48ea4e2..dacee1a 100644 (file)
@@ -22,4 +22,3 @@ modules.
 
 
    
-   
\ No newline at end of file
index 32dabe7..6597b00 100644 (file)
@@ -115,27 +115,27 @@ mk:         a444d7cc_dd514568_da171dd4_229ed4d1_a088c470
 K_aut:      a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 K_encr:     f544a796_43c4d95f_90aaa5b7_74267742
 msk:        8000f5e4_ed05a9bf_17b9ec6a_27f92d9d_f104966b
-            03689665_de45db49_82ecdcc4_85c26910_e886de4f
-            bdfa4218_b4ef2f64_319c9b41_b77b3c90_69d616f9
-            0781438a
+           03689665_de45db49_82ecdcc4_85c26910_e886de4f
+           bdfa4218_b4ef2f64_319c9b41_b77b3c90_69d616f9
+           0781438a
 emsk:       3c87c92f_44193e35_dd18e906_3d7cff8f_cb6d6002
-            bf233300_5df66776_70086929_f0d27970_3e59c480
-            675d6b45_0dc6a79a_51dc34b0_7091a5ff_8ca145ce
-            98accef2
+           bf233300_5df66776_70086929_f0d27970_3e59c480
+           675d6b45_0dc6a79a_51dc34b0_7091a5ff_8ca145ce
+           98accef2
 
 hmac-sha1 key(16): a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 DATA: (96)    01YY0050_120b0000_010d0000_30000000_00000000
-            00000000_00000000_31000000_00000000_00000000
-            00000000_32000000_00000000_00000000_00000000
-            0b050000_00000000_00000000_00000000_00000000
-            1b764ea5_668faa4b_0e7dd876_d25753f8
+           00000000_00000000_31000000_00000000_00000000
+           00000000_32000000_00000000_00000000_00000000
+           0b050000_00000000_00000000_00000000_00000000
+           1b764ea5_668faa4b_0e7dd876_d25753f8
 
 hmac-sha1 mac(20): XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX
 MAC check succeed
 
 hmac-sha1 key(16): a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 DATA: (40)    02YY001c_120b0000_0b050000_00000000_00000000
-            00000000_00000000_30112233_31112233_32112233
+           00000000_00000000_30112233_31112233_32112233
 
 hmac-sha1 mac(20): XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX
 
index 32dabe7..6597b00 100644 (file)
@@ -115,27 +115,27 @@ mk:         a444d7cc_dd514568_da171dd4_229ed4d1_a088c470
 K_aut:      a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 K_encr:     f544a796_43c4d95f_90aaa5b7_74267742
 msk:        8000f5e4_ed05a9bf_17b9ec6a_27f92d9d_f104966b
-            03689665_de45db49_82ecdcc4_85c26910_e886de4f
-            bdfa4218_b4ef2f64_319c9b41_b77b3c90_69d616f9
-            0781438a
+           03689665_de45db49_82ecdcc4_85c26910_e886de4f
+           bdfa4218_b4ef2f64_319c9b41_b77b3c90_69d616f9
+           0781438a
 emsk:       3c87c92f_44193e35_dd18e906_3d7cff8f_cb6d6002
-            bf233300_5df66776_70086929_f0d27970_3e59c480
-            675d6b45_0dc6a79a_51dc34b0_7091a5ff_8ca145ce
-            98accef2
+           bf233300_5df66776_70086929_f0d27970_3e59c480
+           675d6b45_0dc6a79a_51dc34b0_7091a5ff_8ca145ce
+           98accef2
 
 hmac-sha1 key(16): a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 DATA: (96)    01YY0050_120b0000_010d0000_30000000_00000000
-            00000000_00000000_31000000_00000000_00000000
-            00000000_32000000_00000000_00000000_00000000
-            0b050000_00000000_00000000_00000000_00000000
-            1b764ea5_668faa4b_0e7dd876_d25753f8
+           00000000_00000000_31000000_00000000_00000000
+           00000000_32000000_00000000_00000000_00000000
+           0b050000_00000000_00000000_00000000_00000000
+           1b764ea5_668faa4b_0e7dd876_d25753f8
 
 hmac-sha1 mac(20): XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX
 MAC check succeed
 
 hmac-sha1 key(16): a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 DATA: (40)    02YY001c_120b0000_0b050000_00000000_00000000
-            00000000_00000000_30112233_31112233_32112233
+           00000000_00000000_30112233_31112233_32112233
 
 hmac-sha1 mac(20): XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX_XXXXXXXX
 
index 85158b4..a2b0836 100644 (file)
@@ -115,27 +115,27 @@ mk:         a444d7cc_dd514568_da171dd4_229ed4d1_a088c470
 K_aut:      a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 K_encr:     f544a796_43c4d95f_90aaa5b7_74267742
 msk:        8000f5e4_ed05a9bf_17b9ec6a_27f92d9d_f104966b
-            03689665_de45db49_82ecdcc4_85c26910_e886de4f
-            bdfa4218_b4ef2f64_319c9b41_b77b3c90_69d616f9
-            0781438a
+           03689665_de45db49_82ecdcc4_85c26910_e886de4f
+           bdfa4218_b4ef2f64_319c9b41_b77b3c90_69d616f9
+           0781438a
 emsk:       3c87c92f_44193e35_dd18e906_3d7cff8f_cb6d6002
-            bf233300_5df66776_70086929_f0d27970_3e59c480
-            675d6b45_0dc6a79a_51dc34b0_7091a5ff_8ca145ce
-            98accef2
+           bf233300_5df66776_70086929_f0d27970_3e59c480
+           675d6b45_0dc6a79a_51dc34b0_7091a5ff_8ca145ce
+           98accef2
 
 hmac-sha1 key(16): a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 DATA: (96)    01280050_120b0000_010d0000_30000000_00000000
-            00000000_00000000_31000000_00000000_00000000
-            00000000_32000000_00000000_00000000_00000000
-            0b050000_00000000_00000000_00000000_00000000
-            1b764ea5_668faa4b_0e7dd876_d25753f8
+           00000000_00000000_31000000_00000000_00000000
+           00000000_32000000_00000000_00000000_00000000
+           0b050000_00000000_00000000_00000000_00000000
+           1b764ea5_668faa4b_0e7dd876_d25753f8
 
 hmac-sha1 mac(20): a91362ad_f370809a_c998c123_ebcb32bd_6a2915c2
 MAC check succeed
 
 hmac-sha1 key(16): a4c96a3c_1b4e1932_acc3878d_ecb5d9c6
 DATA: (40)    0228001c_120b0000_0b050000_00000000_00000000
-            00000000_00000000_30112233_31112233_32112233
+           00000000_00000000_30112233_31112233_32112233
 
 hmac-sha1 mac(20): 7a3818ad_17959b80_99cd84eb_64e45346_d63098e9
 
index 385208e..d93053f 100644 (file)
@@ -1,9 +1,9 @@
 Input was: |bd029bbe_7f51960b_cf9edb2b_61f06f0f_eb5a38b6|
 Output was: 2070b322_3dba372f_de1c0ffc_7b2e3b49_8b260614
-            3c6c18ba_cb0f6c55_babb1378_8e20d737_a3275116
-            c9ec5c2f_3261cba3_98384ecf_9189707c_20dbe3b6
-            8d6fc9d2_37313854_7338c3f5_7cf68f38_683aea5b
-            f9e60c0d_73b177bc_69edde1b_eb3f596a_9555fee9
-            0d570204_a3044bb5_a67f6509_25f14c1d_0446b252
-            78360140_28faffbf_49840408_ccb30408_00b40408
-            38faffbf_18ef0440_48ae1340_c0970040_48faffbf
+           3c6c18ba_cb0f6c55_babb1378_8e20d737_a3275116
+           c9ec5c2f_3261cba3_98384ecf_9189707c_20dbe3b6
+           8d6fc9d2_37313854_7338c3f5_7cf68f38_683aea5b
+           f9e60c0d_73b177bc_69edde1b_eb3f596a_9555fee9
+           0d570204_a3044bb5_a67f6509_25f14c1d_0446b252
+           78360140_28faffbf_49840408_ccb30408_00b40408
+           38faffbf_18ef0440_48ae1340_c0970040_48faffbf
diff --git a/src/tests/keywords/3gpp b/src/tests/keywords/3gpp
new file mode 100644 (file)
index 0000000..05e3fb2
--- /dev/null
@@ -0,0 +1,19 @@
+#
+#  PRE: update
+#
+update request {
+       3GPP-IMSI := "hello"
+}
+
+#
+#  "request:[0-9]" should be parsed as a list followed
+#  by an attribute.
+#
+update control {
+       Tmp-String-0 := "%{3GPP-IMSI}"
+       Tmp-String-1 := "%{request:3GPP-IMSI}"
+}
+
+update reply {
+       Filter-Id := "filter"
+}
\ No newline at end of file
index eecdd3d..85ccda5 100644 (file)
@@ -7,7 +7,7 @@
 #  The list is unordered.  The order is added in the next step by looking
 #  at precursors.
 #
-FILES := $(filter-out %.conf %.md %.attrs %.mk %~,$(subst $(DIR)/,,$(wildcard $(DIR)/*)))
+KEYWORD_FILES := $(filter-out %.conf %.md %.attrs %.mk %~ %.rej,$(subst $(DIR)/,,$(wildcard $(DIR)/*)))
 
 #
 #  Create the output directory
@@ -21,7 +21,7 @@ $(BUILD_DIR)/tests/keywords:
 #  strip out the ones which exist
 #  move the filenames to the build directory.
 #
-BOOTSTRAP_EXISTS := $(addprefix $(DIR)/,$(addsuffix .attrs,$(FILES)))
+BOOTSTRAP_EXISTS := $(addprefix $(DIR)/,$(addsuffix .attrs,$(KEYWORD_FILES)))
 BOOTSTRAP_NEEDS         := $(filter-out $(wildcard $(BOOTSTRAP_EXISTS)),$(BOOTSTRAP_EXISTS))
 BOOTSTRAP       := $(subst $(DIR),$(BUILD_DIR)/tests/keywords,$(BOOTSTRAP_NEEDS))
 
@@ -34,12 +34,17 @@ BOOTSTRAP_COPY       := $(subst $(DIR),$(BUILD_DIR)/tests/keywords,$(BOOTSTRAP_NEEDS)
 #
 -include $(BUILD_DIR)/tests/keywords/depends.mk
 
-$(BUILD_DIR)/tests/keywords/depends.mk: $(addprefix $(DIR)/,$(FILES)) | $(BUILD_DIR)/tests/keywords
+export OPENSSL_LIBS
+
+$(BUILD_DIR)/tests/keywords/depends.mk: $(addprefix $(DIR)/,$(KEYWORD_FILES)) | $(BUILD_DIR)/tests/keywords
        @rm -f $@
        @for x in $^; do \
                y=`grep 'PRE: ' $$x | sed 's/.*://;s/  / /g;s, , $(BUILD_DIR)/tests/keywords/,g'`; \
-               echo "$$x: $$y" >> $@; \
-               echo "" >> $@; \
+               if [ "$$y" != "" ]; then \
+                       z=`echo $$x | sed 's,src/,$(BUILD_DIR)/',`; \
+                       echo "$$z: $$y" >> $@; \
+                       echo "" >> $@; \
+               fi \
        done
 
 #
@@ -101,7 +106,7 @@ $(BUILD_DIR)/tests/keywords/%: $(DIR)/% $(BUILD_DIR)/tests/keywords/%.attrs $(TE
 #
 #  Get all of the unit test output files
 #
-TESTS.KEYWORDS_FILES := $(addprefix $(BUILD_DIR)/tests/keywords/,$(FILES))
+TESTS.KEYWORDS_FILES := $(addprefix $(BUILD_DIR)/tests/keywords/,$(KEYWORD_FILES))
 
 #
 #  Depend on the output files, and create the directory first.
diff --git a/src/tests/keywords/array b/src/tests/keywords/array
new file mode 100644 (file)
index 0000000..11f3c15
--- /dev/null
@@ -0,0 +1,52 @@
+#
+#  PRE: if
+#
+#  Tests for dereferencing the Nth attribute
+#
+update request {
+       Class := 0x01020304
+       Class += 0x05060708
+       Class += 0x090a0b0c
+}
+
+if (&Class != 0x01020304) {
+       update reply {
+               Filter-Id := "fail 0"
+       }
+}
+
+#  Must be the same as above
+if (&Class[0] != 0x01020304) {
+       update reply {
+               Filter-Id += "fail 0a"
+       }
+}
+
+if (&Class[1] != 0x05060708) {
+       update reply {
+               Filter-Id += "fail 1"
+       }
+}
+
+if (&Class[2] != 0x090a0b0c) {
+       update reply {
+               Filter-Id += "fail 2"
+       }
+}
+
+# must not exist
+if (&Class[3]) {
+       update reply {
+               Filter-Id += "fail 3"
+       }
+}
+
+#
+#  The test passes only if no test above
+#  added a Filter-Id
+#
+if (!reply:Filter-Id) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/keywords/base64 b/src/tests/keywords/base64
new file mode 100644 (file)
index 0000000..9661252
--- /dev/null
@@ -0,0 +1,141 @@
+#
+# PRE: hex
+#
+update reply {
+       Filter-Id := "filter"
+}
+
+update request {
+       Tmp-String-0        := '9870'
+       Tmp-Octets-0        := 0x39383731
+       Tmp-IP-Address-0    := 57.56.55.50
+       Tmp-Date-0          := 959985459
+       Tmp-Integer-0       := 959985460
+       Tmp-Cast-Abinary    := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812'
+       Tmp-Cast-IfId       := '0000:0000:3938:3737'
+       Tmp-Cast-IPv6Addr   := '::3938:3738'
+       Tmp-Cast-IPv6Prefix := '::3938:3739/128'
+       Tmp-Cast-Byte       := 58
+       Tmp-Cast-Short      := 14139
+       Tmp-Cast-Ethernet   := 00:00:39:38:37:3c
+       Tmp-Cast-Integer64  := 1152921505566832445
+       Tmp-Cast-IPv4Prefix := 57.56.55.62/32
+}
+
+update request {
+       Tmp-String-0        := "%{base64:&Tmp-String-0}"
+       Tmp-String-1        := "%{base64:&Tmp-Octets-0}"
+       Tmp-String-2        := "%{base64:&Tmp-IP-Address-0}"
+       Tmp-String-3        := "%{base64:&Tmp-Date-0}"
+       Tmp-String-4        := "%{base64:&Tmp-Integer-0}"
+       Tmp-String-5        := "%{base64:&Tmp-Cast-Abinary}"
+       Tmp-String-6        := "%{base64:&Tmp-Cast-Ifid}"
+       Tmp-String-7        := "%{base64:&Tmp-Cast-IPv6Addr}"
+       Tmp-String-8        := "%{base64:&Tmp-Cast-IPv6Prefix}"
+       Tmp-String-9        := "%{base64:&Tmp-Cast-Byte}"
+}
+
+# String - bin 0x39383730
+if (Tmp-String-0 != 'OTg3MA==') {
+       update reply {
+               Filter-Id += 'fail 0'
+       }
+}
+
+# Octets - bin 0x39383731
+if (Tmp-String-1 != 'OTg3MQ==') {
+       update reply {
+               Filter-Id += 'fail 1'
+       }
+}
+
+# IP Address - bin 0x39383732
+if (Tmp-String-2 != 'OTg3Mg==') {
+       update reply {
+               Filter-Id += 'fail 2'
+       }
+}
+
+# Date - bin 0x39383733
+if (Tmp-String-3 != 'OTg3Mw==') {
+       update reply {
+               Filter-Id += 'fail 3'
+       }
+}
+
+# Integer - bin 0x39383734
+if (Tmp-String-4 != 'OTg3NA==') {
+       update reply {
+               Filter-Id += 'fail 4'
+       }
+}
+
+# Abinary - bin 0x0101000039383735000000002000110000000714000200000000000000000000
+if (Tmp-String-5 != 'AQEAADk4NzUAAAAAIAARAAAABxQAAgAAAAAAAAAAAAA=') {
+       update reply {
+               Filter-Id += 'fail 5'
+       }
+}
+
+# ifid - bin 0x0000000039383737
+if (Tmp-String-6 != 'AAAAADk4Nzc=') {
+       update reply {
+               Filter-Id += 'fail 6'
+       }
+}
+
+# ipv6addr - bin 0x00000000000000000000000039383738
+if (Tmp-String-7 != 'AAAAAAAAAAAAAAAAOTg3OA==') {
+       update reply {
+               Filter-ID += 'fail 7'
+       }
+}
+
+# ipv6addrprefix - bin 0x008000000000000000000000000039383739
+if (Tmp-String-8 != 'AIAAAAAAAAAAAAAAAAA5ODc5') {
+       update reply {
+               Filter-ID += 'fail 8'
+       }
+}
+
+# byte - bin 0x3a
+if (Tmp-String-9 != 'Og==') {
+       update reply {
+               Filter-ID += 'fail 9'
+       }
+}
+
+update request {
+       Tmp-String-0        := "%{base64:&Tmp-Cast-Short}"
+       Tmp-String-1        := "%{base64:&Tmp-Cast-Ethernet}"
+       Tmp-String-2        := "%{base64:&Tmp-Cast-Integer64}"
+       Tmp-String-3        := "%{base64:&Tmp-Cast-IPv4Prefix}"
+}
+
+# short - bin 0x373b
+if (Tmp-String-0 != 'Nzs=') {
+       update reply {
+               Filter-ID += 'fail 9'
+       }
+}
+
+# ethernet - bin 0x00003938373c
+if (Tmp-String-1 != 'AAA5ODc8') {
+       update reply {
+               Filter-Id += 'fail 10'
+       }
+}
+
+# integer64 - bin 0x100000003938373d
+if (Tmp-String-2 != 'EAAAADk4Nz0=') {
+       update reply {
+               Filter-Id += 'fail 11'
+       }
+}
+
+# ipv4prefix - bin 0x00203938373e
+if (Tmp-String-3 != 'ACA5ODc+') {
+       update reply {
+               Filter-Id += 'fail 12'
+       }
+}
diff --git a/src/tests/keywords/cast-ipaddr b/src/tests/keywords/cast-ipaddr
new file mode 100644 (file)
index 0000000..4d5ff9f
--- /dev/null
@@ -0,0 +1,18 @@
+#
+#  PRE: update if
+#
+update {
+       control:Cleartext-Password := 'hello'
+       request:NAS-IP-Address := 127.0.0.1
+       request:Tmp-Integer-0 := 2130706433
+}
+
+update request {
+       Tmp-String-0 := "%{request:NAS-IP-Address}"
+}
+
+if (<ipaddr>Tmp-Integer-0 == NAS-IP-Address) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/keywords/escape b/src/tests/keywords/escape
new file mode 100644 (file)
index 0000000..dc328ad
--- /dev/null
@@ -0,0 +1,44 @@
+#
+#  PRE: update if xlat-attr-index
+#
+update request {
+       control:Cleartext-Password := 'hello'
+       Tmp-Octets-0 := 0x69206861766520736361727920656d626564646564207468696e67730020696e73696465206d65
+       Tmp-Octets-1 := 0x30783031013078303707307830410A307830440D222230786230b0C2b0
+       Tmp-String-0 := "i have scary embedded things\\000 inside me"
+       Tmp-String-0 += "0x01\0010x07\0070x0A\n0x0D\r\"\"0xb0\260°"
+       reply:Filter-Id := "filter"
+}
+
+if ("%{string:Tmp-Octets-0}" != 'i have scary embedded things\\000 inside me') {
+       update reply {
+               Filter-Id := 'fail'
+       }
+}
+
+if ("%{string:Tmp-Octets-1}" != '0x01\\0010x07\\0070x0A\\n0x0D\\r\\"\\"0xb0\\260°') {
+       update reply {
+               Filter-Id := 'fail'
+       }
+}
+
+if ("%{Tmp-String-0[0]}" != 'i have scary embedded things\\000 inside me') {
+       update reply {
+               Filter-Id := 'fail'
+       }
+}
+
+if ("%{Tmp-String-0[1]}" != '0x01\\0010x07\\0070x0A\\n0x0D\\r\\"\\"0xb0\\260°') {
+       update reply {
+               Filter-Id := 'fail'
+       }
+}
+
+# And another slightly different codepath...
+if ("%{Tmp-String-0[*]}" != 'i have scary embedded things\\000 inside me,0x01\\0010x07\\0070x0A\\n0x0D\\r\\"\\"0xb0\\260°') {
+       update reply {
+               Filter-Id := 'fail'
+       }
+}
+
+
index 8608c58..b6109a3 100644 (file)
@@ -3,7 +3,7 @@
 foreach Filter-Id {
        foreach Calling-Station-Id {
                update reply {
-                       Called-Station-Id += "%{Foreach-Variable-0} %{Foreach-Variable-1}"
+                       Called-Station-Id += "%{Foreach-Variable-0} %{Foreach-Variable-1}"
                }
        }
 }
index f3f839d..52d1f81 100644 (file)
@@ -7,19 +7,19 @@ Filter-Id = "1"
 Filter-Id += "2"
 Filter-Id += "3"
 Filter-Id += "4"
-Calling-Station-Id = "foo"
+Calling-Station-Id = "foo\n"
 Calling-Station-Id += "bar"
 
 #
 #  Expected answer
 #
 Response-Packet-Type == Access-Accept
-Called-Station-Id == '1 foo'
+Called-Station-Id == '1 foo\n'
 Called-Station-Id == '1 bar'
-Called-Station-Id == '2 foo'
+Called-Station-Id == '2 foo\n'
 Called-Station-Id == '2 bar'
-Called-Station-Id == '3 foo'
+Called-Station-Id == '3 foo\n'
 Called-Station-Id == '3 bar'
-Called-Station-Id == '4 foo'
+Called-Station-Id == '4 foo\n'
 Called-Station-Id == '4 bar'
 
index 8f2726a..1bb2361 100644 (file)
@@ -21,3 +21,45 @@ foreach Filter-Id {
               Called-Station-Id += "%{Foreach-Variable-0}"
        }
 }
+
+
+#
+# Adding attribute during request and immediately breaking
+#
+update {
+       request:Filter-Id += "1"
+       request:Filter-Id += "2"
+}
+
+foreach &request:Reply-Message {
+       if("%{Foreach-Variable-0}" == "1") {
+               update {
+                       request:Filter-Id += "3"
+               }
+               break
+       }
+}
+
+update {
+       request:Filter-Id !* ANY
+}
+
+#
+# Adding attribute during request and continuing
+#
+update {
+       request:Filter-Id += "1"
+       request:Filter-Id += "2"
+}
+
+foreach &request:Reply-Message {
+       if("%{Foreach-Variable-0}" == "1") {
+               update {
+                       request:Filter-Id += "3"
+               }
+       }
+
+       if ("%{Foreach-Variable-0}" == "3") {
+               break
+       }
+}
diff --git a/src/tests/keywords/foreach-error b/src/tests/keywords/foreach-error
new file mode 100644 (file)
index 0000000..fb4a236
--- /dev/null
@@ -0,0 +1,5 @@
+foreach "%{expr:1 + 2}" {      # ERROR
+       update reply {
+              Called-Station-Id += "%{Foreach-Variable-0}"
+       }
+}
diff --git a/src/tests/keywords/foreach-regex b/src/tests/keywords/foreach-regex
new file mode 100644 (file)
index 0000000..dab57a3
--- /dev/null
@@ -0,0 +1,26 @@
+# PRE: foreach if-regex-match
+
+# This is what most people end up using foreach for,
+# so we should probably test it works.
+update request {
+       Tmp-String-0 := "cisco"
+}
+
+# Expanded regex
+foreach Cisco-AVPair {
+       if ("%{Foreach-Variable-0}"  =~ /^%{Tmp-String-0}=(.*)$/i) {
+               update reply {
+                       Called-Station-Id += "%{1}"
+               }
+       }
+}
+
+# Compiled regex
+foreach Cisco-AVPair {
+       if ("%{Foreach-Variable-0}"  =~ /^stupid=(.*)$/i) {
+               update reply {
+                       Called-Station-Id += "%{1}"
+               }
+       }
+}
+
diff --git a/src/tests/keywords/foreach-regex.attrs b/src/tests/keywords/foreach-regex.attrs
new file mode 100644 (file)
index 0000000..79996c7
--- /dev/null
@@ -0,0 +1,16 @@
+#
+#  Input packet
+#
+User-Name = "bob"
+User-Password = "hello"
+Cisco-AVPair = "stupid=1"
+Cisco-AVPair += "retarded=2"
+Cisco-AVPair += "cisco=3"
+Cisco-AVPair += "shit=4"
+
+#
+#  Expected answer
+#
+Response-Packet-Type == Access-Accept
+Called-Station-Id == "3"
+Called-Station-Id == "1"
diff --git a/src/tests/keywords/hex b/src/tests/keywords/hex
new file mode 100644 (file)
index 0000000..7c4f9a4
--- /dev/null
@@ -0,0 +1,141 @@
+#
+# PRE: update
+#
+update reply {
+       Filter-Id := "filter"
+}
+
+update request {
+       Tmp-String-0        := '9870'
+       Tmp-Octets-0        := 0x39383731
+       Tmp-IP-Address-0    := 57.56.55.50
+       Tmp-Date-0          := 959985459
+       Tmp-Integer-0       := 959985460
+       Tmp-Cast-Abinary    := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812'
+       Tmp-Cast-IfId       := '0000:0000:3938:3737'
+       Tmp-Cast-IPv6Addr   := '::3938:3738'
+       Tmp-Cast-IPv6Prefix := '::3938:3739/128'
+       Tmp-Cast-Byte       := 58
+       Tmp-Cast-Short      := 14139
+       Tmp-Cast-Ethernet   := 00:00:39:38:37:3c
+       Tmp-Cast-Integer64  := 1152921505566832445
+       Tmp-Cast-IPv4Prefix := 57.56.55.62/32
+}
+
+update request {
+       Tmp-String-0        := "%{hex:Tmp-String-0}"
+       Tmp-String-1        := "%{hex:Tmp-Octets-0}"
+       Tmp-String-2        := "%{hex:Tmp-IP-Address-0}"
+       Tmp-String-3        := "%{hex:Tmp-Date-0}"
+       Tmp-String-4        := "%{hex:Tmp-Integer-0}"
+       Tmp-String-5        := "%{hex:Tmp-Cast-Abinary}"
+       Tmp-String-6        := "%{hex:Tmp-Cast-Ifid}"
+       Tmp-String-7        := "%{hex:Tmp-Cast-IPv6Addr}"
+       Tmp-String-8        := "%{hex:Tmp-Cast-IPv6Prefix}"
+       Tmp-String-9        := "%{hex:Tmp-Cast-Byte}"
+}
+
+# String
+if (Tmp-String-0 != '39383730') {
+       update reply {
+               Filter-Id += 'fail 1'
+       }
+}
+
+# Octets
+if (Tmp-String-1 != '39383731') {
+       update reply {
+               Filter-Id += 'fail 2'
+       }
+}
+
+# IP Address
+if (Tmp-String-2 != '39383732') {
+       update reply {
+               Filter-Id += 'fail 3'
+       }
+}
+
+# Date
+if (Tmp-String-3 != '39383733') {
+       update reply {
+               Filter-Id += 'fail 4'
+       }
+}
+
+# Integer
+if (Tmp-String-4 != '39383734') {
+       update reply {
+               Filter-Id += 'fail 5'
+       }
+}
+
+# Abinary
+if (Tmp-String-5 != '0101000039383735000000002000110000000714000200000000000000000000') {
+       update reply {
+               Filter-Id += 'fail 6'
+       }
+}
+
+# ifid
+if (Tmp-String-6 != '0000000039383737') {
+       update reply {
+               Filter-Id += 'fail 7'
+       }
+}
+
+# ipv6addr
+if (Tmp-String-7 != '00000000000000000000000039383738') {
+       update reply {
+               Filter-ID += 'fail 8'
+       }
+}
+
+# ipv6addrprefix
+if (Tmp-String-8 != '008000000000000000000000000039383739') {
+       update reply {
+               Filter-ID += 'fail 9'
+       }
+}
+
+# byte
+if (Tmp-String-9 != '3a') {
+       update reply {
+               Filter-ID += 'fail 10'
+       }
+}
+
+update request {
+       Tmp-String-0        := "%{hex:Tmp-Cast-Short}"
+       Tmp-String-1        := "%{hex:Tmp-Cast-Ethernet}"
+       Tmp-String-2        := "%{hex:Tmp-Cast-Integer64}"
+       Tmp-String-3        := "%{hex:Tmp-Cast-IPv4Prefix}"
+}
+
+# short
+if (Tmp-String-0 != '373b') {
+       update reply {
+               Filter-ID += 'fail 11'
+       }
+}
+
+# ethernet
+if (Tmp-String-1 != '00003938373c') {
+       update reply {
+               Filter-Id += 'fail 12'
+       }
+}
+
+# integer64
+if (Tmp-String-2 != '100000003938373d') {
+       update reply {
+               Filter-Id += 'fail 13'
+       }
+}
+
+# ipv4prefix
+if (Tmp-String-3 != '00203938373e') {
+       update reply {
+               Filter-Id += 'fail 14'
+       }
+}
index e366afa..2b2b11b 100644 (file)
@@ -1,9 +1,96 @@
 # PRE: if
 #
-# May as well exercise the regular expression engine 
-if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+
+# Non matching on attribute ref
+if (User-Name !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) {
+       update reply {
+               Filter-Id += 'Fail 0'
+       }
+}
+
+# Matching on xlat expanded value
+if ("%{User-Name}" !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) {
+       update reply {
+               Filter-Id += 'Fail 1'
+       }
+}
+
+# Matching on attribute ref with capture groups
+if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])%{Tmp-String-0}/) {
+       # Test all the capture groups
        update {
                reply:User-Name := "%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}"
        }
 }
+else {
+       update reply {
+               Filter-Id += 'Fail 2'
+       }
+}
+
+# Checking capture groups are cleared out correctly
+if (User-Name =~ /^([0-9])_%{Tmp-String-0}/) {
+       if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1_1') {
+               update reply {
+                       Filter-Id += 'Fail 3'
+               }
+       }
+}
+else {
+       update reply {
+               Filter-Id += 'Fail 3.5'
+       }
+}
+
+# Checking capture groups are cleared out correctly when there are no matches
+if (User-Name =~ /^.%{Tmp-String-0}/) {
+       if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1') {
+               update reply {
+                       Filter-Id += 'Fail 4'
+               }
+       }
+}
+else {
+       update reply {
+               Filter-Id += 'Fail 4.5'
+       }
+}
+
+# uncompiled - ref - insensitive
+if (Calling-Station-Id !~ /:roamyroam%{Tmp-String-0}$/i) {
+       update reply {
+               Filter-Id += 'Fail 5'
+       }
+}
+
+# uncompiled - expansion - insensitive
+if ("%{Calling-Station-Id}" !~ /:roamyroam%{Tmp-String-0}$/i) {
+       update reply {
+               Filter-Id += 'Fail 6'
+       }
+}
+
+# uncompiled - enum - ref - insensitive
+if (Service-Type !~ /^framed-user%{Tmp-String-0}$/i) {
+       update reply {
+               Filter-Id += 'Fail 7'
+       }
+}
+
+# uncompiled - enum - expansion - insensitive
+if ("%{Service-Type}" !~ /^framed-user%{Tmp-String-0}$/i) {
+       update reply {
+               Filter-Id += 'Fail 8'
+       }
+}
+
+# uncompiled - enum - ref
+if (Service-Type =~ /^framed-user%{Tmp-String-0}$/) {
+       update reply {
+               Filter-Id += 'Fail 9'
+       }
+}
+
+
+
 
diff --git a/src/tests/keywords/if-regex-match-comp b/src/tests/keywords/if-regex-match-comp
new file mode 100644 (file)
index 0000000..8d68142
--- /dev/null
@@ -0,0 +1,94 @@
+# PRE: if
+#
+
+# Non matching on attribute ref
+if (User-Name !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+       update reply {
+               Filter-Id += 'Fail 0'
+       }
+}
+
+# Matching on xlat expanded value
+if ("%{User-Name}" !~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+       update reply {
+               Filter-Id += 'Fail 1'
+       }
+}
+
+# Matching on attribute ref with capture groups
+if (User-Name =~ /^([0-9])_([0-9])?_([0-9]*)_([0-9]+)_([^_])_(6)_([7-8])/) {
+       # Test all the capture groups
+       update {
+               reply:User-Name := "%{7}_%{6}_%{5}_%{4}_%{3}_%{2}_%{1}_%{0}"
+       }
+}
+else {
+       update reply {
+               Filter-Id += 'Fail 2'
+       }
+}
+
+# Checking capture groups are cleared out correctly
+if (User-Name =~ /^([0-9])_/) {
+       if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1_1') {
+               update reply {
+                       Filter-Id += 'Fail 3'
+               }
+       }
+}
+else {
+       update reply {
+               Filter-Id += 'Fail 3.5'
+       }
+}
+
+# Checking capture groups are cleared out correctly when there are no matches
+if (User-Name =~ /^./) {
+       if ("%{0}%{1}%{2}%{3}%{4}%{5}%{6}%{7}" != '1') {
+               update reply {
+                       Filter-Id += 'Fail 4'
+               }
+       }
+}
+else {
+       update reply {
+               Filter-Id += 'Fail 4.5'
+       }
+}
+
+# compiled - ref - insensitive
+if (Calling-Station-Id !~ /:roamyroam$/i) {
+       update reply {
+               Filter-Id += 'Fail 5'
+       }
+}
+
+# compiled - expansion - insensitive
+if ("%{Calling-Station-Id}" !~ /:roamyroam$/i) {
+       update reply {
+               Filter-Id += 'Fail 6'
+       }
+}
+
+# compiled - enum - ref - insensitive
+if (Service-Type !~ /^framed-user$/i) {
+       update reply {
+               Filter-Id += 'Fail 7'
+       }
+}
+
+# compiled - enum - expansion - insensitive
+if ("%{Service-Type}" !~ /^framed-user$/i) {
+       update reply {
+               Filter-Id += 'Fail 8'
+       }
+}
+
+# compiled - enum - ref
+if (Service-Type =~ /^framed-user$/) {
+       update reply {
+               Filter-Id += 'Fail 9'
+       }
+}
+
+
diff --git a/src/tests/keywords/if-regex-match-comp.attrs b/src/tests/keywords/if-regex-match-comp.attrs
new file mode 100644 (file)
index 0000000..ba7188d
--- /dev/null
@@ -0,0 +1,7 @@
+User-Name = '1_2_3_4_5_6_7'
+User-Password = 'hello'
+Service-Type := 'Framed-User'
+Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM'
+
+Response-Packet-Type == Access-Accept
+User-Name == '7_6_5_4_3_2_1_1_2_3_4_5_6_7'
index ab03050..ba7188d 100644 (file)
@@ -1,5 +1,7 @@
 User-Name = '1_2_3_4_5_6_7'
 User-Password = 'hello'
+Service-Type := 'Framed-User'
+Calling-Station-ID := '00:11:22:33:44:55:66:ROAMYROAM'
 
 Response-Packet-Type == Access-Accept
 User-Name == '7_6_5_4_3_2_1_1_2_3_4_5_6_7'
index 691561e..0e74f22 100644 (file)
@@ -3,11 +3,38 @@
 #  Conditions which statically evaluate to "false"
 #  have their entire contents skipped on load.
 #
+#  Conditions which statically evaluate to "true"
+#  have the following "else" sections skipped, too.
+#
 #  i.e. we can reference things which don't exist,
 #  and they'll get ignored.
 #
 if (0) {
-   no-such-module
+       no-such-module
+}
+
+if (0) {
+       no-such-module
+}
+else {
+       ok
+}
+
+if (1) {
+       ok
+}
+else {
+       no-such-module
+}
+
+if (1) {
+       ok
+}
+elsif ("%{foo:bar}") { # no pass2
+       no-such-module
+}
+else {
+       no-such-module
 }
 
 update reply {
diff --git a/src/tests/keywords/integer b/src/tests/keywords/integer
new file mode 100644 (file)
index 0000000..32b9c74
--- /dev/null
@@ -0,0 +1,197 @@
+#
+# PRE: update
+#
+update reply {
+       Filter-Id := "filter"
+}
+
+update request {
+       Tmp-String-0        := '9870'
+       Tmp-String-1        := '98709870'
+       Tmp-String-2        := '987098709870'
+       Tmp-Octets-0        := 0x39383731
+       Tmp-Octets-1        := 0x3938373139383731
+       Tmp-Octets-2        := 0x393837313938373139383731
+       Tmp-IP-Address-0    := 57.56.55.50
+       Tmp-Date-0          := 959985459
+       Tmp-Integer-0       := 959985460
+       Tmp-Cast-Abinary    := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812'
+       Tmp-Cast-IfId       := '0000:0000:3938:3737'
+       Tmp-Cast-IPv6Addr   := '::3938:3738'
+       Tmp-Cast-IPv6Prefix := '::3938:3739/128'
+       Tmp-Cast-Byte       := 58
+       Tmp-Cast-Short      := 14139
+       Tmp-Cast-Ethernet   := 00:00:39:38:37:3c
+       Tmp-Cast-Integer64  := 1152921505566832445
+       Tmp-Cast-IPv4Prefix := 57.56.55.62/32
+}
+
+update request {
+       Tmp-String-2        := "%{integer:Tmp-IP-Address-0}"
+       Tmp-String-3        := "%{integer:Tmp-Date-0}"
+       Tmp-String-4        := "%{integer:Tmp-Integer-0}"
+       Tmp-String-5        := "%{integer:Tmp-Cast-Abinary}"
+       Tmp-String-6        := "%{integer:Tmp-Cast-Ifid}"
+       Tmp-String-7        := "%{integer:Tmp-Cast-IPv6Addr}"
+       Tmp-String-8        := "%{integer:Tmp-Cast-IPv6Prefix}"
+}
+
+# String - network order representation of a 4 char string
+update request {
+       Tmp-Integer-0 := "%{integer:Tmp-String-0}"
+}
+if ((Tmp-String-0 != "%{string:Tmp-Integer-0}") || (Tmp-Integer-0 != 959985456)) {
+       update reply {
+               Filter-Id += 'fail 1'
+       }
+}
+
+# String - network order representation of a 8 char string
+update request {
+       Tmp-Integer64-0 := "%{integer:Tmp-String-1}"
+}
+if ((Tmp-String-1 != "%{string:Tmp-Integer64-0}") || (Tmp-Integer64-0 != 4123106139115632432)) {
+       update reply {
+               Filter-Id += 'fail 2'
+       }
+}
+
+# String - Can't convert 12 byte string to integer (our biggest native size is a 64bit unsigned int)
+if ("%{integer:Tmp-String-2}" != '') {
+       update reply {
+               Filter-Id += 'fail 3'
+       }
+}
+
+# Octets - network order representation of a 4 byte octet string
+update request {
+       Tmp-Integer-0 := "%{integer:Tmp-Octets-0}"
+}
+if ((Tmp-Octets-0 != "0x%{hex:Tmp-Integer-0}") || (Tmp-Integer-0 != 959985457)) {
+       update reply {
+               Filter-Id += 'fail 4'
+       }
+}
+
+# Octets - network order representation of a 8 byte octet string
+update request {
+       Tmp-Integer64-0 := "%{integer:Tmp-Octets-1}"
+}
+if ((Tmp-Octets-1 != "0x%{hex:Tmp-Integer64-0}") || (Tmp-Integer64-0 != 4123106143410599729)) {
+       update reply {
+               Filter-Id += 'fail 5'
+       }
+}
+
+# String - Can't convert 12 byte octet string to integer (our biggest native size is a 64bit unsigned int)
+if ("%{integer:Tmp-Octets-2}" != '') {
+       update reply {
+               Filter-Id += 'fail 6'
+       }
+}
+
+# IP Address
+if (Tmp-String-2 != '959985458') {
+       update reply {
+               Filter-Id += 'fail 7'
+       }
+}
+
+if (<ipaddr>Tmp-String-2 != &Tmp-IP-Address-0) {
+       update reply {
+               Filter-Id += 'fail 8'
+       }
+}
+
+# Date
+if (Tmp-String-3 != '959985459') {
+       update reply {
+               Filter-Id += 'fail 9'
+       }
+}
+
+# Integer
+if (Tmp-String-4 != '959985460') {
+       update reply {
+               Filter-Id += 'fail 10'
+       }
+}
+
+# Abinary - Can't convert ascend binary to an integer
+if (Tmp-String-5 != '') {
+       update reply {
+               Filter-Id += 'fail 11'
+       }
+}
+
+# ifid - Can't convert interface ID to an integer
+if (Tmp-String-6 != '') {
+       update reply {
+               Filter-Id += 'fail 12'
+       }
+}
+
+# ipv6addr - Can't convert IPv6 to integer
+if (Tmp-String-7 != '959985464') {
+       update reply {
+               Filter-ID += 'fail 13'
+       }
+}
+
+# ipv6addrprefix
+if (Tmp-String-8 != '959985465') {
+       update reply {
+               Filter-ID += 'fail 14'
+       }
+}
+update request {
+       Tmp-String-0        := "%{integer:Tmp-Cast-Byte}"
+       Tmp-String-1        := "%{integer:Tmp-Cast-Short}"
+       Tmp-String-2        := "%{integer:Tmp-Cast-Ethernet}"
+       Tmp-String-3        := "%{integer:Tmp-Cast-Integer64}"
+       Tmp-String-4        := "%{integer:Tmp-Cast-IPv4Prefix}"
+}
+
+# byte
+if (Tmp-String-0 != '58') {
+       update reply {
+               Filter-ID += 'fail 15'
+       }
+}
+
+# short
+if (Tmp-String-1 != '14139') {
+       update reply {
+               Filter-ID += 'fail 16'
+       }
+}
+
+# ethernet
+if (Tmp-String-2 != '62913607630848') {
+       update reply {
+               Filter-Id += 'fail 17'
+       }
+}
+if (<ether>Tmp-String-2 != &Tmp-Cast-Ethernet) {
+       update reply {
+               Filter-Id += 'fail 18'
+       }
+}
+
+# integer64
+if (Tmp-String-3 != '1152921505566832445') {
+       update reply {
+               Filter-Id += 'fail 19'
+       }
+}
+
+# ipv4prefix
+if (Tmp-String-4 != '959985470') {
+       update reply {
+               Filter-Id += 'fail 20'
+       }
+}
+
+
+
+
diff --git a/src/tests/keywords/ipprefix b/src/tests/keywords/ipprefix
new file mode 100644 (file)
index 0000000..4d62663
--- /dev/null
@@ -0,0 +1,39 @@
+#
+#  PRE: update if
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+update reply {
+       Filter-Id := "filter"
+}
+
+update control {
+       Tmp-Cast-IPv4Prefix := 198.51.100.255/16
+       Tmp-Cast-IPv6Prefix := ::198.51.100.255/112
+}
+
+if ("%{control:Tmp-Cast-IPv6Prefix}" != '::198.51.0.0/112') {
+       update reply {
+               Filter-Id += "wrong"
+       }
+}
+
+if ("%{control:Tmp-Cast-IPv4Prefix}" != '198.51.0.0/16') {
+       update reply {
+               Filter-Id += "wrong"
+       }
+}
+
+if (control:Tmp-Cast-IPv6Prefix != ::198.51.0.0/112) {
+       update reply {
+               Filter-Id += "wrong"
+       }
+}
+
+if (control:Tmp-Cast-IPv4Prefix != 198.51.0.0/16) {
+       update reply {
+               Filter-Id += "wrong"
+       }
+}
diff --git a/src/tests/keywords/length b/src/tests/keywords/length
new file mode 100644 (file)
index 0000000..95e2971
--- /dev/null
@@ -0,0 +1,155 @@
+#
+# PRE: hex
+#
+update reply {
+       Filter-Id := "filter"
+}
+
+update request {
+       Tmp-String-0        := '\
+abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\
+abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz\
+abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz'
+       Tmp-String-2        := '9870'
+       Tmp-Octets-0        := 0x39383731
+       Tmp-IP-Address-0    := 57.56.55.50
+       Tmp-Date-0          := 959985459
+       Tmp-Integer-0       := 959985460
+       Tmp-Cast-Abinary    := 'ip out forward srcip 57.56.55.53/32 udp dstport = 1812'
+       Tmp-Cast-IfId       := '0000:0000:3938:3737'
+       Tmp-Cast-IPv6Addr   := '::3938:3738'
+       Tmp-Cast-IPv6Prefix := '::3938:3739/128'
+       Tmp-Cast-Byte       := 58
+       Tmp-Cast-Short      := 14139
+       Tmp-Cast-Ethernet   := 00:00:39:38:37:3c
+       Tmp-Cast-Integer64  := 1152921505566832445
+       Tmp-Cast-IPv4Prefix := 57.56.55.62/32
+}
+
+update request {
+       Tmp-Integer-0        := "%{length:Tmp-String-0}"
+}
+
+if (Tmp-Integer-0 != 260) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+update request {
+       Tmp-Integer-0        := "%{length:Tmp-String-2}"
+       Tmp-Integer-1        := "%{length:Tmp-Octets-0}"
+       Tmp-Integer-2        := "%{length:Tmp-IP-Address-0}"
+       Tmp-Integer-3        := "%{length:Tmp-Date-0}"
+       Tmp-Integer-4        := "%{length:Tmp-Integer-0}"
+       Tmp-Integer-5        := "%{length:Tmp-Cast-Abinary}"
+       Tmp-Integer-6        := "%{length:Tmp-Cast-Ifid}"
+       Tmp-Integer-7        := "%{length:Tmp-Cast-IPv6Addr}"
+       Tmp-Integer-8        := "%{length:Tmp-Cast-IPv6Prefix}"
+       Tmp-Integer-9        := "%{length:Tmp-Cast-Byte}"
+}
+
+# String - bin 0x39383730
+if (Tmp-Integer-0 != 4) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# Octets - bin 0x39383731
+if (Tmp-Integer-1 != 4) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# IP Address - bin 0x39383732
+if (Tmp-Integer-2 != 4) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# Date - bin 0x39383733
+if (Tmp-Integer-3 != 4) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# Integer - bin 0x39383734
+if (Tmp-Integer-4 != 4) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# Abinary - bin 0x0101000039383735000000002000110000000714000200000000000000000000
+if (Tmp-Integer-5 != 32) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# ifid - bin 0x0000000039383737
+if (Tmp-Integer-6 != 8) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# ipv6addr - bin 0x00000000000000000000000039383738
+if (Tmp-Integer-7 != 16) {
+       update reply {
+               Filter-ID += 'fail'
+       }
+}
+
+# ipv6addrprefix - bin 0x008000000000000000000000000039383739
+if (Tmp-Integer-8 != 18) {
+       update reply {
+               Filter-ID += 'fail'
+       }
+}
+
+# byte - bin 0x3a
+if (Tmp-Integer-9 != 1) {
+       update reply {
+               Filter-ID += 'fail'
+       }
+}
+
+update request {
+       Tmp-Integer-0        := "%{length:Tmp-Cast-Short}"
+       Tmp-Integer-1        := "%{length:Tmp-Cast-Ethernet}"
+       Tmp-Integer-2        := "%{length:Tmp-Cast-Integer64}"
+       Tmp-Integer-3        := "%{length:Tmp-Cast-IPv4Prefix}"
+}
+
+# short - bin 0x373b
+if (Tmp-Integer-0 != 2) {
+       update reply {
+               Filter-ID += 'fail'
+       }
+}
+
+# ethernet - bin 0x00003938373c
+if (Tmp-Integer-1 != 6) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# integer64 - bin 0x100000003938373d
+if (Tmp-Integer-2 != 8) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# ipv4prefix - bin 0x00203938373e
+if (Tmp-Integer-3 != 6) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
index 9711c56..7c3de2b 100644 (file)
@@ -1,17 +1,50 @@
 #
 # PRE: update if
 #
+update reply {
+       Filter-Id := "filter"
+}
+
 update {
        control:Cleartext-Password := 'hello'
-       request:Called-Station-Id := "%{md5:This is a string\n}"
+       request:Tmp-String-0 := "This is a string\n"
+       request:Tmp-Octets-0 := 0x000504030201
 }
 
 #
 #  Put "This is a string" into a file and call "md5sum" on it.
 #  You should get this string.
 #
-if (Called-Station-Id == "9ac4dbbc3c0ad2429e61d0df5dc28add") {
+if ("%{md5:This is a string\n}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{md5:&Tmp-String-0}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{md5:&request:Tmp-String-0}" != '9ac4dbbc3c0ad2429e61d0df5dc28add') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# This differs than hashing the attribute directly, as the \n is escaped and becomes chars \ and n
+if ("%{md5:%{request:Tmp-String-0}}" != 'b41832e1bd19528e1a09495922182579') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+#
+#  MD5 should also be able to cope with references to octet attributes
+#
+if ("%{md5:&request:Tmp-Octets-0}" != 'c1e7fa505b2fc1fd0da6cac3db6f6f44') {
        update reply {
-               Filter-Id := "filter"
+               Filter-Id += 'fail'
        }
 }
index ffc3a2b..39fe06f 100644 (file)
@@ -5,7 +5,13 @@
 raddb          = raddb
 keyword                = src/tests/keywords
 
-modconfdir = ${raddb}/mods-config
+modconfdir     = ${raddb}/mods-config
+
+#  Only for testing!
+#  Setting this on a production system is a BAD IDEA.
+security {
+       allow_vulnerable_openssl = yes
+}
 
 modules {
        $INCLUDE ${raddb}/mods-enabled/always
@@ -31,6 +37,6 @@ server default {
        }
 
        authenticate {
-               pap
+               pap
        }
 }
diff --git a/src/tests/keywords/regex-error b/src/tests/keywords/regex-error
new file mode 100644 (file)
index 0000000..f618e82
--- /dev/null
@@ -0,0 +1,12 @@
+#
+# PRE: if-regex-match
+#
+
+#
+#  Check that bad regular expressions will fail
+#
+if (&User-Name =~ /[a-3]/) {   # ERROR
+       update reply {
+               Filter-Id := "filter"
+       }
+}
index d4e34d5..37e0f3c 100644 (file)
@@ -3,15 +3,48 @@
 #
 update {
        control:Cleartext-Password := 'hello'
-       request:Called-Station-Id := "%{sha1:This is a string\n}"
+       request:Tmp-String-0 := "This is a string\n"
+       request:Tmp-Octets-0 := 0x000504030201
+}
+
+update reply {
+       Filter-Id := 'filter'
 }
 
 #
 #  Put "This is a string" into a file and call "sha1sum" on it.
 #  You should get this string.
 #
-if (Called-Station-Id == "cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c") {
+if ("%{sha1:This is a string\n}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{sha1:&Tmp-String-0}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{sha1:&request:Tmp-String-0}" != 'cc7edf1ccc4bdf1e0ec8f72b95388b65218ecf0c') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# This differs than hashing the attribute directly, as the \n is escaped and becomes chars \ and n
+if ("%{sha1:%{Tmp-String-0}}" != 'd79c6dae2d4b3f4d78d0c044b38e23f52f376726') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+#
+#  SHA1 should also be able to cope with references to octet attributes
+#
+if ("%{sha1:&request:Tmp-Octets-0}" != '365b244645fe7294dff062174996572319d5a82c') {
        update reply {
-               Filter-Id := "filter"
+               Filter-Id += 'fail'
        }
 }
diff --git a/src/tests/keywords/sha2 b/src/tests/keywords/sha2
new file mode 100644 (file)
index 0000000..7c57e63
--- /dev/null
@@ -0,0 +1,82 @@
+#
+# PRE: update if
+#
+if ("$ENV{OPENSSL_LIBS}" != "") {
+
+update {
+       control:Cleartext-Password := 'hello'
+       request:Tmp-String-0 := "This is a string\n"
+       request:Tmp-Octets-0 := 0x000504030201
+}
+
+update reply {
+       Filter-Id := 'filter'
+}
+
+#
+#  Put "This is a string" into a file and call "sha256sum" on it.
+#  You should get this string.
+#
+if ("%{sha256:This is a string\n}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{sha256:&Tmp-String-0}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{sha256:&request:Tmp-String-0}" != 'b3716a1ab53042bb392034f29071e13b0c38aa19b4edd75d9a76022f91189124') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# This differs than hashing the attribute directly, as the \n is escaped and becomes chars \ and n
+if ("%{sha256:%{Tmp-String-0}}" != 'd1a16b1ef3a074dcd82cbce40590838e4627f5da371618e4d7f9de3786600bb9') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+#
+#  SHA256 should also be able to cope with references to octet attributes
+#
+if ("%{sha256:&request:Tmp-Octets-0}" != 'f307e202b881fded70e58017aa0c4d7b29c76ab25d02bf078301a5f6635187eb') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+#
+#  SHA512 and SHA256 share common code paths, so the tests don't need to be
+#  as exhaustive.
+#
+if ("%{sha512:This is a string\n}" != '56b57df5cce42d4e35c644649798ea23ec16f4f4626e78faf4d2d8f430ea349bcc28cd5532457c82f0aa66bf68988346039fe75b900a92ff94fd53993d45990f') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{sha512:&Tmp-String-0}" != '56b57df5cce42d4e35c644649798ea23ec16f4f4626e78faf4d2d8f430ea349bcc28cd5532457c82f0aa66bf68988346039fe75b900a92ff94fd53993d45990f') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+if ("%{sha512:&request:Tmp-Octets-0}" != 'de80271eb5e03a1c24dd0cd823a22305a743ee3a54f1de5bf97adbf56984561154bfb6928b1da4ccc3f5dde9f4032ad461937b60b9ace4ad3898cf45c90596d7') {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+}
+
+else { # no OPENSSL.  Force the test to pass
+       update reply {
+               Filter-Id := 'filter'
+       }
+}
index 7ad884c..f64aeaf 100644 (file)
@@ -1,4 +1,4 @@
-switch User-Name {
+switch &User-Name {
        case "bob" {
                update reply {
                        Filter-Id := "filter"
@@ -16,5 +16,4 @@ switch User-Name {
                        Filter-Id := "default"
                }
        }
-
-}
\ No newline at end of file
+}
diff --git a/src/tests/keywords/switch-attr-cast b/src/tests/keywords/switch-attr-cast
new file mode 100644 (file)
index 0000000..e271a18
--- /dev/null
@@ -0,0 +1,34 @@
+#
+#  PRE: switch switch-attr-cmp
+#
+
+update request {
+       Service-Type := Login-User
+       Filter-Id := "Login-User"
+}
+
+switch &Service-Type {
+       case "%{expr: 1 + 2}" {
+               update reply {
+                       Filter-Id := "3"
+               }
+       }
+
+       #
+       #  The Filter-Id will get printed to a string,
+       #  have the string parsed as a Service-Type attr,
+       #  and then that compared to the input Service-Type
+       #
+       case &Filter-Id {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "default"
+               }
+       }
+
+}
diff --git a/src/tests/keywords/switch-attr-cmp b/src/tests/keywords/switch-attr-cmp
new file mode 100644 (file)
index 0000000..c5d3b73
--- /dev/null
@@ -0,0 +1,30 @@
+#
+#  PRE: switch
+#
+update request {
+       Tmp-String-0 := &User-Name
+}
+
+#
+#  A switch statement where we compare two attributes
+#
+switch &User-Name {
+       case &Tmp-String-0 {
+               update reply {
+                       Filter-Id := "filter"
+               }
+       }
+
+       case "doug" {
+               update reply {
+                       Filter-Id := "doug"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "default"
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/src/tests/keywords/switch-value-error b/src/tests/keywords/switch-value-error
new file mode 100644 (file)
index 0000000..18db9e1
--- /dev/null
@@ -0,0 +1,29 @@
+#
+#  PRE: switch
+#
+switch &Service-Type {
+       case "%{expr: 1 + 2}" {
+               update reply {
+                       Filter-Id := "3"
+               }
+       }
+
+       case Login-User {
+               update reply {
+                       Filter-Id := "Login-User"
+               }
+       }
+
+       case No-Such-Value {    # ERROR
+               update reply {
+                       Filter-Id := "FAILED"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "default"
+               }
+       }
+
+}
diff --git a/src/tests/keywords/switch-value-error2 b/src/tests/keywords/switch-value-error2
new file mode 100644 (file)
index 0000000..a291e7b
--- /dev/null
@@ -0,0 +1,27 @@
+#
+#  PRE: switch-value-error
+#
+#  The same as "switch-value-error", but the attribute
+#  is hidden inside of an xlat expansion.  We now turn
+#  simple attribute xlats into templates.
+#
+switch "%{Service-Type}" {     # ERROR
+       case "%{expr: 1 + 2}" {
+               update reply {
+                       Filter-Id := "3"
+               }
+       }
+
+       case Login-User {
+               update reply {
+                       Filter-Id := "Login-User"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "default"
+               }
+       }
+
+}
diff --git a/src/tests/keywords/switch-xlat-error b/src/tests/keywords/switch-xlat-error
new file mode 100644 (file)
index 0000000..d03f6d3
--- /dev/null
@@ -0,0 +1,17 @@
+#
+#  PRE: switch
+#
+switch &User-Name {
+       case "%{no-such-module:bob}" {  # ERROR
+               update reply {
+                       Filter-Id := "fail"
+               }
+       }
+
+       case {
+               update reply {
+                       Filter-Id := "default"
+               }
+       }
+
+}
diff --git a/src/tests/keywords/truncation b/src/tests/keywords/truncation
new file mode 100644 (file)
index 0000000..f7b9430
--- /dev/null
@@ -0,0 +1,106 @@
+# cat /dev/urandom | env LC_CTYPE=C tr -cd 'a-f0-9' | head -c <n>
+
+update reply {
+       Filter-Id := "filter"
+}
+
+# 8192 - 0x (2) - '' (2) there are unlikely to be any static buffers this big outside of the conffile parser
+update request {
+       Tmp-Octets-0 := '0x\
+d8abccb7834711af1de1812be2579febe946f5d7beef6fa5c7074c0cb917e9b91e23e14b016f27610097c16c0e0fad88176e077b24198c770746159\
+05b8810d1c8b774d98889fa5c6027cde5e9c56dd4f7c48298c7713aeca5ba5dcfd506032ad05b1396f50e825b633d5a6af0dce6181b640287e03a65\
+8734df46a86341556f28455b3f377313a5a2ac8c8267b8a5de559b95f9b493a68b9e0278485f9e3914d702b2b7b90ee85ff393461f197386d09b836\
+5a8ae85ea025aea095f5d834c2ddf21e9a16b945da03c97f8a52687f19c605af9a245878b4bc5ed15b7937371ad629081159cf7de613d02c43f5cab\
+3529abbe61a6da1e55c685c2310c8eccb452f9758bf63fddfa58cdffb5cbf912f90e628310978dd1b3b7c2d3a08dd7ca6ca51081a114b013cc9d4c1\
+9f5ce0b1af81166c9402c105019a0fa9d15996c4f3d5fa35226bf88166598ff4f619322866df276d8b92fad06d120470c29217d29abb96ff861751e\
+acebbe839e28287c8cd485769d9a09e5bab524bde5776e15037d19503a2d032658e21aec7090032707ba0d662fcfa99e5e5ec183f8c63c6022bf281\
+22cf090fb80263f70c523aa0e00dd9f610c5b669754092253edd6f83b57bdd7a251514cdd99ab86cfa98b11469d1d1118aba0a149db6f0ccceb2f17\
+06bf33f657c7b8547dc284dfc10fdc1678c184901aa17f756d18df43409e1c4ead239cb78c46a9c412ccb097bba2244bb680bb249c15004d796731f\
+3890c1f94d8e0eda81487da9a65f817b5dbac4e639fce063f61a7db87d8b9d4b855120f56c3a0d57cb61ced57092506da881337280a18b2729717f5\
+133978362ca7941f9dbe9462ae5a46886c4d3c51b633278d6ad58a331c95e481cbbae866f046cb1a59ae2545fa32b12d4772244fd24bb91f7611d0c\
+beffbc5dee309c671fb5a171eacf45b20641e1cb4db306965eb43e98cb900ee805065c7adf4db3cdb97bbf8116dbb29f5ac07c7041a5f02d1a4fd1f\
+a7be6da388146d6b80ae8e152ac873cbf1f88c98cdd2e1de02df76461a1c50139f558f43ac1a91824fc48ea35976fab29d412f1ad990e528893ad4c\
+b6045b63d2a7af0657f1c348302e674ef0b39bd689b3b33dd3664c3e2386d0ea3241e70d2a2990b6243638d68033cc6040758e11f82ef783db2ae94\
+d2a8dbccd25fe2dce554837a2ca424199e4bd5442215623a77bffd603b575c847f88763fff1d6d0ff314851b1af06f99fb44d0a3115ad4389462452\
+c2331be24d486f6b5f2224cdfe44909e0a3bec47cdf6fe49fa60518e37950f986677e7f45617adcb20bc02990d3fe60de54ad2d1950bdd423e752c4\
+60f4eeaf2eeece07f3dea26db63cc0bc222a6f3228131cdb3360b9ec7e8f861c3e0bc822f9da00fef17f44ab4de7c166e5cb07d5c671f0364d4bccb\
+dc7f24868e066998bc8b3ab9052171b967162d536082b0a11c24f263c0e5f656b8349f970124af712cf098e8b08910aefcf7d42dd8a74e84ae6f9c4\
+006aa09483715b514833291c9d2e2bcdda567a16a493ac4fa17d5dff7a04b9f7f967febe7a8bdf911b1b668716be73fbf6f2416609224494dca1a18\
+1c1c7bb298671d39bdc1f664c833c9d2d4c08a65866db885929611fcd0c33139bc2959a865cbb686694896b539daa1db265ba78298cb3a09a331ef0\
+1423a7fd04b38ccdfbf1182a493c7a53c720a2dcf486249ff2b674341e77c2c0d5eb78efc07f295d562a77667457df58ea419c488680b9ce51aac01\
+4cd78984f70f29aec2a9b77fd45d38e0b07257b71f2146ccb11cd07681cbfff787b39b7ce9c42ce60513eccec2b490c68378e86bd441735b30410be\
+8710e71b2a326bd8b929abde80f25a0adb312f8ed5bb748866ddd8fb5fa81855d194afbdb1d511be453e21ab3f6481e47fa86b0697342706f85b8e8\
+84c6bf427390ef4c62532797de25004bb9d293e196ed29950e14682c68a8b6ab7c03256bc493b61eb3e44dc16726fcedb6c6776e2e9e0b3450fe6c6\
+24eef42e4806ef7c8abc367b7f1c83b7955ff6579d03362b428ca7711d228cf88402b60fd162ef4819216b9db66b4a1b0a5e0651a7901290eafa3d5\
+c698e40f73b3d66345d9e328d50ef2c767d370879890980d0d989de122ab22d7ef9979832b022e676eba4630d49434ec70025b44cb7146f9688d102\
+ceb55e6ca8ee9b9a66327da80f46367f452de7e2a84bf702b155539c7fa488d86c83faf3f150fda6d75eb476e2c31d73cea88148d9f02d178b0c098\
+5b4061830231ca405fbf41547ed9c036f53fc706b0fd1bb084e84a9e6dec81b4b4eea46c5ea26e99993ee930ccfeff4a185ffbce8a937243fa4734b\
+c24abbf465b0cd1f87686d44762140b5dfe57ae904cd8ed121f055d8a1bbf9c8a977133215f135e563b935451a7f797e72ef8f96390bc2477ad7aac\
+d2679557bac3b9a030926ff0801cc692dd641cf4f8a7bd9bdee21cdab3f7fbcf7d1d0de51101c5d816b19db49109958a8e75662ac700653112cd86c\
+10cbf45d3ea16f861de2d46714dfba8a34c1ad7ed4d5484802270187c3c7f70f7a51858479d8504e3b6267194404011b416329401ec55940f890f33\
+97d4bb3eab46990a4a03cdae9951ccd0bd42584ec88262f7d80193f09843c1fca7627b9cf708c646e84883b19fe3a8987912d04f1c0d1c779b740a7\
+fea0270374416a3987dd91d706e22e8c9698ef870d7f80d1c3042914a34736c185e542976d0145531c8eb20c3a6c5a5f747e97228ff55425fd93222\
+2d802ca36ac61555f69be4d645532579ddadb24a821f0bab1fb1f3b724ed4759b5303fe0015626ad00879e1a363d5e8b3aa0b7f3e572df3dc5f7352\
+7eb30b78e70dbe6ca81052315925d3892485168dd6f900289d1a0f9285e52988480bfda6a2c8b28cbd175b4dec735240c8f79afd5c93ed058da5551\
+8f20fb09edf4b4112c352604876f4083b68e534874d0b9ce2f977be2bcea0f9c123e119a70d269bc26ece608c39c98dbb069118148b01e403c16076\
+330b5d7929a7a7d724a7030d514ff0e38bee78e0044ffa508fc9793d7fc59075fd825e68d34e58568bffd8b20791862a8d0686763e49f1e3ed8a728\
+e33bb32a57c6374d153f93ba3cd283ce0af9a0a3b0ced4f83e3d604ff66f6584a35b51938ff4a0282c51f6f8561bd77b842ed0fd39fc43be825652f\
+f2c021f2b4d2b8c8ed2cf8eeb16f86863e59b374eda5a17e1bfc0b5a54169dd3e62b84a81bc1afd5cc3a1b193484428bcc3afb5344da990697f6787\
+5f72bc1ab04f52f75bc671b8d1239fc811d44032822abc95f8ea3465cdabdce5f83d5895d50fcbd97ad88e6b193172a4cf5f51814d9348498b1736e\
+de25f8b5afc8556d3b9573cb2ae8f51e8f0bca2f048d883592c9ab317e87864d48ca901e2aa2774360c77ba3f0a76c21f10155e32e91a2239d00e6b\
+0279bbc8977fadbea04b7b67575437fd5f6611a8889ecbde5fe45fcab8db9b62e831aadcd0ade54a94d30b6963d727979c116ef9f4a9a16a0e7a76e\
+d4f1177ad0ac7022beb12581629a7fd36c8ca5eeaa40d1a0d88cb0f701b288baae6c00a74c0b0dc3ffa6e9710bf3264684c1b9ff220710b2348cc93\
+d658d9eddae36232a0d0e06439d5dafdc7271eafbb3d16a17e36de7470c7a5ed034d735e2c216073ba6b4be7b319718d5afbd634684f5468d26afd0\
+63edfbc7fe6c8e98914eff397fcfdf44a10b84ba90b30d1a5da6823aab1792d666b32bdd0516ce3590c5dc2b49a6eabdb28cdc72dbc24e60ba0883f\
+953f0a99d29c1089b06063588b8bd965ba42e27f618d2b046fb3db93161981bbcd67fc965b0084ac25993445732b52d18cd2868e6b545214a2d501a\
+0002d2043d9a9790905dde296f16517e295dd295a97991c205d50dba331e86f06b0195e52fdd21f8e65e6b00fe7165e1a951805e5947aaee125f180\
+a2127375d8898aba1af3a3db34470e78bed99917a6bb87725788c1e2c8f35d6022d51bafdc3e69673cbdd80dc9cfde7dc1bd06e3a6437c720cb7b6c\
+2b3bc808111593d5f2a88c6e5b499c51155f4ff0267606abcbfa03dc5e4c108e7f3d33989952df2f4f1476df8fe53eaa39d2338a87b6ccc91587a67\
+bc0da2f5667248ce969b91f1521c5bfda97c8f062b62d768a9d302f4a4bd0e67646c6666a9fea93749a65d06fdcd0168fe81edac542fc287503a592\
+61591a686b418ac2602e2464f6679ef7ecf5387af45e5fdd72224695e454d6676687b03a5858afe71dda7180a59ef2e1d8c81c4a9c8375e62c43711\
+cbd0e7d437a5913bc4d7e8fbb71f68e4805ad41bd624c06e84761dbda3014c1576a866c073ca32b37007884730ac9f831b95db8ce10f0db45e009d0\
+6e1ad8c96c340c98debee0dd0ca9e6694fe7a69306c15e6a7054cfd612e070f538c6ff0c1a34b257df10087b650da852a5b663a5795a335193dfe64\
+1950ea614ae05b0c202cedabd1ebe6da561e167fe81dd9fc190740a20db38f5356e61f8bed7b3db783a6b53e48f408a8b08f9f79738438a3ed0c4bc\
+f444d5922c76ff03c943a3c72c513047aa75caf51381b5c306fdbaedcd4001a2df456c3afef7559241f553dffac2dfd9ac94570e50c571329203bf0\
+38cbdac9bc76f896635356c402f2f5c105eb4e334f7792716e8e8883352870e949623f29bb8fac428bc858ec3297166afe358e1947b0c2337309315\
+062a040129f978b036657e9323550765cc4c14aaa4c24572eb17a0aa940ba336fd8fd385fb46b5b0c067c59ee42922d2702feb7b43d2ffef11cbdf0\
+1f11c92ac18197a387f0633fd19509b19c0b8a2f63983f6c0a6286593c84d5866ae8d91139b141e8bdf3b7ae7df7d92754186e545f4b600fbe69494\
+3ae4bcef324b9a3f47ba5c835b54a010ca42f3b9cc5368278c148c9b02ea8c4c9f244fd49fa69c9a5feac5376e07f52e64be9c873afca3ebf776fe9\
+fc813bd2e58c6d0dfd951570dbff4b5e73ce547cd100ab320b6944e887d611b3425bfcdf3bfa852ff5804119a1f69e33d624eda6f9e5d1fb2811f9f\
+9f5b3224d009e09621a8fcb524f89c6fcf38f7c933aa027de2b48aa9ddbfa55f56b383f06b8feef09e4709bc4fdcbab659bab4a277a2fbebb98970e\
+44bd68a61b264c695d8ae27a676e123b5fb60c7c4cbc88f24b6554dcfebfde607500f50ec85f589eaa4213ccaa237598660a66009bc56d55455487a\
+28fa24d62e50df57feab0d1c8ec77b1085002d922c52d4a3092f8693c3b9ba9725a4d225637350812b534e93209b414b4516642eca67490199a9217\
+614322972fa2fcc5c7fc2695e9b5762d442e2c7dd8403d14228aa90df58e2ffe1a3c9922f6c5d62649664b63c017fbf3859723e7e97ea6710097683\
+6408a97de3ad7d902c7be0296fb3d476de2460602f65eb7edacc2e4f49b1652fe8948f7afb1cc9f83f3283e6013304160cd2ac7c311c492133252a6\
+5ccf45c5af9c05a47905ac8bfe55f9b912c3fca856abe7863f87392b6f6b0069e3d8412a056b1034aaac506bf2ca51760aa180c5f43a751beb06e88\
+fc6afab4c5dd4aae4a2e6f0293c15278d557c2925acba90c73eeea09ebb95f0d469aa77ae983a0e69dacd55bd8a7e78a41df5227de35af05127fa3b\
+a02f4a3ab98d75992d68a15d393387fe9ef01041569570ad6fe884764e55567311bcacfcffae76554dcfebfde607500f50ec85f589eaade607500f5\
+3ae4bcef324b9a3f47ba5c835b54a010ca42f3b9cc5368278c148c9b02ea8c4c9f244fd49fab'
+}
+
+# Actual length of octet string is 4084 bytes
+update request {
+       Tmp-Integer-0 := "%{length:Tmp-Octets-0}"
+}
+
+if (Tmp-Integer-0 != 4084) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# Octets are expanded to 8168 hexits
+if ("%{Tmp-Octets-0}" !~ /^[0-9a-f]{8168}$/) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# Octets are expanded to 8168 hexits
+if (Tmp-Octets-0 !~ /^[0-9a-f]{8168}$/) {
+       update reply {
+               Filter-Id += 'fail'
+       }
+}
+
+# We can't do any more until all the xlat code uses dynamically allocated buffers
diff --git a/src/tests/keywords/update-array b/src/tests/keywords/update-array
new file mode 100644 (file)
index 0000000..c872f4e
--- /dev/null
@@ -0,0 +1,63 @@
+#
+#  PRE: update array
+#
+
+update request {
+       Class := 0x01020304
+       Class += 0x05060708
+       Class += 0x090a0b0c
+}
+
+
+#
+#  Use array references in the RHS
+#  of the update section
+#
+
+update request {
+       Proxy-State += &Class[0]
+       Proxy-State += &Class[1]
+       Proxy-State += &Class[2]
+}
+
+if (&Proxy-State != 0x01020304) {
+       update reply {
+               Filter-Id := "fail 0"
+       }
+}
+
+#  Must be the same as above
+if (&Proxy-State[0] != 0x01020304) {
+       update reply {
+               Filter-Id += "fail 0a"
+       }
+}
+
+if (&Proxy-State[1] != 0x05060708) {
+       update reply {
+               Filter-Id += "fail 1"
+       }
+}
+
+if (&Proxy-State[2] != 0x090a0b0c) {
+       update reply {
+               Filter-Id += "fail 2"
+       }
+}
+
+# must not exist
+if (&Proxy-State[3]) {
+       update reply {
+               Filter-Id += "fail 3"
+       }
+}
+
+#
+#  The test passes only if no test above
+#  added a Filter-Id
+#
+if (!reply:Filter-Id) {
+       update reply {
+               Filter-Id := "filter"
+       }
+}
\ No newline at end of file
diff --git a/src/tests/keywords/update-exec b/src/tests/keywords/update-exec
new file mode 100644 (file)
index 0000000..b9a0f73
--- /dev/null
@@ -0,0 +1,94 @@
+#
+#  PRE: update if redundant
+#
+update control {
+       Cleartext-Password := 'hello'
+}
+
+update reply {
+       Filter-Id := "filter"
+}
+
+#
+# Exec with script output to attribute
+#
+update request {
+       Tmp-String-0 = `/bin/sh -c "echo 'foo bar baz'"`
+}
+
+if (Tmp-String-0 != "foo bar baz") {
+       update reply {
+               Filter-Id += "fail 1"
+       }
+}
+
+#
+# Exec with output to list (single attribute)
+#
+update {
+       request: = `/bin/sh -c "echo Tmp-String-0 := foo"`
+}
+
+if (Tmp-String-0 != 'foo') {
+       update reply {
+               Filter-Id += "fail 2"
+       }
+}
+
+#
+# Exec with output to list (multiple attributes)
+#
+update {
+       request: = `/bin/sh -c 'echo Tmp-String-0 := foo, Tmp-String-1 := bar'`
+}
+
+if ((Tmp-String-0 != 'foo') || (Tmp-String-1 != 'bar')) {
+       update reply {
+               Filter-Id += "fail 3"
+       }
+}
+
+#
+# Failed exec (malformed attributes) - check no attributes are added
+#
+update request {
+       Tmp-String-0 !* ANY
+       Tmp-String-1 !* ANY
+}
+
+redundant {
+       group {
+               update {
+                       request: = `/bin/sh -c 'echo Tmp-String-0 := foo, Tmp-String-1 ?= bar'`
+               }
+       }
+       ok
+}
+if (Tmp-String-0 || Tmp-String-1) {
+       update reply {
+               Filter-Id += "fail 4"
+       }
+}
+
+#
+# Exec with output to list - error code
+#
+update request {
+       Tmp-String-0 !* ANY
+       Tmp-String-1 !* ANY
+}
+
+redundant {
+       group {
+               update {
+                       request: = `/bin/sh -c 'echo Tmp-String-0 := foo; exit 64'`
+               }
+       }
+       ok
+}
+if (Tmp-String-0) {
+       update reply {
+               Filter-Id += "fail 5"
+       }
+}
+
diff --git a/src/tests/keywords/update-list-error b/src/tests/keywords/update-list-error
new file mode 100644 (file)
index 0000000..2613055
--- /dev/null
@@ -0,0 +1,19 @@
+#
+# PRE: update
+#
+#  It's an error to update lists that don't exist.
+#
+update {
+       request := reply
+       config += request
+       reply !* ANY
+}
+
+update {
+       reply += `/path/to/foo bar baz`
+}
+
+
+update {
+       request := nope         # ERROR
+}
diff --git a/src/tests/keywords/update-operator b/src/tests/keywords/update-operator
new file mode 100644 (file)
index 0000000..31a2bdd
--- /dev/null
@@ -0,0 +1,62 @@
+#
+#  PRE: update
+#
+update request {
+       NAS-Port := 1000
+}
+
+#
+#  Filtering
+#
+update request {
+       NAS-Port == 1000
+}
+
+if (NAS-Port != 1000) {
+       update reply {
+               Filter-Id += "fail 1"
+       }
+}
+
+update request {
+       NAS-Port <= 500
+}
+
+if (NAS-Port != 500) {
+       update reply {
+               Filter-Id += "fail 2"
+       }
+}
+
+update request {
+       NAS-Port >= 2000
+}
+
+if (NAS-Port != 2000) {
+       update reply {
+               Filter-Id += "fail 3"
+       }
+}
+
+# non-existent attribute
+update request {
+       Class -= 0xabcdef
+}
+
+update request {
+       Class -= &Class
+}
+
+update request {
+       NAS-Port -= &NAS-Port
+}
+
+if (!reply:Filter-Id) {
+       update control {
+               Cleartext-Password := 'hello'
+       }
+
+       update reply {
+               Filter-Id := "filter"
+       }
+}
diff --git a/src/tests/keywords/update-remove-any b/src/tests/keywords/update-remove-any
new file mode 100644 (file)
index 0000000..e0ef600
--- /dev/null
@@ -0,0 +1,50 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update {
+       request:Tmp-String-0 := 'foobarbaz'
+       request:Tmp-Integer-0 := 123456789
+       request:Tmp-IP-Address-0 := 192.0.2.1
+       request:Tmp-IP-Address-0 += 192.0.2.2
+       control:Tmp-IP-Address-0 := 192.0.2.1
+       control:Tmp-IP-Address-0 += 192.0.2.3
+}
+
+if (("%{Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{Tmp-IP-Address-0[1]}" != 192.0.2.2)) {
+       update {
+               reply:Filter-Id := 'fail 1'
+       }
+}
+
+# Remove all attributes in the control list
+update {
+       request:Tmp-IP-Address-0 !* ANY
+}
+
+# Non Tmp-IP-Address-0 address attributes should still be in the request list
+if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789)) {
+       update reply {
+               reply:Filter-Id += 'fail 2'
+       }
+}
+
+# There should be no Tmp-IP-Address attributes in the request list
+if (Tmp-IP-Address-0 || ("%{Tmp-IP-Address-0[1]}" != '')) {
+       update {
+               reply:Filter-Id += 'fail 3'
+       }
+}
+
+# But there should still be some in the control list
+if ((control:Tmp-IP-Address-0 != 192.0.2.1) || ("%{control:Tmp-IP-Address-0[1]}" != 192.0.2.3)) {
+       update {
+               reply:Filter-Id += 'fail 4'
+       }
+}
diff --git a/src/tests/keywords/update-remove-index b/src/tests/keywords/update-remove-index
new file mode 100644 (file)
index 0000000..58df9a5
--- /dev/null
@@ -0,0 +1,100 @@
+#
+# PRE: update update-remove-value
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update {
+       request:Tmp-String-0 := 'foobarbaz'
+       request:Tmp-Integer-0 := 123456789
+       request:Tmp-IP-Address-0 := 192.0.2.1
+       request:Tmp-IP-Address-0 += 192.0.2.2
+       request:Tmp-IP-Address-0 += 192.0.2.3
+       request:Tmp-IP-Address-0 += 192.0.2.2
+       request:Tmp-IP-Address-0 += 192.0.2.4
+}
+
+
+update request {
+       Tmp-IP-Address-0[3] -= 192.0.2.2
+}
+
+# Only the 1st, 2nd, 3rd and 5th Tmp-IP-Address attributes should still be in the list
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.1') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[2]}" != '192.0.2.3') || \
+    ("%{Tmp-IP-Address-0[3]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[4]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 1'
+       }
+}
+
+# There's still a 192.0.2.2 but it's not at index 3
+update request {
+       Tmp-IP-Address-0[3] -= 192.0.2.2
+}
+
+# Should be the same as the previous result
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.1') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[2]}" != '192.0.2.3') || \
+    ("%{Tmp-IP-Address-0[3]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[4]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 2'
+       }
+}
+
+# Remove whatever's at index 0
+update request {
+       Tmp-IP-Address-0[0] !* ANY
+}
+
+# IP address at index 0 should be removed
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.3') || \
+    ("%{Tmp-IP-Address-0[2]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[3]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 3'
+       }
+}
+
+# Remove whatever's at index 3 (should be nothing)
+update request {
+       Tmp-IP-Address-0[3] !* ANY
+}
+
+# Should be the same as the previous result
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.3') || \
+    ("%{Tmp-IP-Address-0[2]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[3]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 4'
+       }
+}
+
+# Remove all instances of Tmp-IP-Address
+update request {
+       Tmp-IP-Address-0 !* ANY
+}
+
+# No more IP address attributes!
+if ("%{Tmp-IP-Address-0[0]}" != '') {
+       update reply {
+               Filter-Id += 'fail 5'
+       }
+}
+
+# Non Tmp-IP-Address-0 address attributes should still be in the request list
+if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789)) {
+       update reply {
+               Filter-Id += 'fail 6'
+       }
+}
diff --git a/src/tests/keywords/update-remove-list b/src/tests/keywords/update-remove-list
new file mode 100644 (file)
index 0000000..22ae577
--- /dev/null
@@ -0,0 +1,40 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update request {
+       Tmp-String-0 := 'foobarbaz'
+       Tmp-Integer-0 := 123456789
+       Tmp-IP-Address-0 := 192.0.2.1
+}
+
+if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789) || (Tmp-IP-Address-0 != 192.0.2.1)) {
+       update reply {
+               Filter-Id += 'fail 0'
+       }
+}
+
+# Remove all attributes in the control list
+update {
+       request: !* ANY
+}
+
+# All attributes should now of been removed
+if ((Tmp-String-0 && (Tmp-String-0 == 'foobarbaz')) || \
+    (Tmp-Integer-0 && (Tmp-Integer-0 == 123456789)) || \
+    (Tmp-IP-Address-0 && (Tmp-IP-Address-0 == 192.0.2.1))) {
+       update reply {
+               Filter-Id := 'fail 1'
+       }
+}
+
+# This will of been removed too
+update request {
+       User-Password := 'hello'
+}
diff --git a/src/tests/keywords/update-remove-tag b/src/tests/keywords/update-remove-tag
new file mode 100644 (file)
index 0000000..3328789
--- /dev/null
@@ -0,0 +1,275 @@
+#
+# PRE: update update-remove-value update-remove-index update-tag
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update {
+       request:Tunnel-Server-Endpoint += '192.0.1.1'
+       request:Tunnel-Server-Endpoint += '192.0.1.2'
+       request:Tunnel-Server-Endpoint:1 += '192.0.1.1'
+       request:Tunnel-Server-Endpoint:2 += '192.0.2.1'
+       request:Tunnel-Server-Endpoint:2 += '192.0.2.2'
+       request:Tunnel-Server-Endpoint:3 += '192.0.3.1'
+       request:Tunnel-Server-Endpoint:3 += '192.0.3.2'
+       request:Tunnel-Server-Endpoint:3 += '192.0.3.3'
+       control: += request:
+}
+
+# Check [#] is working correctly (should probably be another set of tests)
+if (("%{request:Tunnel-Server-Endpoint[#]}" != 8) || \
+    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 2) || \
+    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
+    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 2) || \
+    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) {
+       update reply {
+               Filter-Id += 'fail 0'
+       }
+}
+
+update {
+       Tunnel-Server-Endpoint !* ANY
+}
+
+# List should now be empty
+if ("%{request:Tunnel-Server-Endpoint[#]}" != 0) {
+       update reply {
+               Filter-Id += 'fail 1'
+       }
+}
+
+# Reset the list
+update {
+       request: += control:
+}
+
+# Now remove all Tunnel-Server-Endpoint attributes with :2
+update {
+       Tunnel-Server-Endpoint:2 !* ANY
+}
+
+if (("%{request:Tunnel-Server-Endpoint[#]}" != 6) || \
+    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 2) || \
+    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
+    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) {
+       update reply {
+               Filter-Id += 'fail 2'
+       }
+}
+
+# Now remove all Tunnel-Server-Endpoint attributes with :0 (no tags)
+update {
+       Tunnel-Server-Endpoint:0 !* ANY
+}
+
+if (("%{request:Tunnel-Server-Endpoint[#]}" != 4) || \
+    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
+    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 3)) {
+       update reply {
+               Filter-Id += 'fail 3'
+       }
+}
+
+# Now remove all Tunnel-Server-Endpoint attributes with :3
+update {
+       Tunnel-Server-Endpoint:3 !* ANY
+}
+
+if (("%{request:Tunnel-Server-Endpoint[#]}" != 1) || \
+    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 1) || \
+    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 0)) {
+       update reply {
+               Filter-Id += 'fail 4'
+       }
+}
+
+# Now remove all Tunnel-Server-Endpoint attributes with :1
+update {
+       Tunnel-Server-Endpoint:1 !* ANY
+}
+
+if (("%{request:Tunnel-Server-Endpoint[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:0[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:1[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:2[#]}" != 0) || \
+    ("%{request:Tunnel-Server-Endpoint:3[#]}" != 0)) {
+       update reply {
+               Filter-Id += 'fail 5'
+       }
+}
+
+# Reset the list
+update {
+       request: += control:
+}
+
+# Remove all Tunnel-Server-Endpoint attributes at :3[0] (none)
+update {
+       Tunnel-Server-Endpoint:1[3] !* ANY
+}
+
+if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
+       update reply {
+               Filter-Id += 'fail 6'
+       }
+}
+
+if (Tunnel-Server-Endpoint:3[1] != '192.0.3.2') {
+       update reply {
+               Filter-Id += 'fail 7'
+       }
+}
+
+if (Tunnel-Server-Endpoint:3[2] != '192.0.3.3') {
+       update reply {
+               Filter-Id += 'fail 8'
+       }
+}
+
+# Remove all Tunnel-Server-Endpoint attributes at :3[1]
+update {
+       Tunnel-Server-Endpoint:3[1] !* ANY
+}
+
+if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
+       update reply {
+               Filter-Id += 'fail 9'
+       }
+}
+
+if (Tunnel-Server-Endpoint:3[1] != '192.0.3.3') {
+       update reply {
+               Filter-Id += 'fail 10'
+       }
+}
+
+# Remove any Tunnel-Server-Endpoint with a value of '192.0.1.1' (should remove both tagged and untagged versions)
+update {
+       Tunnel-Server-Endpoint -= '192.0.1.1'
+}
+
+# Also checks whether presence checks for tagged attributes work correctly
+if (request:Tunnel-Server-Endpoint:1) {
+       update reply {
+               Filter-Id += 'fail 11'
+       }
+}
+
+if (request:Tunnel-Server-Endpoint:0[0] != '192.0.1.2') {
+       update reply {
+               Filter-Id += 'fail 12'
+       }
+}
+
+# Remove any Tunnel-Server-Endpoint with a value of '192.0.3.1'
+update {
+       Tunnel-Server-Endpoint:3 -= '192.0.3.2'
+}
+
+if (request:Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
+       update reply {
+               Filter-Id += 'fail 13'
+       }
+}
+
+if (request:Tunnel-Server-Endpoint:3[1] != '192.0.3.3') {
+       update reply {
+               Filter-Id += 'fail 14'
+       }
+}
+
+# Reset the list
+update {
+       request: !* ANY
+}
+update {
+       request: += control:
+}
+
+# Remove only the tagged version of '192.0.1.1'
+update {
+       request:Tunnel-Server-Endpoint:1 -= '192.0.1.1'
+}
+
+if (request:Tunnel-Server-Endpoint:0[0] != '192.0.1.1') {
+       update reply {
+               Filter-Id += 'fail 15'
+       }
+}
+
+# Reset the list
+update {
+       request: !* ANY
+}
+update {
+       request: += control:
+}
+
+# Remove only the untagged version of '192.0.1.1'
+update {
+       request:Tunnel-Server-Endpoint:0 -= '192.0.1.1'
+}
+
+if (request:Tunnel-Server-Endpoint:1[0] != '192.0.1.1') {
+       update reply {
+               Filter-Id += 'fail 16'
+       }
+}
+
+# Remove the value of Tunnel-Server-Endpoint:3 at index 1 only if it matches '192.0.3.3' (which it does)
+update {
+       Tunnel-Server-Endpoint:3[1] -= '192.0.3.2'
+}
+
+if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
+       update reply {
+               Filter-Id += 'fail 17'
+       }
+}
+
+if (Tunnel-Server-Endpoint:3[1] != '192.0.3.3') {
+       update reply {
+               Filter-Id += 'fail 18'
+       }
+}
+
+# Reset the list
+update {
+       request: !* ANY
+}
+update {
+       request: += control:
+}
+
+# Remove the value of Tunnel-Server-Endpoint:3 at index 1 only if it matches '192.0.3.4' (which it doesn't)
+update {
+       Tunnel-Server-Endpoint:3[1] -= '192.0.3.4'
+}
+
+if (Tunnel-Server-Endpoint:3[0] != '192.0.3.1') {
+       update reply {
+               Filter-Id += 'fail 19'
+       }
+}
+
+if (Tunnel-Server-Endpoint:3[1] != '192.0.3.2') {
+       update reply {
+               Filter-Id += 'fail 20'
+       }
+}
+
+if (Tunnel-Server-Endpoint:3[2] != '192.0.3.3') {
+       update reply {
+               Filter-Id += 'fail 21'
+       }
+}
+
diff --git a/src/tests/keywords/update-remove-value b/src/tests/keywords/update-remove-value
new file mode 100644 (file)
index 0000000..3fd1f94
--- /dev/null
@@ -0,0 +1,116 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update {
+       request:Tmp-String-0 := 'foobarbaz'
+       request:Tmp-Integer-0 := 123456789
+       request:Tmp-IP-Address-0 := 192.0.2.1
+       request:Tmp-IP-Address-0 += 192.0.2.2
+       request:Tmp-IP-Address-0 += 192.0.2.3
+       request:Tmp-IP-Address-0 += 192.0.2.4
+       control:Tmp-IP-Address-0 := 192.0.2.1
+       control:Tmp-IP-Address-0 += 192.0.2.3
+}
+
+if (("%{Tmp-IP-Address-0[0]}" != 192.0.2.1) || \
+    ("%{Tmp-IP-Address-0[1]}" != 192.0.2.2) || \
+    ("%{Tmp-IP-Address-0[2]}" != 192.0.2.3) || \
+    ("%{Tmp-IP-Address-0[3]}" != 192.0.2.4)) {
+       update reply {
+               Filter-Id += 'fail 0'
+       }
+}
+
+# Remove Tmp-IP-Address-0 with a specific value
+update {
+       request:Tmp-IP-Address-0 -= 192.0.2.1
+}
+
+# Only the 2nd, 3rd and 4th Tmp-IP-Address attributes should still be in the list
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.3') || \
+    ("%{Tmp-IP-Address-0[2]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[3]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 1'
+       }
+}
+
+# Remove Tmp-IP-Address-0 with a specific value (somewhere in the middle)
+update {
+       request:Tmp-IP-Address-0 -= 192.0.2.3
+}
+
+# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[2]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 2'
+       }
+}
+
+# Remove Tmp-IP-Address-0 with a specific value (which doesn't exist)
+update {
+       request:Tmp-IP-Address-0 -= 192.0.2.3
+}
+
+# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[1]}" != '192.0.2.4') || \
+    ("%{Tmp-IP-Address-0[2]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 3'
+       }
+}
+
+# Remove Tmp-IP-Address-4 (which doesn't exist - more to check for SEGV/assert)
+update {
+       request:Tmp-IP-Address-4 -= 192.0.2.3
+}
+
+# Remove Tmp-IP-Address-0 with a specific value
+update {
+       request:Tmp-IP-Address-0 -= 192.0.2.4
+}
+
+# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list
+if (("%{Tmp-IP-Address-0[0]}" != '192.0.2.2') || \
+    ("%{Tmp-IP-Address-0[1]}" != '')) {
+       update reply {
+               Filter-Id += 'fail 4'
+       }
+}
+
+# Remove Tmp-IP-Address-0 with a specific value
+update {
+       request:Tmp-IP-Address-0 -= 192.0.2.2
+}
+
+# Only the 1st, and 3rd Tmp-IP-Address attributes should still be in the list
+if ("%{Tmp-IP-Address-0[0]}" != '') {
+       update reply {
+               Filter-Id += 'fail 5'
+       }
+}
+
+# Non Tmp-IP-Address-0 address attributes should still be in the request list
+if ((Tmp-String-0 != 'foobarbaz') || (Tmp-Integer-0 != 123456789)) {
+       update reply {
+               Filter-Id += 'fail 6'
+       }
+}
+
+# But there should still be some in the control list
+if (("%{control:Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{control:Tmp-IP-Address-0[1]}" != 192.0.2.3)) {
+       update {
+               Filter-Id += 'fail 7'
+       }
+}
diff --git a/src/tests/keywords/update-tag b/src/tests/keywords/update-tag
new file mode 100644 (file)
index 0000000..15afd59
--- /dev/null
@@ -0,0 +1,176 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update request {
+       Tunnel-Server-Endpoint:0 := '192.0.1.1' # Should not be tagged
+       Tunnel-Server-Endpoint:0 += '192.0.1.2' # Should not be tagged
+       Tunnel-Server-Endpoint:1 := '192.0.2.1'
+       Tunnel-Server-Endpoint:1 += '192.0.2.2'
+       Tunnel-Server-Endpoint:2 := '192.0.3.1'
+       Tunnel-Server-Endpoint:2 += '192.0.3.2'
+}
+
+update request {
+       Tmp-Integer-0 := "%{debug_attr:request:}"
+}
+
+
+#
+# Selecting on attributes which have no tag (0)
+#
+if (Tunnel-Server-Endpoint:0[0] != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 1'
+       }
+}
+
+if (Tunnel-Server-Endpoint:0[1] != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 2'
+       }
+}
+
+#
+# Selecting on attributes with no tag specified (should match all of that type)
+#
+if (Tunnel-Server-Endpoint[0] != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 3'
+       }
+}
+
+if (Tunnel-Server-Endpoint[1] != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 4'
+       }
+}
+
+if (Tunnel-Server-Endpoint[2] != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 5'
+       }
+}
+
+#
+# Now the none xlat version
+#
+# Check that access attributes by tag works first
+if (Tunnel-Server-Endpoint:2 != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 6'
+       }
+}
+
+if (Tunnel-Server-Endpoint:2 == '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 7'
+       }
+}
+
+if (Tunnel-Server-Endpoint:1 != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 8'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if (Tunnel-Server-Endpoint:2[0] != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 9'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if (Tunnel-Server-Endpoint:2[1] != '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 10'
+       }
+}
+
+#
+# Assignment (bare)
+#
+update request {
+       Tmp-String-1 += &Tunnel-Server-Endpoint:2               # 0
+       Tmp-String-1 += &Tunnel-Server-Endpoint:2               # 1
+       Tmp-String-1 += &Tunnel-Server-Endpoint:1               # 2
+       Tmp-String-1 += &Tunnel-Server-Endpoint:2[0]            # 3
+       Tmp-String-1 += &Tunnel-Server-Endpoint:2[1]            # 4
+       Tmp-String-1 += &Tunnel-Server-Endpoint:0[0]            # 5
+       Tmp-String-1 += &Tunnel-Server-Endpoint:0[1]            # 6
+       Tmp-String-1 += &Tunnel-Server-Endpoint:0[2]            # 7 (No attribute should be added here)
+       Tmp-String-1 += &Tunnel-Server-Endpoint[0]              # 8
+       Tmp-String-1 += &Tunnel-Server-Endpoint[1]              # 9
+       Tmp-String-1 += &Tunnel-Server-Endpoint[2]              # 10
+}
+
+# Check that access attributes by tag works first
+if (Tmp-String-1[0] != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 11'
+       }
+}
+
+if (Tmp-String-1[1] == '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 12'
+       }
+}
+
+if (Tmp-String-1[2] != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 13'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if (Tmp-String-1[3] != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 14'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if (Tmp-String-1[4] != '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 15'
+       }
+}
+
+# Now check the assignment
+if (Tmp-String-1[5] != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 16'
+       }
+}
+
+if (Tmp-String-1[6] != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 17'
+       }
+}
+
+if (Tmp-String-1[7] != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 19'
+       }
+}
+
+if (Tmp-String-1[8] != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 20'
+       }
+}
+
+if (Tmp-String-1[9] != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 21'
+       }
+}
diff --git a/src/tests/keywords/xlat-attr-index b/src/tests/keywords/xlat-attr-index
new file mode 100644 (file)
index 0000000..641c3c5
--- /dev/null
@@ -0,0 +1,67 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update request {
+       Tmp-IP-Address-0 := 192.0.2.1
+       Tmp-IP-Address-0 += 192.0.2.2
+}
+
+if ("%{Tmp-IP-Address-0[#]}" != 2) {
+       update {
+               reply:Filter-Id := 'fail'
+       }
+}
+
+if (("%{Tmp-IP-Address-0[0]}" != 192.0.2.1) || ("%{Tmp-IP-Address-0[1]}" != 192.0.2.2)) {
+       update {
+               reply:Filter-Id := 'fail'
+       }
+}
+
+if ("%{Tmp-IP-Address-0[*]}" != '192.0.2.1,192.0.2.2') {
+       update {
+               reply:Filter-Id := 'fail'
+       }
+}
+
+# Try calling these xlats in mapping too, they may get optimised to VPTs which is a
+# different code path.
+update request {
+       Tmp-IP-Address-1 += "%{Tmp-IP-Address-0[1]}"
+       Tmp-IP-Address-1 += "%{Tmp-IP-Address-0[0]}"
+       Tmp-String-0 = "%{Tmp-IP-Address-0[*]}"
+       Tmp-Integer-0 = "%{Tmp-IP-Address-0[#]}"
+}
+
+if (Tmp-IP-Address-1[0] != 192.0.2.2) {
+       update {
+               reply:Filter-Id += 'fail 3'
+       }
+}
+
+if (Tmp-IP-Address-1[1] != 192.0.2.1) {
+       update {
+               reply:Filter-Id += 'fail 4'
+       }
+}
+
+if (Tmp-String-0 != '192.0.2.1,192.0.2.2') {
+       update {
+               reply:Filter-Id += 'fail 5'
+       }
+}
+
+if (Tmp-Integer-0 != 2) {
+       update {
+               reply:Filter-Id += 'fail 6'
+       }
+}
+
+
diff --git a/src/tests/keywords/xlat-attr-tag b/src/tests/keywords/xlat-attr-tag
new file mode 100644 (file)
index 0000000..c0bd8b6
--- /dev/null
@@ -0,0 +1,225 @@
+#
+# PRE: update
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+update request {
+       Tunnel-Server-Endpoint   := '192.0.1.1' # Should not be tagged
+       Tunnel-Server-Endpoint:0 += '192.0.1.2' # Should not be tagged
+       Tunnel-Server-Endpoint:1 := '192.0.2.1'
+       Tunnel-Server-Endpoint:1 += '192.0.2.2'
+       Tunnel-Server-Endpoint:2 := '192.0.3.1'
+       Tunnel-Server-Endpoint:2 += '192.0.3.2'
+}
+
+update request {
+       Tmp-Integer-0 := "%{debug_attr:request:}"
+}
+
+# Check the tag printing xlat works correctly
+if ("%{tag:Tunnel-Server-Endpoint[0]}" != '') {
+       update {
+               reply:Filter-Id += 'fail 0a'
+       }
+}
+
+if ("%{tag:Tunnel-Server-Endpoint[1]}" != '') {
+       update {
+               reply:Filter-Id += 'fail 0b'
+       }
+}
+
+
+if ("%{tag:Tunnel-Server-Endpoint[2]}" != '1') {
+       update {
+               reply:Filter-Id += 'fail 0c'
+       }
+}
+
+if ("%{tag:Tunnel-Server-Endpoint[5]}" != '2') {
+       update {
+               reply:Filter-Id += 'fail 0d'
+       }
+}
+
+if ("%{tag:Tunnel-Server-Endpoint[6]}" != '') {
+       update {
+               reply:Filter-Id += 'fail 0e'
+       }
+}
+
+if ("%{tag:control:Cleartext-Password}" != '') {
+       update {
+               reply:Filter-Id += 'fail 0f'
+       }
+}
+
+# Check that access attributes by tag works first
+if ("%{Tunnel-Server-Endpoint:2}" != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 1'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint:2}" == '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 2'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint:1}" != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 3'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if ("%{Tunnel-Server-Endpoint:2[0]}" != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 4'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if ("%{Tunnel-Server-Endpoint:2[1]}" != '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 5'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint:0[2]}" != '') {
+       update {
+               reply:Filter-Id += 'fail 6'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint:0[0]}" != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 7'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint:0[1]}" != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 8'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint:0[2]}" != '') {
+       update {
+               reply:Filter-Id += 'fail 9'
+       }
+}
+
+#
+# Selecting on attributes with no tag specified (should match all of that type)
+#
+if ("%{Tunnel-Server-Endpoint[0]}" != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 10'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint[1]}" != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 11'
+       }
+}
+
+if ("%{Tunnel-Server-Endpoint[2]}" != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 12'
+       }
+}
+
+#
+# Assignment (xlat)
+#
+update request {
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:2}"           #0
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:2}"           #1
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:1}"           #2
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[0]}"        #3
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:2[1]}"        #4
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[0]}"        #5
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[1]}"        #6
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint:0[2]}"        #7
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint[0]}"          #8
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint[1]}"          #9
+       Tmp-String-0 += "%{Tunnel-Server-Endpoint[2]}"          #10
+}
+
+# Check that access attributes by tag works first
+if (Tmp-String-0[0] != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 13'
+       }
+}
+
+if (Tmp-String-0[1] == '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 14'
+       }
+}
+
+if (Tmp-String-0[2] != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 15'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if (Tmp-String-0[3] != '192.0.3.1') {
+       update {
+               reply:Filter-Id += 'fail 16'
+       }
+}
+
+# Get the first instance of Tunnel-Server-Endpoint:2
+if (Tmp-String-0[4] != '192.0.3.2') {
+       update {
+               reply:Filter-Id += 'fail 17'
+       }
+}
+
+# Now check the assignment
+if (Tmp-String-0[5] != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 18'
+       }
+}
+
+if (Tmp-String-0[6] != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 19'
+       }
+}
+
+if (Tmp-String-0[7] != '') {
+       update {
+               reply:Filter-Id += 'fail 20'
+       }
+}
+
+if (Tmp-String-0[8] != '192.0.1.1') {
+       update {
+               reply:Filter-Id += 'fail 21'
+       }
+}
+
+if (Tmp-String-0[9] != '192.0.1.2') {
+       update {
+               reply:Filter-Id += 'fail 22'
+       }
+}
+
+if (Tmp-String-0[10] != '192.0.2.1') {
+       update {
+               reply:Filter-Id += 'fail 23'
+       }
+}
diff --git a/src/tests/keywords/xlat-octets b/src/tests/keywords/xlat-octets
new file mode 100644 (file)
index 0000000..e6022c2
--- /dev/null
@@ -0,0 +1,36 @@
+#
+# PRE: update if
+#
+#  Remove all attributes in a list
+#
+update {
+       control:Cleartext-Password := 'hello'
+       reply:Filter-Id := 'filter'
+}
+
+#
+#  Regression test for 0x prefix. xlat expanded
+#  octet strings must NOT have a 0x prefix added
+#
+update request {
+       Tmp-Octets-0 := 0x0001020304050607
+       Tmp-Octets-0 += 0x0706050403020100
+}
+
+if ("%{Tmp-Octets-0}" != '0001020304050607') {
+       update {
+               reply:Filter-Id := 'fail 1'
+       }
+}
+
+if ("%{Tmp-Octets-0[0]}" != '0001020304050607') {
+       update {
+               reply:Filter-Id += 'fail 2'
+       }
+}
+
+if ("%{Tmp-Octets-0[*]}" != '0001020304050607,0706050403020100') {
+       update {
+               reply:Filter-Id += 'fail 3'
+       }
+}
diff --git a/src/tests/keywords/xlat-virtual-attr b/src/tests/keywords/xlat-virtual-attr
new file mode 100644 (file)
index 0000000..9da86ad
--- /dev/null
@@ -0,0 +1,131 @@
+#
+#  PRE: if
+#
+
+update reply {
+       Filter-Id := "filter"
+}
+
+if ("%{Client-Shortname}" != '<UNKNOWN-CLIENT>') {
+       update reply {
+               Filter-Id += "fail 0"
+       }
+}
+
+if ("%{Request-Processing-Stage}" != 'authorize') {
+       update reply {
+               Filter-Id += "fail 1"
+       }
+}
+
+if ("%{Virtual-Server}" != 'default') {
+       update reply {
+               Filter-Id += "fail 2"
+       }
+}
+
+if ("%{Module-Return-Code}" != '') {
+       update reply {
+               Filter-Id += "fail 3a"
+       }
+}
+
+ok
+if ("%{Module-Return-Code}" != 'ok') {
+       update reply {
+               Filter-Id += "fail 3b"
+       }
+}
+
+if ("%{Packet-Type}" != 'Access-Request') {
+       update reply {
+               Filter-Id += "fail 4"
+       }
+}
+
+# Response hasn't been set yet
+if ("%{Response-Packet-Type}" != '') {
+       update reply {
+               Filter-Id += "fail 5"
+       }
+}
+
+if ("%{Packet-Authentication-Vector}" != '00000000000000000000000000000000') {
+       update reply {
+               Filter-Id += "fail 6"
+       }
+}
+
+if ("%{Client-IP-Address}" != 127.0.0.1) {
+       update reply {
+               Filter-Id += "fail 7a"
+       }
+}
+
+if ("%{Packet-Src-IP-Address}" != 127.0.0.1) {
+       update reply {
+               Filter-Id += "fail 7b"
+       }
+}
+
+if ("%{Packet-Dst-IP-Address}" != 127.0.0.1) {
+       update reply {
+               Filter-Id += "fail 8"
+       }
+}
+
+# Can't have both...
+if ("%{Packet-Src-IPv6-Address}" != '') {
+       update reply {
+               Filter-Id += "fail 9"
+       }
+}
+
+if ("%{Packet-Dst-IPv6-Address}" != '') {
+       update reply {
+               Filter-Id += "fail 10"
+       }
+}
+
+if ("%{Packet-Src-Port}" != '18120') {
+       update reply {
+               Filter-Id += "fail 11"
+       }
+}
+
+if ("%{Packet-Dst-Port}" != '1812') {
+       update reply {
+               Filter-Id += "fail 12"
+       }
+}
+
+
+# We should allow the user to overload virtual attributes
+update request {
+       Client-Shortname := 'my_test_client'
+}
+
+if ("%{Client-Shortname}" != 'my_test_client') {
+       update reply {
+               Filter-Id += "fail 13"
+       }
+}
+
+# Operations on virtual attributes should be the same as on real ones
+if ("%{Virtual-Server[0]}" != 'default') {
+       update reply {
+               Filter-Id += "fail 14"
+       }
+}
+
+if ("%{Virtual-Server[*]}" != 'default') {
+       update reply {
+               Filter-Id += "fail 15"
+       }
+}
+
+if ("%{Virtual-Server[#]}" != 1) {
+       update reply {
+               Filter-Id += "fail 16"
+       }
+}
diff --git a/src/tests/panic.gdb b/src/tests/panic.gdb
new file mode 100644 (file)
index 0000000..3ae253a
--- /dev/null
@@ -0,0 +1,4 @@
+info locals
+info args
+thread apply all bt full
+quit
index d0ba170..d59a1d6 100644 (file)
@@ -2,12 +2,12 @@
 #   ./eapol_test -c peap-mschapv2.conf -s testing123
 #
 network={
-        ssid="example"
-        key_mgmt=WPA-EAP
-        eap=PEAP
-        identity="bob"
-        anonymous_identity="anonymous"
-        password="bob"
-        phase2="auth=MSCHAPV2"
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=PEAP
+       identity="bob"
+       anonymous_identity="anonymous"
+       password="bob"
+       phase2="auth=MSCHAPV2"
        phase1="peapver=0"
 }
index 4f32590..a22d3fa 100644 (file)
@@ -9,32 +9,35 @@
  *     This needs to be kept in lockstep with rbtree.c
  */
 
-/* red-black tree description */
-typedef enum { Black, Red } NodeColor;
+/* RED-BLACK tree description */
+typedef enum {
+       BLACK,
+       RED
+} node_colour_t;
 
 struct rbnode_t {
-       rbnode_t    *Left;          /* left child */
-       rbnode_t    *Right;         /* right child */
-       rbnode_t    *Parent;        /* parent */
-       NodeColor   Color;          /* node color (black, red) */
-       void        *Data;          /* data stored in node */
+    rbnode_t           *left;          //!< left child
+    rbnode_t           *right;         //!< right child
+    rbnode_t           *parent;        //!< Parent
+    node_colour_t      colour;         //!< Node colour (BLACK, RED)
+    void               *data;          //!< data stored in node
 };
 
 struct rbtree_t {
 #ifndef NDEBUG
-       uint32_t magic;
+       uint32_t                magic;
 #endif
-       rbnode_t *Root;
-       int     num_elements;
-       int (*Compare)(void const *, void const *);
-       int replace_flag;
-       void (*freeNode)(void *);
+       rbnode_t                *root;
+       int                     num_elements;
+       rb_comparator_t         compare;
+       rb_free_t               free;
+       bool                    replace;
 #ifdef HAVE_PTHREAD_H
-       int lock;
-       pthread_mutex_t mutex;
+       bool                    lock;
+       pthread_mutex_t         mutex;
 #endif
 };
-
+#define RBTREE_MAGIC (0x5ad09c42)
 /* Storage for the NIL pointer. */
 rbnode_t *NIL;
 
@@ -63,9 +66,9 @@ static int print_cb(UNUSED void *ctx, void *i)
 static int r = 0;
 static int rvals[MAXSIZE];
 
-static int store_cb(UNUSED void *ctx, void *i)
+static int store_cb(UNUSED void *ctx, void  *i)
 {
-       rvals[r++] = *(int *)i;
+       rvals[r++] = *(int const *)i;
        return 0;
 }
 
@@ -80,8 +83,8 @@ static int filter_cb(UNUSED void *ctx, void *i)
 }
 
 /*
- * Returns the count of black nodes from root to child leaves, or a
- * negative number indicating which red-black rule was broken.
+ * Returns the count of BLACK nodes from root to child leaves, or a
+ * negative number indicating which RED-BLACK rule was broken.
  */
 static int rbcount(rbtree_t *t)
 {
@@ -89,66 +92,66 @@ static int rbcount(rbtree_t *t)
        int count, count_expect;
 
        count_expect = -1;
-       n = t->Root;
+       n = t->root;
        if (!n || n == NIL) {
                return 0;
        }
-       if (n->Color != Black) {
-               return -2; /* Root not Black */
+       if (n->colour != BLACK) {
+               return -2; /* root not BLACK */
        }
        count = 0;
 descend:
-       while (n->Left != NIL) {
-               if (n->Color == Red) {
-                       if (n->Left->Color != Black || n->Right->Color != Black) {
-                               return -4; /* Children of Red nodes must be Black */
+       while (n->left != NIL) {
+               if (n->colour == RED) {
+                       if (n->left->colour != BLACK || n->right->colour != BLACK) {
+                               return -4; /* Children of RED nodes must be BLACK */
                        }
                }
                else {
                        count++;
                }
-               n = n->Left;
+               n = n->left;
        }
-       if (n->Right != NIL) {
-               if (n->Color == Red) {
-                       if (n->Left->Color != Black || n->Right->Color != Black) {
-                               return -4; /* Children of Red nodes must be Black */
+       if (n->right != NIL) {
+               if (n->colour == RED) {
+                       if (n->left->colour != BLACK || n->right->colour != BLACK) {
+                               return -4; /* Children of RED nodes must be BLACK */
                        }
                }
                else {
                        count++;
                }
-               n = n->Right;
+               n = n->right;
        }
-       if (n->Left != NIL || n->Right != NIL) {
+       if (n->left != NIL || n->right != NIL) {
                goto descend;
        }
        if (count_expect < 0) {
-               count_expect = count + (n->Color == Black);
+               count_expect = count + (n->colour == BLACK);
        }
        else {
-               if (count_expect != count + (n->Color == Black)) {
+               if (count_expect != count + (n->colour == BLACK)) {
                        fprintf(stderr,"Expected %i got %i\n", count_expect, count);
-                       return -5; /* All paths must traverse the same number of Black nodes. */
+                       return -5; /* All paths must traverse the same number of BLACK nodes. */
                }
        }
 ascend:
-       if (!n->Parent) return count_expect;
-       while (n->Parent->Right == n) {
-               n = n->Parent;
-               if (!n->Parent) return count_expect;
-               if (n->Color == Black) {
+       if (!n->parent) return count_expect;
+       while (n->parent->right == n) {
+               n = n->parent;
+               if (!n->parent) return count_expect;
+               if (n->colour == BLACK) {
                        count--;
                }
        }
-       if (n->Parent->Left == n) {
-               if (n->Parent->Right != NIL) {
-                       n = n->Parent->Right;
+       if (n->parent->left == n) {
+               if (n->parent->right != NIL) {
+                       n = n->parent->right;
                        goto descend;
                }
-               n = n->Parent;
-               if (!n->Parent) return count_expect;
-               if (n->Color == Black) {
+               n = n->parent;
+               if (!n->parent) return count_expect;
+               if (n->colour == BLACK) {
                        count--;
                }
        }
@@ -185,7 +188,7 @@ again:
 
        t = rbtree_create(comp, free, RBTREE_FLAG_LOCK);
        /* Find out the value of the NIL node */
-       NIL = t->Root->Left;
+       NIL = t->root->left;
 
        for (i = 0; i < n; i++) {
                int *p;
@@ -212,13 +215,13 @@ again:
 
         *
         */
-       rbtree_walk(t, DeleteOrder, filter_cb, &thresh);
+       rbtree_walk(t, RBTREE_DELETE_ORDER, filter_cb, &thresh);
        i = rbcount(t);
        fprintf(stderr,"After delete rbcount is %i.\n", i);
        if (i < 0) { return i; }
 
        r = 0;
-       rbtree_walk(t, InOrder, &store_cb, NULL);
+       rbtree_walk(t, RBTREE_IN_ORDER, &store_cb, NULL);
 
        for (j = i = 0; i < n; i++) {
                if (i && vals[i-1] == vals[i]) continue;
index 902f059..d9276b9 100755 (executable)
@@ -8,12 +8,7 @@
 rm -f verbose.log
 RCODE=0
 
-rm -rf .cache
-mkdir .cache
-
-#
-#  Bootstrap the tests
-#
+echo "Running tests:"
 for NAME in $@
 do
   TOTAL=`grep TESTS $NAME | sed 's/.*TESTS//'`
@@ -32,40 +27,22 @@ do
     echo "Test-Name = \"$BASE\"," >> .request
     echo 'Test-Number = ' $NUMBER >> .request
 
-    mv .request .cache/$BASE:$NUMBER
-  done
-done
-
-echo "Running tests..."
-
-(cd .cache;ls -1  > ../.foo)
-rm -f .bar
-for x in `cat .foo`
-do
-   echo "-f .cache/$x" >> .bar
-done
-
-$BIN_PATH/radclient `cat .bar` -xFd . 127.0.0.1:$PORT auth $SECRET > radclient.log 2>&1
-if [ "$?" != "0" ]; then
-  echo "Failed running $BIN_PATH/radclient"
-  exit 1
-fi
-
-for x in `cat .foo`
-do
-  RESULT=`egrep ^\\.cache/$x radclient.log | sed 's/.* //'`
-  if [ "$RESULT" = "2" ]; then
-      echo "$x : Success"
+    rm ./radclient.log > /dev/null 2>&1
+    $BIN_PATH/radclient -f .request -xF -D ./ 127.0.0.1:$PORT auth $SECRET 1> ./radclient.log
+    if [ "$?" = "0" ]; then
+      echo "${BASE}_${NUMBER} : Success"
     else
-      echo "$x : FAILED"
+      echo "${BASE}_${NUMBER} : FAILED"
+      cat ./radclient.log
       RCODE=1
-  fi
+    fi
+  done
 done
 
 
 if [ "$RCODE" = "0" ]
 then
-    rm -f radiusd.log radclient.log 
+    rm -f radiusd.log radclient.log
     echo "All tests succeeded"
 else
     echo "See radclient.log for more details"
similarity index 100%
rename from src/tests/gdb.conf
rename to src/tests/tests.gdb
index 01b06c1..89bc542 100644 (file)
@@ -21,7 +21,7 @@ $(BUILD_DIR)/tests/unit:
 #
 $(BUILD_DIR)/tests/unit/%: $(DIR)/% $(TESTBINDIR)/radattr | $(BUILD_DIR)/tests/unit
        @echo UNIT-TEST $(notdir $@)
-       @$(TESTBIN)/radattr -d $(top_srcdir)/share $<
+       @$(TESTBIN)/radattr -D $(top_srcdir)/share $<
        @touch $@
 
 #
index 140d5b8..b2f1aaf 100644 (file)
@@ -4,12 +4,6 @@
 #  $Id$
 #
 
-condition a == b
-data a == b
-
-condition a == b || c == d
-data a == b || c == d
-
 #
 #  A bunch of errors, in the order that the error strings
 #  appear in parser.c
@@ -32,70 +26,73 @@ data ERROR offset 11 No closing brace at end of string
 condition (|| b)
 data ERROR offset 1 Empty string is invalid
 
-condition ((a || b) foo)
-data ERROR offset 10 Unexpected text after condition
+condition ((ok || handled) foo)
+data ERROR offset 17 Unexpected text after condition
 
 # escapes in names are illegal
-condition (a\ foo || b)
-data ERROR offset 2 Unexpected escape
+condition (ok\ foo || handled)
+data ERROR offset 3 Unexpected escape
 
-condition (a FOO b)
-data ERROR offset 3 Invalid text. Expected comparison operator
+condition (ok FOO handled)
+data ERROR offset 4 Invalid text. Expected comparison operator
 
-condition (a !x b)
-data ERROR offset 3 Invalid operator
+condition (ok !x handled)
+data ERROR offset 4 Invalid operator
 
-condition (a =x b)
-data ERROR offset 3 Invalid operator
+condition (ok =x handled)
+data ERROR offset 4 Invalid operator
 
-condition (a =~ b)
-data ERROR offset 6 Expected regular expression
+condition (ok =~ handled)
+data ERROR offset 7 Expected regular expression
 
-condition (a == /b/)
-data ERROR offset 6 Unexpected regular expression
+condition (ok == /foo/)
+data ERROR offset 7 Unexpected regular expression
 
-condition (a == b"foo")
-data ERROR offset 7 Unexpected start of string
+condition (ok == handled"foo")
+data ERROR offset 14 Unexpected start of string
 
 # And now we have a bunch of VALID conditions we want to parse.
 
 # sillyness is OK
-condition ((((((a))))))
-data a
+condition ((((((ok))))))
+data ok
 
-condition (a == b)
-data a == b
+#
+#  Extra braces get squashed
+#
+condition (&User-Name == &User-Password)
+data &User-Name == &User-Password
 
-condition (!a)
-data !a
+condition (!ok)
+data !ok
 
-condition !(a)
-data !a
+condition !(ok)
+data !ok
 
-condition !!a
+condition !!ok
 data ERROR offset 1 Double negation is invalid
 
-condition !(!a)
-data a
+condition !(!ok)
+data ok
 
 #
 #  These next two are identical after normalization
 #
-condition (a == b || c == d)
-data a == b || c == d
+condition (&User-Name == &User-Password || &Filter-Id == &Reply-Message)
+data &User-Name == &User-Password || &Filter-Id == &Reply-Message
 
-condition ((a == b) || (c == d))
-data a == b || c == d
+condition ((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
+data &User-Name == &User-Password || &Filter-Id == &Reply-Message
 
-condition (!(a == b) || (c == d))
-data !a == b || c == d
+condition (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
+data !&User-Name == &User-Password || &Filter-Id == &Reply-Message
 
 #  different from the previous ones.
-condition (!((a == b) || (c == d)))
-data !(a == b || c == d)
+condition (!((&User-Name == &User-Password) || (&Filter-Id == &Reply-Message)))
+data !(&User-Name == &User-Password || &Filter-Id == &Reply-Message)
 
-condition (!(a == b) || (c == d))
-data !a == b || c == d
+condition (!(&User-Name == &User-Password) || (&Filter-Id == &Reply-Message))
+data !&User-Name == &User-Password || &Filter-Id == &Reply-Message
 
 condition ((a == b) || (c == d)))
 data ERROR offset 22 Unexpected closing brace
@@ -111,9 +108,6 @@ data handled && &Response-Packet-Type == Access-Challenge
 condition handled &&&Response-Packet-Type == Access-Challenge
 data handled && &Response-Packet-Type == Access-Challenge
 
-condition (!(!foo))
-data foo
-
 condition /foo/ =~ bar
 data ERROR offset 0 Conditional check cannot begin with a regular expression
 
@@ -144,14 +138,14 @@ data foo =~ /bar/
 #
 #  Convert != to !(COND) for normal checks
 #
-condition foo == "bar"
-data foo == "bar"
+condition &User-Name == &User-Password
+data &User-Name == &User-Password
 
-condition foo != "bar"
-data !foo == "bar"
+condition &User-Name != &User-Password
+data !&User-Name == &User-Password
 
-condition !foo != "bar"
-data foo == "bar"
+condition !&User-Name != &User-Password
+data &User-Name == &User-Password
 
 condition <ipv6addr>foo
 data ERROR offset 0 Cannot do cast for existence check
@@ -168,11 +162,14 @@ data ERROR offset 21 Cannot cast to a different data type
 condition <ipaddr>Filter-Id == <blerg>&Framed-IP-Address
 data ERROR offset 22 Invalid data type in cast
 
+#
+#  Normalize things
+#
 condition <ipaddr>Filter-Id == "127.0.0.1"
-data <ipaddr>&Filter-Id == "127.0.0.1"
+data <ipaddr>&Filter-Id == '127.0.0.1'
 
 condition <ipaddr>127.0.0.1 < &Framed-IP-Address
-data <ipaddr>127.0.0.1 < &Framed-IP-Address
+data &Framed-IP-Address > 127.0.0.1
 
 # =* and !* are only for attrs / lists
 condition "foo" !* bar
@@ -222,13 +219,13 @@ condition Session-Timeout == '10'
 data ERROR offset 19 Value must be an unquoted string
 
 # Except for dates, which can be humanly readable!
-# This one MIGHT be an expansion, so it's left as-is.
-condition Event-Timestamp == "January 1, 2012"
-data &Event-Timestamp == "January 1, 2012"
+# This one is be an expansion, so it's left as-is.
+condition Event-Timestamp == "January 1, 2012 %{blah}"
+data &Event-Timestamp == "January 1, 2012 %{blah}"
 
 # This one is NOT an expansion, so it's parsed into normal form
 condition Event-Timestamp == 'January 1, 2012'
-#data &Event-Timestamp == "Jan  1 2012 00:00:00 EST"
+#data &Event-Timestamp == 'Jan  1 2012 00:00:00 EST'
 
 # literals are parsed when the conditions are parsed
 condition <integer>X == 1
@@ -237,11 +234,24 @@ data ERROR offset 9 Failed to parse field
 condition NAS-Port == X
 data ERROR offset 12 Failed to parse value for attribute
 
+#
+#  The RHS is a static string, so this gets mashed to a literal,
+#  and then statically evaluated.
+#
 condition <ipaddr>127.0.0.1 == "127.0.0.1"
-data <ipaddr>127.0.0.1 == "127.0.0.1"
+data true
+
+condition <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
+data <ipaddr>127.0.0.1 == "%{sql: 127.0.0.1}"
 
 condition <ether> 00:11:22:33:44:55 == "00:11:22:33:44:55"
-data <ether>00:11:22:33:44:55 == "00:11:22:33:44:55"
+data true
+
+condition <ether> 00:11:22:33:44:55 == "ff:11:22:33:44:55"
+data false
+
+condition <ether> 00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
+data <ether>00:11:22:33:44:55 == "%{sql:00:11:22:33:44:55}"
 
 condition <ether> 00:XX:22:33:44:55 == 00:11:22:33:44:55
 data ERROR offset 8 Failed to parse field
@@ -274,7 +284,192 @@ condition true || (User-Name == "bob")
 data true
 
 #
-#  Both sides literals: evaluate at parse time.
+#  Both sides static data with a cast: evaluate at parse time.
 #
 condition <integer>20 < 100
 data true
+
+#
+#  Both sides literal: evaluate at parse time
+#
+condition ('foo' == 'bar')
+data false
+
+condition ('foo' < 'bar')
+data false
+
+condition ('foo' > 'bar')
+data true
+
+condition ('foo' == 'foo')
+data true
+
+#
+#  Double-quotes strings without expansions are literals
+#
+condition ("foo" == "%{sql: foo}")
+data foo == "%{sql: foo}"
+
+condition ("foo bar" == "%{sql: foo}")
+data 'foo bar' == "%{sql: foo}"
+
+condition ("foo" == "bar")
+data false
+
+condition ("foo" == 'bar')
+data false
+
+#
+#  The RHS gets parsed as a VPT_TYPE_DATA, which is
+#  a double-quoted string
+#
+condition (&User-Name == "bob")
+data &User-Name == "bob"
+
+condition (&User-Name == "%{sql: blah}")
+data &User-Name == "%{sql: blah}"
+
+condition <ipaddr>127.0.0.1 == 2130706433
+data true
+
+# /32 suffix should be trimmed for this type
+condition <ipaddr>127.0.0.1/32 == 127.0.0.1
+data true
+
+condition <ipaddr>127.0.0.1/327 == 127.0.0.1
+data ERROR offset 8 Failed to parse field
+
+condition <ipaddr>127.0.0.1/32 == 127.0.0.1
+data true
+
+condition (/foo/)
+data ERROR offset 1 Conditional check cannot begin with a regular expression
+
+#
+#  Tests for (FOO).
+#
+condition (1)
+data true
+
+condition (0)
+data false
+
+condition (true)
+data true
+
+condition (false)
+data false
+
+condition ('')
+data false
+
+condition ("")
+data false
+
+#
+#  Integers are true, as are non-zero strings
+#
+condition (4)
+data true
+
+condition ('a')
+data true
+
+condition (a)
+data ERROR offset 1 Expected a module return code
+
+#
+#  Module return codes are OK
+#
+condition (ok)
+data ok
+
+condition (handled)
+data handled
+
+condition (fail)
+data fail
+
+condition ("a")
+data true
+
+condition (`a`)
+data `a`
+
+condition (User-name)
+data &User-Name
+
+#
+#  Forbidden data types in cast
+#
+condition (<vsa>"foo" == &User-Name)
+data ERROR offset 2 Forbidden data type in cast
+
+#
+#  Must have attribute references on the LHS of a condition.
+#
+condition ("foo" == &User-Name)
+data ERROR offset 1 Cannot use attribute reference on right side of condition
+
+#
+#  If the LHS is a cast to a type, and the RHS is an attribute
+#  of the same type, then re-write it so that the attribute
+#  is on the LHS of the condition.
+#
+condition <string>"foo" == &User-Name
+data &User-Name == "foo"
+
+condition <integer>"%{expr: 1 + 1}" < &NAS-Port
+data &NAS-Port > "%{expr: 1 + 1}"
+
+condition &Filter-Id == &Framed-IP-Address
+data ERROR offset 0 Attribute comparisons must be of the same data type
+
+condition <ipaddr>127.0.0.1 == &Filter-Id
+data ERROR offset 0 Attribute comparisons must be of the same data type
+
+condition <ipaddr>&Tmp-Integer64-0 == &Foo-Stuff-Bar
+data ERROR offset 0 Unknown attribute
+
+#
+#  Casting attributes of different size
+#
+condition <ipaddr>&Tmp-Integer64-0 == &Framed-IP-Address
+data ERROR offset 0 Cannot cast to attribute of incompatible size
+
+condition <ipaddr>&PMIP6-Home-IPv4-HoA == &Framed-IP-Address
+data ERROR offset 0 Cannot cast to attribute of incompatible size
+
+# but these are allowed
+condition <ether>&Tmp-Integer64-0 == "%{module: foo}"
+data <ether>&Tmp-Integer64-0 == "%{module: foo}"
+
+condition <ipaddr>&Filter-Id == &Framed-IP-Address
+data <ipaddr>&Filter-Id == &Framed-IP-Address
+
+condition <ipaddr>&Class == &Framed-IP-Address
+data <ipaddr>&Class == &Framed-IP-Address
+
+#
+#  Tags of zero mean restrict to attributes with no tag
+#
+condition &Tunnel-Password:0 == "Hello"
+data &Tunnel-Password:0 == "Hello"
+
+condition &Tunnel-Password:1 == "Hello"
+data &Tunnel-Password:1 == "Hello"
+
+#
+#  zero offset into arrays get parsed and ignored
+#
+condition &User-Name[0] == "bob"
+data &User-Name[0] == "bob"
+
+condition &User-Name[1] == "bob"
+data &User-Name[1] == "bob"
+
+condition &Tunnel-Password:1[0] == "Hello"
+data &Tunnel-Password:1[0] == "Hello"
+
+condition &Tunnel-Password:1[3] == "Hello"
+data &Tunnel-Password:1[3] == "Hello"
index 0d3260a..21d3e52 100644 (file)
@@ -99,12 +99,42 @@ data
 
 # except for CUI.  Thank you, WiMAX!
 decode 59 02
-data Chargeable-User-Identity = ''
+data Chargeable-User-Identity = 0x
 
 # Hah! Thought you had it figured out, didn't you?
 encode -
 data 59 02
 
+attribute Framed-IP-Address = 127.0.0.1/32
+data Framed-IP-Address = 127.0.0.1
+
+attribute Framed-IP-Address = 127.0.0.1/323
+data Invalid IPv4 mask length "/323".  Should be between 0-32
+
+attribute Framed-IP-Address = 127.0.0.1/30
+data Invalid IPv4 mask length "/30".  Only "/32" permitted for non-prefix types
+
+attribute Framed-IP-Address = *
+data Framed-IP-Address = 0.0.0.0
+
+attribute Framed-IP-Address = 127
+data Framed-IP-Address = 0.0.0.127
+
+attribute Framed-IPv6-Prefix = ::1
+data Framed-IPv6-Prefix = ::1/128
+
+attribute Framed-IPv6-Prefix = ::1/200
+data Invalid IPv6 mask length "/200".  Should be between 0-128
+
+attribute Framed-IPv6-Prefix = ::1/200
+data Invalid IPv6 mask length "/200".  Should be between 0-128
+
+attribute Framed-IPv6-Prefix = 11:22:33:44:55:66:77:88/128
+data Framed-IPv6-Prefix = 11:22:33:44:55:66:77:88/128
+
+attribute Framed-IPv6-Prefix = *
+data Framed-IPv6-Prefix = ::/128
+
 $INCLUDE errors.txt
 $INCLUDE extended.txt
 $INCLUDE lucent.txt
index 74570e6..698d8f2 100644 (file)
@@ -112,3 +112,16 @@ data "Hello %S goo"
 
 xlat "%{Foreach-Variable-0}"
 data "%{Foreach-Variable-0}"
+
+#
+#  3GPP stuff, to distinguish "list:3GPP" from
+#  "attribute:tag"
+#
+xlat "%{request:3GPP-IMSI}"
+data "%{3GPP-IMSI}"
+
+xlat "%{reply:3GPP-IMSI}"
+data "%{reply:3GPP-IMSI}"
+
+xlat "%{reply:3GPP-IMSI[2]}"
+data "%{reply:3GPP-IMSI[2]}"
index c6fca9f..a89c28d 100644 (file)
@@ -1,5 +1,5 @@
 Name:         freeradius-server
-Version:      3.0.1
+Version: 3.0.3
 Release:      0
 License:      GPLv2 ; LGPLv2.1
 Group:        Productivity/Networking/Radius/Servers
@@ -26,6 +26,7 @@ BuildRequires: gdbm-devel
 BuildRequires: glibc-devel
 BuildRequires: libtalloc-devel
 BuildRequires: openldap2-devel
+BuildRequires: openssl
 BuildRequires: openssl-devel
 BuildRequires: pam-devel
 BuildRequires: perl
@@ -216,6 +217,7 @@ rm -rf $RPM_BUILD_ROOT
 %{_sysconfdir}/init.d/freeradius
 %config %{_sysconfdir}/pam.d/radiusd
 %config %{_sysconfdir}/logrotate.d/freeradius-server
+%dir %{_sysconfdir}/tmpfiles.d
 %config %{_sysconfdir}/tmpfiles.d/radiusd.conf
 %dir %attr(755,radiusd,radiusd) %{_localstatedir}/lib/radiusd
 # configs
@@ -243,13 +245,16 @@ rm -rf $RPM_BUILD_ROOT
 %attr(755,root,root) %{_libdir}/freeradius/rlm_*.so*
 
 %files utils
+%defattr(-,root,root)
 /usr/bin/*
 
 %files libs
 # RADIUS shared libs
 %attr(755,root,root) %dir %{_libdir}/freeradius
-%attr(755,root,root) %{_libdir}/freeradius/*.so*
+%attr(755,root,root) %{_libdir}/freeradius/lib*.so*
+%attr(755,root,root) %{_libdir}/freeradius/proto*.so*
 
 %files devel
 %defattr(-,root,root)
+%dir /usr/include/freeradius
 %attr(644,root,root) /usr/include/freeradius/*.h